|
|
/*******************************************************************************
|
|
|
|
|
|
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>
|
|
|
|
|
|
#include <tqstring.h>
|
|
|
#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>
|
|
|
#include <X11/Xlib.h>
|
|
|
#include <X11/XKBlib.h>
|
|
|
#include <X11/extensions/XKBfile.h>
|
|
|
#include <X11/extensions/XKBrules.h>
|
|
|
#include <X11/extensions/XKBgeom.h>
|
|
|
#include <X11/extensions/XKM.h>
|
|
|
|
|
|
#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_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()
|
|
|
{
|
|
|
/* if( m_compiledLayoutFileNames.isEmpty() == false )
|
|
|
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;
|
|
|
|
|
|
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);
|
|
|
|
|
|
disableConfigureFilter();
|
|
|
|
|
|
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 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) {
|
|
|
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"
|