mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-08-15 18:43:59 +00:00
KeyboardLayout: update on keyboard layout change events (hyprland)
This commit is contained in:
@@ -3,6 +3,7 @@ import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services.Keyboard
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -43,6 +44,7 @@ Item {
|
||||
safeUpdateWorkspaces()
|
||||
safeUpdateWindows()
|
||||
queryDisplayScales()
|
||||
queryKeyboardLayout()
|
||||
})
|
||||
initialized = true
|
||||
Logger.i("HyprlandService", "Service started")
|
||||
@@ -56,7 +58,6 @@ Item {
|
||||
hyprlandMonitorsProcess.running = true
|
||||
}
|
||||
|
||||
// Hyprland monitors process for display scale detection
|
||||
// Hyprland monitors process for display scale detection
|
||||
Process {
|
||||
id: hyprlandMonitorsProcess
|
||||
@@ -112,6 +113,50 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function queryKeyboardLayout() {
|
||||
hyprlandDevicesProcess.running = true
|
||||
}
|
||||
// Hyprland devices process for keyboard layout detection
|
||||
Process {
|
||||
id: hyprlandDevicesProcess
|
||||
running: false
|
||||
command: ["hyprctl", "devices", "-j"]
|
||||
|
||||
property string accumulatedOutput: ""
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: function (line) {
|
||||
// Accumulate lines instead of parsing each one
|
||||
hyprlandDevicesProcess.accumulatedOutput += line
|
||||
}
|
||||
}
|
||||
|
||||
onExited: function (exitCode) {
|
||||
if (exitCode !== 0 || !accumulatedOutput) {
|
||||
Logger.e("HyprlandService", "Failed to query devices, exit code:", exitCode)
|
||||
accumulatedOutput = ""
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const devicesData = JSON.parse(accumulatedOutput)
|
||||
for (const keyboard of devicesData.keyboards) {
|
||||
if (keyboard.main) {
|
||||
const layoutName = keyboard.active_keymap
|
||||
KeyboardLayoutService.setCurrentLayout(layoutName)
|
||||
Logger.d("HyprlandService", "Keyboard layout switched:", layoutName)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.e("HyprlandService", "Failed to parse devices:", e)
|
||||
} finally {
|
||||
// Clear accumulated output for next query
|
||||
accumulatedOutput = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Safe update wrapper
|
||||
function safeUpdate() {
|
||||
safeUpdateWindows()
|
||||
@@ -330,6 +375,27 @@ Item {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
function handleActiveLayoutEvent(ev) {
|
||||
try {
|
||||
let beforeParenthesis
|
||||
const parenthesisPos = ev.lastIndexOf('(')
|
||||
|
||||
if (parenthesisPos === -1) {
|
||||
beforeParenthesis = ev
|
||||
} else {
|
||||
beforeParenthesis = ev.substring(0, parenthesisPos)
|
||||
}
|
||||
|
||||
const layoutNameStart = beforeParenthesis.lastIndexOf(',') + 1
|
||||
const layoutName = ev.substring(layoutNameStart)
|
||||
|
||||
KeyboardLayoutService.setCurrentLayout(layoutName)
|
||||
Logger.d("HyprlandService", "Keyboard layout switched:", layoutName)
|
||||
} catch (e) {
|
||||
Logger.e("HyprlandService", "Error handling activelayout:", e)
|
||||
}
|
||||
}
|
||||
|
||||
// Connections to Hyprland
|
||||
Connections {
|
||||
target: Hyprland.workspaces
|
||||
@@ -358,9 +424,14 @@ Item {
|
||||
updateTimer.restart()
|
||||
|
||||
const monitorsEvents = ["configreloaded", "monitoradded", "monitorremoved", "monitoraddedv2", "monitorremovedv2"]
|
||||
|
||||
if (monitorsEvents.includes(event.name)) {
|
||||
Qt.callLater(queryDisplayScales)
|
||||
}
|
||||
|
||||
if (event.name == "activelayout") {
|
||||
handleActiveLayoutEvent(event.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ Item {
|
||||
niriOutputsProcess.running = true
|
||||
}
|
||||
|
||||
|
||||
// Niri outputs process for display scale detection
|
||||
Process {
|
||||
id: niriOutputsProcess
|
||||
@@ -452,6 +451,4 @@ Item {
|
||||
Logger.e("NiriService", "Failed to logout:", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -25,29 +25,6 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = extractLayoutCode(mainKeyboard.active_keymap)
|
||||
} else {
|
||||
root.currentLayout = I18n.tr("system.unknown-layout")
|
||||
}
|
||||
} catch (e) {
|
||||
root.currentLayout = I18n.tr("system.unknown-layout")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process for X11 systems using setxkbmap
|
||||
Process {
|
||||
id: x11LayoutProcess
|
||||
@@ -229,9 +206,9 @@ Singleton {
|
||||
function updateLayout() {
|
||||
// Try compositor-specific methods first
|
||||
if (CompositorService.isHyprland) {
|
||||
hyprlandLayoutProcess.running = true
|
||||
|
||||
} else if (CompositorService.isNiri) {
|
||||
// do nothing, niri calls setCurrentLayout
|
||||
|
||||
} else {
|
||||
// Try detection methods in order of preference
|
||||
if (Qt.platform.os === "linux") {
|
||||
|
||||
Reference in New Issue
Block a user