1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00

hyprland: use raw events to determine window position updates

This commit is contained in:
bbedward
2025-11-09 12:11:22 -05:00
parent 392a1c03c5
commit caa085a646
6 changed files with 61 additions and 136 deletions

View File

@@ -21,21 +21,27 @@ Item {
property real barThickness: 48 property real barThickness: 48
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
property Item windowRoot: (Window.window ? Window.window.contentItem : null) property Item windowRoot: (Window.window ? Window.window.contentItem : null)
property int _workspaceUpdateTrigger: 0
property int _desktopEntriesUpdateTrigger: 0 property int _desktopEntriesUpdateTrigger: 0
property int _toplevelsUpdateTrigger: 0
readonly property var sortedToplevels: { readonly property var sortedToplevels: {
_workspaceUpdateTrigger _toplevelsUpdateTrigger
const toplevels = CompositorService.sortedToplevels const toplevels = CompositorService.sortedToplevels
if (!toplevels || toplevels.length === 0) if (!toplevels || toplevels.length === 0) return []
return []
if (SettingsData.runningAppsCurrentWorkspace) { if (SettingsData.runningAppsCurrentWorkspace) {
const filtered = CompositorService.filterCurrentWorkspace(toplevels, parentScreen?.name) return CompositorService.filterCurrentWorkspace(toplevels, parentScreen?.name) || []
return filtered || []
} }
return toplevels return toplevels
} }
Connections {
target: CompositorService
function onToplevelsChanged() {
_toplevelsUpdateTrigger++
}
}
Connections { Connections {
target: DesktopEntries target: DesktopEntries
function onApplicationsChanged() { function onApplicationsChanged() {
@@ -89,36 +95,6 @@ Item {
height: isVertical ? calculatedSize : barThickness height: isVertical ? calculatedSize : barThickness
visible: windowCount > 0 visible: windowCount > 0
Connections {
target: NiriService
function onAllWorkspacesChanged() {
_workspaceUpdateTrigger++
}
function onWindowsChanged() {
_workspaceUpdateTrigger++
}
}
Connections {
target: Hyprland
function onFocusedWorkspaceChanged() {
_workspaceUpdateTrigger++
}
}
Connections {
target: Hyprland.workspaces
function onValuesChanged() {
_workspaceUpdateTrigger++
}
}
Connections {
target: Hyprland.toplevels
function onValuesChanged() {
_workspaceUpdateTrigger++
}
}
Rectangle { Rectangle {
id: visualBackground id: visualBackground
@@ -241,7 +217,10 @@ Item {
Repeater { Repeater {
id: windowRepeater id: windowRepeater
model: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels model: ScriptModel {
values: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels
objectProp: SettingsData.runningAppsGroupByApp ? "appId" : "address"
}
delegate: Item { delegate: Item {
id: delegateItem id: delegateItem
@@ -470,7 +449,10 @@ Item {
Repeater { Repeater {
id: windowRepeater id: windowRepeater
model: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels model: ScriptModel {
values: SettingsData.runningAppsGroupByApp ? groupedWindows : sortedToplevels
objectProp: SettingsData.runningAppsGroupByApp ? "appId" : "address"
}
delegate: Item { delegate: Item {
id: delegateItem id: delegateItem

View File

@@ -238,7 +238,7 @@ Item {
Connections { Connections {
target: CompositorService target: CompositorService
function onSortedToplevelsChanged() { function onToplevelsChanged() {
dockModel.updateModel() dockModel.updateModel()
} }
} }

View File

@@ -21,17 +21,10 @@ Singleton {
readonly property string swaySocket: Quickshell.env("SWAYSOCK") readonly property string swaySocket: Quickshell.env("SWAYSOCK")
property bool useNiriSorting: isNiri && NiriService property bool useNiriSorting: isNiri && NiriService
property var sortedToplevels: sortedToplevelsCache property var sortedToplevels: []
property var sortedToplevelsCache: []
property bool _sortScheduled: false property bool _sortScheduled: false
property bool _refreshScheduled: false
property bool _hasRefreshedOnce: false
property var _coordCache: ({}) signal toplevelsChanged()
property int _refreshCount: 0
property real _refreshWindowStart: 0
readonly property int _maxRefreshesPerSecond: 3
function getScreenScale(screen) { function getScreenScale(screen) {
if (!screen) return 1 if (!screen) return 1
@@ -59,46 +52,27 @@ Singleton {
} }
Timer { Timer {
id: refreshTimer id: sortDebounceTimer
interval: 40 interval: 100
repeat: false repeat: false
onTriggered: { onTriggered: {
try { _sortScheduled = false
Hyprland.refreshToplevels() if (isHyprland) {
} catch(e) {} try {
_refreshScheduled = false Hyprland.refreshToplevels()
_hasRefreshedOnce = true } catch(e) {
scheduleSort() console.warn("CompositorService: Failed to refresh toplevels:", e)
}
}
sortedToplevels = computeSortedToplevels()
toplevelsChanged()
} }
} }
function scheduleSort() { function scheduleSort() {
if (_sortScheduled) return if (_sortScheduled) return
_sortScheduled = true _sortScheduled = true
Qt.callLater(function() { sortDebounceTimer.restart()
_sortScheduled = false
sortedToplevelsCache = computeSortedToplevels()
})
}
function scheduleRefresh() {
if (!isHyprland) return
if (_refreshScheduled) return
const now = Date.now()
if (now - _refreshWindowStart > 1000) {
_refreshCount = 0
_refreshWindowStart = now
}
if (_refreshCount >= _maxRefreshesPerSecond) {
console.warn("CompositorService: Refresh rate limit exceeded, skipping refresh")
return
}
_refreshCount++
_refreshScheduled = true
refreshTimer.restart()
} }
Connections { Connections {
@@ -106,19 +80,28 @@ Singleton {
function onValuesChanged() { root.scheduleSort() } function onValuesChanged() { root.scheduleSort() }
} }
Connections { Connections {
target: Hyprland.toplevels target: isHyprland ? Hyprland : null
function onValuesChanged() { enabled: isHyprland
root.scheduleSort()
function onRawEvent(event) {
if (event.name === "openwindow" ||
event.name === "closewindow" ||
event.name === "movewindow" ||
event.name === "movewindowv2" ||
event.name === "workspace" ||
event.name === "workspacev2" ||
event.name === "focusedmon" ||
event.name === "focusedmonv2" ||
event.name === "activewindow" ||
event.name === "activewindowv2" ||
event.name === "changefloatingmode" ||
event.name === "fullscreen" ||
event.name === "moveintogroup" ||
event.name === "moveoutofgroup") {
root.scheduleSort()
}
} }
} }
Connections {
target: Hyprland.workspaces
function onValuesChanged() { root.scheduleSort() }
}
Connections {
target: Hyprland
function onFocusedWorkspaceChanged() { root.scheduleSort() }
}
Connections { Connections {
target: NiriService target: NiriService
function onWindowsChanged() { root.scheduleSort() } function onWindowsChanged() { root.scheduleSort() }
@@ -165,7 +148,6 @@ Singleton {
function sortHyprlandToplevelsSafe() { function sortHyprlandToplevelsSafe() {
if (!Hyprland.toplevels || !Hyprland.toplevels.values) return [] if (!Hyprland.toplevels || !Hyprland.toplevels.values) return []
if (_refreshScheduled && sortedToplevelsCache.length > 0) return sortedToplevelsCache
const items = Array.from(Hyprland.toplevels.values) const items = Array.from(Hyprland.toplevels.values)
@@ -177,21 +159,7 @@ Singleton {
} catch(e) { return fb } } catch(e) { return fb }
} }
let currentAddresses = new Set()
for (let i = 0; i < items.length; i++) {
const addr = items[i]?.address
if (addr) currentAddresses.add(addr)
}
for (let cachedAddr in _coordCache) {
if (!currentAddresses.has(cachedAddr)) {
delete _coordCache[cachedAddr]
}
}
let snap = [] let snap = []
let missingAnyPosition = false
let hasNewWindow = false
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
const t = items[i] const t = items[i]
if (!t) continue if (!t) continue
@@ -208,23 +176,8 @@ Singleton {
const wsId = _get(li, ["workspace", "id"], null) ?? _get(t, ["workspace", "id"], Number.MAX_SAFE_INTEGER) const wsId = _get(li, ["workspace", "id"], null) ?? _get(t, ["workspace", "id"], Number.MAX_SAFE_INTEGER)
const at = _get(li, ["at"], null) const at = _get(li, ["at"], null)
let atX = (at !== null && at !== undefined && typeof at[0] === "number") ? at[0] : NaN let atX = (at !== null && at !== undefined && typeof at[0] === "number") ? at[0] : 1e9
let atY = (at !== null && at !== undefined && typeof at[1] === "number") ? at[1] : NaN let atY = (at !== null && at !== undefined && typeof at[1] === "number") ? at[1] : 1e9
if (!(atX === atX) || !(atY === atY)) {
const cached = _coordCache[addr]
if (cached) {
atX = cached.x
atY = cached.y
} else {
if (addr) hasNewWindow = true
missingAnyPosition = true
atX = 1e9
atY = 1e9
}
} else if (addr) {
_coordCache[addr] = { x: atX, y: atY }
}
const relX = Number.isFinite(monX) ? (atX - monX) : atX const relX = Number.isFinite(monX) ? (atX - monX) : atX
const relY = Number.isFinite(monY) ? (atY - monY) : atY const relY = Number.isFinite(monY) ? (atY - monY) : atY
@@ -242,10 +195,6 @@ Singleton {
}) })
} }
if (missingAnyPosition && hasNewWindow && !_hasRefreshedOnce) {
scheduleRefresh()
}
const groups = new Map() const groups = new Map()
for (const it of snap) { for (const it of snap) {
const key = it.monKey + "::" + it.wsId const key = it.monKey + "::" + it.wsId
@@ -400,9 +349,6 @@ Singleton {
isSway = false isSway = false
compositor = "hyprland" compositor = "hyprland"
console.info("CompositorService: Detected Hyprland") console.info("CompositorService: Detected Hyprland")
try {
Hyprland.refreshToplevels()
} catch(e) {}
return return
} }

View File

@@ -32,11 +32,9 @@ Singleton {
} }
for (const appId of appIds){ for (const appId of appIds){
let icon = Quickshell.iconPath(entry?.icon, true) let icon = Quickshell.iconPath(entry?.icon, true)
console.log(icon)
if (icon && icon !== "") return icon if (icon && icon !== "") return icon
let execPath = entry?.execString?.replace(/\/bin.*/, "") let execPath = entry?.execString?.replace(/\/bin.*/, "")
console.log(execPath)
if (!execPath) continue if (!execPath) continue
//Check that the app is installed with nix/guix //Check that the app is installed with nix/guix
@@ -46,13 +44,11 @@ Singleton {
let iconPath = `${basePath}/share/icons/hicolor/scalable/apps/${appId}.svg` let iconPath = `${basePath}/share/icons/hicolor/scalable/apps/${appId}.svg`
icon = Quickshell.iconPath(iconPath, true) icon = Quickshell.iconPath(iconPath, true)
console.log(icon)
if (icon && icon !== "") return icon if (icon && icon !== "") return icon
for (const size of sizes) { for (const size of sizes) {
iconPath = `${basePath}/share/icons/hicolor/${size}/apps/${appId}.png` iconPath = `${basePath}/share/icons/hicolor/${size}/apps/${appId}.png`
icon = Quickshell.iconPath(iconPath, true) icon = Quickshell.iconPath(iconPath, true)
console.log(icon)
if (icon && icon !== "") return icon if (icon && icon !== "") return icon
} }
} }

View File

@@ -118,12 +118,12 @@ Singleton {
import Quickshell.Wayland import Quickshell.Wayland
IdleInhibitor { IdleInhibitor {
active: false enabled: false
} }
` `
mediaInhibitor = Qt.createQmlObject(inhibitorString, root, "IdleService.MediaInhibitor") mediaInhibitor = Qt.createQmlObject(inhibitorString, root, "IdleService.MediaInhibitor")
mediaInhibitor.active = Qt.binding(() => root.idleInhibitorAvailable && SettingsData.preventIdleForMedia && root.mediaPlaying) mediaInhibitor.enabled = Qt.binding(() => root.idleInhibitorAvailable && SettingsData.preventIdleForMedia && root.mediaPlaying)
} }
} catch (e) { } catch (e) {
console.warn("IdleService: Error creating IdleMonitors:", e) console.warn("IdleService: Error creating IdleMonitors:", e)

View File

@@ -3,6 +3,7 @@ import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import qs.Common import qs.Common
import qs.Services
import qs.Widgets import qs.Widgets
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound