forked from Qortal/Brooklyn
309 lines
10 KiB
C++
309 lines
10 KiB
C++
/*
|
|
ksmserver - the KDE session management server
|
|
|
|
SPDX-FileCopyrightText: 2000 Matthias Ettrich <ettrich@kde.org>
|
|
|
|
SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include <config-ksmserver.h>
|
|
#include <config-workspace.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <fixx11h.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <KMessageBox>
|
|
|
|
#include "server.h"
|
|
#include <KLocalizedString>
|
|
#include <KSharedConfig>
|
|
#include <QX11Info>
|
|
#include <kconfig.h>
|
|
#include <kconfiggroup.h>
|
|
#include <kdbusservice.h>
|
|
#include <kmanagerselection.h>
|
|
#include <ksmserver_debug.h>
|
|
#include <kwindowsystem.h>
|
|
|
|
#include <QApplication>
|
|
#include <QCommandLineParser>
|
|
#include <QQuickWindow>
|
|
#include <X11/extensions/Xrender.h>
|
|
|
|
static const char version[] = "0.4";
|
|
static const char description[] = I18N_NOOP("The reliable Plasma session manager that talks the standard X11R6 \nsession management protocol (XSMP).");
|
|
|
|
Display *dpy = nullptr;
|
|
Colormap colormap = 0;
|
|
Visual *visual = nullptr;
|
|
|
|
extern KSMServer *the_server;
|
|
|
|
void IoErrorHandler(IceConn iceConn)
|
|
{
|
|
the_server->ioError(iceConn);
|
|
}
|
|
|
|
bool writeTest(QByteArray path)
|
|
{
|
|
path += "/XXXXXX";
|
|
int fd = mkstemp(path.data());
|
|
if (fd == -1)
|
|
return false;
|
|
if (write(fd, "Hello World\n", 12) == -1) {
|
|
int save_errno = errno;
|
|
close(fd);
|
|
unlink(path.data());
|
|
errno = save_errno;
|
|
return false;
|
|
}
|
|
close(fd);
|
|
unlink(path.data());
|
|
return true;
|
|
}
|
|
|
|
void checkComposite()
|
|
{
|
|
if (qgetenv("KDE_SKIP_ARGB_VISUALS") == "1")
|
|
return;
|
|
// thanks to zack rusin and frederik for pointing me in the right direction
|
|
// for the following bits of X11 code
|
|
dpy = XOpenDisplay(nullptr); // open default display
|
|
if (!dpy) {
|
|
qCCritical(KSMSERVER) << "Cannot connect to the X server";
|
|
return;
|
|
}
|
|
|
|
int screen = DefaultScreen(dpy);
|
|
int eventBase, errorBase;
|
|
|
|
if (XRenderQueryExtension(dpy, &eventBase, &errorBase)) {
|
|
int nvi;
|
|
XVisualInfo templ;
|
|
templ.screen = screen;
|
|
templ.depth = 32;
|
|
templ.c_class = TrueColor;
|
|
XVisualInfo *xvi = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi);
|
|
for (int i = 0; i < nvi; ++i) {
|
|
XRenderPictFormat *format = XRenderFindVisualFormat(dpy, xvi[i].visual);
|
|
if (format->type == PictTypeDirect && format->direct.alphaMask) {
|
|
visual = xvi[i].visual;
|
|
colormap = XCreateColormap(dpy, RootWindow(dpy, screen), visual, AllocNone);
|
|
|
|
XFree(xvi);
|
|
return;
|
|
}
|
|
}
|
|
|
|
XFree(xvi);
|
|
}
|
|
XCloseDisplay(dpy);
|
|
dpy = nullptr;
|
|
}
|
|
|
|
void sanity_check(int argc, char *argv[])
|
|
{
|
|
QString msg;
|
|
QByteArray path = qgetenv("HOME");
|
|
const QByteArray readOnly = qgetenv("KDE_HOME_READONLY");
|
|
if (path.isEmpty()) {
|
|
msg = i18n("$HOME not set!");
|
|
}
|
|
if (msg.isEmpty() && access(path.data(), W_OK)) {
|
|
if (errno == ENOENT)
|
|
msg = i18n("$HOME directory (%1) does not exist.", QFile::decodeName(path));
|
|
else if (readOnly.isEmpty())
|
|
msg = i18n("No write access to $HOME directory (%1).", QFile::decodeName(path));
|
|
}
|
|
if (msg.isEmpty() && access(path.data(), R_OK)) {
|
|
if (errno == ENOENT)
|
|
msg = i18n("$HOME directory (%1) does not exist.", QFile::decodeName(path));
|
|
else
|
|
msg = i18n("No read access to $HOME directory (%1).", QFile::decodeName(path));
|
|
}
|
|
if (msg.isEmpty() && readOnly.isEmpty() && !writeTest(path)) {
|
|
if (errno == ENOSPC)
|
|
msg = i18n("$HOME directory (%1) is out of disk space.", QFile::decodeName(path));
|
|
else
|
|
msg = i18n(
|
|
"Writing to the $HOME directory (%2) failed with "
|
|
"the error '%1'",
|
|
QString::fromLocal8Bit(strerror(errno)),
|
|
QFile::decodeName(path));
|
|
}
|
|
if (msg.isEmpty()) {
|
|
path = getenv("ICEAUTHORITY");
|
|
if (path.isEmpty()) {
|
|
path = qgetenv("HOME");
|
|
path += "/.ICEauthority";
|
|
}
|
|
|
|
if (access(path.data(), W_OK) && (errno != ENOENT))
|
|
msg = i18n("No write access to '%1'.", QFile::decodeName(path));
|
|
else if (access(path.data(), R_OK) && (errno != ENOENT))
|
|
msg = i18n("No read access to '%1'.", QFile::decodeName(path));
|
|
}
|
|
if (msg.isEmpty()) {
|
|
path = getenv("KDETMP");
|
|
if (path.isEmpty())
|
|
path = "/tmp";
|
|
if (!writeTest(path)) {
|
|
if (errno == ENOSPC)
|
|
msg = i18n("Temp directory (%1) is out of disk space.", QFile::decodeName(path));
|
|
else
|
|
msg = i18n(
|
|
"Writing to the temp directory (%2) failed with\n "
|
|
"the error '%1'",
|
|
QString::fromLocal8Bit(strerror(errno)),
|
|
QFile::decodeName(path));
|
|
}
|
|
}
|
|
if (msg.isEmpty() && (path != "/tmp")) {
|
|
path = "/tmp";
|
|
if (!writeTest(path)) {
|
|
if (errno == ENOSPC)
|
|
msg = i18n("Temp directory (%1) is out of disk space.", QFile::decodeName(path));
|
|
else
|
|
msg = i18n(
|
|
"Writing to the temp directory (%2) failed with\n "
|
|
"the error '%1'",
|
|
QString::fromLocal8Bit(strerror(errno)),
|
|
QFile::decodeName(path));
|
|
}
|
|
}
|
|
if (msg.isEmpty()) {
|
|
path += "/.ICE-unix";
|
|
if (access(path.data(), W_OK) && (errno != ENOENT))
|
|
msg = i18n("No write access to '%1'.", QFile::decodeName(path));
|
|
else if (access(path.data(), R_OK) && (errno != ENOENT))
|
|
msg = i18n("No read access to '%1'.", QFile::decodeName(path));
|
|
}
|
|
if (!msg.isEmpty()) {
|
|
const QString msg_pre = i18n(
|
|
"The following installation problem was detected\n"
|
|
"while trying to start Plasma:")
|
|
+ QStringLiteral("\n\n ");
|
|
const QString msg_post = i18n("\n\nPlasma is unable to start.\n");
|
|
fputs(msg_pre.toUtf8().constData(), stderr);
|
|
fprintf(stderr, "%s", msg.toUtf8().constData());
|
|
fputs(msg_post.toUtf8().constData(), stderr);
|
|
|
|
QApplication a(argc, argv);
|
|
const QString qmsg = msg_pre + msg + msg_post;
|
|
KMessageBox::error(nullptr, qmsg, i18n("Plasma Workspace installation problem!"));
|
|
exit(255);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
sanity_check(argc, argv);
|
|
|
|
putenv((char *)"SESSION_MANAGER=");
|
|
checkComposite();
|
|
|
|
// force xcb QPA plugin as ksmserver is very X11 specific
|
|
const QByteArray origQpaPlatform = qgetenv("QT_QPA_PLATFORM");
|
|
qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb"));
|
|
|
|
QQuickWindow::setDefaultAlphaBuffer(true);
|
|
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
|
|
QApplication *a = new QApplication(argc, argv);
|
|
|
|
// now the QPA platform is set, unset variable again to not launch apps with incorrect environment
|
|
if (origQpaPlatform.isEmpty()) {
|
|
qunsetenv("QT_QPA_PLATFORM");
|
|
} else {
|
|
qputenv("QT_QPA_PLATFORM", origQpaPlatform);
|
|
}
|
|
|
|
QApplication::setApplicationName(QStringLiteral("ksmserver"));
|
|
QApplication::setApplicationVersion(QString::fromLatin1(version));
|
|
QApplication::setOrganizationDomain(QStringLiteral("kde.org"));
|
|
|
|
fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, 1);
|
|
|
|
a->setQuitOnLastWindowClosed(false); // #169486
|
|
|
|
QCommandLineParser parser;
|
|
parser.setApplicationDescription(i18n(description));
|
|
parser.addHelpOption();
|
|
parser.addVersionOption();
|
|
|
|
QCommandLineOption restoreOption(QStringList() << QStringLiteral("r") << QStringLiteral("restore"), i18n("Restores the saved user session if available"));
|
|
parser.addOption(restoreOption);
|
|
|
|
QCommandLineOption nolocalOption(QStringLiteral("nolocal"), i18n("Also allow remote connections"));
|
|
parser.addOption(nolocalOption);
|
|
|
|
QCommandLineOption lockscreenOption(QStringLiteral("lockscreen"), i18n("Starts the session in locked mode"));
|
|
parser.addOption(lockscreenOption);
|
|
|
|
QCommandLineOption noLockscreenOption(QStringLiteral("no-lockscreen"),
|
|
i18n("Starts without lock screen support. Only needed if other component provides the lock screen."));
|
|
parser.addOption(noLockscreenOption);
|
|
|
|
parser.process(*a);
|
|
|
|
bool only_local = !parser.isSet(nolocalOption);
|
|
#ifndef HAVE__ICETRANSNOLISTEN
|
|
/* this seems strange, but the default is only_local, so if !only_local
|
|
* the option --nolocal was given, and we warn (the option --nolocal
|
|
* does nothing on this platform, as here the default is reversed)
|
|
*/
|
|
if (!only_local) {
|
|
qCWarning(KSMSERVER, "--nolocal is not supported on your platform. Sorry.");
|
|
}
|
|
only_local = false;
|
|
#endif
|
|
|
|
KSMServer::InitFlags flags = KSMServer::InitFlag::None;
|
|
if (only_local) {
|
|
flags |= KSMServer::InitFlag::OnlyLocal;
|
|
}
|
|
if (parser.isSet(lockscreenOption)) {
|
|
flags |= KSMServer::InitFlag::ImmediateLockScreen;
|
|
}
|
|
if (parser.isSet(noLockscreenOption)) {
|
|
flags |= KSMServer::InitFlag::NoLockScreen;
|
|
}
|
|
|
|
// we use the session_type here as ksmserver is already forced as X above
|
|
// in wayland, kwin manages the lock screen
|
|
if (qgetenv("XDG_SESSION_TYPE") == QByteArrayLiteral("wayland")) {
|
|
flags |= KSMServer::InitFlag::NoLockScreen;
|
|
}
|
|
|
|
KSMServer *server = new KSMServer(flags);
|
|
|
|
// for the KDE-already-running check in startkde
|
|
KSelectionOwner kde_running("_KDE_RUNNING", 0);
|
|
kde_running.claim(false);
|
|
|
|
IceSetIOErrorHandler(IoErrorHandler);
|
|
|
|
KConfigGroup config(KSharedConfig::openConfig(), "General");
|
|
|
|
QString loginMode = config.readEntry("loginMode", "restorePreviousLogout");
|
|
|
|
if (parser.isSet(restoreOption))
|
|
server->restoreSession(QStringLiteral(SESSION_BY_USER));
|
|
else if (loginMode == QLatin1String("restorePreviousLogout"))
|
|
server->restoreSession(QStringLiteral(SESSION_PREVIOUS_LOGOUT));
|
|
else if (loginMode == QLatin1String("restoreSavedSession"))
|
|
server->restoreSession(QStringLiteral(SESSION_BY_USER));
|
|
else
|
|
server->startDefaultSession();
|
|
|
|
KDBusService service(KDBusService::Unique);
|
|
|
|
server->setupShortcuts();
|
|
int ret = a->exec();
|
|
kde_running.release(); // needs to be done before QApplication destruction
|
|
delete a;
|
|
return ret;
|
|
}
|