UNPKG

penguins-eggs

Version:

A remaster system tool, compatible with Almalinux, Alpine, Arch, Debian, Devuan, Fedora, Manjaro, Opensuse, Ubuntu and derivatives

142 lines (141 loc) 4.93 kB
/** * ./src/classes/keyboards.ts * penguins-eggs v.25.7.x / ecmascript 2020 * author: Piero Proietti (modified) * email: piero.proietti@gmail.com * license: MIT */ import fs from 'node:fs'; import { exec } from '../lib/utils.js'; /** * Keyboard class - reads and manages X11 keyboard configuration */ export default class Keyboard { layouts = []; // New change #1: store typed objects instead of raw strings models = []; // New change #1 options = []; // New change #1 variants = []; // New change #1 defaultKeyboardFile = '/etc/default/keyboard'; // New change #2: store default keyboard file path xorgLstFile = '/usr/share/X11/xkb/rules/xorg.lst'; constructor() { // New change #3: Use a safer approach to read xorg.lst if (fs.existsSync(this.xorgLstFile)) { const content = fs.readFileSync(this.xorgLstFile, 'utf8'); this.parseXorgLst(content); } else { this.setDefaults(); } } // Get current layout async getLayout() { const layout = await this.readKeyboardConfig('XKBLAYOUT'); return layout || 'us'; } // Get all layouts getLayouts() { return this.layouts; } // Get current model async getModel() { const model = await this.readKeyboardConfig('XKBMODEL'); return model || 'pc105'; } // Get all models getModels() { return this.models; } // Get current option async getOption() { return await this.readKeyboardConfig('XKBOPTIONS'); } // Get all options getOptions() { return this.options; } // Get current variant async getVariant() { return await this.readKeyboardConfig('XKBVARIANT'); } // Get variants for a specific layout getVariants(layout) { return this.variants.filter((v) => v.lang === layout); } // New change #4: parse xorg.lst with regex instead of fixed slicing parseXorgLst(content) { const sections = ['model', 'layout', 'variant', 'option']; let currentSection = null; for (let line of content.split('\n')) { line = line.trim(); if (!line) continue; if (line.startsWith('!')) { const sectionName = line.slice(2).toLowerCase(); if (sections.includes(sectionName)) { currentSection = sectionName; } else { currentSection = null; } continue; } if (!currentSection) continue; // Separate code and description with regex const match = line.match(/^(\S+)\s+(.*)$/); if (!match) continue; const [_, code, description] = match; const desc = description || ''; switch (currentSection) { case 'layout': { this.layouts.push({ code, description: desc }); break; } case 'model': { this.models.push({ code, description: desc }); break; } case 'option': { this.options.push({ code, description: desc }); break; } case 'variant': { // Extract language if possible const langMatch = desc.match(/^(\S+):\s*(.*)$/); this.variants.push({ code, description: langMatch ? langMatch[2] : desc, lang: langMatch ? langMatch[1] : '' }); break; } } } } // New change #6: read keyboard configuration from file safely async readKeyboardConfig(variable) { if (!fs.existsSync(this.defaultKeyboardFile)) return ''; try { const cmd = `grep ^${variable}= ${this.defaultKeyboardFile} | cut -d= -f2 | tr -d '"'`; const result = await exec(cmd, { capture: true, echo: false, ignore: false }); if (result.code === 0) { return result.data.trim(); } return ''; } catch { return ''; } } // New change #5: set default keyboard data if xorg.lst not found setDefaults() { this.models.push({ code: 'pc105', description: 'Generic 105-key PC' }); const defaultLayouts = ['us', 'fr', 'de', 'gb', 'es', 'it', 'ru', 'jp']; // shortened for example for (const l of defaultLayouts) this.layouts.push({ code: l, description: '' }); this.variants.push({ code: 'none', description: 'none', lang: '' }); this.options.push({ code: 'none', description: 'none' }); } }