diff options
author | Mavridis Philippe <mavridisf@gmail.com> | 2024-10-28 14:40:20 +0200 |
---|---|---|
committer | Mavridis Philippe <mavridisf@gmail.com> | 2025-04-03 11:47:21 +0000 |
commit | aed24cdf3fd4615b0ac42fad074f20c0427cc3ac (patch) | |
tree | 21f348286454071ffbf35ae475b9b041695a19b5 /kxkb/extension.cpp | |
parent | 1686be58c6c34303f33803db2bc8688e634bf6cd (diff) | |
download | tdebase-aed24cdf3fd4615b0ac42fad074f20c0427cc3ac.tar.gz tdebase-aed24cdf3fd4615b0ac42fad074f20c0427cc3ac.zip |
Kxkb: layout switching and UI bugfixes and minor refactoring
* Layout switching
- Improved layout change logic (indicator is now always updated when the actual switch occurs). This fixes layout switching triggered by the X11 shortcut not being synchronized with layout switching from the tray icon click and the TDE shortcut.
- Kxkb will ignore XkbStateNotify events not related to XkbGroupState which caused strange behaviour with the system tray context menu.
- Reapply Xkb settings when a keyboard device changes state
- Do not run setxkbmap without arguments
- Catch and process changes to Xkb layouts and options
- Always check for Xkb opcode in X11 events, this fixes invalid group issue (Michele Calgaro)
* Tray indicator
- Do not reload all Kxkb settings every time we are requested to get a pixmap!
- Tray indicator pixmap manager improvements
- Fix Quit tray icon menu item
* Configuration
- Optimize settings reloading
- Do not reload settings every time getKxkbOptions() is called; if settings actually need to be re-read, it must be done maunally before calling this function
- Use pointer to global KxkbConfig instance instead of keeping own copy
- Fixed optimized loading of initial settings using KxkbConfig::LOAD_INIT_OPTIONS (I had sort of broken it in the past)
- Removed unused KxkbConfig::LOAD_ACTIVE_OPTIONS
- `newInstance()` now delegates calling setLayout() to readSettings()
- Merged `initTray()` into `readSettings()` - no reason to exist as separate function
* Refactoring
- Merged KxkbLabelController into KxkbSystemTray
- Rename LayoutIcon to LayoutIconManager for clarity
- Minor code cleanup in LayoutIconManager
- Remove use of singleton pattern for LayoutIconManager
- Make XKBExtension a singleton.
- 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
- No need to use `tdeApp` pointer in KXKBApp (KUniqueApplication) class
- Consistent code style and more appropriate function names and return types
- Commented option sections for clarity
- Removed superfluous debug messages
- Add proper copyright header to extension.*
* Settings GUI
- Make "Transparent background" checkbox available for theme colors in the GUI
- Add customization options "Stretch flag", "Dim flag", "Show indicator bevel"
- Disable KMilo checkbox when layout notifications disabled
- Fix reading settings for TDE layout hotkeys
This resolves #547.
Signed-off-by: Mavridis Philippe <mavridisf@gmail.com>
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'kxkb/extension.cpp')
-rw-r--r-- | kxkb/extension.cpp | 230 |
1 files changed, 170 insertions, 60 deletions
diff --git a/kxkb/extension.cpp b/kxkb/extension.cpp index da0bf73c2..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,10 +34,13 @@ #include <tqmap.h> #include <tqfile.h> #include <tqdir.h> +#include <tqtimer.h> #include <kdebug.h> +#include <tdeapplication.h> #include <tdestandarddirs.h> #include <tdeprocess.h> +#include <dcopclient.h> #include <X11/Xatom.h> #include <X11/Xos.h> @@ -21,58 +53,77 @@ #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) +static XKBExtension *xkbExtension = nullptr; + +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); - - // watch group change events - XkbSelectEventDetails(m_dpy, XkbUseCoreKbd, XkbStateNotify, - XkbAllStateComponentsMask, XkbGroupStateMask); - - return true; + m_configureFilterCounter = 0; + + 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; + 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() @@ -81,11 +132,32 @@ XKBExtension::~XKBExtension() deletePrecompiledLayouts();*/ } +void XKBExtension::enableConfigureFilter() +{ + ++m_configureFilterCounter; +} + +void XKBExtension::disableConfigureFilter() +{ + // 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_configureFilterCounter; +} + bool XKBExtension::setXkbOptions(const XkbOptions options) { + enableConfigureFilter(); + TQString exe = TDEGlobal::dirs()->findExe("setxkbmap"); if (exe.isEmpty()) + { return false; + } TDEProcess p; p << exe; @@ -101,7 +173,7 @@ bool XKBExtension::setXkbOptions(const XkbOptions options) p << "-variant"; p << options.variants; } - + if (!options.model.isEmpty()) { p << "-model"; p << options.model; @@ -119,7 +191,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) @@ -136,44 +209,81 @@ bool XKBExtension::setXkbOptions(const XkbOptions options) } } + 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); } -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)) + { + 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; } -/** Examines an X Event passed to it and takes actions if the event is of - * interest to KXkb */ +bool XKBExtension::kcmlayoutRunning() +{ + return tdeApp->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) { - emit groupChanged(xkb_event->state.group); - } + 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 if (xkb_event->any.xkb_type == XkbNewKeyboardNotify) + { + if (m_configureFilterCounter > 0 || kcmlayoutRunning()) + { + return; + } + enableConfigureFilter(); + emit optionsChanged(); + disableConfigureFilter(); + } + } } #include "extension.moc" |