/* Copyright (C) 2001, S.R.Haque . Derived from an original by Matthias H�zer-Klpfel released under the QPL. This file is part of the KDE project 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. DESCRIPTION KDE Keyboard Tool. Manages XKB keyboard mappings. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "x11helper.h" #include "kxkb.h" #include "extension.h" #include "rules.h" #include "kxkbconfig.h" #include "layoutmap.h" #include "kxkb.moc" KXKBApp::KXKBApp(bool allowStyles, bool GUIenabled) : KUniqueApplication(allowStyles, GUIenabled), m_prevWinId(X11Helper::UNKNOWN_WINDOW_ID), m_rules(NULL), 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))); m_layoutOwnerMap = new LayoutMap(kxkbConfig); // 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 ); TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices(); connect(hwdevices, TQ_SIGNAL(hardwareAdded(TDEGenericDevice*)), this, TQ_SLOT(hardwareAdded(TDEGenericDevice*))); connect(hwdevices, TQ_SIGNAL(hardwareRemoved(TDEGenericDevice*)), this, TQ_SLOT(hardwareRemoved(TDEGenericDevice*))); connect(hwdevices, TQ_SIGNAL(hardwareUpdated(TDEGenericDevice*)), this, TQ_SLOT(hardwareUpdated(TDEGenericDevice*))); } KXKBApp::~KXKBApp() { delete m_tray; delete m_rules; delete m_extension; delete m_layoutOwnerMap; delete kWinModule; delete keys; } int KXKBApp::newInstance() { if (settingsRead()) { setLayout(m_currentLayout); } return 0; } bool KXKBApp::settingsRead() { kxkbConfig.load(KxkbConfig::LOAD_ALL); applySettings(); if ( kxkbConfig.m_useKxkb == false ) { kapp->quit(); return 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, TQ_SIGNAL(activeWindowChanged(WId)), TQ_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]; } m_currentLayout = kxkbConfig.m_layouts[0]; kdDebug() << "default layout is " << m_currentLayout.toPair() << endl; if( kxkbConfig.m_layouts.count() == 1 && !kxkbConfig.m_showSingle) { kapp->quit(); return false; } initTray(); TDEGlobal::config()->reparseConfiguration(); // kcontrol modified kdeglobals keys->readSettings(); keys->updateConnections(); return true; } void KXKBApp::applySettings() { XkbOptions options = kxkbConfig.getKXkbOptions(); if (!m_extension->setXkbOptions(options)) { kdWarning() << "Setting XKB options failed!" << endl; } } void KXKBApp::initTray() { 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(); } void KXKBApp::hardwareAdded(TDEGenericDevice *dev) { if (dev->type() == TDEGenericDeviceType::Keyboard) { kdDebug() << "keyboard attached: " << dev->friendlyName() << endl; applySettings(); } } void KXKBApp::hardwareRemoved(TDEGenericDevice *dev) { if (dev->type() == TDEGenericDeviceType::Keyboard) { kdDebug() << "keyboard removed: " << dev->friendlyName() << endl; applySettings(); } } void KXKBApp::hardwareUpdated(TDEGenericDevice *dev) { if (dev->type() == TDEGenericDeviceType::Keyboard) { kdDebug() << "keyboard updated: " << dev->friendlyName() << endl; applySettings(); } } // kdcop bool KXKBApp::setLayout(const TQString& layoutPair) { return setLayout((LayoutUnit)layoutPair); } // Activates the keyboard layout specified by 'layoutUnit' bool KXKBApp::setLayout(const LayoutUnit& layoutUnit) { const int group = kxkbConfig.m_layouts.findIndex(layoutUnit); if (group >= 0) { return setLayout(group); } return false; } // 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 (m_extension->getGroup() == group) { if (kxkbConfig.m_enableNotify) { showLayoutNotification(); } return true; } bool ok = m_extension->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; } void KXKBApp::nextLayout() { const LayoutUnit& layout = m_layoutOwnerMap->getNextLayout().layoutUnit; setLayout(layout); } void KXKBApp::prevLayout() { const LayoutUnit& layout = m_layoutOwnerMap->getPrevLayout().layoutUnit; setLayout(layout); } void KXKBApp::menuActivated(int 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 == KxkbSystemTray::HELP_MENU_ID) { TDEApplication::kApplication()->invokeHelp(0, "kxkb"); } } void KXKBApp::slotGroupChanged(uint group) { kdDebug() << "slotGroupChanged: " << group << ", layout count: " << kxkbConfig.m_layouts.count() << endl; if (group >= kxkbConfig.m_layouts.count()) { 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::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, kapp->miniIcon())) { notificationSent = true; } } if (!notificationSent) { KNotifyClient::event(m_tray->winId(), "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), kapp->miniIcon())) { notificationSent = true; } } if (!notificationSent) { KNotifyClient::event(m_tray->winId(), "Error"); } } bool KXKBApp::isKMiloAvailable() { QCStringList modules; TQCString replyType; TQByteArray replyData; if (kapp->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 void KXKBApp::windowChanged(WId winId) { // kdDebug() << "window switch" << endl; if( kxkbConfig.m_switchingPolicy == SWITCH_POLICY_GLOBAL ) { // should not happen actually kdDebug() << "windowChanged() signal in GLOBAL switching policy" << endl; return; } kdDebug() << "old WinId: " << m_prevWinId << ", new WinId: " << winId << endl; if( m_prevWinId != X11Helper::UNKNOWN_WINDOW_ID ) { // saving layout from previous window // m_layoutOwnerMap->setCurrentWindow(m_prevWinId); m_layoutOwnerMap->setCurrentLayout(m_currentLayout); } 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() << " for " << winId << endl; setLayout(layoutState.layoutUnit); } } } void KXKBApp::slotSettingsChanged(int category) { if (category == TDEApplication::SETTINGS_SHORTCUTS) { TDEGlobal::config()->reparseConfiguration(); // kcontrol modified kdeglobals keys->readSettings(); keys->updateConnections(); } } bool KXKBApp::x11EventFilter(XEvent *e) { // let the extension process the event and emit signals if necessary m_extension->processXEvent(e); return TDEApplication::x11EventFilter(e); } 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, "Copyright (C) 2001, S.R.Haque\n(C) 2002-2003, 2006 Andriy Rysin"); TDECmdLineArgs::init(argc, argv, &about); KXKBApp::addCmdLineOptions(); if (!KXKBApp::start()) return 0; KXKBApp app; app.disableSessionManagement(); app.exec(); return 0; }