diff options
Diffstat (limited to 'kcontrol/input/touchpad.cpp')
-rw-r--r-- | kcontrol/input/touchpad.cpp | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/kcontrol/input/touchpad.cpp b/kcontrol/input/touchpad.cpp new file mode 100644 index 000000000..d18e3b698 --- /dev/null +++ b/kcontrol/input/touchpad.cpp @@ -0,0 +1,528 @@ +/******************************************************************************* + tdecm_touchpad + A touchpad module for the TDE Control Centre + + Copyright © 2024 Mavridis Philippe <mavridisf@gmail.com> + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see <https://www.gnu.org/licenses/>. + +*******************************************************************************/ + +// TQt +#include <tqwhatsthis.h> +#include <tqtooltip.h> +#include <tqtabwidget.h> +#include <tqradiobutton.h> +#include <tqbuttongroup.h> +#include <tqcombobox.h> +#include <tqcheckbox.h> +#include <tqslider.h> +#include <tqlayout.h> +#include <tqlabel.h> + +// TDE +#include <tdeglobal.h> +#include <kiconloader.h> +#include <tdeaboutdata.h> +#include <kdialog.h> +#include <kdebug.h> + +// TouchpadConfig +#include "touchpad_settings.h" +#include "touchpad.h" +#include "touchpad.moc" + + +/******************************* TouchpadConfig *******************************/ +TouchpadConfig::TouchpadConfig(TQWidget *parent, const char *name) +: TDECModule(parent, name), + m_error(nullptr) +{ + TDEGlobal::iconLoader()->addAppDir("kcminput"); + + d_settings = new TouchpadSettings; + d_settings->apply(); + + if (!d_settings->supportedTouchpad()) + { + TQString error_str; + + if (!d_settings->foundTouchpad()) + { + error_str = i18n( + "<qt><h1>Touchpad not found</h1>" + "Please check your system installation.</qt>" + ); + } + + else IF_DRIVER(None) + { + error_str = i18n( + "<qt><h1>Unsupported driver</h1>" + "<p>This module only supports the following drivers:" + "<p>Libinput, Synaptics</qt>" + ); + } + + else error_str = i18n("<qt><h1>Unknown error</h1></qt>"); + + m_error = new TQLabel(error_str, this); + m_error->setAlignment(TQt::AlignCenter); + new TQVBoxLayout(this); + layout()->add(m_error); + return; + } + + initWidgets(); + load(); + + kdDebug() << "managed touchpad: " << d_settings->touchpad().name + << " (xid = " << d_settings->touchpad().id << ")" << endl; + + TDEAboutData* about = new TDEAboutData( + "tdecm_touchpad", + I18N_NOOP("Touchpad"), + 0, 0, + TDEAboutData::License_GPL, + I18N_NOOP("(c) 2024 Mavridis Philippe") + ); + about->addAuthor("Mavridis Philippe", 0, 0); + setAboutData(about); +} + +TouchpadConfig::~TouchpadConfig() +{ + DEL(m_error) + DEL(d_settings); +} + +void TouchpadConfig::initWidgets() +{ + // Create containers + m_container = new TQTabWidget(this); + + TQFrame *touchpadWidget = new TQFrame(this); + touchpadWidget->setMargin(0); + new TQVBoxLayout(touchpadWidget); + + // Enable option + TQFrame *enableCheckBox = new TQFrame(touchpadWidget); + enableCheckBox->setSizePolicy(TQSizePolicy::Maximum, TQSizePolicy::Fixed); + + m_enabled = new TQCheckBox(i18n("Enable touchpad"), enableCheckBox); + TQWhatsThis::add(m_enabled, i18n( + "This option determines whether the touchpad is enabled or disabled" + )); + + // Compute margin for idented checkboxes based on checkbox height + int lmargin = m_enabled->height() / 2; + + // Align the Enable checkbox with the other options below + new TQHBoxLayout(enableCheckBox); + enableCheckBox->layout()->addItem(new TQSpacerItem(lmargin, lmargin, TQSizePolicy::Fixed)); + enableCheckBox->layout()->add(m_enabled); + + // Settings frame + TQFrame *settingsFrame = new TQFrame(touchpadWidget); + TQGridLayout *grid = new TQGridLayout(settingsFrame, 3, 2, KDialog::spacingHint()); + + connect(m_enabled, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed())); + connect(m_enabled, TQ_SIGNAL(toggled(bool)), settingsFrame, TQ_SLOT(setEnabled(bool))); + + // Behaviour + m_behaviour = new TQGroupBox(2, TQt::Vertical, i18n("Behaviour"), settingsFrame); + + m_offWhileTyping = new TQCheckBox(i18n("Disable touchpad while typing"), m_behaviour); + TQWhatsThis::add(m_offWhileTyping, i18n( + "If this option is checked, the touchpad is disabled while you are typing, so as " + "to prevent accidental cursor movement and clicks." + )); + connect(m_offWhileTyping, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed())); + + m_mbEmulation = new TQCheckBox(i18n("Middle button emulation"), m_behaviour); + TQWhatsThis::add(m_mbEmulation, i18n( + "If this option is enabled, a simultaneous left and right button click is " + "automatically transformed into a middle button click." + )); + IF_DRIVER(LibInput) + { + connect(m_mbEmulation, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed())); + } + else + { + DISABLE_UNSUPPORTED_OPTION(m_mbEmulation); + } + + // Speed + m_speed = new TQGroupBox(4, TQt::Vertical, i18n("Speed"), settingsFrame); + + TQLabel *accelLabel = new TQLabel(i18n("Acceleration:"), m_speed); + + m_accel = new TQSlider(-100, 100, 5, 0, TQt::Horizontal, m_speed); + + TQWidget *accelSliderMarkBox = new TQWidget(m_speed); + new TQHBoxLayout(accelSliderMarkBox); + accelSliderMarkBox->layout()->setAutoAdd(true); + + TQLabel *l; + l = new TQLabel(i18n("Slower"), accelSliderMarkBox); + l->setAlignment(TQt::AlignLeft); + l = new TQLabel(i18n("Normal"), accelSliderMarkBox); + l->setAlignment(TQt::AlignHCenter); + l = new TQLabel(i18n("Faster"), accelSliderMarkBox); + l->setAlignment(TQt::AlignRight); + l = nullptr; + + m_accelAdaptive = new TQCheckBox(i18n("Use adaptive profile"), m_speed); + + IF_DRIVER(LibInput) + { + connect(m_accel, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(changed())); + connect(m_accelAdaptive, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed())); + + // check available profiles + TQValueList<bool> accelProfilesAvail = d_settings->getAccelProfilesAvailability(); + if (!accelProfilesAvail.count() || accelProfilesAvail[0] == 0 || accelProfilesAvail[1] == 0) + { + m_accelAdaptive->setEnabled(false); + } + } + else + { + DISABLE_UNSUPPORTED_OPTION(m_speed) + } + + // Tapping + m_tapping = new TQGroupBox(5, TQt::Vertical, i18n("Tapping"), settingsFrame); + + m_tapClick = new TQCheckBox(i18n("Tap to click"), m_tapping); + TQWhatsThis::add(m_tapClick, i18n( + "If this option is checked, a tap on the touchpad is interpreted as a button click." + )); + connect(m_tapClick, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed())); + connect(m_tapClick, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(updateWidgetStates())); + + m_tapDrag = new TQCheckBox(i18n("Tap-and-drag"), m_tapping); + TQWhatsThis::add(m_tapDrag, i18n( + "Tap-and-drag is a tap which is immediately followed by a finger down and that finger " + "being held down emulates a button press. Moving the finger around can thus drag the " + "selected item on the screen." + )); + connect(m_tapDrag, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed())); + connect(m_tapDrag, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(updateWidgetStates())); + + m_tapDragLock = new TQCheckBox(i18n("Tap-and-drag lock"), m_tapping); + TQWhatsThis::add(m_tapDragLock, i18n( + "When enabled, lifting a finger while dragging will not immediately stop dragging." + )); + + IF_DRIVER(LibInput) + { + connect(m_tapDragLock, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed())); + } + else + { + DISABLE_UNSUPPORTED_OPTION(m_tapDragLock); + } + + TQLabel *tapMappingLabel = new TQLabel(i18n("Two-finger tap:"), m_tapping); + m_tapMapping = new TQComboBox(m_tapping); // "lrm" and "lmr" + m_tapMapping->setSizePolicy(TQSizePolicy::Maximum, TQSizePolicy::Fixed); + m_tapMapping->insertItem( + TDEGlobal::iconLoader()->loadIcon("mouse3", TDEIcon::Small), + i18n("Right click (three-finger tap for middle click)"), + 0); + m_tapMapping->insertItem( + TDEGlobal::iconLoader()->loadIcon("mouse2", TDEIcon::Small), + i18n("Middle click (three-finger tap for right click)"), + 1); + connect(m_tapMapping, TQ_SIGNAL(activated(const TQString&)), this, TQ_SLOT(changed())); + + // Scrolling options + m_scrolling = new TQGroupBox(4, TQt::Vertical, i18n("Scrolling options"), settingsFrame); + + m_verScroll = new TQCheckBox(i18n("Vertical scrolling"), m_scrolling); + TQWhatsThis::add(m_verScroll, i18n( + "This option enables/disables the vertical scrolling gesture on the touchpad. " + "(The actual gesture depends on the selected scroll method.) " + "Unless the used driver is Synaptics, disabling vertical scrolling also disables " + "horizontal scrolling." + )); + connect(m_verScroll, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed())); + connect(m_verScroll, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(updateWidgetStates())); + + m_horScroll = new TQCheckBox(i18n("Horizontal scrolling"), m_scrolling); + TQWhatsThis::add(m_horScroll, i18n( + "This option enables/disables the horizontal scrolling gesture on the touchpad. " + "(The actual gesture depends on the selected scroll method.)" + )); + connect(m_horScroll, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed())); + connect(m_horScroll, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(updateWidgetStates())); + + m_naturalScroll = new TQCheckBox(i18n("Reverse scroll direction"), m_scrolling); + TQWhatsThis::add(m_naturalScroll, i18n( + "If this option is checked, the scrolling direction is reversed to resemble natural " + "movement of content. This feature is also called natural scrolling." + )); + connect(m_naturalScroll, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(changed())); + + m_naturalScrollDirections = new TQFrame(m_scrolling); + TQWhatsThis::add(m_naturalScrollDirections, i18n( + "This option allows you to select the scrolling directions to which reversed scrolling will be applied. " + "It is only available if the Synaptics driver is used." + )); + TQGridLayout *nsdl = new TQGridLayout(m_naturalScrollDirections, 2, 2, KDialog::spacingHint()); + m_horNaturalScroll = new TQCheckBox(i18n("Apply to horizontal scrolling"), m_naturalScrollDirections); + m_verNaturalScroll = new TQCheckBox(i18n("Apply to vertical scrolling"), m_naturalScrollDirections); + nsdl->addItem(new TQSpacerItem(lmargin, lmargin, TQSizePolicy::Fixed), 0, 0); + nsdl->addItem(new TQSpacerItem(lmargin, lmargin, TQSizePolicy::Fixed), 1, 0); + nsdl->addWidget(m_horNaturalScroll, 0, 1); + nsdl->addWidget(m_verNaturalScroll, 1, 1); + + IF_DRIVER(Synaptics) + { + connect(m_horNaturalScroll, TQ_SIGNAL(toggled(bool)), TQ_SLOT(changed())); + connect(m_verNaturalScroll, TQ_SIGNAL(toggled(bool)), TQ_SLOT(changed())); + connect(m_naturalScroll, TQ_SIGNAL(toggled(bool)), TQ_SLOT(updateWidgetStates())); + } + else + { + // Not only disable, but also force checkboxes to be checked on + // so that the user knows that the natural scrolling option applies + // always to both directions + DISABLE_UNSUPPORTED_OPTION(m_naturalScrollDirections); + m_horNaturalScroll->setChecked(true); + m_verNaturalScroll->setChecked(true); + } + + // Scrolling methods + m_scrollMethods = new TQButtonGroup(3, TQt::Vertical, i18n("Scrolling method"), settingsFrame); + TQWhatsThis::add(m_scrollMethods, i18n( + "Here you can select your preferred scrolling method. The two most common options are " + "two-finger scrolling and edge scrolling. Two-finger scrolling entails a movement with " + "two fingers vertically or horizontally upon the surface of the touchpad. Edge scrolling " + "on the other hand tracks movements with one finger along the right or bottom edge of " + "the touchpad." + )); + connect(m_scrollMethods, TQ_SIGNAL(clicked(int)), this, TQ_SLOT(changed())); + + TQStringList scrollMethodLabels; + scrollMethodLabels << i18n("Two-finger") + << i18n("Edge"); + + IF_DRIVER(LibInput) + { + scrollMethodLabels << i18n("Button"); + } + + TQValueList<bool> scrollMethodAvail = d_settings->getScrollMethodsAvailability(); + Q_ASSERT(scrollMethodLabels.count() == scrollMethodAvail.count()); + + for (int i = 0; i < scrollMethodLabels.count(); ++i) + { + TQRadioButton *rad = new TQRadioButton(scrollMethodLabels[i], m_scrollMethods); + rad->setEnabled(scrollMethodAvail[i]); + } + + // Finalize layout + grid->addWidget(m_behaviour, 0, 0); + grid->addWidget(m_speed, 1, 0); + grid->addMultiCellWidget(m_scrolling, 0, 1, 1, 1); + grid->addWidget(m_scrollMethods, 2, 1); + grid->addWidget(m_tapping, 2, 0); + grid->addItem(new TQSpacerItem(10, 10)); + + // Synaptics deprecation warning + IF_DRIVER(Synaptics) + { + TQLabel *l = new TQLabel(i18n( + "<qt><b>Warning:</b> The Synaptics driver has been deprecated.</qt>" + ), settingsFrame); + TQWhatsThis::add(l, i18n( + "<qt><p><b>The Synaptics driver is no longer in active development.</b>" + "<p>While Libinput is the preferred choice for handling input devices, " + "you might still have valid reasons to use the older Synaptics driver " + "in its place. Please bear in mind that you will probably not receive " + "updates and bug fixes from its upstream.</qt>" + )); + grid->addMultiCellWidget(l, 3, 3, 0, 1); + } + + touchpadWidget->layout()->add(enableCheckBox); + touchpadWidget->layout()->add(settingsFrame); + m_container->addTab(touchpadWidget, SmallIconSet("input-touchpad"), d_settings->touchpad().name); + + new TQVBoxLayout(this, KDialog::marginHint()); + layout()->add(m_container); +} + +// We handle more complex UI cases here +void TouchpadConfig::updateWidgetStates() +{ + if (!d_settings->foundTouchpad()) return; + + // Scrolling related options + bool on; + + IF_DRIVER(LibInput) + { + // To disable vertical scrolling under LibInput one has to disable scrolling entirely + // so we mirror this in the UI + on = m_verScroll->isChecked(); + m_horScroll->setEnabled(on); + } + + else + { + // In case we can control both horizontal and vertical scrolling separately, any UI + // changes should be triggered when both are disabled + on = m_verScroll->isChecked() || m_horScroll->isChecked(); + + // Only enable natural scroll directions options when not under LibInput + m_naturalScrollDirections->setEnabled(on && m_naturalScroll->isChecked()); + } + + m_naturalScroll->setEnabled(on); + m_scrollMethods->setEnabled(on); + + // Tapping related options + m_tapDrag->setEnabled(m_tapClick->isChecked()); + + IF_DRIVER(LibInput) + { + m_tapDragLock->setEnabled(m_tapClick->isChecked() && m_tapDrag->isChecked()); + } +} + +void TouchpadConfig::defaults() +{ + load(true); +} + +void TouchpadConfig::load() +{ + load(false); +} + +void TouchpadConfig::load(bool useDefaults) +{ + if (!d_settings->foundTouchpad()) return; + + d_settings->load(); + + m_enabled->setChecked(d_settings->enabled); + + // Behaviour + m_offWhileTyping->setChecked(d_settings->offWhileTyping); + + IF_DRIVER(LibInput) + { + m_mbEmulation->setChecked(d_settings->midButtonEmulation); + } + + // Speed + IF_DRIVER(LibInput) + { + m_accel->setValue(d_settings->accelSpeed); + m_accelAdaptive->setChecked(d_settings->accelProfile == 0); + } + + // Tapping + m_tapClick->setChecked(d_settings->tapClick); + m_tapDrag->setChecked(d_settings->tapDrag); + + IF_DRIVER(LibInput) + { + m_tapDragLock->setChecked(d_settings->tapDragLock); + } + + m_tapMapping->setCurrentItem(d_settings->tapMapping); + + // Scrolling options + m_horScroll->setChecked(d_settings->scrollDirections & TQt::Horizontal); + m_verScroll->setChecked(d_settings->scrollDirections & TQt::Vertical); + m_naturalScroll->setChecked(d_settings->naturalScroll); + IF_DRIVER(Synaptics) + { + m_naturalScrollDirections->setEnabled(d_settings->naturalScroll); + m_horNaturalScroll->setChecked(d_settings->naturalScrollDirections & TQt::Horizontal); + m_verNaturalScroll->setChecked(d_settings->naturalScrollDirections & TQt::Vertical); + } + + IF_DRIVER(LibInput) + { + m_horScroll->setEnabled(m_verScroll->isOn()); + m_naturalScroll->setEnabled(m_verScroll->isOn()); + m_scrollMethods->setEnabled(m_verScroll->isOn()); + } + + // Scrolling method + m_scrollMethods->setButton(d_settings->scrollMethod); +} + +void TouchpadConfig::save() +{ + if (!d_settings->foundTouchpad()) return; + + d_settings->enabled = m_enabled->isChecked(); + + // Behaviour + d_settings->offWhileTyping = m_offWhileTyping->isChecked(); + + IF_DRIVER(LibInput) + { + d_settings->midButtonEmulation = m_mbEmulation->isChecked(); + } + + // Speed + IF_DRIVER(LibInput) + { + d_settings->accelSpeed = m_accel->value(); + d_settings->accelProfile = (m_accelAdaptive->isChecked() ? 0 : 1); + } + + // Tapping + d_settings->tapClick = m_tapClick->isChecked(); + d_settings->tapDrag = m_tapDrag->isChecked(); + + IF_DRIVER(LibInput) + { + d_settings->tapDragLock = m_tapDragLock->isChecked(); + } + + d_settings->tapMapping = m_tapMapping->currentItem(); + + // Scrolling options + int scrollDirections = 0; + if (m_horScroll->isChecked()) scrollDirections |= TQt::Horizontal; + if (m_verScroll->isChecked()) scrollDirections |= TQt::Vertical; + d_settings->scrollDirections = scrollDirections; + + d_settings->naturalScroll = m_naturalScroll->isChecked(); + + int naturalScrollDirections = 0; + if (m_horNaturalScroll->isChecked()) naturalScrollDirections |= TQt::Horizontal; + if (m_verNaturalScroll->isChecked()) naturalScrollDirections |= TQt::Vertical; + d_settings->naturalScrollDirections = naturalScrollDirections; + + // Scrolling method + d_settings->scrollMethod = m_scrollMethods->selectedId(); + + d_settings->save(); + d_settings->apply(); +} + +Touchpad TouchpadConfig::touchpad() +{ + return d_settings->touchpad(); +} |