diff --git a/src/main.js b/src/main.js
index dc74c89c..6f26df6a 100644
--- a/src/main.js
+++ b/src/main.js
@@ -92,11 +92,21 @@ ipcMain.handle('app:getDesktopCapturerSources', async () => {
return null
}
- const sources = await desktopCapturer.getSources({ types: ['window', 'screen'], fetchWindowIcons: true })
+ const sources = await desktopCapturer.getSources({
+ types: ['screen', 'window'],
+ fetchWindowIcons: true,
+ thumbnailSize: {
+ // 16:9 aspect ratio
+ width: 320,
+ height: 180,
+ },
+ })
+
return sources.map((source) => ({
id: source.id,
name: source.name,
- icon: source.appIcon?.toDataURL(),
+ icon: source.appIcon && !source.appIcon.isEmpty() ? source.appIcon.toDataURL() : null,
+ thumbnail: source.thumbnail && !source.thumbnail.isEmpty() ? source.thumbnail.toDataURL() : null,
}))
})
diff --git a/src/shared/os.utils.js b/src/shared/os.utils.js
index 2cf17019..7d50a16c 100644
--- a/src/shared/os.utils.js
+++ b/src/shared/os.utils.js
@@ -76,11 +76,21 @@ function isWindows() {
return os.type() === 'Windows_NT'
}
+/**
+ * Is it Linux with Wayland window communication protocol?
+ * @return {boolean}
+ */
+function isWayland() {
+ // TODO: is it better than checking for XDG_SESSION_TYPE === 'wayland'?
+ return !!process.env.WAYLAND_DISPLAY
+}
+
/**
* @typedef OsVersion
* @property {boolean} isLinux - Is Linux?
* @property {boolean} isMac - Is Mac?
* @property {boolean} isWindows - Is Windows?
+ * @property {boolean} isWayland - Is Linux with Wayland window communication protocol?
* @property {string} version - Full string representation of OS version
*/
@@ -94,6 +104,7 @@ function getOs() {
isLinux: isLinux(),
isMac: isMac(),
isWindows: isWindows(),
+ isWayland: isWayland(),
version: getOsVersion(),
}
}
@@ -104,5 +115,6 @@ module.exports = {
isLinux,
isMac,
isWindows,
+ isWayland,
getOs,
}
diff --git a/src/talk/renderer/components/DesktopMediaSourceDialog.vue b/src/talk/renderer/components/DesktopMediaSourceDialog.vue
index 3f376660..5e55245d 100644
--- a/src/talk/renderer/components/DesktopMediaSourceDialog.vue
+++ b/src/talk/renderer/components/DesktopMediaSourceDialog.vue
@@ -24,6 +24,7 @@ import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import MdiCancel from '@mdi/svg/svg/cancel.svg?raw'
import MdiMonitorShare from '@mdi/svg/svg/monitor-share.svg?raw'
+import MdiApplicationOutline from 'vue-material-design-icons/ApplicationOutline.vue'
import MdiMonitor from 'vue-material-design-icons/Monitor.vue'
import MdiMonitorSpeaker from 'vue-material-design-icons/MonitorSpeaker.vue'
@@ -35,6 +36,11 @@ import { translate as t } from '@nextcloud/l10n'
const emit = defineEmits(['submit', 'cancel'])
+// On Wayland getting each stream for the live preview requests user to select the source via system dialog again
+// Instead - show static images.
+// See: https://github.com/electron/electron/issues/27732
+const previewType = window.OS.isWayland ? 'thumbnail' : 'live'
+
const selectedSourceId = ref(null)
const sources = ref(null)
const videoElements = {}
@@ -100,13 +106,23 @@ const requestDesktopCapturerSources = async () => {
emit('cancel')
}
- const hasMultipleScreens = sources.value.filter((source) => source.id.startsWith('screen:')).length > 1
+ // On Wayland we don't manually provide the source stream. It is covered by Wayland and custom id is not supported
+ if (!window.OS.isWayland) {
+ // There is no sourceId for the entire desktop with all the screens and audio in Electron.
+ // But it is possible to capture it. "entire-desktop:0:0" is a custom sourceId for this specific case.
+ const hasMultipleScreens = sources.value.filter((source) => source.id.startsWith('screen:')).length > 1
+ sources.value.unshift({
+ id: 'entire-desktop:0:0',
+ name: hasMultipleScreens ? t('talk_desktop', 'All screens with audio') : t('talk_desktop', 'Entire screen with audio'),
+ })
+ }
- // There is no sourceId for the entire desktop in Electron - create a custom one
- sources.value.unshift({
- id: 'entire-desktop:0:0',
- name: hasMultipleScreens ? t('talk_desktop', 'All screens with audio') : t('talk_desktop', 'Entire screen with audio'),
- })
+ // On Wayland there might be no name from the desktopCapturer
+ if (window.OS.isWayland) {
+ for (const source of sources.value) {
+ source.name ||= t('talk_desktop', 'Selected screen or window')
+ }
+ }
// Preselect the first media source if any
selectedSourceId.value = sources.value[0]?.id
@@ -116,16 +132,23 @@ const setVideoSources = async () => Promise.allSettled(sources.value.map(async (
videoElements[source.id].srcObject = await getStreamForMediaSource(source.id)
}))
-onMounted(async () => {
- await requestDesktopCapturerSources()
+const showLivePreviews = async () => {
// Wait for video elements to be mounted
await nextTick()
// Set streams for all ids
await setVideoSources()
+}
+
+onMounted(async () => {
+ await requestDesktopCapturerSources()
+
+ if (previewType === 'live') {
+ await showLivePreviews()
+ }
})
onBeforeUnmount(() => {
- if (!sources.value) {
+ if (!sources.value || previewType !== 'live') {
return
}
// Release all streams, otherwise they are still captured even if no video element is using them
@@ -150,10 +173,17 @@ onBeforeUnmount(() => {
class="capture-source__input"
type="radio"
:value="source.id">
-
+
+
+
{
class="capture-source__caption-icon">
+
{{ source.name }}