mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
KeyboardLayout: stopgap solution for sway
This commit is contained in:
@@ -4,6 +4,7 @@ import Quickshell.I3
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services.Keyboard
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -37,10 +38,12 @@ Item {
|
||||
|
||||
try {
|
||||
I3.refreshWorkspaces()
|
||||
I3.dispatch('(["input"])')
|
||||
Qt.callLater(() => {
|
||||
safeUpdateWorkspaces()
|
||||
safeUpdateWindows()
|
||||
queryDisplayScales()
|
||||
queryKeyboardLayout()
|
||||
})
|
||||
initialized = true
|
||||
Logger.i("SwayService", "Service started")
|
||||
@@ -109,6 +112,60 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: keyboardLayoutUpdateTimer
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
queryKeyboardLayout()
|
||||
}
|
||||
}
|
||||
|
||||
function queryKeyboardLayout() {
|
||||
swayInputsProcess.running = true
|
||||
}
|
||||
// Sway inputs process for keyboard layout detection
|
||||
Process {
|
||||
id: swayInputsProcess
|
||||
running: false
|
||||
command: ["swaymsg", "-t", "get_inputs", "-r"]
|
||||
|
||||
property string accumulatedOutput: ""
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: function (line) {
|
||||
// Accumulate lines instead of parsing each one
|
||||
swayInputsProcess.accumulatedOutput += line
|
||||
}
|
||||
}
|
||||
|
||||
onExited: function (exitCode) {
|
||||
if (exitCode !== 0 || !accumulatedOutput) {
|
||||
Logger.e("SwayService", "Failed to query inputs, exit code:", exitCode)
|
||||
accumulatedOutput = ""
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const inputsData = JSON.parse(accumulatedOutput)
|
||||
for (const input of inputsData) {
|
||||
if (input.type == "keyboard") {
|
||||
const layoutName = input.xkb_active_layout_name
|
||||
KeyboardLayoutService.setCurrentLayout(layoutName)
|
||||
Logger.d("SwayService", "Keyboard layout switched:", layoutName)
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.e("SwayService", "Failed to parse inputs:", e)
|
||||
} finally {
|
||||
// Clear accumulated output for next query
|
||||
accumulatedOutput = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Safe update wrapper
|
||||
function safeUpdate() {
|
||||
safeUpdateWindows()
|
||||
@@ -234,6 +291,27 @@ Item {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
function handleInputEvent(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 I3
|
||||
Connections {
|
||||
target: I3.workspaces
|
||||
@@ -263,6 +341,10 @@ Item {
|
||||
if (event.type === "output") {
|
||||
Qt.callLater(queryDisplayScales)
|
||||
}
|
||||
|
||||
if (event.type == "get_inputs") {
|
||||
handleInputEvent(event.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,122 +12,6 @@ Singleton {
|
||||
property string currentLayout: I18n.tr("system.unknown-layout")
|
||||
property string previousLayout: ""
|
||||
property bool isInitialized: false
|
||||
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 for X11 systems using setxkbmap
|
||||
Process {
|
||||
id: x11LayoutProcess
|
||||
running: false
|
||||
command: ["setxkbmap", "-query"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
const lines = text.split('\n')
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('layout:')) {
|
||||
const layout = line.split(':')[1].trim()
|
||||
root.currentLayout = layout
|
||||
return
|
||||
}
|
||||
}
|
||||
root.currentLayout = I18n.tr("system.unknown-layout")
|
||||
} catch (e) {
|
||||
root.currentLayout = I18n.tr("system.unknown-layout")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process for general Wayland using localectl (systemd)
|
||||
Process {
|
||||
id: localectlProcess
|
||||
running: false
|
||||
command: ["localectl", "status"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
const lines = text.split('\n')
|
||||
for (const line of lines) {
|
||||
if (line.includes("X11 Layout:")) {
|
||||
const layout = line.split(':')[1].trim()
|
||||
if (layout && layout !== "n/a") {
|
||||
root.currentLayout = layout
|
||||
return
|
||||
}
|
||||
}
|
||||
if (line.includes("VC Keymap:")) {
|
||||
const keymap = line.split(':')[1].trim()
|
||||
if (keymap && keymap !== "n/a") {
|
||||
root.currentLayout = extractLayoutCode(keymap)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
root.currentLayout = I18n.tr("system.unknown-layout")
|
||||
} catch (e) {
|
||||
root.currentLayout = I18n.tr("system.unknown-layout")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process for generic keyboard layout detection using gsettings (GNOME-based)
|
||||
Process {
|
||||
id: gsettingsProcess
|
||||
running: false
|
||||
command: ["gsettings", "get", "org.gnome.desktop.input-sources", "current"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
const currentIndex = parseInt(text.trim())
|
||||
gsettingsSourcesProcess.running = true
|
||||
} catch (e) {
|
||||
fallbackToLocalectl()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: gsettingsSourcesProcess
|
||||
running: false
|
||||
command: ["gsettings", "get", "org.gnome.desktop.input-sources", "sources"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
// Parse the sources array and extract layout codes
|
||||
const sourcesText = text.trim()
|
||||
const matches = sourcesText.match(/\('xkb', '([^']+)'\)/g)
|
||||
if (matches && matches.length > 0) {
|
||||
// Get the first layout as default
|
||||
const layoutMatch = matches[0].match(/\('xkb', '([^']+)'\)/)
|
||||
if (layoutMatch) {
|
||||
root.currentLayout = layoutMatch[1].split('+')[0] // Take first part before any variants
|
||||
}
|
||||
} else {
|
||||
fallbackToLocalectl()
|
||||
}
|
||||
} catch (e) {
|
||||
fallbackToLocalectl()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fallbackToLocalectl() {
|
||||
localectlProcess.running = true
|
||||
}
|
||||
|
||||
// Updates current layout from various format strings. Called by compositors
|
||||
function setCurrentLayout(layoutString) {
|
||||
@@ -186,7 +70,6 @@ Singleton {
|
||||
|
||||
Component.onCompleted: {
|
||||
Logger.i("KeyboardLayout", "Service started")
|
||||
updateLayout()
|
||||
// Mark as initialized after a delay to allow first layout update to complete
|
||||
// This prevents showing a toast on the initial load
|
||||
initializationTimer.start()
|
||||
@@ -203,31 +86,6 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function updateLayout() {
|
||||
// Try compositor-specific methods first
|
||||
if (CompositorService.isHyprland) {
|
||||
|
||||
} else if (CompositorService.isNiri) {
|
||||
|
||||
} else {
|
||||
// Try detection methods in order of preference
|
||||
if (Qt.platform.os === "linux") {
|
||||
// Check if we're in X11 or Wayland
|
||||
const sessionType = Qt.application.arguments.find(arg => arg.includes("QT_QPA_PLATFORM")) || process.env.XDG_SESSION_TYPE
|
||||
|
||||
if (sessionType && sessionType.includes("xcb") || process.env.DISPLAY) {
|
||||
// X11 system
|
||||
x11LayoutProcess.running = true
|
||||
} else {
|
||||
// Wayland or unknown - try gsettings first, then localectl
|
||||
gsettingsProcess.running = true
|
||||
}
|
||||
} else {
|
||||
currentLayout = I18n.tr("system.unknown-layout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Comprehensive language name to ISO code mapping
|
||||
property var languageMap: {
|
||||
"english"// English variants
|
||||
|
||||
Reference in New Issue
Block a user