mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-21 18:54:01 -04:00
notifications: auto-enable do not disturb while screen sharing (#3316)
Turn on do not disturb while a screen share is active so notification popups and their contents stay off the shared screen. The unread dot and the existing dnd bypasses are unaffected. Detection reuses the screen sharing privacy indicator: niri cast ipc, otherwise pipewire and xdg-desktop-portal. Neither source says when it has finished reporting, so a share reading as gone is only acted on once it stays gone for four seconds. Engaging stays immediate, and turning the setting off releases right away since that is a user action rather than a detection guess. Turning dnd off by hand during a share is remembered against the sources that were sharing at the time, so it holds for that share and across a shell reload. A share started after that one has ended gets a fresh decision. Co-authored-by: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -36,6 +36,9 @@ Singleton {
|
||||
property bool isLightMode: false
|
||||
property bool doNotDisturb: false
|
||||
property real doNotDisturbUntil: 0
|
||||
property bool doNotDisturbHeldByScreenShare: false
|
||||
property bool screenShareDndDismissed: false
|
||||
property var screenShareDismissedIds: []
|
||||
property bool idleInhibited: false
|
||||
property real idleInhibitedUntil: 0
|
||||
property string terminalOverride: ""
|
||||
@@ -72,7 +75,15 @@ Singleton {
|
||||
id: dndExpireTimer
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: root.setDoNotDisturb(false)
|
||||
onTriggered: root.setDoNotDisturb(false, 0, "timer")
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: screenShareReleaseTimer
|
||||
interval: 4000
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: root._endScreenShareDnd()
|
||||
}
|
||||
|
||||
function _armDndExpireTimer() {
|
||||
@@ -81,7 +92,7 @@ Singleton {
|
||||
return;
|
||||
const remaining = doNotDisturbUntil - Date.now();
|
||||
if (remaining <= 0) {
|
||||
setDoNotDisturb(false);
|
||||
setDoNotDisturb(false, 0, "timer");
|
||||
return;
|
||||
}
|
||||
dndExpireTimer.interval = remaining;
|
||||
@@ -109,6 +120,21 @@ Singleton {
|
||||
root.suppressOSD = true;
|
||||
osdSuppressTimer.restart();
|
||||
root._applyDndExpirySanity();
|
||||
Qt.callLater(root._syncScreenShareDnd);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: (!root.isGreeterMode && typeof SettingsData !== "undefined" && SettingsData.notificationDndWhileScreenSharing && typeof PrivacyService !== "undefined") ? PrivacyService : null
|
||||
function onScreensharingActiveChanged() {
|
||||
root._syncScreenShareDnd();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: typeof SettingsData !== "undefined" ? SettingsData : null
|
||||
function onNotificationDndWhileScreenSharingChanged() {
|
||||
root._syncScreenShareDnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,6 +337,7 @@ Singleton {
|
||||
Theme.generateSystemThemesFromCurrentTheme();
|
||||
|
||||
loaded();
|
||||
Qt.callLater(root._syncScreenShareDnd);
|
||||
|
||||
_checkSessionWritable();
|
||||
} catch (e) {
|
||||
@@ -393,6 +420,7 @@ Singleton {
|
||||
Theme.generateSystemThemesFromCurrentTheme();
|
||||
|
||||
loaded();
|
||||
Qt.callLater(root._syncScreenShareDnd);
|
||||
} catch (e) {
|
||||
_parseError = true;
|
||||
const msg = e.message;
|
||||
@@ -645,11 +673,24 @@ Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
function setDoNotDisturb(enabled, durationMinutes) {
|
||||
function setDoNotDisturb(enabled, durationMinutes, origin) {
|
||||
const src = origin || "user";
|
||||
if (src === "user") {
|
||||
if (!enabled && _screenShareShouldHold()) {
|
||||
screenShareDndDismissed = true;
|
||||
screenShareDismissedIds = _screencastIds();
|
||||
}
|
||||
if (doNotDisturbHeldByScreenShare)
|
||||
doNotDisturbHeldByScreenShare = false;
|
||||
}
|
||||
|
||||
const minutes = Number(durationMinutes) || 0;
|
||||
doNotDisturb = enabled;
|
||||
doNotDisturbUntil = (enabled && minutes > 0) ? Date.now() + minutes * 60 * 1000 : 0;
|
||||
saveSettings();
|
||||
|
||||
if (src !== "screenshare")
|
||||
Qt.callLater(_syncScreenShareDnd);
|
||||
}
|
||||
|
||||
function setIdleInhibited(enabled, durationMinutes) {
|
||||
@@ -669,11 +710,92 @@ Singleton {
|
||||
setDoNotDisturb(false);
|
||||
return;
|
||||
}
|
||||
if (doNotDisturbHeldByScreenShare)
|
||||
doNotDisturbHeldByScreenShare = false;
|
||||
doNotDisturb = true;
|
||||
doNotDisturbUntil = target;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function _screenShareShouldHold() {
|
||||
if (isGreeterMode)
|
||||
return false;
|
||||
if (typeof SettingsData === "undefined" || !SettingsData.notificationDndWhileScreenSharing)
|
||||
return false;
|
||||
if (typeof PrivacyService === "undefined" || !PrivacyService.screensharingActive)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function _screencastIds() {
|
||||
return (typeof PrivacyService !== "undefined") ? PrivacyService.screencastSourceIds() : [];
|
||||
}
|
||||
|
||||
function _dismissalApplies() {
|
||||
if (!screenShareDndDismissed)
|
||||
return false;
|
||||
const dismissed = screenShareDismissedIds || [];
|
||||
if (!dismissed.length)
|
||||
return true;
|
||||
const current = _screencastIds();
|
||||
if (!current.length)
|
||||
return true;
|
||||
return current.some(id => dismissed.indexOf(id) !== -1);
|
||||
}
|
||||
|
||||
function _releaseScreenShareDndHold() {
|
||||
if (!doNotDisturbHeldByScreenShare)
|
||||
return;
|
||||
doNotDisturbHeldByScreenShare = false;
|
||||
if (!doNotDisturb || doNotDisturbUntil > 0) {
|
||||
saveSettings();
|
||||
return;
|
||||
}
|
||||
setDoNotDisturb(false, 0, "screenshare");
|
||||
}
|
||||
|
||||
function _endScreenShareDnd() {
|
||||
_clearScreenShareDismissal();
|
||||
_releaseScreenShareDndHold();
|
||||
}
|
||||
|
||||
function _clearScreenShareDismissal() {
|
||||
if (!screenShareDndDismissed)
|
||||
return;
|
||||
screenShareDndDismissed = false;
|
||||
screenShareDismissedIds = [];
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function _syncScreenShareDnd() {
|
||||
if (isGreeterMode)
|
||||
return;
|
||||
|
||||
if (!_screenShareShouldHold()) {
|
||||
if (!doNotDisturbHeldByScreenShare && !screenShareDndDismissed)
|
||||
return;
|
||||
const settingOn = typeof SettingsData !== "undefined" && SettingsData.notificationDndWhileScreenSharing;
|
||||
if (!settingOn) {
|
||||
screenShareReleaseTimer.stop();
|
||||
_endScreenShareDnd();
|
||||
return;
|
||||
}
|
||||
screenShareReleaseTimer.restart();
|
||||
return;
|
||||
}
|
||||
|
||||
screenShareReleaseTimer.stop();
|
||||
|
||||
if (!_dismissalApplies())
|
||||
_clearScreenShareDismissal();
|
||||
|
||||
if (screenShareDndDismissed || doNotDisturb)
|
||||
return;
|
||||
|
||||
doNotDisturbHeldByScreenShare = true;
|
||||
setDoNotDisturb(true, 0, "screenshare");
|
||||
}
|
||||
|
||||
function setWallpaper(imagePath) {
|
||||
wallpaperPath = imagePath;
|
||||
if (perModeWallpaper) {
|
||||
|
||||
@@ -988,6 +988,7 @@ Singleton {
|
||||
property bool notificationHistorySaveCritical: true
|
||||
property var notificationRules: []
|
||||
property bool notificationDndAllowCritical: true
|
||||
property bool notificationDndWhileScreenSharing: true
|
||||
property bool notificationFocusedMonitor: false
|
||||
// Island is a per-instance render mode: any bar config with island:true draws as an island
|
||||
// instead of a DankBar, and carries its own island* look settings.
|
||||
|
||||
@@ -4,6 +4,9 @@ var SPEC = {
|
||||
isLightMode: { def: false },
|
||||
doNotDisturb: { def: false },
|
||||
doNotDisturbUntil: { def: 0 },
|
||||
doNotDisturbHeldByScreenShare: { def: false },
|
||||
screenShareDndDismissed: { def: false },
|
||||
screenShareDismissedIds: { def: [] },
|
||||
idleInhibited: { def: false },
|
||||
idleInhibitedUntil: { def: 0 },
|
||||
terminalOverride: { def: "" },
|
||||
|
||||
@@ -533,6 +533,7 @@ var SPEC = {
|
||||
notificationHistorySaveCritical: { def: true },
|
||||
notificationRules: { def: [] },
|
||||
notificationDndAllowCritical: { def: true },
|
||||
notificationDndWhileScreenSharing: { def: true },
|
||||
notificationFocusedMonitor: { def: false },
|
||||
|
||||
|
||||
|
||||
@@ -429,6 +429,15 @@ Item {
|
||||
onToggled: checked => SessionData.setDoNotDisturb(checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "notificationDndWhileScreenSharing"
|
||||
tags: ["notification", "dnd", "screenshare", "sharing", "privacy"]
|
||||
text: I18n.tr("Automatically enable while screen sharing", "do not disturb auto enable while screen sharing toggle")
|
||||
description: I18n.tr("Turn on Do Not Disturb while a screen share is active", "do not disturb auto enable while screen sharing description")
|
||||
checked: SettingsData.notificationDndWhileScreenSharing
|
||||
onToggled: checked => SettingsData.set("notificationDndWhileScreenSharing", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "notificationDndAllowCritical"
|
||||
tags: ["notification", "dnd", "critical", "priority", "urgent", "bypass"]
|
||||
|
||||
@@ -126,6 +126,37 @@ Singleton {
|
||||
return /xdg-desktop-portal|xdpw|screencast|screen-cast|screen|gnome shell|kwin|obs|niri/.test(combined)
|
||||
}
|
||||
|
||||
function screencastSourceIds() {
|
||||
const ids = []
|
||||
|
||||
if (CompositorService.isNiri) {
|
||||
for (const cast of NiriService.casts) {
|
||||
if (cast && cast.is_active) {
|
||||
ids.push("niri:" + cast.stream_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Pipewire.ready || !Pipewire.nodes?.values) {
|
||||
return ids
|
||||
}
|
||||
|
||||
for (let i = 0; i < Pipewire.nodes.values.length; i++) {
|
||||
const node = Pipewire.nodes.values[i]
|
||||
if (!node || !node.ready) {
|
||||
continue
|
||||
}
|
||||
|
||||
const isVideoSource = (node.type & PwNodeType.VideoSource) === PwNodeType.VideoSource
|
||||
const isVideoStream = node.properties && node.properties["media.class"] === "Stream/Output/Video"
|
||||
if ((isVideoSource || isVideoStream) && looksLikeScreencast(node)) {
|
||||
ids.push("pw:" + node.id)
|
||||
}
|
||||
}
|
||||
|
||||
return ids.sort()
|
||||
}
|
||||
|
||||
function getMicrophoneStatus() {
|
||||
return microphoneActive ? "active" : "inactive"
|
||||
}
|
||||
|
||||
@@ -7128,6 +7128,32 @@
|
||||
],
|
||||
"description": "Control animation duration for notification popups and history"
|
||||
},
|
||||
{
|
||||
"section": "notificationDndWhileScreenSharing",
|
||||
"label": "Automatically enable while screen sharing",
|
||||
"tabIndex": 17,
|
||||
"category": "Notifications",
|
||||
"keywords": [
|
||||
"active",
|
||||
"alerts",
|
||||
"automatically",
|
||||
"disturb",
|
||||
"dnd",
|
||||
"enable",
|
||||
"messages",
|
||||
"notification",
|
||||
"notifications",
|
||||
"privacy",
|
||||
"screen",
|
||||
"screenshare",
|
||||
"share",
|
||||
"sharing",
|
||||
"toast",
|
||||
"turn",
|
||||
"while"
|
||||
],
|
||||
"description": "Turn on Do Not Disturb while a screen share is active"
|
||||
},
|
||||
{
|
||||
"section": "notificationPopupBodyInvokesAction",
|
||||
"label": "Body Click Runs Default Action",
|
||||
|
||||
Reference in New Issue
Block a user