diff options
Diffstat (limited to 'kxkb/extension.cpp')
-rw-r--r-- | kxkb/extension.cpp | 477 |
1 files changed, 214 insertions, 263 deletions
diff --git a/kxkb/extension.cpp b/kxkb/extension.cpp index 616167944..0fada6932 100644 --- a/kxkb/extension.cpp +++ b/kxkb/extension.cpp @@ -1,3 +1,32 @@ +/******************************************************************************* + + Xkb extension for KXkb + Copyright © 2009-2025 Trinity Desktop project + Copyright © 2001 S.R. Haque <srhaque@iee.org> + + Derived from an original by Matthias H�zer-Klpfel released under the QPL. + + Some portions come from kkbswitch released under the GNU GPL v2 (or later). + Copyright © 2001 Leonid Zeitlin <lz@europe.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +*******************************************************************************/ + +#include <stdlib.h> #include <string.h> #include <errno.h> @@ -5,87 +34,96 @@ #include <tqmap.h> #include <tqfile.h> #include <tqdir.h> +#include <tqtimer.h> #include <kdebug.h> -#include <kstandarddirs.h> -#include <kprocess.h> +#include <tdeapplication.h> +#include <tdestandarddirs.h> +#include <tdeprocess.h> +#include <dcopclient.h> #include <X11/Xatom.h> #include <X11/Xos.h> #include <X11/Xlib.h> #include <X11/XKBlib.h> #include <X11/extensions/XKBfile.h> +#include <X11/extensions/XKBrules.h> #include <X11/extensions/XKBgeom.h> #include <X11/extensions/XKM.h> #include "extension.h" - -TQMap<TQString, FILE*> XKBExtension::fileCache; //TODO: move to class? - +extern "C" +{ + static int IgnoreXError(Display *, XErrorEvent *) { return 0; } +} 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; -} +static XKBExtension *xkbExtension = nullptr; -XKBExtension::XKBExtension(Display *d) +XKBExtension *XKBExtension::the() { - if ( d == NULL ) - d = tqt_xdisplay(); - m_dpy = d; - -// TQStringList dirs = TDEGlobal::dirs()->findDirs ( "tmp", "" ); -// m_tempDir = dirs.count() == 0 ? "/tmp/" : dirs[0]; - m_tempDir = locateLocal("tmp", ""); + if (!xkbExtension) + { + xkbExtension = new XKBExtension; + if (!xkbExtension->init()) + { + kdFatal() << "xkb initialization failed, exiting..." << endl; + ::exit(1); + } + } + return xkbExtension; } bool XKBExtension::init() { - // Verify the Xlib has matching XKB extension. - - int major = XkbMajorVersion; - int minor = XkbMinorVersion; - - if (!XkbLibraryVersion(&major, &minor)) - { - kdError() << "[kxkb-extension] Xlib XKB extension " << major << '.' << minor << - " != " << XkbMajorVersion << '.' << XkbMinorVersion << endl; - return false; - } - - // Verify the X server has matching XKB extension. - - int opcode_rtrn; - int error_rtrn; - int xkb_opcode; - if (!XkbQueryExtension(m_dpy, &opcode_rtrn, &xkb_opcode, &error_rtrn, - &major, &minor)) - { - kdError() << "[kxkb-extension] X server XKB extension " << major << '.' << minor << - " != " << XkbMajorVersion << '.' << XkbMinorVersion << endl; - return false; - } - - // Do it, or face horrible memory corrupting bugs - ::XkbInitAtoms(NULL); - - return true; -} + m_configureFilterCounter = 0; -void XKBExtension::reset() -{ - for(TQMap<TQString, FILE*>::ConstIterator it = fileCache.begin(); it != fileCache.end(); it++) { - fclose(*it); -// remove( TQFile::encodeName(getPrecompiledLayoutFileName(*it)) ); + kdDebug() << "[kxkb-extension] Initializing Xkb extension" << endl; + m_dpy = tqt_xdisplay(); + + // Verify the Xlib has matching XKB extension. + int major = XkbMajorVersion; + int minor = XkbMinorVersion; + + if (!XkbLibraryVersion(&major, &minor)) + { + kdError() << "[kxkb-extension] Xlib XKB extension " << major << '.' << minor << + " != " << XkbMajorVersion << '.' << XkbMinorVersion << endl; + return false; } - fileCache.clear(); + + // Verify the X server has matching XKB extension. + int opcode_rtrn; + int error_rtrn; + if (!XkbQueryExtension(m_dpy, &opcode_rtrn, &m_xkb_opcode, &error_rtrn, &major, &minor)) + { + kdError() << "[kxkb-extension] X server XKB extension " << major << '.' << minor << + " != " << XkbMajorVersion << '.' << XkbMinorVersion << endl; + return false; + } + + enableConfigureFilter(); + + // Do it, or face horrible memory corrupting bugs + ::XkbInitAtoms(nullptr); + + // Watch for interesting events + XkbSelectEventDetails(m_dpy, XkbUseCoreKbd, XkbStateNotify, + XkbAllStateComponentsMask, XkbGroupStateMask); + + XkbSelectEventDetails(m_dpy, XkbUseCoreKbd, XkbNewKeyboardNotify, + XkbAllNewKeyboardEventsMask, XkbAllNewKeyboardEventsMask); + + + m_tempDir = locateLocal("tmp", ""); + + disableConfigureFilter(); + return true; } XKBExtension::~XKBExtension() @@ -94,245 +132,158 @@ XKBExtension::~XKBExtension() deletePrecompiledLayouts();*/ } -bool XKBExtension::setXkbOptions(const TQString& options, bool resetOld) +void XKBExtension::enableConfigureFilter() { - 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); + ++m_configureFilterCounter; +} - return p.normalExit() && (p.exitStatus() == 0); +void XKBExtension::disableConfigureFilter() +{ + // Without this protection in place KXkb would react to configuration + // changes caused by itself + TQTimer::singleShot(500, this, TQ_SLOT(slotReleaseConfigureLock())); } -bool XKBExtension::setLayout(const TQString& model, - const TQString& layout, const TQString& variant, - const TQString& includeGroup, bool useCompiledLayouts) +void XKBExtension::slotReleaseConfigureLock() { - if( useCompiledLayouts == false ) { - return setLayoutInternal( model, layout, variant, includeGroup ); - } - - const TQString layoutKey = getLayoutKey(layout, variant); - - bool res; - if( fileCache.contains(layoutKey) ) { - res = setCompiledLayout( layoutKey ); - kdDebug() << "[kxkb-extension] setCompiledLayout " << layoutKey << ": " << res << endl; - - if( res ) - return res; - } -// else { - res = setLayoutInternal( model, layout, variant, includeGroup ); - kdDebug() << "[kxkb-extension] setRawLayout " << layoutKey << ": " << res << endl; - if( res ) - compileCurrentLayout( layoutKey ); - -// } - return res; + --m_configureFilterCounter; } -// private -bool XKBExtension::setLayoutInternal(const TQString& model, - const TQString& layout, const TQString& variant, - const TQString& includeGroup) +bool XKBExtension::setXkbOptions(const XkbOptions options) { - if ( layout.isEmpty() ) - return false; + enableConfigureFilter(); TQString exe = TDEGlobal::dirs()->findExe("setxkbmap"); - if( exe.isEmpty() ) { - kdError() << "[kxkb-extension] Can't find setxkbmap" << endl; + if (exe.isEmpty()) + { return false; } - 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); - } - - if ( TQFile::exists( TQDir::home().path() + "/.Xmodmap" ) ) { - TDEProcess pXmodmapHome; - pXmodmapHome << "xmodmap" << TQDir::home().path() + "/.Xmodmap"; - pXmodmapHome.start(TDEProcess::Block); - } - - return p.normalExit() && (p.exitStatus() == 0); + TDEProcess p; + p << exe; + + if (!options.layouts.isEmpty()) + { + p << "-layout"; + p << options.layouts; + } + + if (!options.variants.isEmpty()) + { + p << "-variant"; + p << options.variants; + } + + if (!options.model.isEmpty()) { + p << "-model"; + p << options.model; + } + + if (options.resetOld) { + p << "-option"; + } + + if (!options.options.isEmpty()) { + if (options.resetOld) + { + p << "-option" << options.options; + } + else + { + // Avoid duplication of options in Append mode + XkbOptions _opt = getServerOptions(); + TQStringList srvOptions = TQStringList::split(",", _opt.options); + TQStringList kxkbOptions = TQStringList::split(",", options.options); + TQStringList newOptions; + for (TQStringList::Iterator it = kxkbOptions.begin(); it != kxkbOptions.end(); ++it) + { + TQString option(*it); + if (!srvOptions.contains(option)) + { + newOptions << option; + } + } + if (!newOptions.isEmpty()) { + p << "-option" << newOptions.join(","); + } + } + } + + if (p.args().count() < 2) + { + // Either the user has not configured any Xkb options or these options + // are already set and we are in append mode so we want to avoid + // duplicates + kdWarning() << "[setXkbOptions] No options need to be set" << endl; + slotReleaseConfigureLock(); // immediately release the lock + return true; + } + + p << "-synch"; + + kdDebug() << "[setXkbOptions] Command: " << p.args() << endl; + + p.start(TDEProcess::Block); + + disableConfigureFilter(); + + return p.normalExit() && (p.exitStatus() == 0); +} + +XkbOptions XKBExtension::getServerOptions() +{ + XkbOptions options; + XkbRF_VarDefsRec vd; + if (XkbRF_GetNamesProp(tqt_xdisplay(), nullptr, &vd)) + { + options.model = vd.model; + options.layouts = vd.layout; + options.variants = vd.variant; + options.options = vd.options; + } + return options; } bool XKBExtension::setGroup(unsigned int group) { kdDebug() << "[kxkb-extension] Setting group " << group << endl; - return XkbLockGroup( m_dpy, XkbUseCoreKbd, group ); + return XkbLockGroup(m_dpy, XkbUseCoreKbd, group); } -unsigned int XKBExtension::getGroup() const +uint XKBExtension::getGroup() const { XkbStateRec xkbState; - XkbGetState( m_dpy, XkbUseCoreKbd, &xkbState ); + XkbGetState(m_dpy, XkbUseCoreKbd, &xkbState); 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) +bool XKBExtension::kcmlayoutRunning() { - 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; - } - - 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; + return tdeApp->dcopClient()->isApplicationRegistered("TDECModuleProxy-keyboard_layout"); } -/** - * @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; +// Examines an X Event passed to it and takes actions if the event is of +// interest to KXkb +void XKBExtension::processXEvent(XEvent *event) { + if (event->type == m_xkb_opcode) + { + XkbEvent *xkb_event = (XkbEvent*)event; + if (xkb_event->any.xkb_type == XkbStateNotify && xkb_event->state.changed & XkbGroupStateMask) + { + emit groupChanged((uint)xkb_event->state.group); } - } - 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; + else if (xkb_event->any.xkb_type == XkbNewKeyboardNotify) + { + if (m_configureFilterCounter > 0 || kcmlayoutRunning()) + { + return; + } + enableConfigureFilter(); + emit optionsChanged(); + disableConfigureFilter(); + } } - - 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<LayoutUnit, TQString> 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" |