summaryrefslogtreecommitdiffstats
path: root/kxkb/extension.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kxkb/extension.cpp')
-rw-r--r--kxkb/extension.cpp477
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"