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

View File

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

View File

@@ -21,17 +21,10 @@ Singleton {
readonly property string swaySocket: Quickshell.env("SWAYSOCK")
property bool useNiriSorting: isNiri && NiriService
property var sortedToplevels: sortedToplevelsCache
property var sortedToplevelsCache: []
property var sortedToplevels: []
property bool _sortScheduled: false
property bool _refreshScheduled: false
property bool _hasRefreshedOnce: false
property var _coordCache: ({})
property int _refreshCount: 0
property real _refreshWindowStart: 0
readonly property int _maxRefreshesPerSecond: 3
signal toplevelsChanged()
function getScreenScale(screen) {
if (!screen) return 1
@@ -59,46 +52,27 @@ Singleton {
}
Timer {
id: refreshTimer
interval: 40
id: sortDebounceTimer
interval: 100
repeat: false
onTriggered: {
try {
Hyprland.refreshToplevels()
} catch(e) {}
_refreshScheduled = false
_hasRefreshedOnce = true
scheduleSort()
_sortScheduled = false
if (isHyprland) {
try {
Hyprland.refreshToplevels()
} catch(e) {
console.warn("CompositorService: Failed to refresh toplevels:", e)
}
}
sortedToplevels = computeSortedToplevels()
toplevelsChanged()
}
}
function scheduleSort() {
if (_sortScheduled) return
_sortScheduled = true
Qt.callLater(function() {
_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()
sortDebounceTimer.restart()
}
Connections {
@@ -106,19 +80,28 @@ Singleton {
function onValuesChanged() { root.scheduleSort() }
}
Connections {
target: Hyprland.toplevels
function onValuesChanged() {
root.scheduleSort()
target: isHyprland ? Hyprland : null
enabled: isHyprland
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 {
target: NiriService
function onWindowsChanged() { root.scheduleSort() }
@@ -165,7 +148,6 @@ Singleton {
function sortHyprlandToplevelsSafe() {
if (!Hyprland.toplevels || !Hyprland.toplevels.values) return []
if (_refreshScheduled && sortedToplevelsCache.length > 0) return sortedToplevelsCache
const items = Array.from(Hyprland.toplevels.values)
@@ -177,21 +159,7 @@ Singleton {
} 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 missingAnyPosition = false
let hasNewWindow = false
for (let i = 0; i < items.length; i++) {
const t = items[i]
if (!t) continue
@@ -208,23 +176,8 @@ Singleton {
const wsId = _get(li, ["workspace", "id"], null) ?? _get(t, ["workspace", "id"], Number.MAX_SAFE_INTEGER)
const at = _get(li, ["at"], null)
let atX = (at !== null && at !== undefined && typeof at[0] === "number") ? at[0] : NaN
let atY = (at !== null && at !== undefined && typeof at[1] === "number") ? at[1] : NaN
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 }
}
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] : 1e9
const relX = Number.isFinite(monX) ? (atX - monX) : atX
const relY = Number.isFinite(monY) ? (atY - monY) : atY
@@ -242,10 +195,6 @@ Singleton {
})
}
if (missingAnyPosition && hasNewWindow && !_hasRefreshedOnce) {
scheduleRefresh()
}
const groups = new Map()
for (const it of snap) {
const key = it.monKey + "::" + it.wsId
@@ -400,9 +349,6 @@ Singleton {
isSway = false
compositor = "hyprland"
console.info("CompositorService: Detected Hyprland")
try {
Hyprland.refreshToplevels()
} catch(e) {}
return
}

View File

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

View File

@@ -118,12 +118,12 @@ Singleton {
import Quickshell.Wayland
IdleInhibitor {
active: false
enabled: false
}
`
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) {
console.warn("IdleService: Error creating IdleMonitors:", e)

View File

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