File indexing completed on 2024-04-21 04:58:42
0001 /* 0002 Copyright (C) 2009-2010 Collabora Ltd <info@collabora.co.uk> 0003 @author George Goldberg <george.goldberg@collabora.co.uk> 0004 @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk> 0005 Copyright (C) 2007 Alessandro Praduroux <pradu@pradu.it> 0006 Copyright (C) 2001-2003 by Tim Jansen <tim@tjansen.de> 0007 0008 This program is free software; you can redistribute it and/or 0009 modify it under the terms of the GNU General Public 0010 License as published by the Free Software Foundation; either 0011 version 2 of the License, or (at your option) any later version. 0012 0013 This program is distributed in the hope that it will be useful, 0014 but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0016 GNU General Public License for more details. 0017 0018 You should have received a copy of the GNU Lesser General Public License 0019 along with this program. If not, see <http://www.gnu.org/licenses/>. 0020 */ 0021 #include "rfbservermanager.h" 0022 #include "rfbserver.h" 0023 #include "framebuffermanager.h" 0024 #include "sockethelpers.h" 0025 #include "krfbconfig.h" 0026 #include "krfbdebug.h" 0027 #include <QTimer> 0028 #include <QApplication> 0029 #include <QGlobalStatic> 0030 #include <QHostInfo> 0031 #include <qpa/qplatformnativeinterface.h> 0032 0033 #include <KLocalizedString> 0034 #include <KUser> 0035 #include <KNotification> 0036 #include <KWindowSystem> 0037 #include <chrono> 0038 0039 using namespace std::chrono_literals; 0040 0041 static const char *cur = 0042 " " 0043 " x " 0044 " xx " 0045 " xxx " 0046 " xxxx " 0047 " xxxxx " 0048 " xxxxxx " 0049 " xxxxxxx " 0050 " xxxxxxxx " 0051 " xxxxxxxxx " 0052 " xxxxxxxxxx " 0053 " xxxxx " 0054 " xx xxx " 0055 " x xxx " 0056 " xxx " 0057 " xxx " 0058 " xxx " 0059 " xxx " 0060 " "; 0061 0062 static const char *mask = 0063 "xx " 0064 "xxx " 0065 "xxxx " 0066 "xxxxx " 0067 "xxxxxx " 0068 "xxxxxxx " 0069 "xxxxxxxx " 0070 "xxxxxxxxx " 0071 "xxxxxxxxxx " 0072 "xxxxxxxxxxx " 0073 "xxxxxxxxxxxx " 0074 "xxxxxxxxxx " 0075 "xxxxxxxx " 0076 "xxxxxxxx " 0077 "xx xxxxx " 0078 " xxxxx " 0079 " xxxxx " 0080 " xxxxx " 0081 " xxx "; 0082 0083 0084 struct RfbServerManagerStatic 0085 { 0086 RfbServerManager server; 0087 }; 0088 0089 Q_GLOBAL_STATIC(RfbServerManagerStatic, s_instance) 0090 0091 RfbServerManager* RfbServerManager::instance() 0092 { 0093 return &s_instance->server; 0094 } 0095 0096 struct RfbServerManager::Private 0097 { 0098 QSharedPointer<FrameBuffer> fb; 0099 rfbCursorPtr myCursor; 0100 QByteArray desktopName; 0101 QTimer rfbUpdateTimer; 0102 QSet<RfbServer*> servers; 0103 QSet<RfbClient*> clients; 0104 }; 0105 0106 0107 RfbServerManager::RfbServerManager() 0108 : QObject(), d(new Private) 0109 { 0110 init(); 0111 } 0112 0113 RfbServerManager::~RfbServerManager() 0114 { 0115 delete d; 0116 } 0117 0118 QSharedPointer<FrameBuffer> RfbServerManager::framebuffer() const 0119 { 0120 return d->fb; 0121 } 0122 0123 QVariantMap RfbServerManager::s_pluginArgs; 0124 0125 void RfbServerManager::init() 0126 { 0127 //qDebug(); 0128 WId rootWindow = 0; 0129 0130 if (KWindowSystem::isPlatformX11()) { 0131 QPlatformNativeInterface* native = qApp->platformNativeInterface(); 0132 rootWindow = reinterpret_cast<WId>(native->nativeResourceForScreen(QByteArrayLiteral("rootwindow"), QGuiApplication::primaryScreen())); 0133 } 0134 0135 d->fb = FrameBufferManager::instance()->frameBuffer(rootWindow, s_pluginArgs); 0136 d->myCursor = rfbMakeXCursor(19, 19, (char *) cur, (char *) mask); 0137 d->myCursor->cleanup = false; 0138 d->desktopName = QStringLiteral("%1@%2 (shared desktop)") //FIXME check if we can use utf8 0139 .arg(KUser().loginName(),QHostInfo::localHostName()).toLatin1(); 0140 0141 connect(d->fb.data(), &FrameBuffer::frameBufferChanged, this, &RfbServerManager::updateFrameBuffer); 0142 connect(&d->rfbUpdateTimer, &QTimer::timeout, this, &RfbServerManager::updateScreens); 0143 connect(qApp, &QApplication::aboutToQuit, this, &RfbServerManager::cleanup); 0144 } 0145 0146 void RfbServerManager::updateFrameBuffer() 0147 { 0148 for (RfbServer *server : std::as_const(d->servers)) { 0149 server->updateFrameBuffer(d->fb->data(), d->fb->width(), d->fb->height(), d->fb->depth()); 0150 } 0151 } 0152 0153 void RfbServerManager::updateScreens() 0154 { 0155 QList<QRect> rects = d->fb->modifiedTiles(); 0156 const QPoint currentCursorPos = d->fb->cursorPosition(); 0157 0158 for (RfbServer* server : std::as_const(d->servers)) { 0159 server->updateScreen(rects); 0160 server->updateCursorPosition(currentCursorPos); 0161 } 0162 0163 //update() might disconnect some of the clients, which will synchronously 0164 //call the removeClient() method and will change d->clients, so we need 0165 //to copy the set here to avoid problems. 0166 const QSet<RfbClient*> clients = d->clients; 0167 for (RfbClient* client : clients) { 0168 client->update(); 0169 } 0170 } 0171 0172 void RfbServerManager::cleanup() 0173 { 0174 //qDebug(); 0175 0176 //copy because d->servers is going to be modified while we delete the servers 0177 const QSet<RfbServer*> servers = d->servers; 0178 qDeleteAll(servers); 0179 0180 Q_ASSERT(d->servers.isEmpty()); 0181 Q_ASSERT(d->clients.isEmpty()); 0182 0183 d->myCursor->cleanup = true; 0184 rfbFreeCursor(d->myCursor); 0185 d->fb.clear(); 0186 } 0187 0188 void RfbServerManager::registerServer(RfbServer* server) 0189 { 0190 d->servers.insert(server); 0191 } 0192 0193 void RfbServerManager::unregisterServer(RfbServer* server) 0194 { 0195 d->servers.remove(server); 0196 } 0197 0198 rfbScreenInfoPtr RfbServerManager::newScreen() 0199 { 0200 rfbScreenInfoPtr screen = nullptr; 0201 0202 if (!d->fb.isNull()) { 0203 int w = d->fb->width(); 0204 int h = d->fb->height(); 0205 int depth = d->fb->depth(); 0206 int bpp = depth >> 3; 0207 0208 if (bpp != 1 && bpp != 2 && bpp != 4) { 0209 bpp = 4; 0210 } 0211 0212 //qDebug() << "bpp: " << bpp; 0213 0214 rfbLogEnable(KRFB().isDebugEnabled()); 0215 0216 screen = rfbGetScreen(nullptr, nullptr, w, h, 8, 3, bpp); 0217 screen->paddedWidthInBytes = d->fb->paddedWidth(); 0218 d->fb->getServerFormat(screen->serverFormat); 0219 screen->frameBuffer = d->fb->data(); 0220 screen->desktopName = d->desktopName.constData(); 0221 screen->cursor = d->myCursor; 0222 } 0223 0224 return screen; 0225 } 0226 0227 void RfbServerManager::addClient(RfbClient* cc) 0228 { 0229 if (d->clients.size() == 0) { 0230 //qDebug() << "Starting framebuffer monitor"; 0231 d->fb->startMonitor(); 0232 d->rfbUpdateTimer.start(50ms); 0233 } 0234 d->clients.insert(cc); 0235 0236 KNotification::event(QStringLiteral("UserAcceptsConnection"), 0237 i18n("The remote user %1 is now connected.", cc->name())); 0238 0239 Q_EMIT clientConnected(cc); 0240 } 0241 0242 void RfbServerManager::removeClient(RfbClient* cc) 0243 { 0244 d->clients.remove(cc); 0245 if (d->clients.size() == 0) { 0246 //qDebug() << "Stopping framebuffer monitor"; 0247 d->fb->stopMonitor(); 0248 d->rfbUpdateTimer.stop(); 0249 } 0250 0251 KNotification::event(QStringLiteral("ConnectionClosed"), i18n("The remote user %1 disconnected.", cc->name())); 0252 0253 Q_EMIT clientDisconnected(cc); 0254 }