From a67db2d4847d798c01d4fd7584c5bb9297e109e3 Mon Sep 17 00:00:00 2001 From: Mavridis Philippe Date: Fri, 6 Jan 2023 15:30:57 +0200 Subject: Kxkb: Improve layout switching 1) New layout switching approach The new approach is based on the "grp" options group of Xkb and so enables us to use predefined X11 layout (group) switching hotkeys like "Caps Lock" or "Shift+Alt" (you can see the full list in the Options tab). The added bonus to this is that we conform to the Xkb setting. The code lets Xkb handle the keyboard layout switching hotkey(s) and is similar to the one that is used in kkbswitch, monitoring for an Xkb group (layout) change event. This solution required me to remove some hacky and obsolete code which was there to support really old pre-XFree-4.2 era systems and included the "include groups" hack. This means that the "Enable latin layout" checkbox is now gone and setxkbmap is only called when the keyboard layouts and/or options are modified, and not for every layout change. 2) Common layout switching hotkeys combobox A combobox was added to the first page of the Keyboard Layouts KCM module. It provides to the users a quick way to set a layout switching key combination. It also controls the "grp" group in the Xkb tab. A special note about this combobox is that, even if Append Mode was selected in the Xkb Options tab, this hotkey will overwrite previous hotkey options. This means that all grp: options will be forced removed before applying the option from the combobox (in contrast to specifying options via the Xkb Options tab, which, in Append Mode, will not get overwritten until next login). Signed-off-by: Mavridis Philippe --- kxkb/extension.cpp | 263 +++++++---------------------------------------------- 1 file changed, 32 insertions(+), 231 deletions(-) (limited to 'kxkb/extension.cpp') diff --git a/kxkb/extension.cpp b/kxkb/extension.cpp index 616167944..1b6895043 100644 --- a/kxkb/extension.cpp +++ b/kxkb/extension.cpp @@ -21,20 +21,11 @@ #include "extension.h" -TQMap XKBExtension::fileCache; //TODO: move to class? - - static TQString getLayoutKey(const TQString& layout, const TQString& variant) { return layout + "." + variant; } -TQString XKBExtension::getPrecompiledLayoutFilename(const TQString& layoutKey) -{ - TQString compiledLayoutFileName = m_tempDir + layoutKey + ".xkm"; - return compiledLayoutFileName; -} - XKBExtension::XKBExtension(Display *d) { if ( d == NULL ) @@ -76,16 +67,11 @@ bool XKBExtension::init() // Do it, or face horrible memory corrupting bugs ::XkbInitAtoms(NULL); - return true; -} + // watch group change events + XkbSelectEventDetails(m_dpy, XkbUseCoreKbd, XkbStateNotify, + XkbAllStateComponentsMask, XkbGroupStateMask); -void XKBExtension::reset() -{ - for(TQMap::ConstIterator it = fileCache.begin(); it != fileCache.end(); it++) { - fclose(*it); -// remove( TQFile::encodeName(getPrecompiledLayoutFileName(*it)) ); - } - fileCache.clear(); + return true; } XKBExtension::~XKBExtension() @@ -94,106 +80,38 @@ XKBExtension::~XKBExtension() deletePrecompiledLayouts();*/ } -bool XKBExtension::setXkbOptions(const TQString& options, bool resetOld) +bool XKBExtension::setXkbOptions(const XkbOptions options) { - if (options.isEmpty()) - return true; - - TQString exe = TDEGlobal::dirs()->findExe("setxkbmap"); - if (exe.isEmpty()) - return false; - - TDEProcess p; - p << exe; - if( resetOld ) - p << "-option"; - p << "-option" << options; - - p.start(TDEProcess::Block); + TQString exe = TDEGlobal::dirs()->findExe("setxkbmap"); + if (exe.isEmpty()) + return false; - return p.normalExit() && (p.exitStatus() == 0); -} + TDEProcess p; + p << exe; -bool XKBExtension::setLayout(const TQString& model, - const TQString& layout, const TQString& variant, - const TQString& includeGroup, bool useCompiledLayouts) -{ - if( useCompiledLayouts == false ) { - return setLayoutInternal( model, layout, variant, includeGroup ); - } + p << "-layout"; + p << options.layouts; - const TQString layoutKey = getLayoutKey(layout, variant); + p << "-variant"; + p << options.variants; - bool res; - if( fileCache.contains(layoutKey) ) { - res = setCompiledLayout( layoutKey ); - kdDebug() << "[kxkb-extension] setCompiledLayout " << layoutKey << ": " << res << endl; - - if( res ) - return res; + if (!options.model.isEmpty()) { + p << "-model"; + p << options.model; } -// else { - res = setLayoutInternal( model, layout, variant, includeGroup ); - kdDebug() << "[kxkb-extension] setRawLayout " << layoutKey << ": " << res << endl; - if( res ) - compileCurrentLayout( layoutKey ); - -// } - return res; -} -// private -bool XKBExtension::setLayoutInternal(const TQString& model, - const TQString& layout, const TQString& variant, - const TQString& includeGroup) -{ - if ( layout.isEmpty() ) - return false; - - TQString exe = TDEGlobal::dirs()->findExe("setxkbmap"); - if( exe.isEmpty() ) { - kdError() << "[kxkb-extension] Can't find setxkbmap" << endl; - return false; + if (options.resetOld) { + p << "-option"; + } + if (!options.options.isEmpty()) { + p << "-option" << options.options; } - TQString fullLayout = layout; - TQString fullVariant = variant; - if( includeGroup.isEmpty() == false ) { - fullLayout = includeGroup; - fullLayout += ","; - fullLayout += layout; - -// fullVariant = baseVar; - fullVariant = ","; - fullVariant += variant; - } - - TDEProcess p; - p << exe; -// p << "-rules" << rule; - if( model.isEmpty() == false ) - p << "-model" << model; - p << "-layout" << fullLayout; - if( !fullVariant.isNull() && !fullVariant.isEmpty() ) - p << "-variant" << fullVariant; - - p.start(TDEProcess::Block); - - // reload system-wide hotkey-setup keycode -> keysym maps - TQString modmapFileName = TDEGlobal::dirs()->findResource( "data", "kxkb/system.xmodmap" ); - if ( TQFile::exists( modmapFileName ) ) { - TDEProcess pXmodmap; - pXmodmap << "xmodmap" << modmapFileName; - pXmodmap.start(TDEProcess::Block); - } + kdDebug() << "[kxkb-extension] Command: " << p.args() << endl; - if ( TQFile::exists( TQDir::home().path() + "/.Xmodmap" ) ) { - TDEProcess pXmodmapHome; - pXmodmapHome << "xmodmap" << TQDir::home().path() + "/.Xmodmap"; - pXmodmapHome.start(TDEProcess::Block); - } + p.start(TDEProcess::Block); - return p.normalExit() && (p.exitStatus() == 0); + return p.normalExit() && (p.exitStatus() == 0); } bool XKBExtension::setGroup(unsigned int group) @@ -209,130 +127,13 @@ unsigned int XKBExtension::getGroup() const return xkbState.group; } -/** - * @brief Gets the current layout in its binary compiled form - * and write it to the file specified by 'fileName' - * @param[in] fileName file to store compiled layout to - * @return true if no problem, false otherwise - */ -bool XKBExtension::compileCurrentLayout(const TQString &layoutKey) -{ - XkbFileInfo result; - memset(&result, 0, sizeof(result)); - result.type = XkmKeymapFile; - XkbReadFromServer(m_dpy, XkbAllMapComponentsMask, XkbAllMapComponentsMask, &result); - - const TQString fileName = getPrecompiledLayoutFilename(layoutKey); - - kdDebug() << "[kxkb-extension] compiling layout " << this << " cache size: " << fileCache.count() << endl; - if( fileCache.contains(layoutKey) ) { - kdDebug() << "[kxkb-extension] trashing old compiled layout for " << fileName << endl; - if( fileCache[ layoutKey ] != NULL ) - fclose( fileCache[ layoutKey ] ); // recompiling - trash the old file - fileCache.remove(fileName); - } - - FILE *output = fopen(TQFile::encodeName(fileName), "w"); - - if ( output == NULL ) - { - kdWarning() << "[kxkb-extension] Could not open " << fileName << " to precompile: " << strerror(errno) << endl; - XkbFreeKeyboard(result.xkb, XkbAllControlsMask, True); - return false; +/** Examines an X Event passed to it and takes actions if the event is of + * interest to KXkb */ +void XKBExtension::processXEvent(XEvent *event) { + XkbEvent* xkb_event = (XkbEvent*)event; + if (xkb_event->any.xkb_type == XkbStateNotify) { + emit groupChanged(xkb_event->state.group); } - - if( !XkbWriteXKMFile(output, &result) ) { - kdWarning() << "[kxkb-extension] Could not write compiled layout to " << fileName << endl; - fclose(output); - return false; - } - - fclose(output); // TODO: can we change mode w/out reopening? - FILE *input = fopen(TQFile::encodeName(fileName), "r"); - fileCache[ layoutKey ] = input; - - XkbFreeKeyboard(result.xkb, XkbAllControlsMask, True); - return true; } -/** - * @brief takes layout from its compiled binary snapshot in file - * and sets it as current - * TODO: cache layout in memory rather than in file - */ -bool XKBExtension::setCompiledLayout(const TQString &layoutKey) -{ - FILE *input = NULL; - - if( fileCache.contains(layoutKey) ) { - input = fileCache[ layoutKey ]; - } - - if( input == NULL ) { - kdWarning() << "[kxkb-extension] setCompiledLayout trying to reopen xkb file" << endl; // should never happen - const TQString fileName = getPrecompiledLayoutFilename(layoutKey); - input = fopen(TQFile::encodeName(fileName), "r"); - - // FILE *input = fopen(TQFile::encodeName(fileName), "r"); - if ( input == NULL ) { - kdDebug() << "[kxkb-extension] Unable to open " << fileName << ": " << strerror(errno) << endl; - fileCache.remove(layoutKey); - return false; - } - } - else { - rewind(input); - } - - XkbFileInfo result; - memset(&result, 0, sizeof(result)); - if ((result.xkb = XkbAllocKeyboard())==NULL) { - kdWarning() << "[kxkb-extension] Unable to allocate memory for keyboard description" << endl; -// fclose(input); -// fileCache.remove(layoutKey); - return false; - } - - unsigned retVal = XkmReadFile(input, 0, XkmKeymapLegal, &result); - if (retVal == XkmKeymapLegal) - { - // this means reading the Xkm didn't manage to read any section - kdWarning() << "[kxkb-extension] Unable to load map from file" << endl; - XkbFreeKeyboard(result.xkb, XkbAllControlsMask, True); - fclose(input); - fileCache.remove(layoutKey); - return false; - } - - // fclose(input); // don't close - goes in cache - - if (XkbChangeKbdDisplay(m_dpy, &result) == Success) - { - if (!XkbWriteToServer(&result)) - { - kdWarning() << "[kxkb-extension] Unable to write the keyboard layout to X display" << endl; - XkbFreeKeyboard(result.xkb, XkbAllControlsMask, True); - return false; - } - } - else - { - kdWarning() << "[kxkb-extension] Unable prepare the keyboard layout for X display" << endl; - } - - XkbFreeKeyboard(result.xkb, XkbAllControlsMask, True); - return true; -} - - -// Deletes the precompiled layouts stored in temporary files -// void XKBExtension::deletePrecompiledLayouts() -// { -// TQMapConstIterator it, end; -// end = m_compiledLayoutFileNames.end(); -// for (it = m_compiledLayoutFileNames.begin(); it != end; ++it) -// { -// unlink(TQFile::encodeName(it.data())); -// } -// m_compiledLayoutFileNames.clear(); -// } +#include "extension.moc" \ No newline at end of file -- cgit v1.2.3