forked from Shinonome/dots-hyprland
Merge branch 'end-4:main' into fedora
This commit is contained in:
@@ -23,3 +23,5 @@ exec-once = wl-paste --type image --watch bash -c 'cliphist store && qs -c $qsCo
|
||||
# Cursor
|
||||
exec-once = hyprctl setcursor Bibata-Modern-Classic 24
|
||||
|
||||
# Fix dock pinned apps not launching properly (https://github.com/end-4/dots-hyprland/issues/2200)
|
||||
exec-once = sleep 3.5 && hyprctl reload && sleep 0.5 && touch ~/.config/quickshell/ii/shell.qml
|
||||
|
||||
@@ -380,6 +380,7 @@ Singleton {
|
||||
property JsonObject overlay: JsonObject {
|
||||
property bool openingZoomAnimation: true
|
||||
property bool darkenScreen: true
|
||||
property real clickthroughOpacity: 0.7
|
||||
}
|
||||
|
||||
property JsonObject overview: JsonObject {
|
||||
|
||||
@@ -85,7 +85,7 @@ Singleton {
|
||||
property bool pinned: false
|
||||
property bool clickthrough: true
|
||||
property real x: 835
|
||||
property real y: 490
|
||||
property real y: 483
|
||||
}
|
||||
property JsonObject recorder: JsonObject {
|
||||
property bool pinned: false
|
||||
@@ -104,7 +104,14 @@ Singleton {
|
||||
property bool pinned: false
|
||||
property bool clickthrough: false
|
||||
property real x: 80
|
||||
property real y: 250
|
||||
property real y: 280
|
||||
property int tabIndex: 0
|
||||
}
|
||||
property JsonObject fpsLimiter: JsonObject {
|
||||
property bool pinned: false
|
||||
property bool clickthrough: false
|
||||
property real x: 1576
|
||||
property real y: 630
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ Button {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
property bool tabbedTo: root.focus && (focusReason === Qt.TabFocusReason || focusReason === Qt.BacktabFocusReason)
|
||||
background: Rectangle {
|
||||
id: buttonBackground
|
||||
topLeftRadius: root.leftRadius
|
||||
@@ -130,6 +130,9 @@ Button {
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
border.width: root.tabbedTo ? 2 : 0
|
||||
border.color: Appearance.colors.colSecondary
|
||||
}
|
||||
|
||||
contentItem: StyledText {
|
||||
|
||||
@@ -18,5 +18,6 @@ ToolbarButton {
|
||||
iconSize: 22
|
||||
text: iconBtn.text
|
||||
color: iconBtn.colText
|
||||
animateChange: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ Scope {
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.namespace: "quickshell:overlay"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
WlrLayershell.keyboardFocus: GlobalStates.overlayOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||
visible: true
|
||||
color: "transparent"
|
||||
|
||||
@@ -43,6 +43,30 @@ Scope {
|
||||
right: true
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [overlayWindow]
|
||||
active: false
|
||||
onCleared: () => {
|
||||
if (!active) GlobalStates.overlayOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onOverlayOpenChanged() {
|
||||
delayedGrabTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedGrabTimer
|
||||
interval: Appearance.animation.elementMoveFast.duration
|
||||
onTriggered: {
|
||||
grab.active = GlobalStates.overlayOpen;
|
||||
}
|
||||
}
|
||||
|
||||
OverlayContent {
|
||||
id: overlayContent
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -12,6 +12,7 @@ import qs.modules.overlay.crosshair
|
||||
|
||||
Item {
|
||||
id: root
|
||||
focus: true
|
||||
readonly property bool usePasswordChars: !PolkitService.flow?.responseVisible ?? true
|
||||
|
||||
Keys.onPressed: (event) => { // Esc to close
|
||||
|
||||
@@ -9,6 +9,7 @@ Singleton {
|
||||
{ identifier: "recorder", materialSymbol: "screen_record" },
|
||||
{ identifier: "volumeMixer", materialSymbol: "volume_up" },
|
||||
{ identifier: "crosshair", materialSymbol: "point_scan" },
|
||||
{ identifier: "fpsLimiter", materialSymbol: "animation" },
|
||||
{ identifier: "resources", materialSymbol: "browse_activity" }
|
||||
]
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import qs.modules.overlay.crosshair
|
||||
import qs.modules.overlay.volumeMixer
|
||||
import qs.modules.overlay.fpsLimiter
|
||||
import qs.modules.overlay.recorder
|
||||
import qs.modules.overlay.resources
|
||||
|
||||
@@ -17,6 +18,7 @@ DelegateChooser {
|
||||
|
||||
DelegateChoice { roleValue: "crosshair"; Crosshair {} }
|
||||
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
|
||||
DelegateChoice { roleValue: "fpsLimiter"; FpsLimiter {} }
|
||||
DelegateChoice { roleValue: "recorder"; Recorder {} }
|
||||
DelegateChoice { roleValue: "resources"; Resources {} }
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ AbstractOverlayWidget {
|
||||
id: root
|
||||
|
||||
required property Item contentItem
|
||||
property bool fancyBorders: true
|
||||
property bool showCenterButton: false
|
||||
property bool showClickabilityButton: true
|
||||
|
||||
required property var modelData
|
||||
readonly property string identifier: modelData.identifier
|
||||
@@ -29,6 +32,8 @@ AbstractOverlayWidget {
|
||||
property var persistentStateEntry: Persistent.states.overlay[identifier]
|
||||
property real radius: Appearance.rounding.windowRounding
|
||||
property real minWidth: 250
|
||||
property real padding: 6
|
||||
property real contentRadius: radius - padding
|
||||
|
||||
draggable: GlobalStates.overlayOpen
|
||||
x: Math.round(persistentStateEntry.x) // Round or it'll be blurry
|
||||
@@ -38,9 +43,10 @@ AbstractOverlayWidget {
|
||||
drag {
|
||||
minimumX: 0
|
||||
minimumY: 0
|
||||
maximumX: root.parent.width - root.width
|
||||
maximumY: root.parent.height - root.height
|
||||
maximumX: root.parent?.width - root.width
|
||||
maximumY: root.parent?.height - root.height
|
||||
}
|
||||
opacity: (GlobalStates.overlayOpen || !clickthrough) ? 1.0 : Config.options.overlay.clickthroughOpacity
|
||||
|
||||
// Guarded states & registration funcs
|
||||
property bool open: Persistent.states.overlay.open
|
||||
@@ -83,7 +89,7 @@ AbstractOverlayWidget {
|
||||
|
||||
function center() {
|
||||
const targetX = (root.parent.width - contentColumn.width) / 2
|
||||
const targetY = (root.parent.height - contentItem.height) / 2 - titleBar.implicitHeight
|
||||
const targetY = (root.parent.height - contentItem.height) / 2 - titleBar.implicitHeight + border.border.width
|
||||
root.x = targetX
|
||||
root.y = targetY
|
||||
root.savePosition(targetX, targetY)
|
||||
@@ -96,11 +102,15 @@ AbstractOverlayWidget {
|
||||
Rectangle {
|
||||
id: border
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
color: (root.fancyBorders && GlobalStates.overlayOpen) ? Appearance.colors.colLayer1 : "transparent"
|
||||
radius: root.radius
|
||||
border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1)
|
||||
border.width: 1
|
||||
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
layer.enabled: GlobalStates.overlayOpen
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
@@ -110,37 +120,34 @@ AbstractOverlayWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
z: -1
|
||||
z: root.fancyBorders ? 0 : -1
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
// Title bar
|
||||
Rectangle {
|
||||
id: titleBar
|
||||
opacity: GlobalStates.overlayOpen ? 1 : 0
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
property real padding: 2
|
||||
implicitWidth: titleBarRow.implicitWidth + padding * 2
|
||||
implicitHeight: titleBarRow.implicitHeight + padding * 2
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
border.color: Appearance.colors.colOutlineVariant
|
||||
border.width: 1
|
||||
Layout.fillWidth: true
|
||||
implicitWidth: titleBarRow.implicitWidth + root.padding * 2
|
||||
implicitHeight: titleBarRow.implicitHeight + root.padding * 2
|
||||
color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1
|
||||
// border.color: Appearance.colors.colOutlineVariant
|
||||
// border.width: 1
|
||||
|
||||
RowLayout {
|
||||
id: titleBarRow
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: titleBar.padding
|
||||
leftMargin: titleBar.padding + 8
|
||||
margins: root.padding
|
||||
}
|
||||
spacing: 0
|
||||
spacing: 2
|
||||
|
||||
MaterialSymbol {
|
||||
text: root.materialSymbol
|
||||
Layout.leftMargin: 6
|
||||
iconSize: 20
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.rightMargin: 4
|
||||
@@ -153,6 +160,7 @@ AbstractOverlayWidget {
|
||||
}
|
||||
|
||||
TitlebarButton {
|
||||
visible: root.showCenterButton
|
||||
materialSymbol: "recenter"
|
||||
onClicked: root.center()
|
||||
StyledToolTip {
|
||||
@@ -161,6 +169,7 @@ AbstractOverlayWidget {
|
||||
}
|
||||
|
||||
TitlebarButton {
|
||||
visible: (root.pinned && root.showClickabilityButton)
|
||||
materialSymbol: "mouse"
|
||||
toggled: !root.clickthrough
|
||||
onClicked: root.toggleClickthrough()
|
||||
@@ -191,7 +200,11 @@ AbstractOverlayWidget {
|
||||
// Content
|
||||
Item {
|
||||
id: contentContainer
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: root.fancyBorders ? root.padding : 0
|
||||
Layout.topMargin: -border.border.width // Border of a rectangle is drawn inside its bounds, so we do this to make the gap not too big
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
implicitWidth: root.contentItem.implicitWidth
|
||||
implicitHeight: root.contentItem.implicitHeight
|
||||
children: [root.contentItem]
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.overlay
|
||||
|
||||
StyledOverlayWidget {
|
||||
id: root
|
||||
contentItem: CrosshairContent {}
|
||||
fancyBorders: false // Crosshair should be see-through
|
||||
showCenterButton: true
|
||||
opacity: 1 // The crosshair itself already has transparency if configured
|
||||
showClickabilityButton: false
|
||||
clickthrough: true
|
||||
|
||||
contentItem: CrosshairContent {
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.overlay
|
||||
|
||||
StyledOverlayWidget {
|
||||
id: root
|
||||
title: "MangoHud FPS"
|
||||
contentItem: FpsLimiterContent {
|
||||
radius: root.contentRadius
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
enum State { Normal, Success, Error }
|
||||
|
||||
anchors.fill: parent
|
||||
property real padding: 16
|
||||
property var currentState: FpsLimiterContent.State.Normal
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
implicitWidth: content.implicitWidth + (padding * 2)
|
||||
implicitHeight: content.implicitHeight + (padding * 2)
|
||||
|
||||
Timer {
|
||||
id: iconResetTimer
|
||||
interval: 1000
|
||||
onTriggered: {
|
||||
root.currentState = FpsLimiterContent.State.Normal;
|
||||
}
|
||||
}
|
||||
|
||||
function applyLimit() {
|
||||
var fpsValue = parseInt(fpsField.text);
|
||||
if (isNaN(fpsValue) || fpsValue < 0) {
|
||||
root.currentState = FpsLimiterContent.State.Error;
|
||||
iconResetTimer.restart();
|
||||
fpsField.text = "";
|
||||
return;
|
||||
}
|
||||
|
||||
var cfgPaths = [
|
||||
"~/.config/MangoHud/MangoHud.conf",
|
||||
]; // MangoHud config files
|
||||
|
||||
var updateCommands = cfgPaths.map(path => {
|
||||
return "if grep -q '^fps_limit=' " + path + "; " +
|
||||
"then sed -i 's/^fps_limit=.*/fps_limit=" + fpsValue + "/' " + path + "; " +
|
||||
"else echo 'fps_limit=" + fpsValue + "' >> " + path + "; fi";
|
||||
}).join("; ");
|
||||
|
||||
var cmd = updateCommands + "; pkill -SIGUSR2 mangohud";
|
||||
|
||||
fpsSetter.command = ["bash", "-c", cmd];
|
||||
fpsSetter.startDetached();
|
||||
|
||||
root.currentState = FpsLimiterContent.State.Success;
|
||||
iconResetTimer.restart();
|
||||
|
||||
// Clear the field after applying
|
||||
fpsField.text = "";
|
||||
}
|
||||
|
||||
Process {
|
||||
id: fpsSetter
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
ToolbarTextField {
|
||||
id: fpsField
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 200
|
||||
placeholderText: root.currentState === FpsLimiterContent.State.Error ? Translation.tr("Enter a valid number") : Translation.tr("Set FPS limit")
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
focus: true
|
||||
|
||||
onAccepted: {
|
||||
root.applyLimit();
|
||||
}
|
||||
}
|
||||
|
||||
IconToolbarButton {
|
||||
id: applyButton
|
||||
text: switch (root.currentState) {
|
||||
case FpsLimiterContent.State.Error: return "close";
|
||||
case FpsLimiterContent.State.Success: return "check";
|
||||
case FpsLimiterContent.State.Normal:
|
||||
default: return "save";
|
||||
}
|
||||
enabled: root.currentState === FpsLimiterContent.State.Normal && fpsField.text.length > 0
|
||||
onClicked: {
|
||||
root.applyLimit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ StyledOverlayWidget {
|
||||
contentItem: Rectangle {
|
||||
id: contentItem
|
||||
anchors.centerIn: parent
|
||||
radius: root.contentRadius
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
property real padding: 8
|
||||
implicitHeight: contentColumn.implicitHeight + padding * 2
|
||||
@@ -75,7 +76,7 @@ StyledOverlayWidget {
|
||||
colRipple: Appearance.colors.colLayer3Active
|
||||
onClicked: {
|
||||
GlobalStates.overlayOpen = false;
|
||||
Qt.openUrlExternally(Directories.videos);
|
||||
Qt.openUrlExternally(`file://${Config.options.screenRecord.savePath}`);
|
||||
}
|
||||
contentItem: Row {
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -39,6 +39,7 @@ StyledOverlayWidget {
|
||||
id: contentItem
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
radius: root.contentRadius
|
||||
property real padding: 4
|
||||
implicitWidth: 350
|
||||
implicitHeight: 200
|
||||
@@ -49,7 +50,7 @@ StyledOverlayWidget {
|
||||
fill: parent
|
||||
margins: parent.padding
|
||||
}
|
||||
spacing: 10
|
||||
spacing: 8
|
||||
|
||||
SecondaryTabBar {
|
||||
id: tabBar
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.overlay
|
||||
import qs.modules.sidebarRight.volumeMixer
|
||||
|
||||
@@ -10,15 +13,69 @@ StyledOverlayWidget {
|
||||
contentItem: Rectangle {
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
property real padding: 16
|
||||
radius: root.contentRadius
|
||||
property real padding: 6
|
||||
implicitHeight: 600
|
||||
implicitWidth: 350
|
||||
|
||||
VolumeDialogContent {
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.padding
|
||||
isSink: true
|
||||
}
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: parent.padding
|
||||
}
|
||||
spacing: 8
|
||||
|
||||
SecondaryTabBar {
|
||||
id: tabBar
|
||||
|
||||
currentIndex: Persistent.states.overlay.volumeMixer.tabIndex
|
||||
onCurrentIndexChanged: {
|
||||
Persistent.states.overlay.volumeMixer.tabIndex = tabBar.currentIndex;
|
||||
}
|
||||
|
||||
SecondaryTabButton {
|
||||
buttonIcon: "media_output"
|
||||
buttonText: Translation.tr("Output")
|
||||
}
|
||||
SecondaryTabButton {
|
||||
buttonIcon: "mic"
|
||||
buttonText: Translation.tr("Input")
|
||||
}
|
||||
}
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
currentIndex: Persistent.states.overlay.volumeMixer.tabIndex
|
||||
onCurrentIndexChanged: {
|
||||
Persistent.states.overlay.volumeMixer.tabIndex = swipeView.currentIndex;
|
||||
}
|
||||
clip: true
|
||||
|
||||
PaddedVolumeDialogContent {
|
||||
isSink: true
|
||||
}
|
||||
PaddedVolumeDialogContent {
|
||||
isSink: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component PaddedVolumeDialogContent: Item {
|
||||
id: paddedVolumeDialogContent
|
||||
property alias isSink: volDialogContent.isSink
|
||||
property real padding: 12
|
||||
implicitWidth: volDialogContent.implicitWidth + padding * 2
|
||||
implicitHeight: volDialogContent.implicitHeight + padding * 2
|
||||
|
||||
VolumeDialogContent {
|
||||
id: volDialogContent
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: paddedVolumeDialogContent.padding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ ColumnLayout {
|
||||
Layout.topMargin: -22
|
||||
Layout.leftMargin: 0
|
||||
Layout.rightMargin: 0
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
DialogSectionListView {
|
||||
@@ -56,6 +57,7 @@ ColumnLayout {
|
||||
Layout.topMargin: -22
|
||||
Layout.leftMargin: 0
|
||||
Layout.rightMargin: 0
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
DialogSectionListView {
|
||||
|
||||
@@ -28,10 +28,10 @@ Item {
|
||||
sourceSize.height: size
|
||||
source: {
|
||||
let icon;
|
||||
icon = AppSearch.guessIcon(root.node.properties["application.icon-name"]);
|
||||
icon = AppSearch.guessIcon(root.node?.properties["application.icon-name"] ?? "");
|
||||
if (AppSearch.iconExists(icon))
|
||||
return Quickshell.iconPath(icon, "image-missing");
|
||||
icon = AppSearch.guessIcon(root.node.properties["node.name"]);
|
||||
icon = AppSearch.guessIcon(root.node?.properties["node.name"] ?? "");
|
||||
return Quickshell.iconPath(icon, "image-missing");
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ Item {
|
||||
elide: Text.ElideRight
|
||||
text: {
|
||||
// application.name -> description -> name
|
||||
const app = root.node.properties["application.name"] ?? (root.node.description != "" ? root.node.description : root.node.name);
|
||||
const app = root.node?.properties["application.name"] ?? (root.node.description != "" ? root.node.description : root.node.name);
|
||||
const media = root.node.properties["media.name"];
|
||||
return media != undefined ? `${app} • ${media}` : app;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ Item {
|
||||
|
||||
StyledSlider {
|
||||
id: slider
|
||||
value: root.node.audio.volume
|
||||
value: root.node?.audio.volume ?? 0
|
||||
onMoved: root.node.audio.volume = value
|
||||
configuration: StyledSlider.Configuration.S
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ Singleton {
|
||||
// Reload files
|
||||
fileMeminfo.reload()
|
||||
fileStat.reload()
|
||||
fileCpuinfo.reload()
|
||||
|
||||
// Parse memory and swap usage
|
||||
const textMeminfo = fileMeminfo.text()
|
||||
@@ -93,29 +92,6 @@ Singleton {
|
||||
previousCpuStats = { total, idle }
|
||||
}
|
||||
|
||||
// Parse max CPU frequency
|
||||
const textCpuinfo = fileCpuinfo.text()
|
||||
// Try to find 'cpu max MHz', fallback to highest 'cpu MHz'
|
||||
let maxMHz = 0
|
||||
let match
|
||||
// Try cpu max MHz (modern kernels)
|
||||
match = textCpuinfo.match(/cpu max MHz\s*:\s*([\d.]+)/)
|
||||
if (match) {
|
||||
maxMHz = Number(match[1])
|
||||
} else {
|
||||
// Fallback: find all cpu MHz lines and take the max
|
||||
let mhzRegex = /cpu MHz\s*:\s*([\d.]+)/g
|
||||
let mhzMatch
|
||||
let mhzList = []
|
||||
while ((mhzMatch = mhzRegex.exec(textCpuinfo)) !== null) {
|
||||
mhzList.push(Number(mhzMatch[1]))
|
||||
}
|
||||
if (mhzList.length > 0) {
|
||||
maxMHz = Math.max.apply(null, mhzList)
|
||||
}
|
||||
}
|
||||
root.maxAvailableCpuString = maxMHz > 0 ? (maxMHz / 1000).toFixed(1) + "GHz" : "--"
|
||||
|
||||
root.updateHistories()
|
||||
interval = Config.options?.resources?.updateInterval ?? 3000
|
||||
}
|
||||
@@ -123,5 +99,16 @@ Singleton {
|
||||
|
||||
FileView { id: fileMeminfo; path: "/proc/meminfo" }
|
||||
FileView { id: fileStat; path: "/proc/stat" }
|
||||
FileView { id: fileCpuinfo; path: "/proc/cpuinfo" }
|
||||
|
||||
Process {
|
||||
id: findCpuMaxFreqProc
|
||||
command: ["bash", "-c", "lscpu | grep 'CPU max MHz' | awk '{print $4}'"]
|
||||
running: true
|
||||
stdout: StdioCollector {
|
||||
id: outputCollector
|
||||
onStreamFinished: {
|
||||
root.maxAvailableCpuString = (parseFloat(outputCollector.text) / 1000).toFixed(0) + " GHz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user