diff --git a/CMakeLists.txt b/CMakeLists.txt
index fbdf139ded..1e43bfc54c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -93,6 +93,7 @@ set(kwin_KDEINIT_SRCS
workspace.cpp
dbusinterface.cpp
client.cpp
+ client_machine.cpp
tabgroup.cpp
placement.cpp
atoms.cpp
diff --git a/client.cpp b/client.cpp
index 3d334a1bbe..ad5c3db04c 100644
--- a/client.cpp
+++ b/client.cpp
@@ -41,6 +41,7 @@ along with this program. If not, see .
#include
#include "bridge.h"
+#include "client_machine.h"
#include "composite.h"
#include "group.h"
#include "workspace.h"
@@ -211,6 +212,8 @@ Client::Client(Workspace* ws)
connect(this, SIGNAL(clientStartUserMovedResized(KWin::Client*)), SIGNAL(moveResizedChanged()));
connect(this, SIGNAL(clientFinishUserMovedResized(KWin::Client*)), SIGNAL(moveResizedChanged()));
+ connect(clientMachine(), SIGNAL(localhostChanged()), SLOT(updateCaption()));
+
// SELI TODO: Initialize xsizehints??
}
@@ -1373,21 +1376,20 @@ void Client::killProcess(bool ask, Time timestamp)
if (m_killHelperPID && !::kill(m_killHelperPID, 0)) // means the process is alive
return;
Q_ASSERT(!ask || timestamp != CurrentTime);
- QByteArray machine = wmClientMachine(true);
pid_t pid = info->pid();
- if (pid <= 0 || machine.isEmpty()) // Needed properties missing
+ if (pid <= 0 || clientMachine()->hostName().isEmpty()) // Needed properties missing
return;
- kDebug(1212) << "Kill process:" << pid << "(" << machine << ")";
+ kDebug(1212) << "Kill process:" << pid << "(" << clientMachine()->hostName() << ")";
if (!ask) {
- if (machine != "localhost") {
+ if (!clientMachine()->isLocal()) {
QStringList lst;
- lst << machine << "kill" << QString::number(pid);
+ lst << clientMachine()->hostName() << "kill" << QString::number(pid);
QProcess::startDetached("xon", lst);
} else
::kill(pid, SIGTERM);
} else {
QProcess::startDetached(KStandardDirs::findExe("kwin_killer_helper"),
- QStringList() << "--pid" << QByteArray().setNum(unsigned(pid)) << "--hostname" << machine
+ QStringList() << "--pid" << QByteArray().setNum(unsigned(pid)) << "--hostname" << clientMachine()->hostName()
<< "--windowname" << caption()
<< "--applicationname" << resourceClass()
<< "--wid" << QString::number(window())
@@ -1774,8 +1776,8 @@ void Client::setCaption(const QString& _s, bool force)
cap_suffix.clear();
QString machine_suffix;
if (!options->condensedTitle()) { // machine doesn't qualify for "clean"
- if (wmClientMachine(false) != "localhost" && !isLocalMachine(wmClientMachine(false)))
- machine_suffix = QString(" <@") + wmClientMachine(true) + '>' + LRM;
+ if (clientMachine()->hostName() != ClientMachine::localhost() && !clientMachine()->isLocal())
+ machine_suffix = QString(" <@") + clientMachine()->hostName() + '>' + LRM;
}
QString shortcut_suffix = !shortcut().isEmpty() ? (" {" + shortcut().toString() + '}') : QString();
cap_suffix = machine_suffix + shortcut_suffix;
diff --git a/client.h b/client.h
index dbc7ca9d86..4cbc20f8e2 100644
--- a/client.h
+++ b/client.h
@@ -499,7 +499,6 @@ public:
void updateCompositeBlocking(bool readProperty = false);
QString caption(bool full = true, bool stripped = false) const;
- void updateCaption();
void keyPressEvent(uint key_code); // FRAME ??
void updateMouseGrab();
@@ -654,6 +653,7 @@ public:
public slots:
void closeWindow();
+ void updateCaption();
private slots:
void autoRaise();
diff --git a/client_machine.cpp b/client_machine.cpp
new file mode 100644
index 0000000000..c7821b4629
--- /dev/null
+++ b/client_machine.cpp
@@ -0,0 +1,237 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin
+
+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, see .
+*********************************************************************/
+// own
+#include "client_machine.h"
+// KWin
+#include "utils.h"
+// KDE
+#include
+// Qt
+#include
+#include
+// system
+#include
+#include
+
+namespace KWin {
+
+static QByteArray getHostName()
+{
+#ifdef HOST_NAME_MAX
+ char hostnamebuf[HOST_NAME_MAX];
+#else
+ char hostnamebuf[256];
+#endif
+ if (gethostname(hostnamebuf, sizeof hostnamebuf) >= 0) {
+ hostnamebuf[sizeof(hostnamebuf)-1] = 0;
+ return QByteArray(hostnamebuf);
+ }
+ return QByteArray();
+}
+
+GetAddrInfo::GetAddrInfo(const QByteArray &hostName, QObject *parent)
+ : QObject(parent)
+ , m_resolving(false)
+ , m_resolved(false)
+ , m_ownResolved(false)
+ , m_hostName(hostName)
+ , m_addressHints(new addrinfo)
+ , m_address(NULL)
+ , m_ownAddress(NULL)
+ , m_watcher(new QFutureWatcher(this))
+ , m_ownAddressWatcher(new QFutureWatcher(this))
+{
+ // watcher will be deleted together with the GetAddrInfo once the future
+ // got canceled or finished
+ connect(m_watcher, SIGNAL(canceled()), SLOT(deleteLater()));
+ connect(m_watcher, SIGNAL(finished()), SLOT(slotResolved()));
+ connect(m_ownAddressWatcher, SIGNAL(canceled()), SLOT(deleteLater()));
+ connect(m_ownAddressWatcher, SIGNAL(finished()), SLOT(slotOwnAddressResolved()));
+}
+
+GetAddrInfo::~GetAddrInfo()
+{
+ if (m_watcher && m_watcher->isRunning()) {
+ m_watcher->cancel();
+ }
+ if (m_ownAddressWatcher && m_ownAddressWatcher->isRunning()) {
+ m_ownAddressWatcher->cancel();
+ }
+ if (m_address) {
+ freeaddrinfo(m_address);
+ }
+ if (m_ownAddress) {
+ freeaddrinfo(m_ownAddress);
+ }
+ delete m_addressHints;
+}
+
+void GetAddrInfo::resolve()
+{
+ if (m_resolving) {
+ return;
+ }
+ m_resolving = true;
+ memset(m_addressHints, 0, sizeof(*m_addressHints));
+ m_addressHints->ai_family = PF_UNSPEC;
+ m_addressHints->ai_socktype = SOCK_STREAM;
+ m_addressHints->ai_flags |= AI_CANONNAME;
+
+ // TODO: C++11 nullptr
+ const char* nullPtr = NULL;
+ m_watcher->setFuture(QtConcurrent::run(getaddrinfo, m_hostName, nullPtr, m_addressHints, &m_address));
+ m_ownAddressWatcher->setFuture(QtConcurrent::run(getaddrinfo, getHostName(), nullPtr, m_addressHints, &m_ownAddress));
+}
+
+void GetAddrInfo::slotResolved()
+{
+ if (resolved(m_watcher)) {
+ m_resolved = true;
+ compare();
+ }
+}
+
+void GetAddrInfo::slotOwnAddressResolved()
+{
+ if (resolved(m_ownAddressWatcher)) {
+ m_ownResolved = true;
+ compare();
+ }
+}
+
+bool GetAddrInfo::resolved(QFutureWatcher< int >* watcher)
+{
+ if (!watcher->isFinished()) {
+ return false;
+ }
+ if (watcher->result() != 0) {
+ kDebug(1212) << "getaddrinfo failed with error:" << gai_strerror(watcher->result());
+ // call failed;
+ deleteLater();
+ return false;
+ }
+ return true;
+}
+
+void GetAddrInfo::compare()
+{
+ if (!m_resolved || !m_ownResolved) {
+ return;
+ }
+ addrinfo *address = m_address;
+ while (address) {
+ if (address->ai_canonname && m_hostName == QByteArray(address->ai_canonname).toLower()) {
+ addrinfo *ownAddress = m_ownAddress;
+ bool localFound = false;
+ while (ownAddress) {
+ if (ownAddress->ai_canonname && QByteArray(ownAddress->ai_canonname).toLower() == m_hostName) {
+ localFound = true;
+ break;
+ }
+ ownAddress = ownAddress->ai_next;
+ }
+ if (localFound) {
+ emit local();
+ break;
+ }
+ }
+ address = address->ai_next;
+ }
+ deleteLater();
+}
+
+
+ClientMachine::ClientMachine(QObject *parent)
+ : QObject(parent)
+ , m_localhost(false)
+ , m_resolved(false)
+ , m_resolving(false)
+{
+}
+
+ClientMachine::~ClientMachine()
+{
+}
+
+void ClientMachine::resolve(xcb_window_t window, xcb_window_t clientLeader)
+{
+ if (m_resolved) {
+ return;
+ }
+ QByteArray name = getStringProperty(window, XCB_ATOM_WM_CLIENT_MACHINE);
+ if (name.isEmpty() && clientLeader && clientLeader != window) {
+ name = getStringProperty(clientLeader, XCB_ATOM_WM_CLIENT_MACHINE);
+ }
+ if (name.isEmpty()) {
+ name = localhost();
+ }
+ if (name == localhost()) {
+ setLocal();
+ }
+ m_hostName = name;
+ checkForLocalhost();
+ m_resolved = true;
+}
+
+void ClientMachine::checkForLocalhost()
+{
+ if (isLocal()) {
+ // nothing to do
+ return;
+ }
+ QByteArray host = getHostName();
+
+ if (!host.isEmpty()) {
+ host = host.toLower();
+ const QByteArray lowerHostName(m_hostName.toLower());
+ if (host == lowerHostName) {
+ setLocal();
+ return;
+ }
+ if (char *dot = strchr(host.data(), '.')) {
+ *dot = '\0';
+ if (host == lowerHostName) {
+ setLocal();
+ return;
+ }
+ } else {
+ m_resolving = true;
+ // check using information from get addr info
+ // GetAddrInfo gets automatically destroyed once it finished or not
+ GetAddrInfo *info = new GetAddrInfo(lowerHostName, this);
+ connect(info, SIGNAL(local()), SLOT(setLocal()));
+ connect(info, SIGNAL(destroyed(QObject*)), SLOT(resolveFinished()));
+ info->resolve();
+ }
+ }
+}
+
+void ClientMachine::setLocal()
+{
+ m_localhost = true;
+ emit localhostChanged();
+}
+
+void ClientMachine::resolveFinished()
+{
+ m_resolving = false;
+}
+
+} // namespace
diff --git a/client_machine.h b/client_machine.h
new file mode 100644
index 0000000000..e56ffc70cf
--- /dev/null
+++ b/client_machine.h
@@ -0,0 +1,117 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin
+
+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, see .
+*********************************************************************/
+#ifndef KWIN_CLIENT_MACHINE_H
+#define KWIN_CLIENT_MACHINE_H
+
+#include
+#include
+
+// forward declaration
+struct addrinfo;
+template
+class QFutureWatcher;
+
+namespace KWin {
+
+class GetAddrInfo : public QObject
+{
+ Q_OBJECT
+public:
+ explicit GetAddrInfo(const QByteArray &hostName, QObject *parent = NULL);
+ virtual ~GetAddrInfo();
+
+ void resolve();
+
+Q_SIGNALS:
+ void local();
+
+private Q_SLOTS:
+ void slotResolved();
+ void slotOwnAddressResolved();
+
+private:
+ void compare();
+ bool resolved(QFutureWatcher *watcher);
+ bool m_resolving;
+ bool m_resolved;
+ bool m_ownResolved;
+ QByteArray m_hostName;
+ addrinfo *m_addressHints;
+ addrinfo *m_address;
+ addrinfo *m_ownAddress;
+ QFutureWatcher *m_watcher;
+ QFutureWatcher *m_ownAddressWatcher;
+};
+
+class ClientMachine : public QObject
+{
+ Q_OBJECT
+public:
+ explicit ClientMachine(QObject *parent = NULL);
+ virtual ~ClientMachine();
+
+ void resolve(xcb_window_t window, xcb_window_t clientLeader);
+ const QByteArray &hostName() const;
+ bool isLocal() const;
+ static QByteArray localhost();
+ bool isResolving() const;
+
+Q_SIGNALS:
+ void localhostChanged();
+
+private Q_SLOTS:
+ void setLocal();
+ void resolveFinished();
+
+private:
+ void checkForLocalhost();
+ QByteArray m_hostName;
+ bool m_localhost;
+ bool m_resolved;
+ bool m_resolving;
+};
+
+inline
+bool ClientMachine::isLocal() const
+{
+ return m_localhost;
+}
+
+inline
+const QByteArray &ClientMachine::hostName() const
+{
+ return m_hostName;
+}
+
+inline
+QByteArray ClientMachine::localhost()
+{
+ return "localhost";
+}
+
+inline
+bool ClientMachine::isResolving() const
+{
+ return m_resolving;
+}
+
+} // namespace
+
+#endif
diff --git a/kcmkwin/kwinrules/CMakeLists.txt b/kcmkwin/kwinrules/CMakeLists.txt
index 56dbf4b068..1d06cd3a31 100644
--- a/kcmkwin/kwinrules/CMakeLists.txt
+++ b/kcmkwin/kwinrules/CMakeLists.txt
@@ -1,7 +1,7 @@
ADD_DEFINITIONS(-DKCMRULES)
########### next target ###############
-set (kwinrules_MOC_HDRS yesnobox.h)
+set (kwinrules_MOC_HDRS yesnobox.h ../../client_machine.h)
qt4_wrap_cpp(kwinrules_MOC_SRCS ${kwinrules_MOC_HDRS})
set(kwinrules_SRCS ruleswidget.cpp ruleslist.cpp kwinsrc.cpp detectwidget.cpp ${kwinrules_MOC_SRCS})
diff --git a/kcmkwin/kwinrules/kwinsrc.cpp b/kcmkwin/kwinrules/kwinsrc.cpp
index 1ea305dc0d..bc14986f12 100644
--- a/kcmkwin/kwinrules/kwinsrc.cpp
+++ b/kcmkwin/kwinrules/kwinsrc.cpp
@@ -24,3 +24,4 @@
#include "../../placement.cpp"
#include "../../options.cpp"
#include "../../utils.cpp"
+#include "../../client_machine.cpp"
diff --git a/kcmkwin/kwinrules/main.cpp b/kcmkwin/kwinrules/main.cpp
index 194349c86d..b040172e7f 100644
--- a/kcmkwin/kwinrules/main.cpp
+++ b/kcmkwin/kwinrules/main.cpp
@@ -27,6 +27,7 @@
#include "ruleswidget.h"
#include "../../rules.h"
+#include "../../client_machine.h"
#include
namespace KWin
@@ -72,6 +73,8 @@ static Rules* findRule(const QList< Rules* >& rules, Window wid, bool whole_app)
NET::WM2WindowClass | NET::WM2WindowRole | NET::WM2ClientMachine);
if (!info.valid()) // shouldn't really happen
return NULL;
+ ClientMachine clientMachine;
+ clientMachine.resolve(info.win(), info.groupLeader());
QByteArray wmclass_class = info.windowClassClass().toLower();
QByteArray wmclass_name = info.windowClassName().toLower();
QByteArray role = info.windowRole().toLower();
@@ -79,7 +82,7 @@ static Rules* findRule(const QList< Rules* >& rules, Window wid, bool whole_app)
| NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask
| NET::UtilityMask | NET::SplashMask);
QString title = info.name();
- QByteArray machine = info.clientMachine().toLower();
+ QByteArray machine = clientMachine.hostName();
Rules* best_match = NULL;
int match_quality = 0;
for (QList< Rules* >::ConstIterator it = rules.constBegin();
@@ -126,7 +129,7 @@ static Rules* findRule(const QList< Rules* >& rules, Window wid, bool whole_app)
if (!rule->matchType(type)
|| !rule->matchRole(role)
|| !rule->matchTitle(title)
- || !rule->matchClientMachine(machine))
+ || !rule->matchClientMachine(machine, clientMachine.isLocal()))
continue;
if (quality > match_quality) {
best_match = rule;
diff --git a/rules.cpp b/rules.cpp
index f29e320e7e..8a1edc8531 100644
--- a/rules.cpp
+++ b/rules.cpp
@@ -30,6 +30,7 @@ along with this program. If not, see .
#ifndef KCMRULES
#include
#include "client.h"
+#include "client_machine.h"
#include "workspace.h"
#endif
@@ -394,12 +395,12 @@ bool Rules::matchTitle(const QString& match_title) const
return true;
}
-bool Rules::matchClientMachine(const QByteArray& match_machine) const
+bool Rules::matchClientMachine(const QByteArray& match_machine, bool local) const
{
if (clientmachinematch != UnimportantMatch) {
// if it's localhost, check also "localhost" before checking hostname
- if (match_machine != "localhost" && isLocalMachine(match_machine)
- && matchClientMachine("localhost"))
+ if (match_machine != "localhost" && local
+ && matchClientMachine("localhost", true))
return true;
if (clientmachinematch == RegExpMatch
&& QRegExp(clientmachine).indexIn(match_machine) == -1)
@@ -425,7 +426,7 @@ bool Rules::match(const Client* c) const
return false;
if (!matchTitle(c->caption(false)))
return false;
- if (!matchClientMachine(c->wmClientMachine(false)))
+ if (!matchClientMachine(c->clientMachine()->hostName(), c->clientMachine()->isLocal()))
return false;
return true;
}
diff --git a/rules.h b/rules.h
index 8be58f79e9..2299dd8593 100644
--- a/rules.h
+++ b/rules.h
@@ -160,7 +160,7 @@ private:
bool matchWMClass(const QByteArray& match_class, const QByteArray& match_name) const;
bool matchRole(const QByteArray& match_role) const;
bool matchTitle(const QString& match_title) const;
- bool matchClientMachine(const QByteArray& match_machine) const;
+ bool matchClientMachine(const QByteArray& match_machine, bool local) const;
// All these values are saved to the cfg file, and are also used in kstart!
enum {
Unused = 0,
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 918a480f66..739483d112 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -33,3 +33,20 @@ target_link_libraries( testVirtualDesktops
${QT_QTCORE_LIBRARY}
${QT_QTTEST_LIBRARY}
)
+
+########################################################
+# Test ClientMachine
+########################################################
+set( testClientMachine_SRCS
+ test_client_machine.cpp
+ ../client_machine.cpp
+)
+kde4_add_unit_test( testClientMachine TESTNAME kwin-TestClientMachine ${testClientMachine_SRCS} )
+
+target_link_libraries( testClientMachine
+ ${QT_QTTEST_LIBRARY}
+ ${QT_QTCORE_LIBRARY}
+ ${KDE4_KDEUI_LIBS}
+ ${XCB_XCB_LIBRARIES}
+ ${X11_XCB_LIBRARIES}
+)
diff --git a/tests/test_client_machine.cpp b/tests/test_client_machine.cpp
new file mode 100644
index 0000000000..bccb691c3b
--- /dev/null
+++ b/tests/test_client_machine.cpp
@@ -0,0 +1,197 @@
+/********************************************************************
+KWin - the KDE window manager
+This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin
+
+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, see .
+*********************************************************************/
+// KWin
+#include "../client_machine.h"
+#include "../utils.h"
+// Qt
+#include
+#include
+// xcb
+#include
+// system
+#include
+#include
+
+namespace KWin {
+
+// mock required function from utils
+QByteArray getStringProperty(WId w, Atom prop, char separator)
+{
+ Q_UNUSED(separator)
+ ScopedCPointer property(xcb_get_property_reply(connection(),
+ xcb_get_property_unchecked(connection(), false, w, prop, XCB_ATOM_STRING, 0, 10000),
+ NULL));
+ if (property.isNull()) {
+ return QByteArray();
+ }
+ void *data = xcb_get_property_value(property.data());
+ if (data && property->value_len > 0) {
+ QByteArray result = QByteArray((const char*) data, property->value_len);
+ return result;
+ }
+ return QByteArray();
+}
+
+}
+
+using namespace KWin;
+
+class TestClientMachine : public QObject
+{
+ Q_OBJECT
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+ void hostName_data();
+ void hostName();
+ void emptyHostName();
+
+private:
+ xcb_window_t createWindow();
+ void setClientMachineProperty(xcb_window_t window, const QByteArray &hostname);
+ xcb_window_t m_testWindow;
+ QByteArray m_hostName;
+ QByteArray m_fqdn;
+};
+
+xcb_window_t TestClientMachine::createWindow()
+{
+ xcb_window_t w = xcb_generate_id(connection());
+ const uint32_t values[] = { true };
+ xcb_create_window(connection(), 0, w, rootWindow(),
+ 0, 0, 10, 10,
+ 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT,
+ XCB_CW_OVERRIDE_REDIRECT, values);
+ return w;
+}
+
+void TestClientMachine::setClientMachineProperty(xcb_window_t window, const QByteArray &hostname)
+{
+ xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, window,
+ XCB_ATOM_WM_CLIENT_MACHINE, XCB_ATOM_STRING, 8,
+ hostname.length(), hostname.constData());
+}
+
+void TestClientMachine::initTestCase()
+{
+#ifdef HOST_NAME_MAX
+ char hostnamebuf[HOST_NAME_MAX];
+#else
+ char hostnamebuf[256];
+#endif
+ if (gethostname(hostnamebuf, sizeof hostnamebuf) >= 0) {
+ hostnamebuf[sizeof(hostnamebuf)-1] = 0;
+ m_hostName = hostnamebuf;
+ }
+ addrinfo *res;
+ addrinfo addressHints;
+ memset(&addressHints, 0, sizeof(addressHints));
+ addressHints.ai_family = PF_UNSPEC;
+ addressHints.ai_socktype = SOCK_STREAM;
+ addressHints.ai_flags |= AI_CANONNAME;
+ if (getaddrinfo(m_hostName.constData(), NULL, &addressHints, &res) == 0) {
+ if (res->ai_canonname) {
+ m_fqdn = QByteArray(res->ai_canonname);
+ }
+ }
+ freeaddrinfo(res);
+}
+
+void TestClientMachine::cleanupTestCase()
+{
+}
+
+void TestClientMachine::init()
+{
+ m_testWindow = XCB_WINDOW_NONE;
+}
+
+void TestClientMachine::cleanup()
+{
+ if (m_testWindow != XCB_WINDOW_NONE) {
+ xcb_destroy_window(connection(), m_testWindow);
+ }
+}
+
+void TestClientMachine::hostName_data()
+{
+ QTest::addColumn("hostName");
+ QTest::addColumn("expectedHost");
+ QTest::addColumn("local");
+
+ QTest::newRow("empty") << QByteArray() << QByteArray("localhost") << true;
+ QTest::newRow("localhost") << QByteArray("localhost") << QByteArray("localhost") << true;
+ QTest::newRow("hostname") << m_hostName << m_hostName << true;
+ QTest::newRow("HOSTNAME") << m_hostName.toUpper() << m_hostName.toUpper() << true;
+ QByteArray cutted(m_hostName);
+ cutted.remove(0, 1);
+ QTest::newRow("ostname") << cutted << cutted << false;
+ QByteArray domain("random.name.not.exist.tld");
+ QTest::newRow("domain") << domain << domain << false;
+ QTest::newRow("fqdn") << m_fqdn << m_fqdn << true;
+ QTest::newRow("FQDN") << m_fqdn.toUpper() << m_fqdn.toUpper() << true;
+ cutted = m_fqdn;
+ cutted.remove(0, 1);
+ QTest::newRow("qdn") << cutted << cutted << false;
+}
+
+void TestClientMachine::hostName()
+{
+ xcb_window_t window = createWindow();
+ QFETCH(QByteArray, hostName);
+ QFETCH(bool, local);
+ setClientMachineProperty(window, hostName);
+
+ ClientMachine clientMachine;
+ QSignalSpy spy(&clientMachine, SIGNAL(localhostChanged()));
+ clientMachine.resolve(window, XCB_WINDOW_NONE);
+ QTEST(clientMachine.hostName(), "expectedHost");
+
+ int i=0;
+ while (clientMachine.isResolving() && i++ < 50) {
+ // name is being resolved in an external thread, so let's wait a little bit
+ QTest::qWait(250);
+ }
+
+ QCOMPARE(clientMachine.isLocal(), local);
+ QCOMPARE(spy.isEmpty(), !local);
+}
+
+void TestClientMachine::emptyHostName()
+{
+ xcb_window_t window = createWindow();
+ ClientMachine clientMachine;
+ QSignalSpy spy(&clientMachine, SIGNAL(localhostChanged()));
+ clientMachine.resolve(window, XCB_WINDOW_NONE);
+ QCOMPARE(clientMachine.hostName(), ClientMachine::localhost());
+ QVERIFY(clientMachine.isLocal());
+ // should be local
+ QCOMPARE(spy.isEmpty(), false);
+}
+
+// need to implement main manually as we need a QApplication for X11 interaction
+int main(int argc, char *argv[]) {
+ QApplication app(argc, argv);
+ TestClientMachine tc;
+ return QTest::qExec(&tc, argc, argv);
+}
+#include "test_client_machine.moc"
diff --git a/toplevel.cpp b/toplevel.cpp
index 6c718100c0..3b426f77b6 100644
--- a/toplevel.cpp
+++ b/toplevel.cpp
@@ -24,6 +24,7 @@ along with this program. If not, see .
#include "atoms.h"
#include "client.h"
+#include "client_machine.h"
#include "effects.h"
#include "shadow.h"
@@ -42,6 +43,7 @@ Toplevel::Toplevel(Workspace* ws)
, damage_handle(None)
, is_shape(false)
, effect_window(NULL)
+ , m_clientMachine(new ClientMachine(this))
, wmClientLeaderWin(0)
, unredirect(false)
, unredirectSuspend(false)
@@ -132,7 +134,8 @@ void Toplevel::copyToDeleted(Toplevel* c)
effect_window->setWindow(this);
resource_name = c->resourceName();
resource_class = c->resourceClass();
- client_machine = c->wmClientMachine(false);
+ m_clientMachine = c->m_clientMachine;
+ m_clientMachine->setParent(this);
wmClientLeaderWin = c->wmClientLeader();
window_role = c->windowRole();
opaque_region = c->opaqueRegion();
@@ -233,11 +236,7 @@ QByteArray Toplevel::wmCommand()
void Toplevel::getWmClientMachine()
{
- client_machine = getStringProperty(window(), XA_WM_CLIENT_MACHINE);
- if (client_machine.isEmpty() && wmClientLeaderWin && wmClientLeaderWin != window())
- client_machine = getStringProperty(wmClientLeaderWin, XA_WM_CLIENT_MACHINE);
- if (client_machine.isEmpty())
- client_machine = "localhost";
+ m_clientMachine->resolve(window(), wmClientLeader());
}
/*!
@@ -246,13 +245,15 @@ void Toplevel::getWmClientMachine()
*/
QByteArray Toplevel::wmClientMachine(bool use_localhost) const
{
- QByteArray result = client_machine;
- if (use_localhost) {
- // special name for the local machine (localhost)
- if (result != "localhost" && isLocalMachine(result))
- result = "localhost";
+ if (!m_clientMachine) {
+ // this should never happen
+ return QByteArray();
}
- return result;
+ if (use_localhost && m_clientMachine->isLocal()) {
+ // special name for the local machine (localhost)
+ return ClientMachine::localhost();
+ }
+ return m_clientMachine->hostName();
}
/*!
diff --git a/toplevel.h b/toplevel.h
index 05242d6f3c..1c8f1ac20e 100644
--- a/toplevel.h
+++ b/toplevel.h
@@ -41,6 +41,7 @@ class NETWinInfo2;
namespace KWin
{
+class ClientMachine;
class Workspace;
class EffectWindowImpl;
class Shadow;
@@ -223,6 +224,7 @@ public:
QByteArray resourceClass() const;
QByteArray wmCommand();
QByteArray wmClientMachine(bool use_localhost) const;
+ const ClientMachine *clientMachine() const;
Window wmClientLeader() const;
pid_t pid() const;
static bool resourceMatch(const Toplevel* c1, const Toplevel* c2);
@@ -383,7 +385,7 @@ private:
EffectWindowImpl* effect_window;
QByteArray resource_name;
QByteArray resource_class;
- QByteArray client_machine;
+ ClientMachine *m_clientMachine;
WId wmClientLeaderWin;
QByteArray window_role;
bool unredirect;
@@ -646,6 +648,11 @@ inline bool Toplevel::unredirected() const
return unredirect;
}
+inline const ClientMachine *Toplevel::clientMachine() const
+{
+ return m_clientMachine;
+}
+
QDebug& operator<<(QDebug& stream, const Toplevel*);
QDebug& operator<<(QDebug& stream, const ToplevelList&);
QDebug& operator<<(QDebug& stream, const ConstToplevelList&);
diff --git a/utils.cpp b/utils.cpp
index 070bc977eb..1079043de1 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -27,12 +27,12 @@ along with this program. If not, see .
#include "utils.h"
-#include
+#include
+#include
#ifndef KCMRULES
#include
#include
-#include
#include
#include
#include
@@ -43,7 +43,6 @@ along with this program. If not, see .
#include
#include
-#include
#include
#include
@@ -179,6 +178,8 @@ bool KWinSelectionOwner::genericReply(Atom target_P, Atom property_P, Window req
Atom KWinSelectionOwner::xa_version = None;
+#endif
+
QByteArray getStringProperty(WId w, Atom prop, char separator)
{
Atom type;
@@ -204,6 +205,7 @@ QByteArray getStringProperty(WId w, Atom prop, char separator)
return result;
}
+#ifndef KCMRULES
static Time next_x_time;
static Bool update_x_time_predicate(Display*, XEvent* event, XPointer)
{
@@ -406,33 +408,6 @@ Qt::KeyboardModifiers x11ToQtKeyboardModifiers(int state)
#endif
-bool isLocalMachine(const QByteArray& host)
-{
-#ifdef HOST_NAME_MAX
- char hostnamebuf[HOST_NAME_MAX];
-#else
- char hostnamebuf[256];
-#endif
- if (gethostname(hostnamebuf, sizeof hostnamebuf) >= 0) {
- hostnamebuf[sizeof(hostnamebuf)-1] = 0;
- if (host == hostnamebuf)
- return true;
- if (char *dot = strchr(hostnamebuf, '.')) {
- *dot = '\0';
- if (host == hostnamebuf)
- return true;
- } else { // e.g. LibreOffice likes to give FQDN, even if gethostname() doesn't include domain
- QByteArray h = hostnamebuf;
- if( getdomainname( hostnamebuf, sizeof hostnamebuf ) >= 0 ) {
- hostnamebuf[sizeof(hostnamebuf)-1] = 0;
- if( host == h + '.' + QByteArray( hostnamebuf ))
- return true;
- }
- }
- }
- return false;
-}
-
#ifndef KCMRULES
ShortcutDialog::ShortcutDialog(const QKeySequence& cut)
: _shortcut(cut)
diff --git a/utils.h b/utils.h
index e0e565248c..e6b9a679be 100644
--- a/utils.h
+++ b/utils.h
@@ -363,8 +363,6 @@ Time timestampDiff(Time time1, Time time2) // returns time2 - time1
return NET::timestampDiff(time1, time2);
}
-bool isLocalMachine(const QByteArray& host);
-
QPoint cursorPos();
// converting between X11 mouse/keyboard state mask and Qt button/keyboard states