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:
@@ -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
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ Item {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: CompositorService
|
target: CompositorService
|
||||||
function onSortedToplevelsChanged() {
|
function onToplevelsChanged() {
|
||||||
dockModel.updateModel()
|
dockModel.updateModel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user