[wayland] Add initial support for taking over VirtualTerminals
A new Singleton VirtualTerminal is added. It interacts with Logind to get the VTNr to take over. To get the signal to release and acquire the VT we use a signalfd with a QSocketNotifier to monitor for signals. The used signals must be blocked for all threads prior to startup otherwise they are delivered to secondary threads causing issues.
This commit is contained in:
parent
3d5899cbfd
commit
f4005c7f74
4 changed files with 280 additions and 0 deletions
|
@ -88,6 +88,12 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
|
|||
XmlGui
|
||||
)
|
||||
|
||||
find_package(Threads)
|
||||
set_package_properties(Threads PROPERTIES
|
||||
PURPOSE "Needed for VirtualTerminal support in KWin Wayland"
|
||||
TYPE REQUIRED
|
||||
)
|
||||
|
||||
# optional frameworks
|
||||
find_package(KF5Activities ${KF5_MIN_VERSION} CONFIG)
|
||||
set_package_properties(KF5Activities PROPERTIES
|
||||
|
@ -417,6 +423,7 @@ if(HAVE_WAYLAND)
|
|||
abstract_backend.cpp
|
||||
screens_wayland.cpp
|
||||
screens_x11windowed.cpp
|
||||
virtual_terminal.cpp
|
||||
wayland_backend.cpp
|
||||
wayland_server.cpp
|
||||
x11windowed_backend.cpp
|
||||
|
@ -504,6 +511,7 @@ set(kwin_WAYLAND_LIBS
|
|||
XKB::XKB
|
||||
KF5::WaylandClient
|
||||
KF5::WaylandServer
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
set(kwin_WAYLAND_EGL_LIBS
|
||||
|
|
|
@ -349,6 +349,12 @@ KWIN_EXPORT int kdemain(int argc, char * argv[])
|
|||
signal(SIGINT, SIG_IGN);
|
||||
if (signal(SIGHUP, KWin::sighandler) == SIG_IGN)
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
// ensure that no thread takes SIGUSR
|
||||
sigset_t userSiganls;
|
||||
sigemptyset(&userSiganls);
|
||||
sigaddset(&userSiganls, SIGUSR1);
|
||||
sigaddset(&userSiganls, SIGUSR2);
|
||||
pthread_sigmask(SIG_BLOCK, &userSiganls, nullptr);
|
||||
|
||||
// enforce wayland plugin, unfortunately command line switch has precedence
|
||||
setenv("QT_QPA_PLATFORM", "wayland", true);
|
||||
|
|
206
virtual_terminal.cpp
Normal file
206
virtual_terminal.cpp
Normal file
|
@ -0,0 +1,206 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "virtual_terminal.h"
|
||||
// kwin
|
||||
#include "logind.h"
|
||||
#include "utils.h"
|
||||
// Qt
|
||||
#include <QDebug>
|
||||
#include <QSocketNotifier>
|
||||
// linux
|
||||
#include <linux/major.h>
|
||||
#include <linux/vt.h>
|
||||
// system
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define RELEASE_SIGNAL SIGUSR1
|
||||
#define ACQUISITION_SIGNAL SIGUSR2
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
KWIN_SINGLETON_FACTORY(VirtualTerminal)
|
||||
|
||||
VirtualTerminal::VirtualTerminal(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void VirtualTerminal::init()
|
||||
{
|
||||
auto logind = LogindIntegration::self();
|
||||
if (logind->vt() != -1) {
|
||||
setup(logind->vt());
|
||||
}
|
||||
connect(logind, &LogindIntegration::virtualTerminalChanged, this, &VirtualTerminal::setup);
|
||||
if (logind->isConnected()) {
|
||||
logind->takeControl();
|
||||
} else {
|
||||
connect(logind, &LogindIntegration::connectedChanged, logind, &LogindIntegration::takeControl);
|
||||
}
|
||||
}
|
||||
|
||||
VirtualTerminal::~VirtualTerminal()
|
||||
{
|
||||
s_self = nullptr;
|
||||
closeFd();
|
||||
}
|
||||
|
||||
static bool isTty(int fd)
|
||||
{
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == -1) {
|
||||
return false;
|
||||
}
|
||||
if (major(st.st_rdev) != TTY_MAJOR || minor (st.st_rdev) <= 0 || minor(st.st_rdev) >= 64) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VirtualTerminal::setup(int vtNr)
|
||||
{
|
||||
if (m_vt != -1) {
|
||||
return;
|
||||
}
|
||||
if (vtNr == -1) {
|
||||
// error condition
|
||||
return;
|
||||
}
|
||||
QString ttyName = QStringLiteral("/dev/tty%1").arg(vtNr);
|
||||
|
||||
m_vt = LogindIntegration::self()->takeDevice(ttyName.toUtf8().constData());
|
||||
if (m_vt < 0) {
|
||||
qCWarning(KWIN_CORE) << "Failed to open tty through logind, trying without";
|
||||
}
|
||||
m_vt = open(ttyName.toUtf8().constData(), O_RDWR|O_CLOEXEC|O_NONBLOCK);
|
||||
if (m_vt < 0) {
|
||||
qCWarning(KWIN_CORE) << "Failed to open tty" << vtNr;
|
||||
return;
|
||||
}
|
||||
if (!isTty(m_vt)) {
|
||||
qCWarning(KWIN_CORE) << vtNr << " is no tty";
|
||||
closeFd();
|
||||
return;
|
||||
}
|
||||
if (!createSignalHandler()) {
|
||||
qCWarning(KWIN_CORE) << "Failed to create signalfd";
|
||||
closeFd();
|
||||
return;
|
||||
}
|
||||
vt_mode mode = {
|
||||
VT_PROCESS,
|
||||
0,
|
||||
RELEASE_SIGNAL,
|
||||
ACQUISITION_SIGNAL,
|
||||
0
|
||||
};
|
||||
if (ioctl(m_vt, VT_SETMODE, &mode) < 0) {
|
||||
qCWarning(KWIN_CORE) << "Failed to take over virtual terminal";
|
||||
closeFd();
|
||||
return;
|
||||
}
|
||||
setActive(true);
|
||||
}
|
||||
|
||||
void VirtualTerminal::closeFd()
|
||||
{
|
||||
if (m_vt < 0) {
|
||||
return;
|
||||
}
|
||||
if (m_notifier) {
|
||||
const int sfd = m_notifier->socket();
|
||||
delete m_notifier;
|
||||
m_notifier = nullptr;
|
||||
close(sfd);
|
||||
}
|
||||
close(m_vt);
|
||||
m_vt = -1;
|
||||
}
|
||||
|
||||
bool VirtualTerminal::createSignalHandler()
|
||||
{
|
||||
if (m_notifier) {
|
||||
return false;
|
||||
}
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, RELEASE_SIGNAL);
|
||||
sigaddset(&mask, ACQUISITION_SIGNAL);
|
||||
pthread_sigmask(SIG_BLOCK, &mask, nullptr);
|
||||
|
||||
const int fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
|
||||
connect(m_notifier, &QSocketNotifier::activated, this,
|
||||
[this] {
|
||||
if (m_vt < 0) {
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
signalfd_siginfo sigInfo;
|
||||
if (read(m_notifier->socket(), &sigInfo, sizeof(sigInfo)) != sizeof(sigInfo)) {
|
||||
break;
|
||||
}
|
||||
switch (sigInfo.ssi_signo) {
|
||||
case RELEASE_SIGNAL:
|
||||
ioctl(m_vt, VT_RELDISP, 1);
|
||||
setActive(false);
|
||||
break;
|
||||
case ACQUISITION_SIGNAL:
|
||||
ioctl(m_vt, VT_RELDISP, VT_ACKACQ);
|
||||
setActive(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
void VirtualTerminal::activate(int vt)
|
||||
{
|
||||
if (m_vt < 0) {
|
||||
return;
|
||||
}
|
||||
ioctl(m_vt, VT_ACTIVATE, vt);
|
||||
setActive(false);
|
||||
}
|
||||
|
||||
void VirtualTerminal::setActive(bool active)
|
||||
{
|
||||
if (m_active == active) {
|
||||
return;
|
||||
}
|
||||
m_active = active;
|
||||
emit activeChanged(m_active);
|
||||
}
|
||||
|
||||
}
|
60
virtual_terminal.h
Normal file
60
virtual_terminal.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_VIRTUAL_TERMINAL_H
|
||||
#define KWIN_VIRTUAL_TERMINAL_H
|
||||
#include <kwinglobals.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QSocketNotifier;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class VirtualTerminal : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~VirtualTerminal();
|
||||
|
||||
void init();
|
||||
void activate(int vt);
|
||||
bool isActive() const {
|
||||
return m_active;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void activeChanged(bool);
|
||||
|
||||
private:
|
||||
void setup(int vtNr);
|
||||
void closeFd();
|
||||
bool createSignalHandler();
|
||||
void setActive(bool active);
|
||||
int m_vt = -1;
|
||||
QSocketNotifier *m_notifier = nullptr;
|
||||
bool m_active = false;
|
||||
|
||||
KWIN_SINGLETON(VirtualTerminal)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue