/******************************************************************************* Xkb extension for KXkb Copyright © 2009-2025 Trinity Desktop project Copyright © 2001 S.R. Haque 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 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extension.h" extern "C" { static int IgnoreXError(Display *, XErrorEvent *) { return 0; } } static TQString getLayoutKey(const TQString& layout, const TQString& variant) { return layout + "." + variant; } static XKBExtension *xkbExtension = nullptr; XKBExtension *XKBExtension::the() { if (!xkbExtension) { xkbExtension = new XKBExtension; if (!xkbExtension->init()) { kdFatal() << "xkb initialization failed, exiting..." << endl; ::exit(1); } } return xkbExtension; } bool XKBExtension::init() { m_configureLock = 0; acquireConfigureLock(); 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; } // 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(nullptr); // Watch for interesting events XkbSelectEventDetails(m_dpy, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask); XkbSelectEventDetails(m_dpy, XkbUseCoreKbd, XkbNewKeyboardNotify, XkbAllNewKeyboardEventsMask, XkbAllNewKeyboardEventsMask); // TQStringList dirs = TDEGlobal::dirs()->findDirs ( "tmp", "" ); // m_tempDir = dirs.count() == 0 ? "/tmp/" : dirs[0]; m_tempDir = locateLocal("tmp", ""); releaseConfigureLock(); return true; } XKBExtension::~XKBExtension() { /* if( m_compiledLayoutFileNames.isEmpty() == false ) deletePrecompiledLayouts();*/ } void XKBExtension::acquireConfigureLock() { ++m_configureLock; } void XKBExtension::releaseConfigureLock() { // Without this protection in place KXkb would react to configuration // changes caused by itself TQTimer::singleShot(500, this, TQ_SLOT(slotReleaseConfigureLock())); } void XKBExtension::slotReleaseConfigureLock() { --m_configureLock; } bool XKBExtension::setXkbOptions(const XkbOptions options) { acquireConfigureLock(); TQString exe = TDEGlobal::dirs()->findExe("setxkbmap"); if (exe.isEmpty()) { return false; } 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); releaseConfigureLock(); 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); } uint XKBExtension::getGroup() const { XkbStateRec xkbState; XkbGetState(m_dpy, XkbUseCoreKbd, &xkbState); return xkbState.group; } bool XKBExtension::kcmlayoutRunning() { return kapp->dcopClient()->isApplicationRegistered("TDECModuleProxy-keyboard_layout"); } // 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); } else if (xkb_event->any.xkb_type == XkbNewKeyboardNotify) { if (m_configureLock > 0 || kcmlayoutRunning()) { return; } acquireConfigureLock(); emit optionsChanged(); releaseConfigureLock(); } } #include "extension.moc"