noctalia-shell/Services/KeyboardLayoutService.qml

112 lines
2.7 KiB
QML

pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import qs.Commons
import qs.Services
Singleton {
id: root
property string currentLayout: "Unknown"
property int updateInterval: 1000 // Update every second
// Timer to periodically update the layout
Timer {
id: updateTimer
interval: updateInterval
running: true
repeat: true
onTriggered: {
updateLayout()
}
}
// Process to get current keyboard layout using niri msg (Wayland native)
Process {
id: niriLayoutProcess
running: false
command: ["niri", "msg", "-j", "keyboard-layouts"]
stdout: StdioCollector {
onStreamFinished: {
try {
const data = JSON.parse(text)
const layoutName = data.names[data.current_idx]
root.currentLayout = mapLayoutNameToCode(layoutName)
} catch (e) {
root.currentLayout = "Unknown"
}
}
}
}
// Process to get current keyboard layout using hyprctl (Hyprland)
Process {
id: hyprlandLayoutProcess
running: false
command: ["hyprctl", "-j", "devices"]
stdout: StdioCollector {
onStreamFinished: {
try {
const data = JSON.parse(text)
// Find the main keyboard and get its active keymap
const mainKeyboard = data.keyboards.find(kb => kb.main === true)
if (mainKeyboard && mainKeyboard.active_keymap) {
root.currentLayout = mapLayoutNameToCode(mainKeyboard.active_keymap)
} else {
root.currentLayout = "Unknown"
}
} catch (e) {
root.currentLayout = "Unknown"
}
}
}
}
// Layout name to ISO code mapping
property var layoutMap: {
"German": "de",
"English (US)": "us",
"English (UK)": "gb",
"French": "fr",
"Spanish": "es",
"Italian": "it",
"Portuguese (Brazil)": "br",
"Portuguese": "pt",
"Russian": "ru",
"Polish": "pl",
"Swedish": "se",
"Norwegian": "no",
"Danish": "dk",
"Finnish": "fi",
"Hungarian": "hu",
"Turkish": "tr",
"Czech": "cz",
"Slovak": "sk",
"Japanese": "jp",
"Korean": "kr",
"Chinese": "cn"
}
// Map layout names to ISO codes
function mapLayoutNameToCode(layoutName) {
return layoutMap[layoutName] || layoutName // fallback to raw name if not found
}
Component.onCompleted: {
Logger.log("KeyboardLayout", "Service started")
updateLayout()
}
function updateLayout() {
if (CompositorService.isHyprland) {
hyprlandLayoutProcess.running = true
} else if (CompositorService.isNiri) {
niriLayoutProcess.running = true
} else {
currentLayout = "Unknown"
}
}
}