QortalOS Brooklyn for Raspberry Pi 4
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

365 lines
13 KiB

/*
ksmserver - the KDE session management server
SPDX-FileCopyrightText: 2000 Matthias Ettrich <[email protected]>
SPDX-FileCopyrightText: 2005 Lubos Lunak <[email protected]>
SPDX-FileContributor: Oswald Buddenhagen <[email protected]>
some code taken from the dcopserver (part of the KDE libraries), which is
SPDX-FileCopyrightText: 1999 Matthias Ettrich <[email protected]>
SPDX-FileCopyrightText: 1999 Preston Brown <[email protected]>
SPDX-License-Identifier: MIT
*/
#include <QDebug>
#include <QElapsedTimer>
#include <QX11Info>
#include <config-workspace.h>
#include <ksmserver_debug.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include "server.h"
#include <unistd.h>
#include <KSharedConfig>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kshell.h>
#include <kwindowsystem.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
/*
* Legacy session management
*/
#ifndef NO_LEGACY_SESSION_MANAGEMENT
static WindowMap *windowMapPtr = nullptr;
static Atom wm_save_yourself = XNone;
static Atom wm_protocols = XNone;
static Atom wm_client_leader = XNone;
static Atom sm_client_id = XNone;
static int winsErrorHandler(Display *, XErrorEvent *ev)
{
if (windowMapPtr) {
WindowMap::Iterator it = windowMapPtr->find(ev->resourceid);
if (it != windowMapPtr->end())
(*it).type = SM_ERROR;
}
return 0;
}
void KSMServer::performLegacySessionSave()
{
qCDebug(KSMSERVER) << "Saving legacy session apps";
if (state == ClosingSubSession)
return; // FIXME implement later
KSharedConfig::Ptr config = KSharedConfig::openConfig();
config->reparseConfiguration(); // config may have changed in the KControl module
KConfigGroup cg(config, "General");
int wmSaveYourselfTimeout = cg.readEntry("legacySaveTimeoutSecs", 4) * 1000;
// Setup error handler
legacyWindows.clear();
windowMapPtr = &legacyWindows;
XErrorHandler oldHandler = XSetErrorHandler(winsErrorHandler);
// Compute set of leader windows that need legacy session management
// and determine which style (WM_COMMAND or WM_SAVE_YOURSELF)
if (wm_save_yourself == (Atom)XNone) {
Atom atoms[4];
const char *const names[] = {"WM_SAVE_YOURSELF", "WM_PROTOCOLS", "WM_CLIENT_LEADER", "SM_CLIENT_ID"};
XInternAtoms(QX11Info::display(), const_cast<char **>(names), 4, False, atoms);
wm_save_yourself = atoms[0];
wm_protocols = atoms[1];
wm_client_leader = atoms[2];
sm_client_id = atoms[3];
}
const QList<WId> windows = KWindowSystem::windows();
for (QList<WId>::ConstIterator it = windows.begin(); it != windows.end(); ++it) {
WId leader = windowWmClientLeader(*it);
if (!legacyWindows.contains(leader) && windowSessionId(*it, leader).isEmpty()) {
SMType wtype = SM_WMCOMMAND;
int nprotocols = 0;
Atom *protocols = nullptr;
if (XGetWMProtocols(QX11Info::display(), leader, &protocols, &nprotocols)) {
for (int i = 0; i < nprotocols; i++)
if (protocols[i] == wm_save_yourself) {
wtype = SM_WMSAVEYOURSELF;
break;
}
XFree((void *)protocols);
}
SMData data;
data.type = wtype;
XClassHint classHint;
if (XGetClassHint(QX11Info::display(), leader, &classHint)) {
data.wmclass1 = QString::fromLocal8Bit(classHint.res_name);
data.wmclass2 = QString::fromLocal8Bit(classHint.res_class);
XFree(classHint.res_name);
XFree(classHint.res_class);
}
legacyWindows.insert(leader, data);
}
}
// Open fresh display for sending WM_SAVE_YOURSELF
XSync(QX11Info::display(), False);
Display *newdisplay = XOpenDisplay(DisplayString(QX11Info::display()));
if (!newdisplay) {
windowMapPtr = nullptr;
XSetErrorHandler(oldHandler);
return;
}
WId root = DefaultRootWindow(newdisplay);
XGrabKeyboard(newdisplay, root, False, GrabModeAsync, GrabModeAsync, CurrentTime);
XGrabPointer(newdisplay, root, False, Button1Mask | Button2Mask | Button3Mask, GrabModeAsync, GrabModeAsync, XNone, XNone, CurrentTime);
// Send WM_SAVE_YOURSELF messages
XEvent ev;
int awaiting_replies = 0;
for (WindowMap::Iterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) {
if ((*it).type == SM_WMSAVEYOURSELF) {
WId w = it.key();
awaiting_replies += 1;
memset(&ev, 0, sizeof(ev));
ev.xclient.type = ClientMessage;
ev.xclient.window = w;
ev.xclient.message_type = wm_protocols;
ev.xclient.format = 32;
ev.xclient.data.l[0] = wm_save_yourself;
ev.xclient.data.l[1] = QX11Info::appTime();
XSelectInput(newdisplay, w, PropertyChangeMask | StructureNotifyMask);
XSendEvent(newdisplay, w, False, 0, &ev);
qCDebug(KSMSERVER) << "sent >save yourself< to legacy app " << (*it).wmclass1 << (*it).wmclass2;
}
}
// Wait for change in WM_COMMAND with timeout
XFlush(newdisplay);
QElapsedTimer start;
while (awaiting_replies > 0) {
if (XPending(newdisplay)) {
/* Process pending event */
XNextEvent(newdisplay, &ev);
if ((ev.xany.type == UnmapNotify) || (ev.xany.type == PropertyNotify && ev.xproperty.atom == XA_WM_COMMAND)) {
WindowMap::Iterator it = legacyWindows.find(ev.xany.window);
if (it != legacyWindows.end() && (*it).type != SM_WMCOMMAND) {
awaiting_replies -= 1;
if ((*it).type != SM_ERROR)
(*it).type = SM_WMCOMMAND;
}
}
} else {
/* Check timeout */
int msecs = start.elapsed();
if (msecs >= wmSaveYourselfTimeout) {
qCDebug(KSMSERVER) << "legacy timeout expired";
break;
}
/* Wait for more events */
fd_set fds;
FD_ZERO(&fds);
int fd = ConnectionNumber(newdisplay);
FD_SET(fd, &fds);
struct timeval tmwait;
tmwait.tv_sec = (wmSaveYourselfTimeout - msecs) / 1000;
tmwait.tv_usec = ((wmSaveYourselfTimeout - msecs) % 1000) * 1000;
::select(fd + 1, &fds, nullptr, &fds, &tmwait);
}
}
// Terminate work in new display
XAllowEvents(newdisplay, ReplayPointer, CurrentTime);
XAllowEvents(newdisplay, ReplayKeyboard, CurrentTime);
XSync(newdisplay, False);
XCloseDisplay(newdisplay);
// Restore old error handler
XSync(QX11Info::display(), False);
XSetErrorHandler(oldHandler);
for (WindowMap::Iterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) {
if ((*it).type != SM_ERROR) {
WId w = it.key();
(*it).wmCommand = windowWmCommand(w);
(*it).wmClientMachine = windowWmClientMachine(w);
}
}
qCDebug(KSMSERVER) << "Done saving " << legacyWindows.count() << " legacy session apps";
}
/*!
Stores legacy session management data
*/
void KSMServer::storeLegacySession(KConfig *config)
{
if (state == ClosingSubSession)
return; // FIXME implement later
// Write LegacySession data
config->deleteGroup(QStringLiteral("Legacy") + sessionGroup);
KConfigGroup group(config, QStringLiteral("Legacy") + sessionGroup);
int count = 0;
for (WindowMap::ConstIterator it = legacyWindows.constBegin(); it != legacyWindows.constEnd(); ++it) {
if ((*it).type != SM_ERROR) {
if (excludeApps.contains((*it).wmclass1.toLower()) || excludeApps.contains((*it).wmclass2.toLower()))
continue;
if (!(*it).wmCommand.isEmpty() && !(*it).wmClientMachine.isEmpty()) {
count++;
QString n = QString::number(count);
group.writeEntry(QStringLiteral("command") + n, (*it).wmCommand);
group.writeEntry(QStringLiteral("clientMachine") + n, (*it).wmClientMachine);
}
}
}
group.writeEntry("count", count);
}
/*!
Restores legacy session management data (i.e. restart applications)
*/
void KSMServer::restoreLegacySession(KConfig *config)
{
if (config->hasGroup(QStringLiteral("Legacy") + sessionGroup)) {
KConfigGroup group(config, QStringLiteral("Legacy") + sessionGroup);
restoreLegacySessionInternal(&group);
}
}
void KSMServer::restoreLegacySessionInternal(KConfigGroup *config, char sep)
{
int count = config->readEntry("count", 0);
for (int i = 1; i <= count; i++) {
QString n = QString::number(i);
QStringList wmCommand = (sep == ',') ? // why is this named "wmCommand"?
config->readEntry(QStringLiteral("command") + n, QStringList())
: KShell::splitArgs(config->readEntry(QStringLiteral("command") + n, QString())); // close enough(?)
if (wmCommand.isEmpty())
continue;
startApplication(wmCommand,
config->readEntry(QStringLiteral("clientMachine") + n, QString()),
config->readEntry(QStringLiteral("userId") + n, QString()));
}
}
static QByteArray getQCStringProperty(WId w, Atom prop)
{
Atom type;
int format, status;
unsigned long nitems = 0;
unsigned long extra = 0;
unsigned char *data = nullptr;
QByteArray result = "";
status = XGetWindowProperty(QX11Info::display(), w, prop, 0, 10000, false, XA_STRING, &type, &format, &nitems, &extra, &data);
if (status == Success) {
if (data)
result = (char *)data;
XFree(data);
}
return result;
}
static QStringList getQStringListProperty(WId w, Atom prop)
{
Atom type;
int format, status;
unsigned long nitems = 0;
unsigned long extra = 0;
unsigned char *data = nullptr;
QStringList result;
status = XGetWindowProperty(QX11Info::display(), w, prop, 0, 10000, false, XA_STRING, &type, &format, &nitems, &extra, &data);
if (status == Success) {
if (!data)
return result;
for (int i = 0; i < (int)nitems; i++) {
result << QLatin1String((const char *)data + i);
while (data[i])
i++;
}
XFree(data);
}
return result;
}
QStringList KSMServer::windowWmCommand(WId w)
{
QStringList ret = getQStringListProperty(w, XA_WM_COMMAND);
// hacks here
if (ret.count() == 1) {
QString command = ret.first();
// Mozilla is launched using wrapper scripts, so it's launched using "mozilla",
// but the actual binary is "mozilla-bin" or "<path>/mozilla-bin", and that's what
// will be also in WM_COMMAND - using this "mozilla-bin" doesn't work at all though
if (command.endsWith(QLatin1String("mozilla-bin")))
return QStringList() << QStringLiteral("mozilla");
if (command.endsWith(QLatin1String("firefox-bin")))
return QStringList() << QStringLiteral("firefox");
if (command.endsWith(QLatin1String("thunderbird-bin")))
return QStringList() << QStringLiteral("thunderbird");
if (command.endsWith(QLatin1String("sunbird-bin")))
return QStringList() << QStringLiteral("sunbird");
if (command.endsWith(QLatin1String("seamonkey-bin")))
return QStringList() << QStringLiteral("seamonkey");
}
return ret;
}
QString KSMServer::windowWmClientMachine(WId w)
{
QByteArray result = getQCStringProperty(w, XA_WM_CLIENT_MACHINE);
if (result.isEmpty()) {
result = "localhost";
} else {
// special name for the local machine (localhost)
char hostnamebuf[80];
if (gethostname(hostnamebuf, sizeof hostnamebuf) >= 0) {
hostnamebuf[sizeof(hostnamebuf) - 1] = 0;
if (result == hostnamebuf)
result = "localhost";
if (char *dot = strchr(hostnamebuf, '.')) {
*dot = '\0';
if (result == hostnamebuf)
result = "localhost";
}
}
}
return QLatin1String(result);
}
WId KSMServer::windowWmClientLeader(WId w)
{
Atom type;
int format, status;
unsigned long nitems = 0;
unsigned long extra = 0;
unsigned char *data = nullptr;
Window result = w;
status = XGetWindowProperty(QX11Info::display(), w, wm_client_leader, 0, 10000, false, XA_WINDOW, &type, &format, &nitems, &extra, &data);
if (status == Success) {
if (data && nitems > 0)
result = *((Window *)data);
XFree(data);
}
return result;
}
/*
Returns sessionId for this client,
taken either from its window or from the leader window.
*/
QByteArray KSMServer::windowSessionId(WId w, WId leader)
{
QByteArray result = getQCStringProperty(w, sm_client_id);
if (result.isEmpty() && leader != (WId)None && leader != w)
result = getQCStringProperty(leader, sm_client_id);
return result;
}
#endif