/*************************************************************************** threads.cpp - threads ------------------- begin : Thu May 09 17:01:44 CET 2002 copyright : (C) 2015 Timothy Pearson (C) 2002 by Tim Jansen email : tim@tjansen.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "kvncview.h" #include #include #include "vncviewer.h" #include "threads.h" #include #include #include extern void pnmscale_fractional(const TQImage& src, TQImage& dst, int x, int y, int w, int h); int getPassword(char * &passwd); extern rfbBool newclient(rfbClient *cl) { int width = cl->width, height = cl->height, depth = cl->format.bitsPerPixel; int size = width * height * (depth / 8); uint8_t *buf = new uint8_t[size]; memset(buf, '\0', size); cl->frameBuffer = buf; cl->format.bitsPerPixel = 32; cl->format.redShift = 16; cl->format.greenShift = 8; cl->format.blueShift = 0; cl->format.redMax = 0xff; cl->format.greenMax = 0xff; cl->format.blueMax = 0xff; SetFormatAndEncodings(cl); return true; } extern void updatefb(rfbClient* cl, int x, int y, int w, int h) { // kdDebug(5011) << "updated client: x: " << x << ", y: " << y << ", w: " << w << ", h: " << h << endl; int width = cl->width, height = cl->height; TQImage img(cl->frameBuffer, width, height, 32, NULL, 256, TQImage::IgnoreEndian); if (img.isNull()) kdDebug(5011) << "image not loaded" << endl; ControllerThreadObject *t = (ControllerThreadObject*)rfbClientGetClientData(cl, 0); t->setImage(img); t->queueDrawRegion(x, y, w, h); } extern char *passwd(rfbClient *cl) { Q_UNUSED(cl) kdDebug(5011) << "password request" << endl; char *passwd; if (getPassword(passwd)) { return passwd; } else { return NULL; } } extern void authresults(rfbClient *cl, uint32_t authResult) { kdDebug(5011) << "authentication result: " << authResult << endl; ControllerThreadObject *t = (ControllerThreadObject*)rfbClientGetClientData(cl, 0); t->authenticationResults(authResult); } extern void networkstat(rfbClient *cl, uint32_t statuscode) { kdDebug(5011) << "network status: " << statuscode << endl; ControllerThreadObject *t = (ControllerThreadObject*)rfbClientGetClientData(cl, 0); t->networkStatus(statuscode); } extern void output(const char *format, ...) { va_list args; va_start(args, format); TQString message; message.vsprintf(format, args); va_end(args); kdDebug(5011) << message.local8Bit(); if (message.contains("Could not open")) { kdDebug(5011) << "Server not found!" << endl; } if (message.contains("VNC authentication succeeded")) { kdDebug(5011) << "Password OK" << endl; } } ControllerThreadObject::ControllerThreadObject(KVncView *v, volatile bool &quitFlag) : cl(0L), m_view(v), m_status(REMOTE_VIEW_CONNECTING), m_quitFlag(quitFlag), m_scaling(false), m_scalingWidth(-1), m_scalingHeight(-1), m_resizeEntireFrame(false) { TQMutexLocker locker(&mutex); cl = rfbGetClient(8, 3, 4); } ControllerThreadObject::~ControllerThreadObject() { TQMutexLocker locker(&mutex); rfbClientCleanup(cl); } void ControllerThreadObject::changeStatus(RemoteViewStatus s) { m_status = s; TQApplication::postEvent(m_view, new StatusChangeEvent(s)); } void ControllerThreadObject::sendFatalError(ErrorCode s) { m_quitFlag = true; TQApplication::postEvent(m_view, new FatalErrorEvent(s)); } void ControllerThreadObject::queueDrawRegion(int x, int y, int w, int h) { if (m_scaling) { // Rescale desktop int new_x; int new_y; int new_w; int new_h; mutex.lock(); if (m_resizeEntireFrame) { m_scaledImage.create(m_scalingWidth, m_scalingHeight, 32); new_x = 0; new_y = 0; new_w = m_scalingWidth; new_h = m_scalingHeight; TQImage scaledBlock = m_image.smoothScale(new_w, new_h); bitBlt(&m_scaledImage, new_x, new_y, &scaledBlock, 0, 0, new_w, new_h); m_resizeEntireFrame = false; } else { // Extend redraw boundaries to avoid pixelation artifacts due to rounding errors x = x - round((2.0 * m_image.width()) / m_scalingWidth); y = y - round((2.0 * m_image.height()) / m_scalingHeight); w = w + round((4.0 * m_image.width()) / m_scalingWidth); h = h + round((4.0 * m_image.height()) / m_scalingHeight); if (x < 0) { x = 0; } if (y < 0) { y = 0; } if (w > m_image.width()) { w = m_image.width(); } if (h > m_image.height()) { h = m_image.height(); } new_x = round((x * m_scalingWidth) / m_image.width()); new_y = round((y * m_scalingHeight) / m_image.height()); new_w = round((w * m_scalingWidth) / m_image.width()); new_h = round((h * m_scalingHeight) / m_image.height()); TQImage scaledBlock(m_scalingWidth, m_scalingHeight, 32); pnmscale_fractional(m_image, scaledBlock, new_x, new_y, new_w, new_h); bitBlt(&m_scaledImage, new_x, new_y, &scaledBlock, new_x, new_y, new_w, new_h); } mutex.unlock(); DrawScreenRegion(new_x, new_y, new_w, new_h); } else { DrawScreenRegion(x, y, w, h); } } void ControllerThreadObject::setImage(const TQImage &img) { TQMutexLocker locker(&mutex); m_image = img; } const TQImage ControllerThreadObject::image(int x, int y, int w, int h) { TQMutexLocker locker(&mutex); if (m_scaling) { return m_scaledImage.copy(x, y, w, h); } else { return m_image.copy(x, y, w, h); } } void ControllerThreadObject::setScaling(int w, int h) { bool scale; if (w <= 0) { scale = false; } else { scale = true; } if ((m_scalingWidth != w) || (m_scalingHeight = h) || (m_scaling != scale)) { m_resizeEntireFrame = true; } m_scaling = scale; m_scalingWidth = w; m_scalingHeight = h; } void ControllerThreadObject::run() { mutex.lock(); rfbClientLog = output; rfbClientErr = output; cl->MallocFrameBuffer = newclient; cl->canHandleNewFBSize = true; cl->GetPassword = passwd; cl->AuthenticationResults = authresults; cl->NetworkStatus = networkstat; cl->GotFrameBufferUpdate = updatefb; rfbClientSetClientData(cl, 0, this); // make a copy of the host string... char *host = (char*) malloc(m_view->host().length()); strcpy(host, m_view->host().ascii()); cl->serverHost = host; int port = m_view->port(); if(port >= 0 && port < 100) // the user most likely used the short form (e.g. :1) port += 5900; cl->serverPort = port; mutex.unlock(); if(!rfbInitClient(cl, 0, 0)) { sendFatalError(ERROR_INTERNAL); // Terminate thread TQThread::exit(); return; } TQApplication::postEvent(m_view, new ScreenResizeEvent(cl->width, cl->height)); changeStatus(REMOTE_VIEW_CONNECTED); while (!m_quitFlag) { int i = WaitForMessage(cl, 500); if (i < 0) { m_quitFlag = true; changeStatus(REMOTE_VIEW_DISCONNECTED); // Terminate thread TQThread::exit(); return; } if (i) { if(!HandleRFBServerMessage(cl)) { m_quitFlag = true; changeStatus(REMOTE_VIEW_DISCONNECTED); // Terminate thread TQThread::exit(); return; } } } m_quitFlag = true; changeStatus(REMOTE_VIEW_DISCONNECTED); // Terminate thread TQThread::exit(); } void ControllerThreadObject::authenticationResults(int resultCode) { if (resultCode == rfbVncAuthOK) { changeStatus(REMOTE_VIEW_PREPARING); } else { sendFatalError(ERROR_AUTHENTICATION); // Terminate thread TQThread::exit(); } } void ControllerThreadObject::networkStatus(int statusCode) { if (statusCode == rfbNetworkConnectionSuccess) { // Stage 1 OK... changeStatus(REMOTE_VIEW_AUTHENTICATING); } else if (statusCode == rfbNetworkRFBConnectionSuccess) { // Stage 2 OK! } else { if (statusCode == rfbNetworkConnectionClosed) { sendFatalError(ERROR_CONNECTION); } else if (statusCode == rfbNetworkConnectionFailed) { sendFatalError(ERROR_CONNECTION); } else if (statusCode == rfbNetworkNameResolutionFailed) { sendFatalError(ERROR_NAME); } else if (statusCode == rfbNetworkRFBServerNotValid) { sendFatalError(ERROR_IO); } else if (statusCode == rfbNetworkRFBProtocolFailure) { sendFatalError(ERROR_PROTOCOL); } // Terminate thread TQThread::exit(); } } enum RemoteViewStatus ControllerThreadObject::status() { return m_status; } void ControllerThreadObject::queueMouseEvent(int x, int y, int buttonMask) { SendPointerEvent(cl, x, y, buttonMask); } void ControllerThreadObject::queueKeyEvent(unsigned int k, bool down) { SendKeyEvent(cl, k, down); } void ControllerThreadObject::queueClientCut(const TQString &text) { SendClientCutText(cl, (char*)text.ascii(), text.length()); } #include "threads.moc"