From 71cdd06ef5fc35948c90b2028cab2a0cf299bb96 Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 12 Jun 2026 21:54:02 +0200 Subject: [PATCH] Allow screen share takeover --- kkkmeet-server.el | 16 +++++++++++++++- public/app.js | 20 ++++++++++++++++---- server.js | 14 ++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/kkkmeet-server.el b/kkkmeet-server.el index 6074f37..0f06de1 100644 --- a/kkkmeet-server.el +++ b/kkkmeet-server.el @@ -500,7 +500,21 @@ When KEEP-OPEN is nil, close PROCESS after writing." (sharing . ,(if (alist-get 'sharing payload) t :false)) (updatedAt . ,(floor (* 1000 (float-time))))))) (if (alist-get 'sharing payload) - (puthash safe-room-id event kkkmeet-server--presentation-states) + (let ((previous (gethash safe-room-id kkkmeet-server--presentation-states))) + (when (and previous + (alist-get 'peerId previous) + (not (equal (alist-get 'peerId previous) from))) + (kkkmeet-server--broadcast + safe-room-id from `((type . "presentation-state") + (from . ,(alist-get 'peerId previous)) + (peerId . ,(alist-get 'peerId previous)) + (name . ,(alist-get 'name previous)) + (sharing . :false) + (reason . "takeover") + (replacedBy . ,from) + (replacedByName . ,(alist-get 'name event)) + (updatedAt . ,(floor (* 1000 (float-time))))))) + (puthash safe-room-id event kkkmeet-server--presentation-states)) (when (equal (alist-get 'peerId (gethash safe-room-id kkkmeet-server--presentation-states)) from) (remhash safe-room-id kkkmeet-server--presentation-states))) diff --git a/public/app.js b/public/app.js index 13030d2..84ec8f1 100644 --- a/public/app.js +++ b/public/app.js @@ -1337,10 +1337,20 @@ async function handleSignal(message) { } if (message.type === "presentation-state") { + if ( + message.reason === "takeover" && + message.from === peerId && + !message.sharing && + screenStream + ) { + await stopScreenShare({ announce: false }); + } setRemotePresentation(message.from, message.sharing, message.name); setActivity( message.name, - message.sharing ? "started sharing their screen." : "stopped sharing their screen." + message.reason === "takeover" + ? `was replaced by ${participantDisplayName(message.replacedByName)} as presenter.` + : message.sharing ? "started sharing their screen." : "stopped sharing their screen." ); return; } @@ -1576,7 +1586,7 @@ async function startScreenShare() { } } -async function stopScreenShare() { +async function stopScreenShare({ announce = true } = {}) { if (!screenStream || stoppingScreenShare) return; stoppingScreenShare = true; @@ -1590,8 +1600,10 @@ async function stopScreenShare() { await replaceOutboundVideoTrack(cameraTrack); } shareButton.textContent = "Share Screen"; - await sendRoomEvent({ type: "presentation-state", sharing: false }); - setActivity(displayName, "stopped sharing their screen."); + if (announce) { + await sendRoomEvent({ type: "presentation-state", sharing: false }); + setActivity(displayName, "stopped sharing their screen."); + } } finally { streamToStop.getTracks().forEach((track) => { track.onended = null; diff --git a/server.js b/server.js index b2fe0bd..b1b14e1 100644 --- a/server.js +++ b/server.js @@ -327,6 +327,20 @@ const server = http.createServer(async (req, res) => { }; if (event.sharing) { + const previous = presentationStates.get(roomId); + if (previous?.peerId && previous.peerId !== payload.from) { + broadcast(roomId, payload.from, { + type: "presentation-state", + from: previous.peerId, + peerId: previous.peerId, + name: previous.name, + sharing: false, + reason: "takeover", + replacedBy: payload.from, + replacedByName: event.name, + updatedAt: Date.now() + }); + } presentationStates.set(roomId, { ...event, peerId: payload.from }); } else if (presentationStates.get(roomId)?.peerId === payload.from) { presentationStates.delete(roomId);