diff options
Diffstat (limited to 'kxkb/kxkb.cpp')
-rw-r--r-- | kxkb/kxkb.cpp | 532 |
1 files changed, 302 insertions, 230 deletions
diff --git a/kxkb/kxkb.cpp b/kxkb/kxkb.cpp index 6a37dbf3a..6aa5b8ee2 100644 --- a/kxkb/kxkb.cpp +++ b/kxkb/kxkb.cpp @@ -24,29 +24,35 @@ DESCRIPTION */ #include <unistd.h> -#include <stdlib.h> #include <assert.h> #include <tqregexp.h> #include <tqfile.h> #include <tqstringlist.h> #include <tqimage.h> +#include <tqtimer.h> #include <tdeaboutdata.h> #include <tdecmdlineargs.h> +#ifdef WITH_TDEHWLIB +#include <tdehardwaredevices.h> +#endif #include <tdeglobal.h> -#include <kglobalaccel.h> +#include <tdeglobalaccel.h> #include <tdelocale.h> -#include <kprocess.h> +#include <tdeprocess.h> #include <twinmodule.h> #include <twin.h> #include <tdetempfile.h> -#include <kstandarddirs.h> +#include <tdestandarddirs.h> #include <kipc.h> #include <tdeaction.h> #include <tdepopupmenu.h> #include <kdebug.h> #include <tdeconfig.h> +#include <knotifyclient.h> +#include <dcopclient.h> +#include <dcopref.h> #include "x11helper.h" #include "kxkb.h" @@ -59,227 +65,337 @@ DESCRIPTION KXKBApp::KXKBApp(bool allowStyles, bool GUIenabled) - : KUniqueApplication(allowStyles, GUIenabled), + : TDEUniqueApplication(allowStyles, GUIenabled), m_prevWinId(X11Helper::UNKNOWN_WINDOW_ID), - m_rules(NULL), - m_tray(NULL), - kWinModule(NULL), - m_forceSetXKBMap( false ) + m_rules(nullptr), + m_tray(nullptr), + kWinModule(nullptr) { - m_extension = new XKBExtension(); - if( !m_extension->init() ) { - kdDebug() << "xkb initialization failed, exiting..." << endl; - ::exit(1); - } - - // keep in sync with kcmlayout.cpp - keys = new TDEGlobalAccel(TQT_TQOBJECT(this)); -#include "kxkbbindings.cpp" - keys->updateConnections(); + 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(slotSyncXkbOptions())); m_layoutOwnerMap = new LayoutMap(kxkbConfig); - connect( this, TQT_SIGNAL(settingsChanged(int)), TQT_SLOT(slotSettingsChanged(int)) ); + // keep in sync with kcmlayout.cpp + keys = new TDEGlobalAccel(this); +#include "kxkbbindings.cpp" + + connect( this, TQ_SIGNAL(settingsChanged(int)), TQ_SLOT(slotSettingsChanged(int)) ); addKipcEventMask( KIPC::SettingsChanged ); -} +#if WITH_TDEHWLIB + TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); + connect(hwdevices, TQ_SIGNAL(hardwareAdded(TDEGenericDevice*)), this, TQ_SLOT(hardwareChanged(TDEGenericDevice*))); + connect(hwdevices, TQ_SIGNAL(hardwareRemoved(TDEGenericDevice*)), this, TQ_SLOT(hardwareChanged(TDEGenericDevice*))); + connect(hwdevices, TQ_SIGNAL(hardwareUpdated(TDEGenericDevice*)), this, TQ_SLOT(hardwareChanged(TDEGenericDevice*))); +#endif +} KXKBApp::~KXKBApp() { -// deletePrecompiledLayouts(); - - delete keys; - delete m_tray; - delete m_rules; - delete m_extension; + delete m_tray; + delete m_rules; delete m_layoutOwnerMap; delete kWinModule; + delete keys; } int KXKBApp::newInstance() { - m_extension->reset(); - - if( settingsRead() ) - layoutApply(); - + readSettings(); return 0; } -bool KXKBApp::settingsRead() +void KXKBApp::readSettings() { - kxkbConfig.load( KxkbConfig::LOAD_ACTIVE_OPTIONS ); + // Xkb options + kxkbConfig.load(KxkbConfig::LOAD_INIT_OPTIONS); + + if (!kxkbConfig.m_useKxkb) + { + kdDebug() << "kxkb is disabled, applying xkb options and exiting" << endl; + applyXkbOptions(); + quit(); + return; + } + + kdDebug() << "applying xkb options and layouts" << endl; + kxkbConfig.load(KxkbConfig::LOAD_ALL_OPTIONS); + applyXkbOptions(); + + // Active window watcher + m_prevWinId = X11Helper::UNKNOWN_WINDOW_ID; + + if (kxkbConfig.m_switchingPolicy == SWITCH_POLICY_GLOBAL) + { + delete kWinModule; + kWinModule = nullptr; + } + + else + { + TQDesktopWidget desktopWidget; + if (desktopWidget.numScreens() > 1 && !desktopWidget.isVirtualDesktop()) + { + kdWarning() << "With non-virtual desktop only global switching policy supported on non-primary screens" << endl; + //TODO: find out how to handle that + } - if( kxkbConfig.m_enableXkbOptions ) { - kdDebug() << "Setting XKB options " << kxkbConfig.m_options << endl; - if( !m_extension->setXkbOptions(kxkbConfig.m_options, kxkbConfig.m_resetOldOptions) ) { - kdDebug() << "Setting XKB options failed!" << endl; + if (!kWinModule) + { + kWinModule = new KWinModule(nullptr, KWinModule::INFO_DESKTOP); + connect(kWinModule, TQ_SIGNAL(activeWindowChanged(WId)), TQ_SLOT(windowChanged(WId))); } + + m_prevWinId = kWinModule->activeWindow(); + kdDebug() << "Active window " << m_prevWinId << endl; } - if ( kxkbConfig.m_useKxkb == false ) { - kapp->quit(); - return false; + // Init layout owner map + m_layoutOwnerMap->reset(); + m_layoutOwnerMap->setCurrentWindow( m_prevWinId ); + + // Init rules + if (!m_rules) + { + m_rules = new XkbRules(false); } - - m_prevWinId = X11Helper::UNKNOWN_WINDOW_ID; - - if( kxkbConfig.m_switchingPolicy == SWITCH_POLICY_GLOBAL ) { - delete kWinModule; - kWinModule = NULL; - } - else { - TQDesktopWidget desktopWidget; - if( desktopWidget.numScreens() > 1 && desktopWidget.isVirtualDesktop() == false ) { - kdWarning() << "With non-virtual desktop only global switching policy supported on non-primary screens" << endl; - //TODO: find out how to handle that - } - - if( kWinModule == NULL ) { - kWinModule = new KWinModule(0, KWinModule::INFO_DESKTOP); - connect(kWinModule, TQT_SIGNAL(activeWindowChanged(WId)), TQT_SLOT(windowChanged(WId))); - } - m_prevWinId = kWinModule->activeWindow(); - kdDebug() << "Active window " << m_prevWinId << endl; - } - - m_layoutOwnerMap->reset(); - m_layoutOwnerMap->setCurrentWindow( m_prevWinId ); - - if( m_rules == NULL ) - m_rules = new XkbRules(false); - - for(int ii=0; ii<(int)kxkbConfig.m_layouts.count(); ii++) { - LayoutUnit& layoutUnit = kxkbConfig.m_layouts[ii]; - layoutUnit.defaultGroup = m_rules->getDefaultGroup(layoutUnit.layout, layoutUnit.includeGroup); - kdDebug() << "default group for " << layoutUnit.toPair() << " is " << layoutUnit.defaultGroup << endl; - } - - m_currentLayout = kxkbConfig.getDefaultLayout(); - - if( kxkbConfig.m_layouts.count() == 1 ) { - TQString layoutName = m_currentLayout.layout; - TQString variantName = m_currentLayout.variant; - TQString includeName = m_currentLayout.includeGroup; - int group = m_currentLayout.defaultGroup; - - if( !m_extension->setLayout(kxkbConfig.m_model, layoutName, variantName, includeName, false) - || !m_extension->setGroup( group ) ) { - kdDebug() << "Error switching to single layout " << m_currentLayout.toPair() << endl; - // TODO: alert user - } - - if( kxkbConfig.m_showSingle == false ) { - kapp->quit(); - return false; - } - } - else { -// initPrecompiledLayouts(); - } - initTray(); - - TDEGlobal::config()->reparseConfiguration(); // kcontrol modified kdeglobals - keys->readSettings(); - keys->updateConnections(); + // Init layouts + for (int i = 0; i < kxkbConfig.m_layouts.count(); i++) + { + LayoutUnit& layoutUnit = kxkbConfig.m_layouts[i]; + } + + m_currentLayout = kxkbConfig.m_layouts[0]; + setLayout(m_currentLayout); + + kdDebug() << "default layout is " << m_currentLayout.toPair() << endl; - return true; + if (kxkbConfig.m_layouts.count() == 1 && !kxkbConfig.m_showSingle) + { + quit(); + return; + } + + TDEGlobal::config()->reparseConfiguration(); // kcontrol modified kdeglobals + + // Init tray + if (!m_tray) + { + m_tray = new KxkbSystemTray(&kxkbConfig); + connect(m_tray, TQ_SIGNAL(menuActivated(int)), this, TQ_SLOT(menuActivated(int))); + connect(m_tray, TQ_SIGNAL(toggled()), this, TQ_SLOT(nextLayout())); + } + + m_tray->initLayoutList(kxkbConfig.m_layouts, *m_rules); + m_tray->setCurrentLayout(m_currentLayout); + m_tray->show(); + + // Init keybindings + keys->readSettings(); + keys->updateConnections(); } -void KXKBApp::initTray() +void KXKBApp::applyXkbOptions() { - if( !m_tray ) - { - KSystemTray* sysTray = new KxkbSystemTray(); - TDEPopupMenu* popupMenu = sysTray->contextMenu(); - // popupMenu->insertTitle( kapp->miniIcon(), kapp->caption() ); - - m_tray = new KxkbLabelController(sysTray, popupMenu); - connect(popupMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(menuActivated(int))); - connect(sysTray, TQT_SIGNAL(toggled()), this, TQT_SLOT(toggled())); - } - - m_tray->setShowFlag(kxkbConfig.m_showFlag); - m_tray->initLayoutList(kxkbConfig.m_layouts, *m_rules); - m_tray->setCurrentLayout(m_currentLayout); - m_tray->show(); + XkbOptions options = kxkbConfig.getKXkbOptions(); + if (!XKBExtension::the()->setXkbOptions(options)) { + kdWarning() << "Setting XKB options failed!" << endl; + } } -// This function activates the keyboard layout specified by the -// configuration members (m_currentLayout) -void KXKBApp::layoutApply() +void KXKBApp::hardwareChanged(TDEGenericDevice *dev) { - setLayout(m_currentLayout); +# if WITH_TDEHWLIB + if (dev->type() == TDEGenericDeviceType::Keyboard) + { + TQTimer::singleShot(500, this, TQ_SLOT(applyXkbOptions())); + } +# endif } // kdcop bool KXKBApp::setLayout(const TQString& layoutPair) { - const LayoutUnit layoutUnitKey(layoutPair); - if( kxkbConfig.m_layouts.contains(layoutUnitKey) ) { - return setLayout( *kxkbConfig.m_layouts.find(layoutUnitKey) ); - } - return false; + return setLayout((LayoutUnit)layoutPair); } - // Activates the keyboard layout specified by 'layoutUnit' -bool KXKBApp::setLayout(const LayoutUnit& layoutUnit, int group) +bool KXKBApp::setLayout(const LayoutUnit& layoutUnit) { - bool res = false; - - if( group == -1 ) - group = layoutUnit.defaultGroup; - - res = m_extension->setLayout(kxkbConfig.m_model, - layoutUnit.layout, layoutUnit.variant, - layoutUnit.includeGroup); - if( res ) - m_extension->setGroup(group); // not checking for ret - not important - - if( res ) - m_currentLayout = layoutUnit; + const int group = kxkbConfig.m_layouts.findIndex(layoutUnit); + if (group >= 0) { + return setLayout(group); + } + return false; +} - if (m_tray) { - if( res ) - m_tray->setCurrentLayout(layoutUnit); - else - m_tray->setError(layoutUnit.toPair()); - } +// Activates the keyboard layout specified by group number +bool KXKBApp::setLayout(const uint group) +{ + // If this group is already set, just show the notification and return + if (XKBExtension::the()->getGroup() == group) { + if (kxkbConfig.m_enableNotify) { + showLayoutNotification(); + } + return true; + } + + bool ok = XKBExtension::the()->setGroup(group); + if (!ok) { + TQString layout = kxkbConfig.m_layouts[group].toPair(); + if (m_tray) { + m_tray->setError(layout); + } + + if (kxkbConfig.m_enableNotify) { + showErrorNotification(layout); + } + } + return ok; +} - return res; +void KXKBApp::nextLayout() +{ + const LayoutUnit& layout = m_layoutOwnerMap->getNextLayout().layoutUnit; + setLayout(layout); } -void KXKBApp::toggled() +void KXKBApp::prevLayout() { - const LayoutUnit& layout = m_layoutOwnerMap->getNextLayout().layoutUnit; - setLayout(layout); + const LayoutUnit& layout = m_layoutOwnerMap->getPrevLayout().layoutUnit; + setLayout(layout); } void KXKBApp::menuActivated(int id) { - if( KxkbLabelController::START_MENU_ID <= id - && id < KxkbLabelController::START_MENU_ID + (int)kxkbConfig.m_layouts.count() ) - { - const LayoutUnit& layout = kxkbConfig.m_layouts[id - KxkbLabelController::START_MENU_ID]; - m_layoutOwnerMap->setCurrentLayout( layout ); - setLayout( layout ); - } - else if (id == KxkbLabelController::CONFIG_MENU_ID) + if (id >= KxkbSystemTray::START_MENU_ID && + id < KxkbSystemTray::START_MENU_ID + kxkbConfig.m_layouts.count()) + { + setLayout(id - KxkbSystemTray::START_MENU_ID); + } + else if (id == KxkbSystemTray::CONFIG_MENU_ID) { TDEProcess p; p << "tdecmshell" << "keyboard_layout"; p.start(TDEProcess::DontCare); - } - else if (id == KxkbLabelController::HELP_MENU_ID) + } + else if (id == KxkbSystemTray::HELP_MENU_ID) + { + invokeHelp(0, "kxkb"); + } + else + { + quit(); + } +} + +void KXKBApp::slotGroupChanged(uint group) +{ + if (!kxkbConfig.m_layouts.count()) { + kdError() << "[kxkb] no layout found!" << endl; + return; + } + + if (group >= kxkbConfig.m_layouts.count()) { + kdError() << "[kxkb] unknown group requested: " << group << endl; + if (m_tray) + { + m_tray->setError(i18n("Unknown")); + } + if (kxkbConfig.m_enableNotify) + { + showErrorNotification(i18n("Unknown")); + } + return; + } + + m_currentLayout = kxkbConfig.m_layouts[group]; + m_layoutOwnerMap->setCurrentLayout(m_currentLayout); + + if (m_tray) { + m_tray->setCurrentLayout(m_currentLayout); + } + + if (kxkbConfig.m_enableNotify) { + showLayoutNotification(); + } +} + +void KXKBApp::slotSyncXkbOptions() +{ + // Make sure the X11 server has had enough time to apply the change + TQTimer::singleShot(100, this, TQ_SLOT(syncXkbOptions())); +} + +void KXKBApp::syncXkbOptions() +{ + XkbOptions options = XKBExtension::the()->getServerOptions(); + if (kxkbConfig.setFromXkbOptions(options)) { - TDEApplication::kApplication()->invokeHelp(0, "kxkb"); + m_layoutOwnerMap->reset(); + if (m_tray) + { + m_tray->initLayoutList(kxkbConfig.m_layouts, *m_rules); + } } -// else -// { -// quit(); -// } + slotGroupChanged(XKBExtension::the()->getGroup()); +} + +void KXKBApp::showLayoutNotification() +{ + bool useKMilo = kxkbConfig.m_notifyUseKMilo && isKMiloAvailable(), + notificationSent = false; + + TQString layoutName(m_rules->getLayoutName(m_currentLayout)); + + if (useKMilo) { + DCOPRef kmilo("kded", "kmilod"); + if (kmilo.send("displayText(TQString,TQPixmap)", layoutName, miniIcon())) { + notificationSent = true; + } + } + + if (!notificationSent) { + WId wid = (m_tray ? m_tray->winId() : 0); + KNotifyClient::event(wid, "LayoutChange", layoutName); + } +} + +void KXKBApp::showErrorNotification(TQString layout) { + bool useKMilo = kxkbConfig.m_notifyUseKMilo && isKMiloAvailable(), + notificationSent = false; + + if (useKMilo) { + DCOPRef kmilo("kded", "kmilod"); + if (kmilo.send("displayText(TQString,TQPixmap)", i18n("Error changing keyboard layout to '%1'").arg(layout), miniIcon())) { + notificationSent = true; + } + } + + if (!notificationSent) { + WId wid = (m_tray ? m_tray->winId() : 0); + KNotifyClient::event(wid, "Error"); + } +} + +bool KXKBApp::isKMiloAvailable() { + QCStringList modules; + TQCString replyType; + TQByteArray replyData; + if (dcopClient()->call("kded", "kded", "loadedModules()", + TQByteArray(), replyType, replyData)) + { + if (replyType == "QCStringList") { + TQDataStream reply(replyData, IO_ReadOnly); + reply >> modules; + return modules.contains("kmilod"); + } + } + return false; } // TODO: we also have to handle deleted windows @@ -291,88 +407,44 @@ void KXKBApp::windowChanged(WId winId) return; } - int group = m_extension->getGroup(); - kdDebug() << "old WinId: " << m_prevWinId << ", new WinId: " << winId << endl; - - if( m_prevWinId != X11Helper::UNKNOWN_WINDOW_ID ) { // saving layout/group from previous window -// kdDebug() << "storing " << m_currentLayout.toPair() << ":" << group << " for " << m_prevWinId << endl; + + if( m_prevWinId != X11Helper::UNKNOWN_WINDOW_ID ) { // saving layout from previous window // m_layoutOwnerMap->setCurrentWindow(m_prevWinId); m_layoutOwnerMap->setCurrentLayout(m_currentLayout); - m_layoutOwnerMap->setCurrentGroup(group); } - + m_prevWinId = winId; if( winId != X11Helper::UNKNOWN_WINDOW_ID ) { m_layoutOwnerMap->setCurrentWindow(winId); const LayoutState& layoutState = m_layoutOwnerMap->getCurrentLayout(); - + if( layoutState.layoutUnit != m_currentLayout ) { - kdDebug() << "switching to " << layoutState.layoutUnit.toPair() << ":" << group << " for " << winId << endl; - setLayout( layoutState.layoutUnit, layoutState.group ); - } - else if( layoutState.group != group ) { // we need to change only the group - m_extension->setGroup(layoutState.group); + kdDebug() << "switching to " << layoutState.layoutUnit.toPair() << " for " << winId << endl; + setLayout(layoutState.layoutUnit); } } } - void KXKBApp::slotSettingsChanged(int category) { - if ( category != TDEApplication::SETTINGS_SHORTCUTS) - return; - - TDEGlobal::config()->reparseConfiguration(); // kcontrol modified kdeglobals - keys->readSettings(); - keys->updateConnections(); + if (category == TDEApplication::SETTINGS_SHORTCUTS) { + TDEGlobal::config()->reparseConfiguration(); // kcontrol modified kdeglobals + keys->readSettings(); + keys->updateConnections(); + } } -/* - Viki (onscreen keyboard) has problems determining some modifiers states - when kxkb uses precompiled layouts instead of setxkbmap. Probably a bug - in the xkb functions used for the precompiled layouts *shrug*. -*/ -void KXKBApp::forceSetXKBMap( bool set ) -{ - if( m_forceSetXKBMap == set ) - return; - m_forceSetXKBMap = set; - layoutApply(); +bool KXKBApp::x11EventFilter(XEvent *e) { + // let the extension process the event and emit signals if necessary + XKBExtension::the()->processXEvent(e); + return TDEApplication::x11EventFilter(e); } -/*Precompiles the keyboard layouts for faster activation later. -This is done by loading each one of them and then dumping the compiled -map from the X server into our local buffer.*/ -// void KXKBApp::initPrecompiledLayouts() -// { -// TQStringList dirs = TDEGlobal::dirs()->findDirs ( "tmp", "" ); -// TQString tempDir = dirs.count() == 0 ? "/tmp/" : dirs[0]; -// -// TQValueList<LayoutUnit>::ConstIterator end = kxkbConfig.m_layouts.end(); -// -// for (TQValueList<LayoutUnit>::ConstIterator it = kxkbConfig.m_layouts.begin(); it != end; ++it) -// { -// LayoutUnit layoutUnit(*it); -// // const char* baseGr = m_includes[layout]; -// // int group = m_rules->getGroup(layout, baseGr); -// // if( m_extension->setLayout(m_model, layout, m_variants[layout], group, baseGr) ) { -// TQString compiledLayoutFileName = tempDir + layoutUnit.layout + "." + layoutUnit.variant + ".xkm"; -// // if( m_extension->getCompiledLayout(compiledLayoutFileName) ) -// m_compiledLayoutFileNames[layoutUnit.toPair()] = compiledLayoutFileName; -// // } -// // else { -// // kdDebug() << "Error precompiling layout " << layout << endl; -// // } -// } -// } - - -const char * DESCRIPTION = - I18N_NOOP("A utility to switch keyboard maps"); - -extern "C" KDE_EXPORT int kdemain(int argc, char *argv[]) +const char *DESCRIPTION = I18N_NOOP("A utility to switch keyboard maps"); + +extern "C" TDE_EXPORT int kdemain(int argc, char *argv[]) { TDEAboutData about("kxkb", I18N_NOOP("TDE Keyboard Tool"), "1.0", DESCRIPTION, TDEAboutData::License_LGPL, |