summaryrefslogtreecommitdiffstats
path: root/kxkb
diff options
context:
space:
mode:
authorMavridis Philippe <[email protected]>2025-01-14 16:02:12 +0200
committerMavridis Philippe <[email protected]>2025-01-14 17:02:10 +0200
commita6f556ee0f3d66f3337cdc513b3e8ac3edd79842 (patch)
tree26ddc47b29f3389cb54cc76cf44f1de8d0c1c67a /kxkb
parent040ee36ad5ddf0d8f08d30d21976c7d719bea266 (diff)
downloadtdebase-a6f556ee0f3d66f3337cdc513b3e8ac3edd79842.tar.gz
tdebase-a6f556ee0f3d66f3337cdc513b3e8ac3edd79842.zip
KXkb: Catch and process changes to Xkb layouts and options
The XKBExtension class reports likely configuration changes to the main app which then updates its internal options object accordingly and marks some option sets as 'tainted'. A notification is produced to inform the user that an external application such as setxkbmap has changed some options. The next time the keyboard layout configuration module opens a dialog box asks the user to select to load either the KXkb configuration on disk or the currently active Xkb options. In both cases the user can then save and apply the preferred version of the configuration. Other significant changes: * Make XKBExtension a singleton so that only one instance of the class exists. The extension is initialized on first use. * Add mutex to XKBExtension to prevent it from processing configuration changes likely caused by KXkb * XKBExtension::getServerOptions() now also returns layout and variant information in a XkbOptions struct * New KxkbConfig::setFromXkbOptions() member can update current configuration from a XkbOptions struct * Add proper copyright header to extension.* Signed-off-by: Mavridis Philippe <[email protected]>
Diffstat (limited to 'kxkb')
-rw-r--r--kxkb/eventsrc5
-rw-r--r--kxkb/extension.cpp183
-rw-r--r--kxkb/extension.h54
-rw-r--r--kxkb/kcmlayout.cpp67
-rw-r--r--kxkb/kcmlayout.h6
-rw-r--r--kxkb/kxkb.cpp49
-rw-r--r--kxkb/kxkb.h2
-rw-r--r--kxkb/kxkbconfig.cpp87
-rw-r--r--kxkb/kxkbconfig.h6
9 files changed, 374 insertions, 85 deletions
diff --git a/kxkb/eventsrc b/kxkb/eventsrc
index 1e06d70c3..e80332fbe 100644
--- a/kxkb/eventsrc
+++ b/kxkb/eventsrc
@@ -10,4 +10,9 @@ default_presentation=16
[Error]
Name=Keyboard layout switching error
Comment=Error while attempting to switch the keyboard layout
+default_presentation=16
+
+[ExternalChange]
+Name=External configuration change
+Comment=An external application has modified the active keyboard configuration
default_presentation=16 \ No newline at end of file
diff --git a/kxkb/extension.cpp b/kxkb/extension.cpp
index a7da41ead..c238811fd 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 <[email protected]>
+
+ 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 <[email protected]>
+
+ 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,6 +34,7 @@
#include <tqmap.h>
#include <tqfile.h>
#include <tqdir.h>
+#include <tqtimer.h>
#include <kdebug.h>
#include <kstandarddirs.h>
@@ -21,58 +51,78 @@
#include "extension.h"
+extern "C"
+{
+ static int IgnoreXError(Display *, XErrorEvent *) { return 0; }
+}
static TQString getLayoutKey(const TQString& layout, const TQString& variant)
{
return layout + "." + variant;
}
-XKBExtension::XKBExtension(Display *d)
-{
- if ( d == NULL )
- d = tqt_xdisplay();
- m_dpy = d;
+static XKBExtension *xkbExtension = nullptr;
-// TQStringList dirs = TDEGlobal::dirs()->findDirs ( "tmp", "" );
-// m_tempDir = dirs.count() == 0 ? "/tmp/" : dirs[0];
- m_tempDir = locateLocal("tmp", "");
+XKBExtension *XKBExtension::the()
+{
+ 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.
+ m_configureLock.lock();
+ kdDebug() << "[kxkb-extension] Initializing Xkb extension" << endl;
+ m_dpy = tqt_xdisplay();
- int major = XkbMajorVersion;
- int minor = XkbMinorVersion;
+ // 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;
- }
+ 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;
- }
+ 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);
+ ::XkbInitAtoms(nullptr);
+
+ // Watch for interesting events
+ XkbSelectEventDetails(m_dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, XkbGroupStateMask);
+
+ XkbSelectEventDetails(m_dpy, XkbUseCoreKbd, XkbNewKeyboardNotify,
+ XkbAllNewKeyboardEventsMask, XkbAllNewKeyboardEventsMask);
- // watch group change events
- XkbSelectEventDetails(m_dpy, XkbUseCoreKbd, XkbStateNotify,
- XkbAllStateComponentsMask, XkbGroupStateMask);
- return true;
+// TQStringList dirs = TDEGlobal::dirs()->findDirs ( "tmp", "" );
+// m_tempDir = dirs.count() == 0 ? "/tmp/" : dirs[0];
+ m_tempDir = locateLocal("tmp", "");
+
+ m_configureLock.unlock();
+ return true;
}
XKBExtension::~XKBExtension()
@@ -81,8 +131,22 @@ XKBExtension::~XKBExtension()
deletePrecompiledLayouts();*/
}
+void XKBExtension::safeConfigureLockRelease()
+{
+ // Without this protection in place KXkb would react to configuration
+ // changes caused by itself
+ TQTimer::singleShot(500, this, TQ_SLOT(configureDone()));
+}
+
+void XKBExtension::configureDone()
+{
+ m_configureLock.unlock();
+}
+
bool XKBExtension::setXkbOptions(const XkbOptions options)
{
+ m_configureLock.lock();
+
TQString exe = TDEGlobal::dirs()->findExe("setxkbmap");
if (exe.isEmpty())
{
@@ -121,7 +185,8 @@ bool XKBExtension::setXkbOptions(const XkbOptions options)
else
{
// Avoid duplication of options in Append mode
- TQStringList srvOptions = TQStringList::split(",", XKBExtension::getServerOptions());
+ 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)
@@ -144,6 +209,7 @@ bool XKBExtension::setXkbOptions(const XkbOptions 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;
+ m_configureLock.unlock();
return true;
}
@@ -151,42 +217,55 @@ bool XKBExtension::setXkbOptions(const XkbOptions options)
p.start(TDEProcess::Block);
+ safeConfigureLockRelease();
+
return p.normalExit() && (p.exitStatus() == 0);
}
-TQString XKBExtension::getServerOptions()
+XkbOptions XKBExtension::getServerOptions()
{
- XkbRF_VarDefsRec vd;
- if (XkbRF_GetNamesProp(tqt_xdisplay(), nullptr, &vd) && vd.options)
- {
- kdDebug() << "[kxkb-extension] Got server options " << vd.options << endl;
- return TQString(vd.options);
- }
- return TQString::null;
+ XkbOptions options;
+ XkbRF_VarDefsRec vd;
+ if (XkbRF_GetNamesProp(tqt_xdisplay(), nullptr, &vd) && vd.options)
+ {
+ 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);
}
uint XKBExtension::getGroup() const
{
XkbStateRec xkbState;
- XkbGetState( m_dpy, XkbUseCoreKbd, &xkbState );
+ XkbGetState(m_dpy, XkbUseCoreKbd, &xkbState);
return xkbState.group;
}
-/** Examines an X Event passed to it and takes actions if the event is of
- * interest to KXkb */
+// 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 &&
- xkb_event->state.changed & XkbGroupStateMask)
- {
- emit groupChanged((uint)xkb_event->state.group);
- }
+ 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 if (xkb_event->any.xkb_type == XkbNewKeyboardNotify)
+ {
+ if (!m_configureLock.tryLock())
+ {
+ return;
+ }
+ emit optionsChanged();
+ safeConfigureLockRelease();
+ }
}
#include "extension.moc"
diff --git a/kxkb/extension.h b/kxkb/extension.h
index 091cce582..0b82f70b5 100644
--- a/kxkb/extension.h
+++ b/kxkb/extension.h
@@ -1,8 +1,38 @@
+/*******************************************************************************
+
+ Xkb extension for KXkb
+ Copyright © 2009-2025 Trinity Desktop project
+ Copyright © 2001 S.R. Haque <[email protected]>
+
+ 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 <[email protected]>
+
+ 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.
+
+*******************************************************************************/
+
#ifndef __EXTENSION_H__
#define __EXTENSION_H__
#include <X11/Xlib.h>
+
#include <tqobject.h>
+#include <tqmutex.h>
#include "kxkbconfig.h"
@@ -11,24 +41,40 @@ class XKBExtension : public TQObject
TQ_OBJECT
public:
- XKBExtension(Display *display=NULL);
+ static XKBExtension *the();
~XKBExtension();
+
+ XKBExtension(XKBExtension const&) = delete;
+ void operator=(XKBExtension const&) = delete;
+
bool init();
- static bool setXkbOptions(const XkbOptions options);
- static TQString getServerOptions();
+ bool setXkbOptions(const XkbOptions options);
bool setGroup(uint group);
+
uint getGroup() const;
+ XkbOptions getServerOptions();
+
void processXEvent(XEvent *ev);
+ void safeConfigureLockRelease();
+
+private slots:
+ void configureDone();
+
+protected:
+ XKBExtension() {}
+
private:
- Display *m_dpy;
+ Display *m_dpy;
TQString m_tempDir;
int m_keycode;
static TQMap<TQString, FILE*> fileCache;
+ TQMutex m_configureLock;
signals:
void groupChanged(uint group);
+ void optionsChanged();
};
#endif
diff --git a/kxkb/kcmlayout.cpp b/kxkb/kcmlayout.cpp
index 94c7b7d49..aee98ff22 100644
--- a/kxkb/kcmlayout.cpp
+++ b/kxkb/kcmlayout.cpp
@@ -14,6 +14,7 @@
#include <tqbuttongroup.h>
#include <tqspinbox.h>
#include <tqvbox.h>
+#include <tqtimer.h>
#include <tdefontrequester.h>
#include <kcolorbutton.h>
@@ -190,8 +191,8 @@ LayoutConfig::LayoutConfig(TQWidget *parent, const char *name)
#include "kxkbbindings.cpp"
makeOptionsTab();
- load();
makeShortcutsTab();
+ TQTimer::singleShot(0, this, TQ_SLOT(load()));
}
@@ -205,11 +206,58 @@ LayoutConfig::~LayoutConfig()
void LayoutConfig::load()
{
m_kxkbConfig.load(KxkbConfig::LOAD_ALL_OPTIONS);
+
+ // Check if the active settings are tainted (i.e. modified by an external
+ // program) and present the user with a choice on whether they want to show
+ // the default or tainted options in the config dialog
+ XkbOptions options = XKBExtension::the()->getServerOptions();
+
+ // Either we load the active tainted configuration or the user-set
+ // KXkb configuration. Either way we want the 'Apply' and 'Ok' buttons
+ // to be active. In the first case, pressing either button will save
+ // the tainted configuration to disk; in the second case, pressing
+ // either button would apply the saved KXkb configuration.
+ // This is why we will pass `modified` as a parameter to initUI() below
+ // in order to ensure these GUI elements are accordingly enabled.
+ bool modified = m_kxkbConfig.setFromXkbOptions(options);
+ if (modified)
+ {
+ TQStringList taintedList;
+ int tainted = m_kxkbConfig.m_tainted;
+
+ // The i18n strings below need to match the tab names
+ if (tainted & KxkbConfig::TAINTED_LAYOUTS)
+ {
+ taintedList << i18n("Layout");
+ }
+
+ if (tainted & KxkbConfig::TAINTED_XKB_OPTS)
+ {
+ taintedList << i18n("Xkb Options");
+ }
+
+ int result = KMessageBox::warningYesNoList(this,
+ i18n("<qt>Some settings have been modified by an external "
+ "application (e.g. <tt>setxkbmap</tt>). Would you like to "
+ "import the currently active settings to the configuration "
+ "dialog?</qt>"),
+ taintedList,
+ i18n("Import current Xkb configuration?"),
+ i18n("Import"), i18n("Discard"),
+ "kxkbImportActiveSettings",
+ KMessageBox::Notify);
+
+ if (result == KMessageBox::No)
+ {
+ m_kxkbConfig.load(KxkbConfig::LOAD_BASIC_OPTIONS);
+ }
+ }
+
keys->readSettings();
- initUI();
+ initUI(modified);
}
-void LayoutConfig::initUI() {
+void LayoutConfig::initUI(bool modified) {
const char* modelName = m_rules->models()[m_kxkbConfig.m_model];
if( modelName == NULL )
modelName = DEFAULT_MODEL;
@@ -226,7 +274,6 @@ void LayoutConfig::initUI() {
for ( ; src_it.current(); ++src_it ) {
TQListViewItem* srcItem = src_it.current();
-
if ( layoutUnit.layout == src_it.current()->text(LAYOUT_COLUMN_MAP) ) { // check if current config knows about this layout
TQListViewItem* newItem = copyLVI(srcItem, widget->listLayoutsDst);
@@ -353,7 +400,7 @@ void LayoutConfig::initUI() {
updateOptionsCommand();
updateHotkeyCombo(true);
- emit TDECModule::changed( false );
+ emit TDECModule::changed(modified);
}
@@ -432,7 +479,8 @@ void LayoutConfig::save()
if (m_forceGrpOverwrite)
{
// First get all the server's options
- TQStringList srvOptions = TQStringList::split(",", XKBExtension::getServerOptions());
+ XkbOptions _opt = XKBExtension::the()->getServerOptions();
+ TQStringList srvOptions = TQStringList::split(",", _opt.options);
TQStringList newOptions;
// Then remove all grp: options
@@ -449,7 +497,7 @@ void LayoutConfig::save()
xkbOptions.options = newOptions.join(",");
xkbOptions.resetOld = true;
- if (!XKBExtension::setXkbOptions(xkbOptions))
+ if (!XKBExtension::the()->setXkbOptions(xkbOptions))
{
kdWarning() << "[LayoutConfig::save] Could not overwrite previous grp: options!" << endl;
}
@@ -1007,7 +1055,8 @@ void LayoutConfig::updateHotkeyCombo(bool initial) {
// Get server options first
if (initial || widget->xkbOptsMode->selectedId() == 1)
{
- TQStringList opts = TQStringList::split(",", XKBExtension::getServerOptions());
+ XkbOptions _opt = XKBExtension::the()->getServerOptions();
+ TQStringList opts = TQStringList::split(",", _opt.options);
for (TQStringList::Iterator it = opts.begin(); it != opts.end(); ++it)
{
TQString option(*it);
@@ -1267,7 +1316,7 @@ extern "C"
kapp->startServiceByDesktopName("kxkb");
}
else {
- if (!XKBExtension::setXkbOptions(m_kxkbConfig.getKXkbOptions())) {
+ if (!XKBExtension::the()->setXkbOptions(m_kxkbConfig.getKXkbOptions())) {
kdDebug() << "Setting XKB options failed!" << endl;
}
}
diff --git a/kxkb/kcmlayout.h b/kxkb/kcmlayout.h
index 256946ad1..0d2e153b2 100644
--- a/kxkb/kcmlayout.h
+++ b/kxkb/kcmlayout.h
@@ -23,11 +23,13 @@ public:
LayoutConfig(TQWidget *parent = 0L, const char *name = 0L);
virtual ~LayoutConfig();
+ void initUI(bool modified = false);
+ virtual TQString handbookDocPath() const;
+
+public slots:
void load();
void save();
void defaults();
- void initUI();
- virtual TQString handbookDocPath() const;
protected:
TQString createOptionString();
diff --git a/kxkb/kxkb.cpp b/kxkb/kxkb.cpp
index 8bced3fea..9db61278a 100644
--- a/kxkb/kxkb.cpp
+++ b/kxkb/kxkb.cpp
@@ -24,7 +24,6 @@ DESCRIPTION
*/
#include <unistd.h>
-#include <stdlib.h>
#include <assert.h>
#include <tqregexp.h>
@@ -70,13 +69,10 @@ KXKBApp::KXKBApp(bool allowStyles, bool GUIenabled)
m_tray(NULL),
kWinModule(NULL)
{
- X11Helper::initializeTranslations();
- m_extension = new XKBExtension();
- if( !m_extension->init() ) {
- kdDebug() << "xkb initialization failed, exiting..." << endl;
- ::exit(1);
- }
- connect(m_extension, TQ_SIGNAL(groupChanged(uint)), this, TQ_SLOT(slotGroupChanged(uint)));
+ X11Helper::initializeTranslations();
+ XKBExtension *xkb = XKBExtension::the();
+ connect(xkb, TQ_SIGNAL(groupChanged(uint)), this, TQ_SLOT(slotGroupChanged(uint)));
+ connect(xkb, TQ_SIGNAL(optionsChanged()), this, TQ_SLOT(slotXkbOptionsChanged()));
m_layoutOwnerMap = new LayoutMap(kxkbConfig);
@@ -97,7 +93,6 @@ KXKBApp::~KXKBApp()
{
delete m_tray;
delete m_rules;
- delete m_extension;
delete m_layoutOwnerMap;
delete kWinModule;
delete keys;
@@ -203,7 +198,7 @@ void KXKBApp::readSettings()
void KXKBApp::applyXkbOptions()
{
XkbOptions options = kxkbConfig.getKXkbOptions();
- if (!m_extension->setXkbOptions(options)) {
+ if (!XKBExtension::the()->setXkbOptions(options)) {
kdWarning() << "Setting XKB options failed!" << endl;
}
}
@@ -236,14 +231,14 @@ bool KXKBApp::setLayout(const LayoutUnit& layoutUnit)
bool KXKBApp::setLayout(const uint group)
{
// If this group is already set, just show the notification and return
- if (m_extension->getGroup() == group) {
+ if (XKBExtension::the()->getGroup() == group) {
if (kxkbConfig.m_enableNotify) {
showLayoutNotification();
}
return true;
}
- bool ok = m_extension->setGroup(group);
+ bool ok = XKBExtension::the()->setGroup(group);
if (!ok) {
TQString layout = kxkbConfig.m_layouts[group].toPair();
if (m_tray) {
@@ -317,7 +312,32 @@ void KXKBApp::slotGroupChanged(uint group)
}
}
-void KXKBApp::showLayoutNotification() {
+void KXKBApp::slotXkbOptionsChanged()
+{
+ TQTimer::singleShot(500, this, TQ_SLOT(syncXkbOptions()));
+}
+
+void KXKBApp::syncXkbOptions()
+{
+ XkbOptions options = XKBExtension::the()->getServerOptions();
+ if (kxkbConfig.setFromXkbOptions(options))
+ {
+ m_layoutOwnerMap->reset();
+ m_tray->initLayoutList(kxkbConfig.m_layouts, *m_rules);
+
+ // This event is not related to layout switching hence the notification-related
+ // options do not apply here, but only options from the system notifications
+ // control module.
+ KNotifyClient::event(
+ m_tray->winId(), "ExternalChange",
+ i18n("An external application has modified the active keyboard configuration"));
+ }
+
+ slotGroupChanged(XKBExtension::the()->getGroup());
+}
+
+void KXKBApp::showLayoutNotification()
+{
bool useKMilo = kxkbConfig.m_notifyUseKMilo && isKMiloAvailable(),
notificationSent = false;
@@ -396,7 +416,6 @@ void KXKBApp::windowChanged(WId winId)
}
}
-
void KXKBApp::slotSettingsChanged(int category)
{
if (category == TDEApplication::SETTINGS_SHORTCUTS) {
@@ -408,7 +427,7 @@ void KXKBApp::slotSettingsChanged(int category)
bool KXKBApp::x11EventFilter(XEvent *e) {
// let the extension process the event and emit signals if necessary
- m_extension->processXEvent(e);
+ XKBExtension::the()->processXEvent(e);
return TDEApplication::x11EventFilter(e);
}
diff --git a/kxkb/kxkb.h b/kxkb/kxkb.h
index b5ac7b7bf..9e10a6c4c 100644
--- a/kxkb/kxkb.h
+++ b/kxkb/kxkb.h
@@ -74,6 +74,7 @@ protected slots:
void menuActivated(int id);
void windowChanged(WId winId);
void slotGroupChanged(uint group);
+ void slotXkbOptionsChanged();
void slotSettingsChanged(int category);
void showLayoutNotification();
@@ -83,6 +84,7 @@ protected slots:
void readSettings();
void applyXkbOptions();
+ void syncXkbOptions();
private:
void initTray();
diff --git a/kxkb/kxkbconfig.cpp b/kxkb/kxkbconfig.cpp
index 166dab174..42de6d8ce 100644
--- a/kxkb/kxkbconfig.cpp
+++ b/kxkb/kxkbconfig.cpp
@@ -33,9 +33,12 @@ const char* DEFAULT_MODEL = "pc104";
void KxkbConfig::load(int loadMode)
{
// INITIAL OPTIONS (loaded regardless of whether KXkb is enabled)
+
TDEConfig *config = new TDEConfig("kxkbrc", true, false);
config->setGroup("Layout");
+ m_tainted = TAINTED_NONE;
+
m_useKxkb = config->readBoolEntry("Use", false);
m_resetOldOptions = config->readBoolEntry("ResetOldOptions", true);
@@ -46,7 +49,8 @@ void KxkbConfig::load(int loadMode)
return;
}
- // ALL OTHER OPTIONS (loaded only when KXkb is enabled)
+ // BASIC OPTIONS (passed to setxkbmap, can be tainted)
+
m_model = config->readEntry("Model", DEFAULT_MODEL);
// Layouts
@@ -93,6 +97,13 @@ void KxkbConfig::load(int loadMode)
}
}
+ if (loadMode == LOAD_BASIC_OPTIONS)
+ {
+ return;
+ }
+
+ // ALL OTHER OPTIONS (of interest only to KXkb itself)
+
// Tray indicator
m_showSingle = config->readBoolEntry("ShowSingle", false);
@@ -169,6 +180,8 @@ void KxkbConfig::load(int loadMode)
void KxkbConfig::save()
{
+ m_tainted = TAINTED_NONE;
+
TDEConfig *config = new TDEConfig("kxkbrc", false, false);
config->setGroup("Layout");
@@ -245,6 +258,8 @@ void KxkbConfig::save()
void KxkbConfig::setDefaults()
{
+ m_tainted = TAINTED_NONE;
+
m_model = DEFAULT_MODEL;
m_resetOldOptions = true;
@@ -267,6 +282,75 @@ void KxkbConfig::setDefaults()
m_stickySwitchingDepth = 2;
}
+bool KxkbConfig::setFromXkbOptions(XkbOptions options)
+{
+ XkbOptions curOptions = getKXkbOptions();
+ uint tainted = TAINTED_NONE;
+
+ // We need to fix the variants string if it is empty, otherwise the
+ // comparison below will often wrongly assume that the variants have
+ // changed
+ if (options.variants.isNull())
+ {
+ for (int i = 0; i < options.layouts.contains(","); ++i)
+ {
+ options.variants += ",";
+ }
+ }
+
+ // Check if keyboard layout options have changed
+ if ((options.model != curOptions.model && !options.model.isNull())
+ || (options.layouts != curOptions.layouts)
+ || (options.variants != curOptions.variants))
+ {
+ tainted |= TAINTED_LAYOUTS;
+ kdWarning() << "Keyboard layouts modified by external application!" << endl;
+
+ kdDebug() << "model " << curOptions.model << "=>" << options.model << endl;
+ kdDebug() << "layouts " << curOptions.layouts << "=>" << options.layouts << endl;
+ kdDebug() << "variants " << curOptions.variants << "=>" << options.variants << endl;
+ m_model = options.model;
+
+ m_layouts.clear();
+
+ TQStringList layouts = TQStringList::split(",", options.layouts, true);
+ TQStringList variants = TQStringList::split(",", options.variants, true);
+ TQStringList::Iterator lit = layouts.begin();
+ TQStringList::Iterator vit = variants.begin();
+
+ while (lit != layouts.end())
+ {
+ TQString layout = *lit;
+ TQString variant = vit != variants.end() ? *vit : TQString::null;
+ m_layouts.append(LayoutUnit(layout, variant));
+
+ ++lit;
+ if (vit != variants.end())
+ {
+ ++vit;
+ }
+ }
+ }
+
+ // Check if Xkb options have changed
+ if (options.options != options.options || options.resetOld != options.resetOld)
+ {
+ tainted |= TAINTED_XKB_OPTS;
+ kdWarning() << "Xkb options modified by external application!" << endl;
+ m_options = options.options;
+ m_resetOldOptions = options.resetOld;
+ }
+
+ if (tainted != TAINTED_NONE)
+ {
+ kdDebug() << "Loaded tainted config with " << m_layouts.count()
+ << " layouts" << endl;
+ m_tainted = tainted;
+ }
+
+ return (tainted != TAINTED_NONE);
+}
+
TQStringList KxkbConfig::getLayoutStringList(/*bool compact*/)
{
TQStringList layoutList;
@@ -325,7 +409,6 @@ const XkbOptions KxkbConfig::getKXkbOptions() {
options.variants = variants.join(",");
options.model = m_model;
options.options = m_options;
- kdDebug() << "[getKXkbOptions] options: " << m_options << endl;
options.resetOld = m_resetOldOptions;
return options;
}
diff --git a/kxkb/kxkbconfig.h b/kxkb/kxkbconfig.h
index a365ef252..229824b77 100644
--- a/kxkb/kxkbconfig.h
+++ b/kxkb/kxkbconfig.h
@@ -45,7 +45,8 @@ extern const char* DEFAULT_MODEL;
class KxkbConfig
{
public:
- enum { LOAD_INIT_OPTIONS, LOAD_ALL_OPTIONS };
+ enum { LOAD_INIT_OPTIONS, LOAD_BASIC_OPTIONS, LOAD_ALL_OPTIONS };
+ enum { TAINTED_NONE = 0, TAINTED_LAYOUTS, TAINTED_XKB_OPTS };
bool m_useKxkb;
bool m_showSingle;
@@ -74,6 +75,8 @@ public:
TQString m_options;
TQValueList<LayoutUnit> m_layouts;
+ uint m_tainted;
+
void load(int loadMode);
void save();
void setDefaults();
@@ -82,6 +85,7 @@ public:
static TQString getDefaultDisplayName(const TQString& code_);
static TQString getDefaultDisplayName(const LayoutUnit& layoutUnit, bool single=false);
+ bool setFromXkbOptions(XkbOptions options);
const XkbOptions getKXkbOptions();
private: