diff --git a/doc/kcontrol/cursortheme/index.docbook b/doc/kcontrol/cursortheme/index.docbook
index 690bb8c19..2d4b0a8f6 100644
--- a/doc/kcontrol/cursortheme/index.docbook
+++ b/doc/kcontrol/cursortheme/index.docbook
@@ -1,59 +1,59 @@
]>
Cursor Theme
&Mike.McBride; &Mike.McBride.mail;
&Brad.Hards; &Brad.Hards.mail;
2018-03-24
Plasma 5.12
This is the documentation for the &systemsettings; module to
customize the mouse cursor appearance.
KDE
Systemsettings
mouse
cursor
This module allows you to select from a number of installed cursor themes
from a preview of the cursor display.
The features provided by this module may not be available on some systems.
Your system may need to be updated to support cursor themes.
If you have additional cursor themes available, you can install and remove
them using the buttons at the bottom of the list box. Note that you cannot remove the default themes.
Use the Get new Themes button to launch the Get Hot New Stuff dialog and
download additional themes from the Internet.
Some themes allow to choose the cursor size. Available sizes are
displayed hoovering a theme.
-The size can be set to the Resolution dependent or one of the sizes available for your theme.
+The size can be set to one of the sizes available for your theme.
diff --git a/kcms/cursortheme/CMakeLists.txt b/kcms/cursortheme/CMakeLists.txt
index da2007614..a429ba906 100644
--- a/kcms/cursortheme/CMakeLists.txt
+++ b/kcms/cursortheme/CMakeLists.txt
@@ -1,61 +1,62 @@
# KI18N Translation Domain for this library
add_definitions(-DTRANSLATION_DOMAIN=\"kcm_cursortheme\")
include_directories( ${LIBUSB_INCLUDE_DIR} )
set( libnoinst_SRCS
xcursor/thememodel.cpp
xcursor/cursortheme.cpp
xcursor/xcursortheme.cpp
xcursor/previewwidget.cpp
xcursor/sortproxymodel.cpp
../krdb/krdb.cpp )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/xcursor/ )
########### next target ###############
set(kcm_cursortheme_PART_SRCS kcmcursortheme.cpp ${libnoinst_SRCS})
kconfig_add_kcfg_files(kcm_cursortheme_PART_SRCS cursorthemesettings.kcfgc GENERATE_MOC)
add_library(kcm_cursortheme MODULE ${kcm_cursortheme_PART_SRCS})
target_link_libraries(kcm_cursortheme
Qt5::DBus
Qt5::X11Extras
Qt5::Quick
KF5::Archive
KF5::KCMUtils
KF5::I18n
KF5::GuiAddons
KF5::WindowSystem
KF5::KIOCore
KF5::KIOWidgets
KF5::KDELibs4Support
KF5::NewStuffCore
KF5::QuickAddons
${X11_LIBRARIES}
XCB::XCB
PW::KWorkspace
)
if (X11_Xcursor_FOUND)
target_link_libraries(kcm_cursortheme ${X11_Xcursor_LIB})
endif ()
if (X11_Xfixes_FOUND)
target_link_libraries(kcm_cursortheme ${X11_Xfixes_LIB})
endif ()
install(TARGETS kcm_cursortheme DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms )
kcoreaddons_desktop_to_json(kcm_cursortheme "kcm_cursortheme.desktop")
########### install files ###############
install(FILES cursorthemesettings.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR})
+install(FILES delete_cursor_old_default_size.upd delete_cursor_old_default_size.pl DESTINATION ${KDE_INSTALL_DATADIR}/kconf_update)
install( FILES kcm_cursortheme.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} )
install( FILES xcursor/xcursor.knsrc DESTINATION ${KDE_INSTALL_KNSRCDIR} )
kpackage_install_package(package kcm_cursortheme kcms)
diff --git a/kcms/cursortheme/cursorthemesettings.kcfg b/kcms/cursortheme/cursorthemesettings.kcfg
index c6a8810ff..2b0d93e25 100644
--- a/kcms/cursortheme/cursorthemesettings.kcfg
+++ b/kcms/cursortheme/cursorthemesettings.kcfg
@@ -1,17 +1,17 @@
breeze_cursors
- 0
+ 24
diff --git a/kcms/cursortheme/delete_cursor_old_default_size.pl b/kcms/cursortheme/delete_cursor_old_default_size.pl
new file mode 100644
index 000000000..7fec429e2
--- /dev/null
+++ b/kcms/cursortheme/delete_cursor_old_default_size.pl
@@ -0,0 +1,10 @@
+#! /usr/bin/perl
+
+use strict;
+
+while (<>)
+{
+ chomp;
+ s/cursorSize=0/# DELETE cursorSize/;
+ print "$_\n";
+}
diff --git a/kcms/cursortheme/delete_cursor_old_default_size.upd b/kcms/cursortheme/delete_cursor_old_default_size.upd
new file mode 100644
index 000000000..d615b4c85
--- /dev/null
+++ b/kcms/cursortheme/delete_cursor_old_default_size.upd
@@ -0,0 +1,8 @@
+Version=5
+
+# Delete cursor size if it's old default
+Id=DeleteCursorOldDefaultSize
+Options=overwrite
+File=kcminputrc
+Group=Mouse
+Script=delete_cursor_old_default_size.pl,perl
diff --git a/kcms/cursortheme/kcmcursortheme.cpp b/kcms/cursortheme/kcmcursortheme.cpp
index bb0ee9824..bee0e3024 100644
--- a/kcms/cursortheme/kcmcursortheme.cpp
+++ b/kcms/cursortheme/kcmcursortheme.cpp
@@ -1,595 +1,588 @@
/*
* Copyright © 2003-2007 Fredrik Höglund
* Copyright © 2019 Benjamin Port
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include
#include "kcmcursortheme.h"
#include "xcursor/thememodel.h"
#include "xcursor/sortproxymodel.h"
#include "xcursor/cursortheme.h"
#include "xcursor/previewwidget.h"
#include "../krdb/krdb.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if KNEWSTUFFCORE_VERSION_MAJOR==5 && KNEWSTUFFCORE_VERSION_MINOR>=68
#include
#endif
#include
#include
#include
#include
#include
#include
#include "cursorthemesettings.h"
#ifdef HAVE_XFIXES
# include
#endif
K_PLUGIN_FACTORY_WITH_JSON(CursorThemeConfigFactory, "kcm_cursortheme.json", registerPlugin();)
CursorThemeConfig::CursorThemeConfig(QObject *parent, const QVariantList &args)
: KQuickAddons::ManagedConfigModule(parent, args),
m_settings(new CursorThemeSettings(this)),
m_canInstall(true),
m_canResize(true),
m_canConfigure(true)
{
m_preferredSize = m_settings->cursorSize();
connect(m_settings, &CursorThemeSettings::cursorThemeChanged, this, &CursorThemeConfig::updateSizeComboBox);
qmlRegisterType("org.kde.private.kcm_cursortheme", 1, 0, "PreviewWidget");
qmlRegisterType();
qmlRegisterType();
KAboutData* aboutData = new KAboutData(QStringLiteral("kcm_cursortheme"), i18n("Cursors"),
QStringLiteral("1.0"), QString(), KAboutLicense::GPL, i18n("(c) 2003-2007 Fredrik Höglund"));
aboutData->addAuthor(i18n("Fredrik Höglund"));
aboutData->addAuthor(i18n("Marco Martin"));
setAboutData(aboutData);
m_themeModel = new CursorThemeModel(this);
m_themeProxyModel = new SortProxyModel(this);
m_themeProxyModel->setSourceModel(m_themeModel);
m_themeProxyModel->setFilterCaseSensitivity(Qt::CaseSensitive);
m_themeProxyModel->sort(NameColumn, Qt::AscendingOrder);
m_sizesModel = new QStandardItemModel(this);
// Disable the install button if we can't install new themes to ~/.icons,
// or Xcursor isn't set up to look for cursor themes there.
if (!m_themeModel->searchPaths().contains(QDir::homePath() + "/.icons") || !iconsIsWritable()) {
setCanInstall(false);
}
}
CursorThemeConfig::~CursorThemeConfig()
{
/* */
}
CursorThemeSettings *CursorThemeConfig::cursorThemeSettings() const
{
return m_settings;
}
void CursorThemeConfig::setCanInstall(bool can)
{
if (m_canInstall == can) {
return;
}
m_canInstall = can;
emit canInstallChanged();
}
bool CursorThemeConfig::canInstall() const
{
return m_canInstall;
}
void CursorThemeConfig::setCanResize(bool can)
{
if (m_canResize == can) {
return;
}
m_canResize = can;
emit canResizeChanged();
}
bool CursorThemeConfig::canResize() const
{
return m_canResize;
}
void CursorThemeConfig::setCanConfigure(bool can)
{
if (m_canConfigure == can) {
return;
}
m_canConfigure = can;
emit canConfigureChanged();
}
int CursorThemeConfig::preferredSize() const
{
return m_preferredSize;
}
void CursorThemeConfig::setPreferredSize(int size)
{
if (m_preferredSize == size) {
return;
}
m_preferredSize = size;
emit preferredSizeChanged();
}
bool CursorThemeConfig::canConfigure() const
{
return m_canConfigure;
}
bool CursorThemeConfig::downloadingFile() const
{
return m_tempCopyJob;
}
QAbstractItemModel *CursorThemeConfig::cursorsModel()
{
return m_themeProxyModel;
}
QAbstractItemModel *CursorThemeConfig::sizesModel()
{
return m_sizesModel;
}
bool CursorThemeConfig::iconsIsWritable() const
{
const QFileInfo icons = QFileInfo(QDir::homePath() + "/.icons");
const QFileInfo home = QFileInfo(QDir::homePath());
return ((icons.exists() && icons.isDir() && icons.isWritable()) ||
(!icons.exists() && home.isWritable()));
}
void CursorThemeConfig::updateSizeComboBox()
{
// clear the combo box
m_sizesModel->clear();
// refill the combo box and adopt its icon size
int row = cursorThemeIndex(m_settings->cursorTheme());
QModelIndex selected = m_themeProxyModel->index(row, 0);
int maxIconWidth = 0;
int maxIconHeight = 0;
if (selected.isValid()) {
const CursorTheme *theme = m_themeProxyModel->theme(selected);
const QList sizes = theme->availableSizes();
QIcon m_icon;
// only refill the combobox if there is more that 1 size
if (sizes.size() > 1) {
int i;
QList comboBoxList;
QPixmap m_pixmap;
// insert the items
m_pixmap = theme->createIcon(0);
if (m_pixmap.width() > maxIconWidth) {
maxIconWidth = m_pixmap.width();
}
if (m_pixmap.height() > maxIconHeight) {
maxIconHeight = m_pixmap.height();
}
- QStandardItem *item = new QStandardItem(QIcon(m_pixmap),
- i18nc("@item:inlistbox size", "Resolution dependent"));
- item->setData(0);
- m_sizesModel->appendRow(item);
- comboBoxList << 0;
+
foreach (i, sizes) {
m_pixmap = theme->createIcon(i);
if (m_pixmap.width() > maxIconWidth) {
maxIconWidth = m_pixmap.width();
}
if (m_pixmap.height() > maxIconHeight) {
maxIconHeight = m_pixmap.height();
}
QStandardItem *item = new QStandardItem(QIcon(m_pixmap), QString::number(i));
item->setData(i);
m_sizesModel->appendRow(item);
comboBoxList << i;
}
// select an item
int size = m_preferredSize;
int selectItem = comboBoxList.indexOf(size);
// cursor size not available for this theme
if (selectItem < 0) {
/* Search the value next to cursor size. The first entry (0)
is ignored. (If cursor size would have been 0, then we
would had found it yet. As cursor size is not 0, we won't
default to "automatic size".)*/
int j;
int distance;
int smallestDistance;
selectItem = 1;
j = comboBoxList.value(selectItem);
size = j;
smallestDistance = qAbs(m_preferredSize - j);
for (int i = 2; i < comboBoxList.size(); ++i) {
j = comboBoxList.value(i);
distance = qAbs(m_preferredSize - j);
if (distance < smallestDistance || (distance == smallestDistance && j > m_preferredSize)) {
smallestDistance = distance;
selectItem = i;
size = j;
}
}
}
m_settings->setCursorSize(size);
}
}
// enable or disable the combobox
if (m_settings->isImmutable("cursorSize")) {
setCanResize(false);
} else {
setCanResize(m_sizesModel->rowCount() > 0);
}
// We need to emit a cursorSizeChanged in all case to refresh UI
emit m_settings->cursorSizeChanged();
}
bool CursorThemeConfig::applyTheme(const CursorTheme *theme, const int size)
{
// Require the Xcursor version that shipped with X11R6.9 or greater, since
// in previous versions the Xfixes code wasn't enabled due to a bug in the
// build system (freedesktop bug #975).
#if HAVE_XFIXES && XFIXES_MAJOR >= 2 && XCURSOR_LIB_VERSION >= 10105
if (!theme) {
return false;
}
QByteArray themeName = QFile::encodeName(theme->name());
// Set up the proper launch environment for newly started apps
UpdateLaunchEnvJob launchEnvJob(QStringLiteral("XCURSOR_THEME"), themeName);
// Update the Xcursor X resources
runRdb(0);
// Reload the standard cursors
QStringList names;
if (CursorTheme::haveXfixes()) {
// Qt cursors
names << "left_ptr" << "up_arrow" << "cross" << "wait"
<< "left_ptr_watch" << "ibeam" << "size_ver" << "size_hor"
<< "size_bdiag" << "size_fdiag" << "size_all" << "split_v"
<< "split_h" << "pointing_hand" << "openhand"
<< "closedhand" << "forbidden" << "whats_this" << "copy" << "move" << "link";
// X core cursors
names << "X_cursor" << "right_ptr" << "hand1"
<< "hand2" << "watch" << "xterm"
<< "crosshair" << "left_ptr_watch" << "center_ptr"
<< "sb_h_double_arrow" << "sb_v_double_arrow" << "fleur"
<< "top_left_corner" << "top_side" << "top_right_corner"
<< "right_side" << "bottom_right_corner" << "bottom_side"
<< "bottom_left_corner" << "left_side" << "question_arrow"
<< "pirate";
foreach (const QString &name, names) {
XFixesChangeCursorByName(QX11Info::display(), theme->loadCursor(name, size), QFile::encodeName(name));
}
}
updateSizeComboBox();
emit themeApplied();
return true;
#else
Q_UNUSED(theme)
return false;
#endif
}
int CursorThemeConfig::cursorSizeIndex(int cursorSize) const
{
if (m_sizesModel->rowCount() > 0) {
- if (cursorSize == 0) {
- return 0;
- }
const auto items = m_sizesModel->findItems(QString::number(cursorSize));
if (items.count() == 1) {
return items.first()->row();
}
}
return -1;
}
int CursorThemeConfig::cursorSizeFromIndex(int index)
{
Q_ASSERT (index < m_sizesModel->rowCount() && index >= 0);
return m_sizesModel->item(index)->data().toInt();
}
int CursorThemeConfig::cursorThemeIndex(const QString &cursorTheme) const
{
auto results = m_themeProxyModel->findIndex(cursorTheme);
return results.row();
}
QString CursorThemeConfig::cursorThemeFromIndex(int index) const
{
QModelIndex idx = m_themeProxyModel->index(index, 0);
return m_themeProxyModel->theme(idx)->name();
}
void CursorThemeConfig::save()
{
ManagedConfigModule::save();
setPreferredSize(m_settings->cursorSize());
int row = cursorThemeIndex(m_settings->cursorTheme());
QModelIndex selected = m_themeProxyModel->index(row, 0);
const CursorTheme *theme = selected.isValid() ? m_themeProxyModel->theme(selected) : nullptr;
if (!applyTheme(theme, m_settings->cursorSize())) {
emit showInfoMessage(i18n("You have to restart the Plasma session for these changes to take effect."));
}
KGlobalSettings::self()->emitChange(KGlobalSettings::CursorChanged);
}
void CursorThemeConfig::load()
{
ManagedConfigModule::load();
setPreferredSize(m_settings->cursorSize());
// Get the name of the theme KDE is configured to use
QString currentTheme = m_settings->cursorTheme();
// Disable the listview and the buttons if we're in kiosk mode
if (m_settings->isImmutable( QStringLiteral( "cursorTheme" ))) {
setCanConfigure(false);
setCanInstall(false);
}
updateSizeComboBox(); // This handles also the kiosk mode
setNeedsSave(false);
}
void CursorThemeConfig::defaults()
{
ManagedConfigModule::defaults();
m_preferredSize = m_settings->cursorSize();
}
void CursorThemeConfig::ghnsEntriesChanged(const QQmlListReference &changedEntries)
{
#if KNEWSTUFFCORE_VERSION_MAJOR==5 && KNEWSTUFFCORE_VERSION_MINOR>=68
for (int i = 0; i < changedEntries.count(); ++i) {
KNSCore::EntryWrapper* entry = qobject_cast(changedEntries.at(i));
if (entry) {
if (entry->entry().status() == KNS3::Entry::Deleted) {
for (const QString& deleted : entry->entry().uninstalledFiles()) {
QVector list = deleted.splitRef(QLatin1Char('/'));
if (list.last() == QLatin1Char('*')) {
list.takeLast();
}
QModelIndex idx = m_themeModel->findIndex(list.last().toString());
if (idx.isValid()) {
m_themeModel->removeTheme(idx);
}
}
} else if (entry->entry().status() == KNS3::Entry::Installed) {
for (const QString& created : entry->entry().installedFiles()) {
QStringList list = created.split(QLatin1Char('/'));
if (list.last() == QLatin1Char('*')) {
list.takeLast();
}
// Because we sometimes get some extra slashes in the installed files list
list.removeAll({});
// Because we'll also get the containing folder, if it was not already there
// we need to ignore it.
if (list.last() == QLatin1String(".icons")) {
continue;
}
m_themeModel->addTheme(list.join(QLatin1Char('/')));
}
}
}
}
#endif
}
void CursorThemeConfig::installThemeFromFile(const QUrl &url)
{
if (url.isLocalFile()) {
installThemeFile(url.toLocalFile());
return;
}
if (m_tempCopyJob) {
return;
}
m_tempInstallFile.reset(new QTemporaryFile());
if (!m_tempInstallFile->open()) {
emit showErrorMessage(i18n("Unable to create a temporary file."));
m_tempInstallFile.reset();
return;
}
m_tempCopyJob = KIO::file_copy(url,QUrl::fromLocalFile(m_tempInstallFile->fileName()),
-1, KIO::Overwrite);
m_tempCopyJob->uiDelegate()->setAutoErrorHandlingEnabled(true);
emit downloadingFileChanged();
connect(m_tempCopyJob, &KIO::FileCopyJob::result, this, [this, url](KJob *job) {
if (job->error() != KJob::NoError) {
emit showErrorMessage(i18n("Unable to download the icon theme archive: %1", job->errorText()));
return;
}
installThemeFile(m_tempInstallFile->fileName());
m_tempInstallFile.reset();
});
connect(m_tempCopyJob, &QObject::destroyed, this, &CursorThemeConfig::downloadingFileChanged);
}
void CursorThemeConfig::installThemeFile(const QString &path)
{
KTar archive(path);
archive.open(QIODevice::ReadOnly);
const KArchiveDirectory *archiveDir = archive.directory();
QStringList themeDirs;
// Extract the dir names of the cursor themes in the archive, and
// append them to themeDirs
foreach(const QString &name, archiveDir->entries()) {
const KArchiveEntry *entry = archiveDir->entry(name);
if (entry->isDirectory() && entry->name().toLower() != "default") {
const KArchiveDirectory *dir = static_cast(entry);
if (dir->entry("index.theme") && dir->entry("cursors")) {
themeDirs << dir->name();
}
}
}
if (themeDirs.isEmpty()) {
emit showErrorMessage(i18n("The file is not a valid icon theme archive."));
return;
}
// The directory we'll install the themes to
QString destDir = QDir::homePath() + "/.icons/";
if (!QDir().mkpath(destDir)) {
emit showErrorMessage(i18n("Failed to create 'icons' folder."));
return;
}
// Process each cursor theme in the archive
foreach (const QString &dirName, themeDirs) {
QDir dest(destDir + dirName);
if (dest.exists()) {
QString question = i18n("A theme named %1 already exists in your icon "
"theme folder. Do you want replace it with this one?", dirName);
int answer = KMessageBox::warningContinueCancel(nullptr, question,
i18n("Overwrite Theme?"),
KStandardGuiItem::overwrite());
if (answer != KMessageBox::Continue) {
continue;
}
// ### If the theme that's being replaced is the current theme, it
// will cause cursor inconsistencies in newly started apps.
}
// ### Should we check if a theme with the same name exists in a global theme dir?
// If that's the case it will effectively replace it, even though the global theme
// won't be deleted. Checking for this situation is easy, since the global theme
// will be in the listview. Maybe this should never be allowed since it might
// result in strange side effects (from the average users point of view). OTOH
// a user might want to do this 'upgrade' a global theme.
const KArchiveDirectory *dir = static_cast
(archiveDir->entry(dirName));
dir->copyTo(dest.path());
m_themeModel->addTheme(dest);
}
archive.close();
emit showSuccessMessage(i18n("Theme installed successfully."));
m_themeModel->refreshList();
}
void CursorThemeConfig::removeTheme(int row)
{
QModelIndex idx = m_themeProxyModel->index(row, 0);
if (!idx.isValid()) {
return;
}
const CursorTheme *theme = m_themeProxyModel->theme(idx);
// Don't let the user delete the currently configured theme
if (theme->name() == m_settings->cursorTheme()) {
KMessageBox::sorry(nullptr, i18n("You cannot delete the theme you are currently "
"using.
You have to switch to another theme first."));
return;
}
// Get confirmation from the user
QString question = i18n("Are you sure you want to remove the "
"%1 cursor theme?
"
"This will delete all the files installed by this theme.",
theme->title());
int answer = KMessageBox::warningContinueCancel(nullptr, question,
i18n("Confirmation"), KStandardGuiItem::del());
if (answer != KMessageBox::Continue) {
return;
}
// Delete the theme from the harddrive
KIO::del(QUrl::fromLocalFile(theme->path())); // async
// Remove the theme from the model
m_themeProxyModel->removeTheme(idx);
// TODO:
// Since it's possible to substitute cursors in a system theme by adding a local
// theme with the same name, we shouldn't remove the theme from the list if it's
// still available elsewhere. We could add a
// bool CursorThemeModel::tryAddTheme(const QString &name), and call that, but
// since KIO::del() is an asynchronos operation, the theme we're deleting will be
// readded to the list again before KIO has removed it.
}
#include "kcmcursortheme.moc"
diff --git a/kcms/krdb/krdb.cpp b/kcms/krdb/krdb.cpp
index 397edd849..902d813e7 100644
--- a/kcms/krdb/krdb.cpp
+++ b/kcms/krdb/krdb.cpp
@@ -1,716 +1,716 @@
/****************************************************************************
**
**
** KRDB - puts current KDE color scheme into preprocessor statements
** cats specially written application default files and uses xrdb -merge to
** write to RESOURCE_MANAGER. Thus it gives a simple way to make non-KDE
** applications fit in with the desktop
**
** Copyright (C) 1998 by Mark Donohoe
** Copyright (C) 1999 by Dirk A. Mueller (reworked for KDE 2.0)
** Copyright (C) 2001 by Matthias Ettrich (add support for GTK applications )
** Copyright (C) 2001 by Waldo Bastian
** Copyright (C) 2002 by Karol Szwed
** This application is freely distributable under the GNU Public License.
**
*****************************************************************************/
#include
#include
#include
#include
#include
#include
#undef Unsorted
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "krdb.h"
#if HAVE_X11
#include
#include
#endif
inline const char * gtkEnvVar(int version)
{
return 2==version ? "GTK2_RC_FILES" : "GTK_RC_FILES";
}
inline const char * sysGtkrc(int version)
{
if(2==version)
{
if(access("/etc/opt/gnome/gtk-2.0", F_OK) == 0)
return "/etc/opt/gnome/gtk-2.0/gtkrc";
else
return "/etc/gtk-2.0/gtkrc";
}
else
{
if(access("/etc/opt/gnome/gtk", F_OK) == 0)
return "/etc/opt/gnome/gtk/gtkrc";
else
return "/etc/gtk/gtkrc";
}
}
inline const char * userGtkrc(int version)
{
return 2==version ? "/.gtkrc-2.0" : "/.gtkrc";
}
// -----------------------------------------------------------------------------
static QString writableGtkrc(int version)
{
QString gtkrc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QDir dir;
dir.mkpath(gtkrc);
gtkrc += 2==version?"/gtkrc-2.0":"/gtkrc";
return gtkrc;
}
// -----------------------------------------------------------------------------
static void applyGtkStyles(bool active, int version)
{
QString gtkkde = writableGtkrc(version);
QByteArray gtkrc = getenv(gtkEnvVar(version));
QStringList list = QFile::decodeName(gtkrc).split( QLatin1Char(':'));
QString userHomeGtkrc = QDir::homePath()+userGtkrc(version);
if (!list.contains(userHomeGtkrc))
list.prepend(userHomeGtkrc);
QLatin1String systemGtkrc = QLatin1String(sysGtkrc(version));
if (!list.contains(systemGtkrc))
list.prepend(systemGtkrc);
list.removeAll(QLatin1String(""));
list.removeAll(gtkkde);
list.append(gtkkde);
// Pass env. var to kdeinit.
QString name = gtkEnvVar(version);
QString value = list.join(QLatin1Char(':'));
UpdateLaunchEnvJob(name, value);
}
// -----------------------------------------------------------------------------
static void applyQtColors( KSharedConfigPtr kglobalcfg, QSettings& settings, QPalette& newPal )
{
QStringList actcg, inactcg, discg;
/* export kde color settings */
int i;
for (i = 0; i < QPalette::NColorRoles; i++)
actcg << newPal.color(QPalette::Active,
(QPalette::ColorRole) i).name();
for (i = 0; i < QPalette::NColorRoles; i++)
inactcg << newPal.color(QPalette::Inactive,
(QPalette::ColorRole) i).name();
for (i = 0; i < QPalette::NColorRoles; i++)
discg << newPal.color(QPalette::Disabled,
(QPalette::ColorRole) i).name();
settings.setValue(QStringLiteral("/qt/Palette/active"), actcg);
settings.setValue(QStringLiteral("/qt/Palette/inactive"), inactcg);
settings.setValue(QStringLiteral("/qt/Palette/disabled"), discg);
// export kwin's colors to qtrc for kstyle to use
KConfigGroup wmCfgGroup(kglobalcfg, "WM");
// active colors
QColor clr = newPal.color( QPalette::Active, QPalette::Window );
clr = wmCfgGroup.readEntry("activeBackground", clr);
settings.setValue(QStringLiteral("/qt/KWinPalette/activeBackground"), clr.name());
if (QPixmap::defaultDepth() > 8)
clr = clr.darker(110);
clr = wmCfgGroup.readEntry("activeBlend", clr);
settings.setValue(QStringLiteral("/qt/KWinPalette/activeBlend"), clr.name());
clr = newPal.color( QPalette::Active, QPalette::HighlightedText );
clr = wmCfgGroup.readEntry("activeForeground", clr);
settings.setValue(QStringLiteral("/qt/KWinPalette/activeForeground"), clr.name());
clr = newPal.color( QPalette::Active,QPalette::Window );
clr = wmCfgGroup.readEntry("frame", clr);
settings.setValue(QStringLiteral("/qt/KWinPalette/frame"), clr.name());
clr = wmCfgGroup.readEntry("activeTitleBtnBg", clr);
settings.setValue(QStringLiteral("/qt/KWinPalette/activeTitleBtnBg"), clr.name());
// inactive colors
clr = newPal.color(QPalette::Inactive, QPalette::Window);
clr = wmCfgGroup.readEntry("inactiveBackground", clr);
settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveBackground"), clr.name());
if (QPixmap::defaultDepth() > 8)
clr = clr.darker(110);
clr = wmCfgGroup.readEntry("inactiveBlend", clr);
settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveBlend"), clr.name());
clr = newPal.color(QPalette::Inactive, QPalette::Window).darker();
clr = wmCfgGroup.readEntry("inactiveForeground", clr);
settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveForeground"), clr.name());
clr = newPal.color(QPalette::Inactive, QPalette::Window);
clr = wmCfgGroup.readEntry("inactiveFrame", clr);
settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveFrame"), clr.name());
clr = wmCfgGroup.readEntry("inactiveTitleBtnBg", clr);
settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveTitleBtnBg"), clr.name());
KConfigGroup kdeCfgGroup(kglobalcfg, "KDE");
settings.setValue(QStringLiteral("/qt/KDE/contrast"), kdeCfgGroup.readEntry("contrast", 7));
}
// -----------------------------------------------------------------------------
static void applyQtSettings( KSharedConfigPtr kglobalcfg, QSettings& settings )
{
/* export font settings */
// NOTE keep this in sync with kfontsettingsdata in plasma-integration (cf. also Bug 378262)
QFont defaultFont(QStringLiteral("Noto Sans"), 10, -1);
defaultFont.setStyleHint(QFont::SansSerif);
const KConfigGroup configGroup(KSharedConfig::openConfig(), QStringLiteral("General"));
const QString fontInfo = configGroup.readEntry(QStringLiteral("font"), QString());
if (!fontInfo.isEmpty()) {
defaultFont.fromString(fontInfo);
}
settings.setValue(QStringLiteral("/qt/font"), defaultFont.toString());
/* export effects settings */
KConfigGroup kdeCfgGroup(kglobalcfg, "General");
bool effectsEnabled = kdeCfgGroup.readEntry("EffectsEnabled", false);
bool fadeMenus = kdeCfgGroup.readEntry("EffectFadeMenu", false);
bool fadeTooltips = kdeCfgGroup.readEntry("EffectFadeTooltip", false);
bool animateCombobox = kdeCfgGroup.readEntry("EffectAnimateCombo", false);
QStringList guieffects;
if (effectsEnabled) {
guieffects << QStringLiteral("general");
if (fadeMenus)
guieffects << QStringLiteral("fademenu");
if (animateCombobox)
guieffects << QStringLiteral("animatecombo");
if (fadeTooltips)
guieffects << QStringLiteral("fadetooltip");
}
else
guieffects << QStringLiteral("none");
settings.setValue(QStringLiteral("/qt/GUIEffects"), guieffects);
}
// -----------------------------------------------------------------------------
static void addColorDef(QString& s, const char* n, const QColor& col)
{
QString tmp;
tmp.sprintf("#define %s #%02x%02x%02x\n",
n, col.red(), col.green(), col.blue());
s += tmp;
}
// -----------------------------------------------------------------------------
static void copyFile(QFile& tmp, QString const& filename, bool )
{
QFile f( filename );
if ( f.open(QIODevice::ReadOnly) ) {
QByteArray buf( 8192, ' ' );
while ( !f.atEnd() ) {
int read = f.read( buf.data(), buf.size() );
if ( read > 0 )
tmp.write( buf.data(), read );
}
}
}
// -----------------------------------------------------------------------------
static QString item( int i ) {
return QString::number( i / 255.0, 'f', 3 );
}
static QString color( const QColor& col )
{
return QStringLiteral( "{ %1, %2, %3 }" ).arg( item( col.red() ) ).arg( item( col.green() ) ).arg( item( col.blue() ) );
}
static void createGtkrc( bool exportColors, const QPalette& cg, bool exportGtkTheme, const QString& gtkTheme, int version )
{
// lukas: why does it create in ~/.kde/share/config ???
// pfeiffer: so that we don't overwrite the user's gtkrc.
// it is found via the GTK_RC_FILES environment variable.
QSaveFile saveFile( writableGtkrc(version) );
if ( !saveFile.open(QIODevice::WriteOnly) )
return;
QTextStream t ( &saveFile );
t.setCodec( QTextCodec::codecForLocale () );
t << i18n(
"# created by KDE Plasma, %1\n"
"#\n", QDateTime::currentDateTime().toString());
if ( 2==version ) { // we should maybe check for MacOS settings here
t << endl;
t << "gtk-alternative-button-order = 1" << endl;
t << endl;
}
if (exportGtkTheme)
{
QString gtkStyle;
if (gtkTheme.toLower() == QLatin1String("oxygen"))
gtkStyle = QStringLiteral("oxygen-gtk");
else
gtkStyle = gtkTheme;
bool exist_gtkrc = false;
QByteArray gtkrc = getenv(gtkEnvVar(version));
QStringList listGtkrc = QFile::decodeName(gtkrc).split(QLatin1Char(':'));
if (listGtkrc.contains(saveFile.fileName()))
listGtkrc.removeAll(saveFile.fileName());
listGtkrc.append(QDir::homePath() + userGtkrc(version));
listGtkrc.append(QDir::homePath() + "/.gtkrc-2.0-kde");
listGtkrc.append(QDir::homePath() + "/.gtkrc-2.0-kde4");
listGtkrc.removeAll(QLatin1String(""));
listGtkrc.removeDuplicates();
for (int i = 0; i < listGtkrc.size(); ++i)
{
if ((exist_gtkrc = QFile::exists(listGtkrc.at(i))))
break;
}
if (!exist_gtkrc)
{
QString gtk2ThemeFilename;
gtk2ThemeFilename = QStringLiteral("%1/.themes/%2/gtk-2.0/gtkrc").arg(QDir::homePath()).arg(gtkStyle);
if (!QFile::exists(gtk2ThemeFilename)) {
QStringList gtk2ThemePath;
gtk2ThemeFilename.clear();
QByteArray xdgDataDirs = getenv("XDG_DATA_DIRS");
gtk2ThemePath.append(QDir::homePath() + "/.local");
gtk2ThemePath.append(QFile::decodeName(xdgDataDirs).split(QLatin1Char(':')));
gtk2ThemePath.removeDuplicates();
for (int i = 0; i < gtk2ThemePath.size(); ++i)
{
gtk2ThemeFilename = QStringLiteral("%1/themes/%2/gtk-2.0/gtkrc").arg(gtk2ThemePath.at(i)).arg(gtkStyle);
if (QFile::exists(gtk2ThemeFilename))
break;
else
gtk2ThemeFilename.clear();
}
}
if (!gtk2ThemeFilename.isEmpty())
{
t << "include \"" << gtk2ThemeFilename << "\"" << endl;
t << endl;
t << "gtk-theme-name=\"" << gtkStyle << "\"" << endl;
t << endl;
if (gtkStyle == QLatin1String("oxygen-gtk"))
exportColors = false;
}
}
}
if (exportColors)
{
t << "style \"default\"" << endl;
t << "{" << endl;
t << " bg[NORMAL] = " << color( cg.color( QPalette::Active, QPalette::Window ) ) << endl;
t << " bg[SELECTED] = " << color( cg.color(QPalette::Active, QPalette::Highlight) ) << endl;
t << " bg[INSENSITIVE] = " << color( cg.color( QPalette::Active, QPalette::Window ) ) << endl;
t << " bg[ACTIVE] = " << color( cg.color( QPalette::Active, QPalette::Mid ) ) << endl;
t << " bg[PRELIGHT] = " << color( cg.color( QPalette::Active, QPalette::Window ) ) << endl;
t << endl;
t << " base[NORMAL] = " << color( cg.color( QPalette::Active, QPalette::Base ) ) << endl;
t << " base[SELECTED] = " << color( cg.color(QPalette::Active, QPalette::Highlight) ) << endl;
t << " base[INSENSITIVE] = " << color( cg.color( QPalette::Active, QPalette::Window ) ) << endl;
t << " base[ACTIVE] = " << color( cg.color(QPalette::Active, QPalette::Highlight) ) << endl;
t << " base[PRELIGHT] = " << color( cg.color(QPalette::Active, QPalette::Highlight) ) << endl;
t << endl;
t << " text[NORMAL] = " << color( cg.color(QPalette::Active, QPalette::Text) ) << endl;
t << " text[SELECTED] = " << color( cg.color(QPalette::Active, QPalette::HighlightedText) ) << endl;
t << " text[INSENSITIVE] = " << color( cg.color( QPalette::Active, QPalette::Mid ) ) << endl;
t << " text[ACTIVE] = " << color( cg.color(QPalette::Active, QPalette::HighlightedText) ) << endl;
t << " text[PRELIGHT] = " << color( cg.color(QPalette::Active, QPalette::HighlightedText) ) << endl;
t << endl;
t << " fg[NORMAL] = " << color ( cg.color( QPalette::Active, QPalette::WindowText ) ) << endl;
t << " fg[SELECTED] = " << color( cg.color(QPalette::Active, QPalette::HighlightedText) ) << endl;
t << " fg[INSENSITIVE] = " << color( cg.color( QPalette::Active, QPalette::Mid ) ) << endl;
t << " fg[ACTIVE] = " << color( cg.color( QPalette::Active, QPalette::WindowText ) ) << endl;
t << " fg[PRELIGHT] = " << color( cg.color( QPalette::Active, QPalette::WindowText ) ) << endl;
t << "}" << endl;
t << endl;
t << "class \"*\" style \"default\"" << endl;
t << endl;
// tooltips don't have the standard background color
t << "style \"ToolTip\"" << endl;
t << "{" << endl;
t << " bg[NORMAL] = " << color( cg.color( QPalette::ToolTipBase ) ) << endl;
t << " base[NORMAL] = " << color( cg.color( QPalette::ToolTipBase ) ) << endl;
t << " text[NORMAL] = " << color( cg.color( QPalette::ToolTipText ) ) << endl;
t << " fg[NORMAL] = " << color( cg.color( QPalette::ToolTipText ) ) << endl;
t << "}" << endl;
t << endl;
t << "widget \"gtk-tooltip\" style \"ToolTip\"" << endl;
t << "widget \"gtk-tooltips\" style \"ToolTip\"" << endl;
t << "widget \"gtk-tooltip*\" style \"ToolTip\"" << endl;
t << endl;
// highlight the current (mouse-hovered) menu-item
// not every button, checkbox, etc.
t << "style \"MenuItem\"" << endl;
t << "{" << endl;
t << " bg[PRELIGHT] = " << color( cg.color(QPalette::Highlight) ) << endl;
t << " fg[PRELIGHT] = " << color( cg.color(QPalette::HighlightedText) ) << endl;
t << "}" << endl;
t << endl;
t << "class \"*MenuItem\" style \"MenuItem\"" << endl;
t << endl;
}
saveFile.commit();
}
// -----------------------------------------------------------------------------
void runRdb( uint flags )
{
// Obtain the application palette that is about to be set.
bool exportColors = flags & KRdbExportColors;
bool exportQtColors = flags & KRdbExportQtColors;
bool exportQtSettings = flags & KRdbExportQtSettings;
bool exportXftSettings = flags & KRdbExportXftSettings;
bool exportGtkTheme = flags & KRdbExportGtkTheme;
KSharedConfigPtr kglobalcfg = KSharedConfig::openConfig( QStringLiteral("kdeglobals") );
KConfigGroup kglobals(kglobalcfg, "KDE");
QPalette newPal = KColorScheme::createApplicationPalette(kglobalcfg);
QTemporaryFile tmpFile;
if (!tmpFile.open())
{
qDebug() << "Couldn't open temp file";
exit(0);
}
KConfigGroup generalCfgGroup(kglobalcfg, "General");
QString gtkTheme;
if (kglobals.hasKey("widgetStyle"))
gtkTheme = kglobals.readEntry("widgetStyle");
else
gtkTheme = QStringLiteral("oxygen");
createGtkrc( exportColors, newPal, exportGtkTheme, gtkTheme, 1 );
createGtkrc( exportColors, newPal, exportGtkTheme, gtkTheme, 2 );
// Export colors to non-(KDE/Qt) apps (e.g. Motif, GTK+ apps)
if (exportColors)
{
KConfigGroup g(KSharedConfig::openConfig(), "WM");
QString preproc;
QColor backCol = newPal.color( QPalette::Active, QPalette::Window );
addColorDef(preproc, "FOREGROUND" , newPal.color( QPalette::Active, QPalette::WindowText ) );
addColorDef(preproc, "BACKGROUND" , backCol);
addColorDef(preproc, "HIGHLIGHT" , backCol.lighter(100+(2*KColorScheme::contrast()+4)*16/1));
addColorDef(preproc, "LOWLIGHT" , backCol.darker(100+(2*KColorScheme::contrast()+4)*10));
addColorDef(preproc, "SELECT_BACKGROUND" , newPal.color( QPalette::Active, QPalette::Highlight));
addColorDef(preproc, "SELECT_FOREGROUND" , newPal.color( QPalette::Active, QPalette::HighlightedText));
addColorDef(preproc, "WINDOW_BACKGROUND" , newPal.color( QPalette::Active, QPalette::Base ) );
addColorDef(preproc, "WINDOW_FOREGROUND" , newPal.color( QPalette::Active, QPalette::Text ) );
addColorDef(preproc, "INACTIVE_BACKGROUND", g.readEntry("inactiveBackground", QColor(224, 223, 222)));
addColorDef(preproc, "INACTIVE_FOREGROUND", g.readEntry("inactiveBackground", QColor(224, 223, 222)));
addColorDef(preproc, "ACTIVE_BACKGROUND" , g.readEntry("activeBackground", QColor(48, 174, 232)));
addColorDef(preproc, "ACTIVE_FOREGROUND" , g.readEntry("activeBackground", QColor(48, 174, 232)));
//---------------------------------------------------------------
tmpFile.write( preproc.toLatin1(), preproc.length() );
QStringList list;
const QStringList adPaths = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
QStringLiteral("kdisplay/app-defaults/"), QStandardPaths::LocateDirectory);
for (QStringList::ConstIterator it = adPaths.constBegin(); it != adPaths.constEnd(); ++it) {
QDir dSys( *it );
if ( dSys.exists() ) {
dSys.setFilter( QDir::Files );
dSys.setSorting( QDir::Name );
dSys.setNameFilters(QStringList(QStringLiteral("*.ad")));
list += dSys.entryList();
}
}
for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
copyFile(tmpFile, QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kdisplay/app-defaults/"+(*it)), true);
}
// Merge ~/.Xresources or fallback to ~/.Xdefaults
QString homeDir = QDir::homePath();
QString xResources = homeDir + "/.Xresources";
// very primitive support for ~/.Xresources by appending it
if ( QFile::exists( xResources ) )
copyFile(tmpFile, xResources, true);
else
copyFile(tmpFile, homeDir + "/.Xdefaults", true);
// Export the Xcursor theme & size settings
KConfigGroup mousecfg(KSharedConfig::openConfig( QStringLiteral("kcminputrc") ), "Mouse" );
QString theme = mousecfg.readEntry("cursorTheme", QString("breeze_cursors"));
- QString size = mousecfg.readEntry("cursorSize", QString("0"));
+ QString size = mousecfg.readEntry("cursorSize", QString("24"));
QString contents;
if (!theme.isNull())
contents = "Xcursor.theme: " + theme + '\n';
if (!size.isNull())
contents += "Xcursor.size: " + size + '\n';
if (exportXftSettings)
{
contents += QLatin1String("Xft.antialias: ");
if(generalCfgGroup.readEntry("XftAntialias", true))
contents += QLatin1String("1\n");
else
contents += QLatin1String("0\n");
QString hintStyle = generalCfgGroup.readEntry("XftHintStyle", "hintslight");
contents += QLatin1String("Xft.hinting: ");
if(hintStyle.isEmpty())
contents += QLatin1String("-1\n");
else
{
if(hintStyle!=QLatin1String("hintnone"))
contents += QLatin1String("1\n");
else
contents += QLatin1String("0\n");
contents += "Xft.hintstyle: " + hintStyle + '\n';
}
QString subPixel = generalCfgGroup.readEntry("XftSubPixel", "rgb");
if(!subPixel.isEmpty())
contents += "Xft.rgba: " + subPixel + '\n';
KConfig _cfgfonts( QStringLiteral("kcmfonts") );
KConfigGroup cfgfonts(&_cfgfonts, "General");
int dpi;
//even though this sets up the X rdb, we want to use the value the
//user has set to use when under wayland - as X apps will be scaled by the compositor
if (KWindowSystem::isPlatformWayland()) {
dpi = cfgfonts.readEntry( "forceFontDPIWayland", 0);
if (dpi == 0) { //with wayland we want xwayland to run at 96 dpi (unless set otherwise) as we have wayland scaling on top
dpi = 96;
}
} else {
dpi = cfgfonts.readEntry( "forceFontDPI", 0);
}
if( dpi != 0 )
contents += "Xft.dpi: " + QString::number(dpi) + '\n';
else
{
KProcess proc;
proc << QStringLiteral("xrdb") << QStringLiteral("-quiet") << QStringLiteral("-remove") << QStringLiteral("-nocpp");
proc.start();
if (proc.waitForStarted())
{
proc.write( QByteArray( "Xft.dpi\n" ) );
proc.closeWriteChannel();
proc.waitForFinished();
}
}
}
if (contents.length() > 0)
tmpFile.write( contents.toLatin1(), contents.length() );
tmpFile.flush();
KProcess proc;
#ifndef NDEBUG
proc << QStringLiteral("xrdb") << QStringLiteral("-merge") << tmpFile.fileName();
#else
proc << "xrdb" << "-quiet" << "-merge" << tmpFile.fileName();
#endif
proc.execute();
applyGtkStyles(exportColors, 1);
applyGtkStyles(exportColors, 2);
/* Qt exports */
if ( exportQtColors || exportQtSettings )
{
QSettings* settings = new QSettings(QStringLiteral("Trolltech"));
if ( exportQtColors )
applyQtColors( kglobalcfg, *settings, newPal ); // For kcmcolors
if ( exportQtSettings )
applyQtSettings( kglobalcfg, *settings ); // For kcmstyle
delete settings;
QCoreApplication::processEvents();
#if HAVE_X11
if (qApp->platformName() == QLatin1String("xcb")) {
// We let KIPC take care of ourselves, as we are in a KDE app with
// QApp::setDesktopSettingsAware(false);
// Instead of calling QApp::x11_apply_settings() directly, we instead
// modify the timestamp which propagates the settings changes onto
// Qt-only apps without adversely affecting ourselves.
// Cheat and use the current timestamp, since we just saved to qtrc.
QDateTime settingsstamp = QDateTime::currentDateTime();
static Atom qt_settings_timestamp = 0;
if (!qt_settings_timestamp) {
QString atomname(QStringLiteral("_QT_SETTINGS_TIMESTAMP_"));
atomname += XDisplayName( nullptr ); // Use the $DISPLAY envvar.
qt_settings_timestamp = XInternAtom( QX11Info::display(), atomname.toLatin1(), False);
}
QBuffer stamp;
QDataStream s(&stamp.buffer(), QIODevice::WriteOnly);
s << settingsstamp;
XChangeProperty( QX11Info::display(), QX11Info::appRootWindow(), qt_settings_timestamp,
qt_settings_timestamp, 8, PropModeReplace,
(unsigned char*) stamp.buffer().data(),
stamp.buffer().size() );
QApplication::flush();
}
#endif
}
//Legacy support:
//Try to sync kde4 settings with ours
Kdelibs4Migration migration;
//kf5 congig groups for general and icons
KConfigGroup generalGroup(kglobalcfg, "General");
KConfigGroup iconsGroup(kglobalcfg, "Icons");
const QString colorSchemeName = generalGroup.readEntry("ColorScheme", QStringLiteral("Breeze"));
QString colorSchemeSrcFile;
if (colorSchemeName != QLatin1String("Default")) {
//fix filename, copied from ColorsCM::saveScheme()
QString colorSchemeFilename = colorSchemeName;
colorSchemeFilename.remove('\''); // So Foo's does not become FooS
QRegExp fixer(QStringLiteral("[\\W,.-]+(.?)"));
int offset;
while ((offset = fixer.indexIn(colorSchemeFilename)) >= 0)
colorSchemeFilename.replace(offset, fixer.matchedLength(), fixer.cap(1).toUpper());
colorSchemeFilename.replace(0, 1, colorSchemeFilename.at(0).toUpper());
//clone the color scheme
colorSchemeSrcFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "color-schemes/" + colorSchemeFilename + ".colors");
const QString dest = migration.saveLocation("data", QStringLiteral("color-schemes")) + colorSchemeName + ".colors";
QFile::remove(dest);
QFile::copy(colorSchemeSrcFile, dest);
}
//Apply the color scheme
QString configFilePath = migration.saveLocation("config") + "kdeglobals";
if (configFilePath.isEmpty()) {
return;
}
KConfig kde4config(configFilePath, KConfig::SimpleConfig);
KConfigGroup kde4generalGroup(&kde4config, "General");
kde4generalGroup.writeEntry("ColorScheme", colorSchemeName);
//fonts
QString font = generalGroup.readEntry("font", QString());
if (!font.isEmpty()) {
kde4generalGroup.writeEntry("font", font);
}
font = generalGroup.readEntry("desktopFont", QString());
if (!font.isEmpty()) {
kde4generalGroup.writeEntry("desktopFont", font);
}
font = generalGroup.readEntry("menuFont", QString());
if (!font.isEmpty()) {
kde4generalGroup.writeEntry("menuFont", font);
}
font = generalGroup.readEntry("smallestReadableFont", QString());
if (!font.isEmpty()) {
kde4generalGroup.writeEntry("smallestReadableFont", font);
}
font = generalGroup.readEntry("taskbarFont", QString());
if (!font.isEmpty()) {
kde4generalGroup.writeEntry("taskbarFont", font);
}
font = generalGroup.readEntry("toolBarFont", QString());
if (!font.isEmpty()) {
kde4generalGroup.writeEntry("toolBarFont", font);
}
//TODO: does exist any way to check if a qt4 widget style is present from a qt5 app?
//kde4generalGroup.writeEntry("widgetStyle", "qtcurve");
kde4generalGroup.sync();
KConfigGroup kde4IconGroup(&kde4config, "Icons");
QString iconTheme = iconsGroup.readEntry("Theme", QString());
if (!iconTheme.isEmpty()) {
kde4IconGroup.writeEntry("Theme", iconTheme);
}
kde4IconGroup.sync();
if (!colorSchemeSrcFile.isEmpty()) {
//copy all the groups in the color scheme in kdeglobals
KSharedConfigPtr kde4ColorConfig = KSharedConfig::openConfig(colorSchemeSrcFile, KConfig::SimpleConfig);
foreach (const QString &grp, kde4ColorConfig->groupList()) {
KConfigGroup cg(kde4ColorConfig, grp);
KConfigGroup cg2(&kde4config, grp);
cg.copyTo(&cg2);
}
}
//widgets settings
KConfigGroup kglobals4(&kde4config, "KDE");
kglobals4.writeEntry("ShowIconsInMenuItems", kglobals.readEntry("ShowIconsInMenuItems", true));
kglobals4.writeEntry("ShowIconsOnPushButtons", kglobals.readEntry("ShowIconsOnPushButtons", true));
kglobals4.writeEntry("contrast", kglobals.readEntry("contrast", 4));
//FIXME: this should somehow check if the kde4 version of the style is installed
kde4generalGroup.writeEntry("widgetStyle", kglobals.readEntry("widgetStyle", "breeze"));
//toolbar style
KConfigGroup toolbars4(&kde4config, "Toolbar style");
KConfigGroup toolbars5(kglobalcfg, "Toolbar style");
toolbars4.writeEntry("ToolButtonStyle", toolbars5.readEntry("ToolButtonStyle", "TextBesideIcon"));
toolbars4.writeEntry("ToolButtonStyleOtherToolbars", toolbars5.readEntry("ToolButtonStyleOtherToolbars", "TextBesideIcon"));
}
diff --git a/kcms/lookandfeel/kcm.cpp b/kcms/lookandfeel/kcm.cpp
index 296725e6f..50e625982 100644
--- a/kcms/lookandfeel/kcm.cpp
+++ b/kcms/lookandfeel/kcm.cpp
@@ -1,707 +1,707 @@
/* This file is part of the KDE Project
Copyright (c) 2014 Marco Martin
Copyright (c) 2014 Vishesh Handa
Copyright (c) 2019 Cyril Rossi
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
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 "kcm.h"
#include "../krdb/krdb.h"
#include "config-kcm.h"
#include "config-workspace.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lookandfeelsettings.h"
#ifdef HAVE_XCURSOR
# include "../cursortheme/xcursor/xcursortheme.h"
# include
#endif
#ifdef HAVE_XFIXES
# include
#endif
KCMLookandFeel::KCMLookandFeel(QObject *parent, const QVariantList &args)
: KQuickAddons::ManagedConfigModule(parent, args)
, m_settings(new LookAndFeelSettings(this))
, m_config(QStringLiteral("kdeglobals"))
, m_configGroup(m_config.group("KDE"))
, m_applyColors(true)
, m_applyWidgetStyle(true)
, m_applyIcons(true)
, m_applyPlasmaTheme(true)
, m_applyCursors(true)
, m_applyWindowSwitcher(true)
, m_applyDesktopSwitcher(true)
, m_resetDefaultLayout(false)
, m_applyWindowDecoration(true)
{
qmlRegisterType();
qmlRegisterType();
qmlRegisterType();
KAboutData *about = new KAboutData(QStringLiteral("kcm_lookandfeel"), i18n("Global Theme"),
QStringLiteral("0.1"), QString(), KAboutLicense::LGPL);
about->addAuthor(i18n("Marco Martin"), QString(), QStringLiteral("mart@kde.org"));
setAboutData(about);
setButtons(Apply | Default);
m_model = new QStandardItemModel(this);
QHash roles = m_model->roleNames();
roles[PluginNameRole] = "pluginName";
roles[DescriptionRole] = "description";
roles[ScreenshotRole] = "screenshot";
roles[FullScreenPreviewRole] = "fullScreenPreview";
roles[HasSplashRole] = "hasSplash";
roles[HasLockScreenRole] = "hasLockScreen";
roles[HasRunCommandRole] = "hasRunCommand";
roles[HasLogoutRole] = "hasLogout";
roles[HasColorsRole] = "hasColors";
roles[HasWidgetStyleRole] = "hasWidgetStyle";
roles[HasIconsRole] = "hasIcons";
roles[HasPlasmaThemeRole] = "hasPlasmaTheme";
roles[HasCursorsRole] = "hasCursors";
roles[HasWindowSwitcherRole] = "hasWindowSwitcher";
roles[HasDesktopSwitcherRole] = "hasDesktopSwitcher";
m_model->setItemRoleNames(roles);
loadModel();
}
KCMLookandFeel::~KCMLookandFeel()
{
}
void KCMLookandFeel::reloadModel()
{
loadModel();
}
QStandardItemModel *KCMLookandFeel::lookAndFeelModel() const
{
return m_model;
}
int KCMLookandFeel::pluginIndex(const QString &pluginName) const
{
const auto results = m_model->match(m_model->index(0, 0), PluginNameRole, pluginName);
if (results.count() == 1) {
return results.first().row();
}
return -1;
}
QList KCMLookandFeel::availablePackages(const QStringList &components)
{
QList packages;
QStringList paths;
const QStringList dataPaths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
paths.reserve(dataPaths.count());
for (const QString &path : dataPaths) {
QDir dir(path + QStringLiteral("/plasma/look-and-feel"));
paths << dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
}
for (const QString &path : paths) {
KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel"));
pkg.setPath(path);
pkg.setFallbackPackage(KPackage::Package());
if (components.isEmpty()) {
packages << pkg;
} else {
for (const auto &component : components) {
if (!pkg.filePath(component.toUtf8()).isEmpty()) {
packages << pkg;
break;
}
}
}
}
return packages;
}
LookAndFeelSettings *KCMLookandFeel::lookAndFeelSettings() const
{
return m_settings;
}
void KCMLookandFeel::loadModel()
{
m_model->clear();
const QList pkgs = availablePackages({"defaults", "layouts"});
for (const KPackage::Package &pkg : pkgs) {
if (!pkg.metadata().isValid()) {
continue;
}
QStandardItem *row = new QStandardItem(pkg.metadata().name());
row->setData(pkg.metadata().pluginId(), PluginNameRole);
row->setData(pkg.metadata().description(), DescriptionRole);
row->setData(pkg.filePath("preview"), ScreenshotRole);
row->setData(pkg.filePath("fullscreenpreview"), FullScreenPreviewRole);
//What the package provides
row->setData(!pkg.filePath("splashmainscript").isEmpty(), HasSplashRole);
row->setData(!pkg.filePath("lockscreenmainscript").isEmpty(), HasLockScreenRole);
row->setData(!pkg.filePath("runcommandmainscript").isEmpty(), HasRunCommandRole);
row->setData(!pkg.filePath("logoutmainscript").isEmpty(), HasLogoutRole);
if (!pkg.filePath("defaults").isEmpty()) {
KSharedConfigPtr conf = KSharedConfig::openConfig(pkg.filePath("defaults"));
KConfigGroup cg(conf, "kdeglobals");
cg = KConfigGroup(&cg, "General");
bool hasColors = !cg.readEntry("ColorScheme", QString()).isEmpty();
if (!hasColors) {
hasColors = !pkg.filePath("colors").isEmpty();
}
row->setData(hasColors, HasColorsRole);
cg = KConfigGroup(&cg, "KDE");
row->setData(!cg.readEntry("widgetStyle", QString()).isEmpty(), HasWidgetStyleRole);
cg = KConfigGroup(conf, "kdeglobals");
cg = KConfigGroup(&cg, "Icons");
row->setData(!cg.readEntry("Theme", QString()).isEmpty(), HasIconsRole);
cg = KConfigGroup(conf, "kdeglobals");
cg = KConfigGroup(&cg, "Theme");
row->setData(!cg.readEntry("name", QString()).isEmpty(), HasPlasmaThemeRole);
cg = KConfigGroup(conf, "kcminputrc");
cg = KConfigGroup(&cg, "Mouse");
row->setData(!cg.readEntry("cursorTheme", QString()).isEmpty(), HasCursorsRole);
cg = KConfigGroup(conf, "kwinrc");
cg = KConfigGroup(&cg, "WindowSwitcher");
row->setData(!cg.readEntry("LayoutName", QString()).isEmpty(), HasWindowSwitcherRole);
cg = KConfigGroup(conf, "kwinrc");
cg = KConfigGroup(&cg, "DesktopSwitcher");
row->setData(!cg.readEntry("LayoutName", QString()).isEmpty(), HasDesktopSwitcherRole);
}
m_model->appendRow(row);
}
m_model->sort(0 /*column*/);
//Model has been cleared so pretend the selected look and fell changed to force view update
emit m_settings->lookAndFeelPackageChanged();
}
void KCMLookandFeel::load()
{
ManagedConfigModule::load();
m_package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel"));
m_package.setPath(m_settings->lookAndFeelPackage());
}
void KCMLookandFeel::save()
{
KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel"));
package.setPath(m_settings->lookAndFeelPackage());
if (!package.isValid()) {
return;
}
ManagedConfigModule::save();
if (m_resetDefaultLayout) {
QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"), QStringLiteral("/PlasmaShell"),
QStringLiteral("org.kde.PlasmaShell"), QStringLiteral("loadLookAndFeelDefaultLayout"));
QList args;
args << m_settings->lookAndFeelPackage();
message.setArguments(args);
QDBusConnection::sessionBus().call(message, QDBus::NoBlock);
}
if (!package.filePath("defaults").isEmpty()) {
KSharedConfigPtr conf = KSharedConfig::openConfig(package.filePath("defaults"));
KConfigGroup cg(conf, "kdeglobals");
cg = KConfigGroup(&cg, "KDE");
if (m_applyWidgetStyle) {
setWidgetStyle(cg.readEntry("widgetStyle", QString()));
}
if (m_applyColors) {
QString colorsFile = package.filePath("colors");
KConfigGroup cg(conf, "kdeglobals");
cg = KConfigGroup(&cg, "General");
QString colorScheme = cg.readEntry("ColorScheme", QString());
if (!colorsFile.isEmpty()) {
if (!colorScheme.isEmpty()) {
setColors(colorScheme, colorsFile);
} else {
setColors(package.metadata().name(), colorsFile);
}
} else if (!colorScheme.isEmpty()) {
colorScheme.remove(QLatin1Char('\'')); // So Foo's does not become FooS
QRegExp fixer(QStringLiteral("[\\W,.-]+(.?)"));
int offset;
while ((offset = fixer.indexIn(colorScheme)) >= 0) {
colorScheme.replace(offset, fixer.matchedLength(), fixer.cap(1).toUpper());
}
colorScheme.replace(0, 1, colorScheme.at(0).toUpper());
//NOTE: why this loop trough all the scheme files?
//the scheme theme name is an heuristic, there is no plugin metadata whatsoever.
//is based on the file name stripped from weird characters or the
//eventual id- prefix store.kde.org puts, so we can just find a
//theme that ends as the specified name
bool schemeFound = false;
const QStringList schemeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory);
for (const QString &dir : schemeDirs) {
const QStringList fileNames = QDir(dir).entryList(QStringList()<lookAndFeelPackage());
setLockScreen(m_settings->lookAndFeelPackage());
m_configGroup.sync();
m_package.setPath(m_settings->lookAndFeelPackage());
runRdb(KRdbExportQtColors | KRdbExportGtkTheme | KRdbExportColors | KRdbExportQtSettings | KRdbExportXftSettings);
}
void KCMLookandFeel::setWidgetStyle(const QString &style)
{
if (style.isEmpty()) {
return;
}
m_configGroup.writeEntry("widgetStyle", style);
m_configGroup.sync();
//FIXME: changing style on the fly breaks QQuickWidgets
KGlobalSettings::self()->emitChange(KGlobalSettings::StyleChanged);
}
void KCMLookandFeel::setColors(const QString &scheme, const QString &colorFile)
{
if (scheme.isEmpty() && colorFile.isEmpty()) {
return;
}
KSharedConfigPtr conf = KSharedConfig::openConfig(colorFile, KSharedConfig::CascadeConfig);
foreach (const QString &grp, conf->groupList()) {
KConfigGroup cg(conf, grp);
KConfigGroup cg2(&m_config, grp);
cg.copyTo(&cg2);
}
KConfigGroup configGroup(&m_config, "General");
configGroup.writeEntry("ColorScheme", scheme);
configGroup.sync();
KGlobalSettings::self()->emitChange(KGlobalSettings::PaletteChanged);
}
void KCMLookandFeel::setIcons(const QString &theme)
{
if (theme.isEmpty()) {
return;
}
KConfigGroup cg(&m_config, "Icons");
cg.writeEntry("Theme", theme);
cg.sync();
for (int i=0; i < KIconLoader::LastGroup; i++) {
KIconLoader::emitChange(KIconLoader::Group(i));
}
}
void KCMLookandFeel::setPlasmaTheme(const QString &theme)
{
if (theme.isEmpty()) {
return;
}
KConfig config(QStringLiteral("plasmarc"));
KConfigGroup cg(&config, "Theme");
cg.writeEntry("name", theme);
cg.sync();
}
void KCMLookandFeel::setCursorTheme(const QString themeName)
{
//TODO: use pieces of cursor kcm when moved to plasma-desktop
if (themeName.isEmpty()) {
return;
}
KConfig config(QStringLiteral("kcminputrc"));
KConfigGroup cg(&config, "Mouse");
cg.writeEntry("cursorTheme", themeName);
cg.sync();
#ifdef HAVE_XCURSOR
// Require the Xcursor version that shipped with X11R6.9 or greater, since
// in previous versions the Xfixes code wasn't enabled due to a bug in the
// build system (freedesktop bug #975).
#if defined(HAVE_XFIXES) && XFIXES_MAJOR >= 2 && XCURSOR_LIB_VERSION >= 10105
- const int cursorSize = cg.readEntry("cursorSize", 0);
+ const int cursorSize = cg.readEntry("cursorSize", 24);
QDir themeDir = cursorThemeDir(themeName, 0);
if (!themeDir.exists()) {
return;
}
XCursorTheme theme(themeDir);
if (!CursorTheme::haveXfixes()) {
return;
}
UpdateLaunchEnvJob launchEnvJob(QStringLiteral("XCURSOR_THEME"), themeName);
// Update the Xcursor X resources
runRdb(0);
// Notify all applications that the cursor theme has changed
KGlobalSettings::self()->emitChange(KGlobalSettings::CursorChanged);
// Reload the standard cursors
QStringList names;
// Qt cursors
names << QStringLiteral("left_ptr") << QStringLiteral("up_arrow") << QStringLiteral("cross") << QStringLiteral("wait")
<< QStringLiteral("left_ptr_watch") << QStringLiteral("ibeam") << QStringLiteral("size_ver") << QStringLiteral("size_hor")
<< QStringLiteral("size_bdiag") << QStringLiteral("size_fdiag") << QStringLiteral("size_all") << QStringLiteral("split_v")
<< QStringLiteral("split_h") << QStringLiteral("pointing_hand") << QStringLiteral("openhand")
<< QStringLiteral("closedhand") << QStringLiteral("forbidden") << QStringLiteral("whats_this") << QStringLiteral("copy") << QStringLiteral("move") << QStringLiteral("link");
// X core cursors
names << QStringLiteral("X_cursor") << QStringLiteral("right_ptr") << QStringLiteral("hand1")
<< QStringLiteral("hand2") << QStringLiteral("watch") << QStringLiteral("xterm")
<< QStringLiteral("crosshair") << QStringLiteral("left_ptr_watch") << QStringLiteral("center_ptr")
<< QStringLiteral("sb_h_double_arrow") << QStringLiteral("sb_v_double_arrow") << QStringLiteral("fleur")
<< QStringLiteral("top_left_corner") << QStringLiteral("top_side") << QStringLiteral("top_right_corner")
<< QStringLiteral("right_side") << QStringLiteral("bottom_right_corner") << QStringLiteral("bottom_side")
<< QStringLiteral("bottom_left_corner") << QStringLiteral("left_side") << QStringLiteral("question_arrow")
<< QStringLiteral("pirate");
foreach (const QString &name, names) {
XFixesChangeCursorByName(QX11Info::display(), theme.loadCursor(name, cursorSize), QFile::encodeName(name));
}
#else
KMessageBox::information(this,
i18n("You have to restart the Plasma session for these changes to take effect."),
i18n("Cursor Settings Changed"), "CursorSettingsChanged");
#endif
#endif
}
QDir KCMLookandFeel::cursorThemeDir(const QString &theme, const int depth)
{
// Prevent infinite recursion
if (depth > 10) {
return QDir();
}
// Search each icon theme directory for 'theme'
foreach (const QString &baseDir, cursorSearchPaths()) {
QDir dir(baseDir);
if (!dir.exists() || !dir.cd(theme)) {
continue;
}
// If there's a cursors subdir, we'll assume this is a cursor theme
if (dir.exists(QStringLiteral("cursors"))) {
return dir;
}
// If the theme doesn't have an index.theme file, it can't inherit any themes.
if (!dir.exists(QStringLiteral("index.theme"))) {
continue;
}
// Open the index.theme file, so we can get the list of inherited themes
KConfig config(dir.path() + QStringLiteral("/index.theme"), KConfig::NoGlobals);
KConfigGroup cg(&config, "Icon Theme");
// Recurse through the list of inherited themes, to check if one of them
// is a cursor theme.
QStringList inherits = cg.readEntry("Inherits", QStringList());
foreach (const QString &inherit, inherits) {
// Avoid possible DoS
if (inherit == theme) {
continue;
}
if (cursorThemeDir(inherit, depth + 1).exists()) {
return dir;
}
}
}
return QDir();
}
const QStringList KCMLookandFeel::cursorSearchPaths()
{
if (!m_cursorSearchPaths.isEmpty())
return m_cursorSearchPaths;
#ifdef HAVE_XCURSOR
#if XCURSOR_LIB_MAJOR == 1 && XCURSOR_LIB_MINOR < 1
// These are the default paths Xcursor will scan for cursor themes
QString path("~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons");
// If XCURSOR_PATH is set, use that instead of the default path
char *xcursorPath = std::getenv("XCURSOR_PATH");
if (xcursorPath)
path = xcursorPath;
#else
// Get the search path from Xcursor
QString path = XcursorLibraryPath();
#endif
// Separate the paths
m_cursorSearchPaths = path.split(QLatin1Char(':'), QString::SkipEmptyParts);
// Remove duplicates
QMutableStringListIterator i(m_cursorSearchPaths);
while (i.hasNext())
{
const QString path = i.next();
QMutableStringListIterator j(i);
while (j.hasNext())
if (j.next() == path)
j.remove();
}
// Expand all occurrences of ~/ to the home dir
m_cursorSearchPaths.replaceInStrings(QRegExp(QStringLiteral("^~\\/")), QDir::home().path() + QLatin1Char('/'));
return m_cursorSearchPaths;
#else
return QStringList();
#endif
}
void KCMLookandFeel::setSplashScreen(const QString &theme)
{
if (theme.isEmpty()) {
return;
}
KConfig config(QStringLiteral("ksplashrc"));
KConfigGroup cg(&config, "KSplash");
cg.writeEntry("Theme", theme);
//TODO: a way to set none as spash in the l&f
cg.writeEntry("Engine", "KSplashQML");
cg.sync();
}
void KCMLookandFeel::setLockScreen(const QString &theme)
{
if (theme.isEmpty()) {
return;
}
KConfig config(QStringLiteral("kscreenlockerrc"));
KConfigGroup cg(&config, "Greeter");
cg.writeEntry("Theme", theme);
cg.sync();
}
void KCMLookandFeel::setWindowSwitcher(const QString &theme)
{
if (theme.isEmpty()) {
return;
}
KConfig config(QStringLiteral("kwinrc"));
KConfigGroup cg(&config, "TabBox");
cg.writeEntry("LayoutName", theme);
cg.sync();
}
void KCMLookandFeel::setDesktopSwitcher(const QString &theme)
{
if (theme.isEmpty()) {
return;
}
KConfig config(QStringLiteral("kwinrc"));
KConfigGroup cg(&config, "TabBox");
cg.writeEntry("DesktopLayout", theme);
cg.writeEntry("DesktopListLayout", theme);
cg.sync();
}
void KCMLookandFeel::setWindowDecoration(const QString &library, const QString &theme)
{
if (library.isEmpty()) {
return;
}
KConfig config(QStringLiteral("kwinrc"));
KConfigGroup cg(&config, "org.kde.kdecoration2");
cg.writeEntry("library", library);
cg.writeEntry("theme", theme);
cg.sync();
// Reload KWin.
QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"),
QStringLiteral("org.kde.KWin"),
QStringLiteral("reloadConfig"));
QDBusConnection::sessionBus().send(message);
}
void KCMLookandFeel::setResetDefaultLayout(bool reset)
{
if (m_resetDefaultLayout == reset) {
return;
}
m_resetDefaultLayout = reset;
emit resetDefaultLayoutChanged();
if (reset) {
setNeedsSave(true);
}
}
bool KCMLookandFeel::resetDefaultLayout() const
{
return m_resetDefaultLayout;
}
diff --git a/kcms/mouse/backends/x11/x11_backend.cpp b/kcms/mouse/backends/x11/x11_backend.cpp
index d256279a1..54d5d4e9f 100644
--- a/kcms/mouse/backends/x11/x11_backend.cpp
+++ b/kcms/mouse/backends/x11/x11_backend.cpp
@@ -1,156 +1,146 @@
/*
* Copyright 2017 Xuetian Weng
* Copyright 2018 Roman Gilg
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "x11_backend.h"
#include "x11_evdev_backend.h"
#include "x11_libinput_backend.h"
#include "logging.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_XCURSOR
#include
#include
#endif
X11Backend *X11Backend::implementation(QObject *parent)
{
auto dpy = QX11Info::display();
Atom testAtom = XInternAtom(dpy, LIBINPUT_PROP_ACCEL, True);
//There are multiple possible drivers
if (testAtom) {
qCDebug(KCM_MOUSE) << "Using libinput driver on X11.";
return new X11LibinputBackend(parent);
}
else {
qCDebug(KCM_MOUSE) << "Using evdev driver on X11.";
return new X11EvdevBackend(parent);
}
}
X11Backend::X11Backend(QObject* parent)
: InputBackend(parent)
{
m_platformX11 = QX11Info::isPlatformX11();
if (m_platformX11) {
m_dpy = QX11Info::display();
} else {
// TODO: remove this - not needed anymore with Wayland backend!
// let's hope we have a compatibility system like Xwayland ready
m_dpy = XOpenDisplay(nullptr);
}
}
X11Backend::~X11Backend()
{
if (!m_platformX11 && m_dpy) {
XCloseDisplay(m_dpy);
}
}
QString X11Backend::currentCursorTheme()
{
if (!m_dpy) {
return QString();
}
QByteArray name = XGetDefault(m_dpy, "Xcursor", "theme");
#ifdef HAVE_XCURSOR
if (name.isEmpty()) {
name = QByteArray(XcursorGetTheme(m_dpy));
}
#endif
return QFile::decodeName(name);
}
void X11Backend::applyCursorTheme(const QString& theme, int size)
{
#ifdef HAVE_XCURSOR
// Apply the KDE cursor theme to ourselves
if (m_dpy) {
return;
}
if (!theme.isEmpty()) {
XcursorSetTheme(m_dpy, QFile::encodeName(theme));
}
if (size >= 0) {
XcursorSetDefaultSize(m_dpy, size);
}
// Load the default cursor from the theme and apply it to the root window.
Cursor handle = XcursorLibraryLoadCursor(m_dpy, "left_ptr");
XDefineCursor(m_dpy, DefaultRootWindow(m_dpy), handle);
XFreeCursor(m_dpy, handle); // Don't leak the cursor
XFlush(m_dpy);
#endif
}
void X11Backend::kcmInit()
{
auto config = KSharedConfig::openConfig("kcminputrc", KConfig::NoGlobals);
KConfigGroup group = config->group("Mouse");
QString theme = group.readEntry("cursorTheme", QString());
- QString size = group.readEntry("cursorSize", QString());
-
- int intSize = -1;
- if (size.isEmpty()) {
- bool ok;
- uint value = size.toUInt(&ok);
- if (ok) {
- intSize = value;
- }
- }
+ const int size = group.readEntry("cursorSize", 24);
+
// Note: If you update this code, update kapplymousetheme as well.
// use a default value for theme only if it's not configured at all, not even in X resources
if (theme.isEmpty() && currentCursorTheme().isEmpty()) {
theme = "breeze_cursors";
}
- applyCursorTheme(theme, intSize);
+ applyCursorTheme(theme, size);
// Tell klauncher to set the XCURSOR_THEME and XCURSOR_SIZE environment
// variables when launching applications.
if (!theme.isEmpty()) {
UpdateLaunchEnvJob launchEnvJob(QStringLiteral("XCURSOR_THEME"), theme);
}
- if (!size.isEmpty()) {
- UpdateLaunchEnvJob launchEnvJob(QStringLiteral("XCURSOR_SIZE"), size);
- }
+ UpdateLaunchEnvJob launchEnvJob(QStringLiteral("XCURSOR_SIZE"), QByteArray::number(size));
}