Skip to content

Random crashes when opening a new window #411

@9bingyin

Description

@9bingyin

General crash information

General information
===== Build Information =====
Git Revision: tag-v0.2.1
Buildtime Qt Version: 6.10.1
Build Type: RelWithDebInfo
Compiler: GNU (14.3.0)
Complie Flags: 

Build configuration:
  Distributor: Nixpkgs
  Distributor provided debuginfo: TRUE
  Disable precompild headers (dev): OFF
  Build tests (dev): OFF
  ASAN (dev): OFF
  Keep Frame Pointers (dev): OFF
  Crash Handling: ON
  Use jemalloc: ON
  Unix Sockets: ON
  Wayland: ON
    Wlroots Layer-Shell: ON
    Session Lock: ON
    Foreign Toplevel Management: ON
    Hyprland: ON
      Hyprland IPC: ON
      Hyprland Global Shortcuts: ON
      Hyprland Focus Grabbing: ON
      Hyprland Surface Extensions: ON
    Screencopy: ON
      Image Copy Capture: ON
      Wlroots Screencopy: ON
      Hyprland Toplevel Export: ON
  X11: ON
  I3/Sway: ON
    I3/Sway IPC: ON
  System Tray: ON
  PipeWire: ON
  Mpris: ON
  Pam: ON
  Greetd: ON
  UPower: ON
  Notifications: ON
  Bluetooth: ON

===== Runtime Information =====
Runtime Qt Version: 6.10.1
Crashed process ID: 1818778
Run ID: t1xzlidy6t
Shell ID: c1f59de81923405ebac28b985f4d2fa4
Config Path: /nix/store/rmij6w837bc6kpx18dwlyly18368srqi-hm_quickshellconfig/shell.qml

===== Report Integrity =====
Minidump save status: 0
Log save status: 0
Binary copy status: 0

===== System Information =====

/etc/os-release:
ANSI_COLOR="0;38;2;126;186;228"
BUG_REPORT_URL="https://github.com/NixOS/nixpkgs/issues"
BUILD_ID="26.05.20251205.a672be6"
CPE_NAME="cpe:/o:nixos:nixos:26.05"
DEFAULT_HOSTNAME=nixos
DOCUMENTATION_URL="https://nixos.org/learn.html"
HOME_URL="https://nixos.org/"
ID=nixos
ID_LIKE=""
IMAGE_ID=""
IMAGE_VERSION=""
LOGO="nix-snowflake"
NAME=NixOS
PRETTY_NAME="NixOS 26.05 (Yarara)"
SUPPORT_URL="https://nixos.org/community.html"
VARIANT=""
VARIANT_ID=""
VENDOR_NAME=NixOS
VENDOR_URL="https://nixos.org/"
VERSION="26.05 (Yarara)"
VERSION_CODENAME=yarara
VERSION_ID="26.05"

/etc/lsb-release:
DISTRIB_CODENAME=yarara
DISTRIB_DESCRIPTION="NixOS 26.05 (Yarara)"
DISTRIB_ID=nixos
DISTRIB_RELEASE="26.05"
LSB_VERSION="26.05 (Yarara)"

What caused the crash

No response

Minidump

minidump.dmp.log

Log file

log.qslog.log

Configuration

shell.qml

//@ pragma UseQApplication
import Quickshell
import QtQuick

ShellRoot {
  Connections {
    target: Quickshell
    function onReloadCompleted() {
      Quickshell.inhibitReloadPopup()
    }
    function onReloadFailed() {
      Quickshell.inhibitReloadPopup()
    }
  }

  Bar {}
}

Bar.qml

import Quickshell
import Quickshell.Hyprland
import Quickshell.Services.SystemTray
import Quickshell.Services.Pipewire
import Quickshell.Services.UPower
import Quickshell.Services.Mpris
import Quickshell.Io
import QtQuick
import QtQuick.Layouts

Scope {
  id: root

  // 网络名称
  property string networkName: ""

  // 当前媒体播放器
  property MprisPlayer currentPlayer: Mpris.players.values.length > 0 ? Mpris.players.values[0] : null

  // Cava 音频可视化
  property var cavaValues: Array(16).fill(0)
  property bool cavaRunning: currentPlayer !== null && currentPlayer.isPlaying

  // 电源模式配置
  property bool powerMenuVisible: false
  property int acProfile: PowerProfile.Performance
  property int batProfile: PowerProfile.PowerSaver
  property bool powerInitialized: false

  // 是否有有效的活动窗口(通过监听 Hyprland 原始事件)
  property bool hasActiveWindow: false

  // 监听 Hyprland 原始事件来判断是否有活动窗口
  Connections {
    target: Hyprland
    function onRawEvent(event) {
      if (event.name === "activewindow") {
        // activewindow 事件:格式为 "class,title"
        // 如果两者都为空,说明没有活动窗口
        root.hasActiveWindow = event.data.length > 1
      }
    }
  }

  // 监听电源状态变化自动切换
  Connections {
    target: UPower
    function onOnBatteryChanged() {
      if (root.powerInitialized) {
        PowerProfiles.profile = UPower.onBattery ? root.batProfile : root.acProfile
      }
    }
  }

  // 启动时设置电源模式和初始化活动窗口状态
  Component.onCompleted: {
    PowerProfiles.profile = UPower.onBattery ? batProfile : acProfile
    powerInitialized = true
    hasActiveWindow = Hyprland.activeToplevel !== null
  }

  // 监听活动窗口变化,刷新 toplevel 数据以保持图标和标题同步
  Connections {
    target: Hyprland
    function onActiveToplevelChanged() {
      if (Hyprland.activeToplevel !== null) {
        Hyprland.refreshToplevels()
      }
    }
  }

  // 绑定音频节点以获取音量信息
  PwObjectTracker {
    objects: [Pipewire.defaultAudioSink]
  }

  // 获取网络信息
  Process {
    id: networkProc
    command: ["sh", "-c", "nmcli -t -f NAME connection show --active | grep -v '^lo$' | head -1"]
    running: true

    stdout: SplitParser {
      onRead: data => root.networkName = data.trim()
    }
  }

  Timer {
    interval: 5000
    running: true
    repeat: true
    onTriggered: {
      networkProc.running = true
    }
  }

  // Cava 进程
  Process {
    id: cavaProcess
    stdinEnabled: true
    running: root.cavaRunning
    command: ["cava", "-p", "/dev/stdin"]

    onStarted: {
      write("[general]\n")
      write("bars=16\n")
      write("framerate=60\n")
      write("sensitivity=100\n")
      write("[output]\n")
      write("method=raw\n")
      write("data_format=ascii\n")
      write("ascii_max_range=100\n")
      write("channels=mono\n")
      stdinEnabled = false
    }

    onExited: {
      stdinEnabled = true
      root.cavaValues = Array(16).fill(0)
    }

    stdout: SplitParser {
      onRead: data => {
        var parts = data.slice(0, -1).split(";")
        root.cavaValues = parts.map(function(v) { return parseInt(v, 10) / 100 })
      }
    }
  }

  Variants {
    model: Quickshell.screens

    PanelWindow {
      id: panel
      required property var modelData
      screen: modelData

      anchors {
        top: true
        left: true
        right: true
      }

      implicitHeight: 32
      color: Qt.rgba(0.102, 0.106, 0.149, 0.85)

      // 电源模式弹出菜单
      PopupWindow {
        id: powerMenu
        anchor.window: panel
        anchor.rect.x: 0
        anchor.rect.y: panel.height
        anchor.adjustment: PopupAdjustment.None
        visible: root.powerMenuVisible
        implicitWidth: panel.width
        implicitHeight: 1000
        color: "transparent"

        // 点击遮罩关闭菜单
        MouseArea {
          anchors.fill: parent
          onClicked: root.powerMenuVisible = false
        }

        // 菜单内容
        Rectangle {
          width: 60
          height: 28 * 3
          color: Qt.rgba(0.102, 0.106, 0.149, 0.95)

          Column {
            id: menuColumn
            anchors.fill: parent

            Repeater {
              model: [
                { label: "性能", profile: PowerProfile.Performance },
                { label: "平衡", profile: PowerProfile.Balanced },
                { label: "省电", profile: PowerProfile.PowerSaver }
              ]

              Item {
                required property var modelData
                width: menuColumn.width
                height: 28

                property bool isActive: PowerProfiles.profile === modelData.profile

                Text {
                  id: menuItemText
                  anchors.centerIn: parent
                  text: modelData.label
                  color: parent.isActive ? "#ffffff" : "#c0caf5"
                  font.pixelSize: 13
                }

                Rectangle {
                  anchors.top: menuItemText.bottom
                  anchors.topMargin: 2
                  anchors.horizontalCenter: parent.horizontalCenter
                  width: menuItemText.implicitWidth
                  height: 2
                  color: "#ffffff"
                  visible: parent.isActive
                }

                MouseArea {
                  anchors.fill: parent
                  onClicked: {
                    PowerProfiles.profile = modelData.profile
                    root.powerMenuVisible = false
                  }
                }
              }
            }
          }
        }
      }

      // 点击 bar 其他区域关闭菜单
      MouseArea {
        id: powerMenuCloser
        anchors.fill: parent
        enabled: root.powerMenuVisible
        onClicked: root.powerMenuVisible = false
        z: -1
      }

      // 左侧: 工作区指示器
      Row {
        anchors.left: parent.left
        anchors.leftMargin: 8
        anchors.verticalCenter: parent.verticalCenter
        spacing: 8

        // 发行版图标 + 电源模式切换
        Image {
          id: distroIcon
          width: 18
          height: 18
          source: Quickshell.iconPath("nix-snowflake", "distributor-logo-nixos")
          fillMode: Image.PreserveAspectFit
          anchors.verticalCenter: parent.verticalCenter

          MouseArea {
            anchors.fill: parent
            onClicked: root.powerMenuVisible = !root.powerMenuVisible
          }
        }

        Repeater {
          model: Hyprland.workspaces

          Item {
            required property var modelData
            width: 14
            height: 24

            // 背景:当工作区有紧急窗口时显示红色
            Rectangle {
              anchors.fill: parent
              color: "#f38ba8"
              visible: modelData.urgent
            }

            Text {
              anchors.horizontalCenter: parent.horizontalCenter
              anchors.top: parent.top
              text: modelData.id
              color: modelData.focused ? "#ffffff" : "#6c7086"
              font.pixelSize: 14
              font.bold: modelData.focused
            }

            Rectangle {
              anchors.horizontalCenter: parent.horizontalCenter
              anchors.bottom: parent.bottom
              width: 12
              height: 2
              color: "#ffffff"
              visible: modelData.focused
            }

            MouseArea {
              anchors.fill: parent
              onClicked: modelData.activate()
            }
          }
        }

        // 媒体组件
        Item {
          id: mediaWidget
          visible: root.currentPlayer !== null && root.currentPlayer.isPlaying
          width: 150
          height: 24
          clip: true

          property string mediaText: {
            if (!root.currentPlayer) return ""
            var artist = root.currentPlayer.trackArtist
            var title = root.currentPlayer.trackTitle || "Unknown"
            return artist ? (artist + " - " + title) : title
          }

          onMediaTextChanged: scrollingRow.updatePosition()
          onVisibleChanged: if (visible) scrollingRow.updatePosition()

          // 音频波形背景
          Canvas {
            id: visualizerCanvas
            anchors.fill: parent
            opacity: 0.5
            z: 0

            onPaint: {
              var ctx = getContext("2d")
              ctx.reset()

              var barCount = root.cavaValues.length
              if (barCount === 0) return

              var barWidth = width / barCount
              var barGap = 1

              ctx.fillStyle = "#89b4fa"

              for (var i = 0; i < barCount; i++) {
                var barHeight = root.cavaValues[i] * height
                var x = i * barWidth
                var y = height - barHeight
                ctx.fillRect(x + barGap / 2, y, barWidth - barGap, barHeight)
              }
            }
          }

          Connections {
            target: root
            function onCavaValuesChanged() {
              visualizerCanvas.requestPaint()
            }
          }

          Text {
            id: mediaTextMetrics
            visible: false
            text: parent.mediaText
            font.pixelSize: 12
            onImplicitWidthChanged: scrollingRow.updatePosition()
          }

          Item {
            anchors.fill: parent
            clip: true
            z: 1

            Row {
              id: scrollingRow
              spacing: 50
              anchors.verticalCenter: parent.verticalCenter

              property bool needsScroll: mediaTextMetrics.implicitWidth > mediaWidget.width

              function updatePosition() {
                scrollAnimation.stop()
                var containerWidth = mediaWidget.width
                var textWidth = mediaTextMetrics.implicitWidth

                if (textWidth <= 0 || containerWidth <= 0) {
                  x = 0
                  return
                }

                if (needsScroll) {
                  x = 0
                  scrollAnimation.start()
                } else {
                  x = Math.max(0, (containerWidth - textWidth) / 2)
                }
              }

              Text {
                text: mediaTextMetrics.text
                color: "#cdd6f4"
                font.pixelSize: 12
              }

              Text {
                visible: scrollingRow.needsScroll
                text: mediaTextMetrics.text
                color: "#cdd6f4"
                font.pixelSize: 12
              }

              NumberAnimation {
                id: scrollAnimation
                target: scrollingRow
                property: "x"
                from: 0
                to: -(mediaTextMetrics.implicitWidth + 50)
                duration: Math.max(5000, mediaTextMetrics.text.length * 150)
                loops: Animation.Infinite
              }
            }
          }
        }
      }

      // 中间: 活动窗口 (全局居中)
      Item {
        anchors.centerIn: parent
        width: windowRow.visible ? Math.min(windowTextMetrics.implicitWidth + 26, 600) : 0
        height: 24
        visible: root.hasActiveWindow && Hyprland.activeToplevel !== null

        property string windowTitle: Hyprland.activeToplevel?.title ?? ""

        Text {
          id: windowTextMetrics
          visible: false
          text: parent.windowTitle
          font.pixelSize: 14
        }

        Row {
          id: windowRow
          anchors.verticalCenter: parent.verticalCenter
          spacing: 6
          visible: root.hasActiveWindow && Hyprland.activeToplevel !== null

          Image {
            width: 20
            height: 20
            source: {
              if (Hyprland.activeToplevel === null) return ""
              var ipcObj = Hyprland.activeToplevel.lastIpcObject
              var appClass = ipcObj?.initialClass || ipcObj?.class || ""
              var entry = DesktopEntries.heuristicLookup(appClass)
              return entry ? Quickshell.iconPath(entry.icon) : Quickshell.iconPath(appClass)
            }
            fillMode: Image.PreserveAspectFit
            anchors.verticalCenter: parent.verticalCenter
          }

          Item {
            width: Math.min(windowTextMetrics.implicitWidth, 574)
            height: 24
            clip: true
            anchors.verticalCenter: parent.verticalCenter

            Row {
              id: windowScrollingRow
              spacing: 50
              anchors.verticalCenter: parent.verticalCenter

              property bool needsScroll: windowTextMetrics.implicitWidth > 574

              function updatePosition() {
                windowScrollAnimation.stop()
                var containerWidth = 574
                var textWidth = windowTextMetrics.implicitWidth

                if (textWidth <= 0 || containerWidth <= 0) {
                  x = 0
                  return
                }

                if (needsScroll) {
                  x = 0
                  windowScrollAnimation.start()
                } else {
                  x = 0
                }
              }

              Text {
                text: windowTextMetrics.text
                color: "#c0caf5"
                font.pixelSize: 14
              }

              Text {
                visible: windowScrollingRow.needsScroll
                text: windowTextMetrics.text
                color: "#c0caf5"
                font.pixelSize: 14
              }

              NumberAnimation {
                id: windowScrollAnimation
                target: windowScrollingRow
                property: "x"
                from: 0
                to: -(windowTextMetrics.implicitWidth + 50)
                duration: Math.max(5000, windowTextMetrics.text.length * 150)
                loops: Animation.Infinite
              }

              Component.onCompleted: updatePosition()
            }

            Connections {
              target: windowRow.parent
              function onWindowTitleChanged() {
                windowScrollingRow.updatePosition()
              }
            }
          }
        }
      }

      // 右侧
      Row {
        anchors.right: parent.right
        anchors.rightMargin: 8
        anchors.verticalCenter: parent.verticalCenter
        spacing: 12

        // 系统托盘
        Row {
          spacing: 4
          Repeater {
            model: SystemTray.items

            Item {
              id: trayItem
              required property var modelData
              width: 20
              height: 20

              Image {
                anchors.fill: parent
                source: modelData.icon
                fillMode: Image.PreserveAspectFit
              }

              QsMenuAnchor {
                id: menuAnchor
                menu: modelData.menu
                anchor.item: trayItem
              }

              MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton | Qt.RightButton
                onClicked: function(mouse) {
                  if (mouse.button === Qt.LeftButton) {
                    if (modelData.onlyMenu && modelData.hasMenu) {
                      menuAnchor.open()
                    } else {
                      modelData.activate()
                    }
                  } else if (mouse.button === Qt.RightButton && modelData.hasMenu) {
                    menuAnchor.open()
                  }
                }
              }
            }
          }
        }

        // 网络
        Text {
          text: root.networkName || "---"
          color: "#c0caf5"
          font.pixelSize: 14
        }

        // 音量
        Item {
          width: volumeText.implicitWidth
          height: 24

          property real volumePercent: Pipewire.defaultAudioSink?.audio?.volume ?? 0
          property bool isMuted: Pipewire.defaultAudioSink?.audio?.muted ?? false

          Text {
            id: volumeText
            anchors.top: parent.top
            text: parent.isMuted ? "0%" : (Math.round(parent.volumePercent * 100) + "%")
            color: "#c0caf5"
            font.pixelSize: 14
          }

          Rectangle {
            anchors.bottom: parent.bottom
            anchors.left: parent.left
            width: parent.isMuted ? parent.width : (parent.width * Math.min(1, parent.volumePercent))
            height: 2
            color: parent.isMuted ? "#f38ba8" : "#89b4fa"
          }
        }

        // 电量
        Repeater {
          model: UPower.devices

          Item {
            required property var modelData
            visible: modelData.isLaptopBattery
            width: batteryText.implicitWidth
            height: 24

            property real batteryPercent: modelData.energy / modelData.energyCapacity
            property bool isCharging: modelData.state === UPowerDeviceState.Charging

            Text {
              id: batteryText
              anchors.top: parent.top
              text: Math.round(parent.batteryPercent * 100) + "%"
              color: "#c0caf5"
              font.pixelSize: 14
            }

            Item {
              id: batteryBar
              anchors.bottom: parent.bottom
              anchors.left: parent.left
              width: parent.width
              height: 2
              clip: true

              Rectangle {
                visible: !batteryBar.parent.isCharging
                width: parent.width * batteryBar.parent.batteryPercent
                height: parent.height
                color: {
                  var pct = batteryBar.parent.batteryPercent
                  if (pct <= 0.2) return "#f38ba8"
                  if (pct <= 0.4) return "#fab387"
                  return "#a6e3a1"
                }
              }

              Rectangle {
                id: chargingWave
                visible: batteryBar.parent.isCharging
                width: 8
                height: parent.height
                color: "#ffffff"

                NumberAnimation on x {
                  running: chargingWave.visible
                  from: -8
                  to: batteryBar.width
                  duration: 1000
                  loops: Animation.Infinite
                }
              }
            }
          }
        }

        // 时间
        Text {
          color: "#c0caf5"
          font.pixelSize: 14

          SystemClock {
            id: systemClock
            precision: SystemClock.Minutes
          }

          text: Qt.formatDateTime(systemClock.date, "hh:mm")
        }
      }
    }
  }
}

Backtrace

No response

Executable

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcrash

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions