diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 804332e5..ae1fad40 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -32,6 +32,7 @@ #include "QUtility.h" #include "ProcessorArch.h" #include "SslCertificate.h" +#include "ShutdownCh.h" #include #include @@ -773,6 +774,9 @@ void MainWindow::stopDesktop() appendLogInfo("stopping barrier desktop process"); if (barrierProcess()->isOpen()) { + // try to shutdown child gracefully + barrierProcess()->write(&ShutdownCh, 1); + barrierProcess()->waitForFinished(5000); barrierProcess()->close(); } diff --git a/src/gui/src/ShutdownCh.h b/src/gui/src/ShutdownCh.h new file mode 100644 index 00000000..2462cae7 --- /dev/null +++ b/src/gui/src/ShutdownCh.h @@ -0,0 +1,22 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2018 Debauchee Open Source Group + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package 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 . + */ + +#pragma once + +// included in both the GUI and the child apps (server & client) +const char ShutdownCh = 'S'; + diff --git a/src/lib/base/EventQueue.cpp b/src/lib/base/EventQueue.cpp index 0a22c9dd..7ccb99fe 100644 --- a/src/lib/base/EventQueue.cpp +++ b/src/lib/base/EventQueue.cpp @@ -27,6 +27,7 @@ #include "base/EventTypes.h" #include "base/Log.h" #include "base/XBase.h" +#include "../gui/src/ShutdownCh.h" EVENT_TYPE_ACCESSOR(Client) EVENT_TYPE_ACCESSOR(IStream) @@ -205,6 +206,12 @@ EventQueue::getEvent(Event& event, double timeout) { Stopwatch timer(true); retry: + // check to see if parent wants us to shutdown now + char ch; + if (m_parentStream.try_read_char(ch) && ch == ShutdownCh) { + event = Event(Event::kQuit); + return false; + } // if no events are waiting then handle timers and then wait while (m_buffer->isEmpty()) { // handle timers first diff --git a/src/lib/base/EventQueue.h b/src/lib/base/EventQueue.h index d4fb6ffd..bbfcfcdf 100644 --- a/src/lib/base/EventQueue.h +++ b/src/lib/base/EventQueue.h @@ -26,6 +26,7 @@ #include "base/Stopwatch.h" #include "common/stdmap.h" #include "common/stdset.h" +#include "base/NonBlockingStream.h" #include @@ -184,6 +185,7 @@ private: Mutex* m_readyMutex; CondVar* m_readyCondVar; std::queue m_pending; + NonBlockingStream m_parentStream; }; #define EVENT_TYPE_ACCESSOR(type_) \ diff --git a/src/lib/base/NonBlockingStream.cpp b/src/lib/base/NonBlockingStream.cpp new file mode 100644 index 00000000..da20cad3 --- /dev/null +++ b/src/lib/base/NonBlockingStream.cpp @@ -0,0 +1,56 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2008 Debauchee Open Source Group + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package 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 . + */ + +#include "base/NonBlockingStream.h" + +#include // tcgetattr/tcsetattr, read +#include // tcgetattr/tcsetattr +#include +#include +#include + +NonBlockingStream::NonBlockingStream(int fd) : + _fd(fd) +{ + // disable ICANON & ECHO so we don't have to wait for a newline + // before we get data (and to keep it from being echoed back out) + termios ta; + tcgetattr(fd, &ta); + _p_ta_previous = new termios(ta); + ta.c_lflag &= ~(ICANON | ECHO); + tcsetattr(fd, TCSANOW, &ta); + + // prevent IO from blocking so we can poll (read()) + int _cntl_previous = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, _cntl_previous | O_NONBLOCK); +} + +NonBlockingStream::~NonBlockingStream() +{ + tcsetattr(_fd, TCSANOW, _p_ta_previous); + fcntl(_fd, F_SETFL, _cntl_previous); + delete _p_ta_previous; +} + +bool NonBlockingStream::try_read_char(char &ch) +{ + int result = read(_fd, &ch, 1); + if (result == 1) + return true; + assert(result == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)); + return false; +} diff --git a/src/lib/base/NonBlockingStream.h b/src/lib/base/NonBlockingStream.h new file mode 100644 index 00000000..61faf3d0 --- /dev/null +++ b/src/lib/base/NonBlockingStream.h @@ -0,0 +1,34 @@ +/* + * barrier -- mouse and keyboard sharing utility + * Copyright (C) 2008 Debauchee Open Source Group + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package 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 . + */ + +#pragma once + +struct termios; + +class NonBlockingStream +{ +public: + explicit NonBlockingStream(int fd = 0); + ~NonBlockingStream(); + + bool try_read_char(char &ch); + +private: + int _fd; + termios * _p_ta_previous; + int _cntl_previous; +};