summaryrefslogtreecommitdiffstats
path: root/kcontrol/input/touchpad.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kcontrol/input/touchpad.cpp')
-rw-r--r--kcontrol/input/touchpad.cpp528
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();
+}