Add RFB server class implementation

Change-Id: I3532b5ecdef87cb8f64327de6bd22a175b68f865
Signed-off-by: Eddie James <eajames@linux.ibm.com>
diff --git a/ikvm_server.cpp b/ikvm_server.cpp
index 679f47c..98d45ef 100644
--- a/ikvm_server.cpp
+++ b/ikvm_server.cpp
@@ -1,15 +1,209 @@
 #include "ikvm_server.hpp"
 
+#include <rfb/rfbproto.h>
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
 namespace ikvm
 {
 
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
 Server::Server(const Args& args, Input& i, Video& v) :
-    input(i), video(v)
+    pendingResize(false), frameCounter(0), numClients(0), input(i), video(v)
 {
+    std::string ip("localhost");
+    const Args::CommandLine& commandLine = args.getCommandLine();
+    int argc = commandLine.argc;
+
+    server = rfbGetScreen(&argc, commandLine.argv, video.getWidth(),
+                          video.getHeight(), Video::bitsPerSample,
+                          Video::samplesPerPixel, Video::bytesPerPixel);
+
+    if (!server)
+    {
+        log<level::ERR>("Failed to get VNC screen due to invalid arguments");
+        elog<InvalidArgument>(
+            xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_NAME(""),
+            xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_VALUE(""));
+    }
+
+    framebuffer.resize(
+        video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
+
+    server->screenData = this;
+    server->desktopName = "OpenBMC IKVM";
+    server->frameBuffer = framebuffer.data();
+    server->newClientHook = newClient;
+
+    rfbStringToAddr(&ip[0], &server->listenInterface);
+
+    rfbInitServer(server);
+
+    rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
+
+    server->kbdAddEvent = Input::keyEvent;
+    server->ptrAddEvent = Input::pointerEvent;
+
+    processTime = (1000000 / video.getFrameRate()) - 100;
 }
 
 Server::~Server()
 {
+    rfbScreenCleanup(server);
+}
+
+void Server::resize()
+{
+    if (frameCounter > video.getFrameRate())
+    {
+        doResize();
+    }
+    else
+    {
+        pendingResize = true;
+    }
+}
+
+void Server::run()
+{
+    rfbProcessEvents(server, processTime);
+
+    if (server->clientHead)
+    {
+        input.sendReport();
+
+        frameCounter++;
+        if (pendingResize && frameCounter > video.getFrameRate())
+        {
+            doResize();
+            pendingResize = false;
+        }
+    }
+}
+
+void Server::sendFrame()
+{
+    char* data = video.getData();
+    rfbClientIteratorPtr it;
+    rfbClientPtr cl;
+
+    if (!data || pendingResize)
+    {
+        return;
+    }
+
+    it = rfbGetClientIterator(server);
+
+    while ((cl = rfbClientIteratorNext(it)))
+    {
+        ClientData* cd = (ClientData*)cl->clientData;
+        rfbFramebufferUpdateMsg* fu = (rfbFramebufferUpdateMsg*)cl->updateBuf;
+
+        if (!cd)
+        {
+            continue;
+        }
+
+        if (cd->skipFrame)
+        {
+            cd->skipFrame--;
+            continue;
+        }
+
+        if (cl->enableLastRectEncoding)
+        {
+            fu->nRects = 0xFFFF;
+        }
+        else
+        {
+            fu->nRects = Swap16IfLE(1);
+        }
+
+        fu->type = rfbFramebufferUpdate;
+        cl->ublen = sz_rfbFramebufferUpdateMsg;
+        rfbSendUpdateBuf(cl);
+
+        cl->tightEncoding = rfbEncodingTight;
+        rfbSendTightHeader(cl, 0, 0, video.getWidth(), video.getHeight());
+
+        cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
+        rfbSendCompressedDataTight(cl, data, video.getFrameSize());
+
+        if (cl->enableLastRectEncoding)
+        {
+            rfbSendLastRectMarker(cl);
+        }
+
+        rfbSendUpdateBuf(cl);
+    }
+
+    rfbReleaseClientIterator(it);
+}
+
+void Server::clientGone(rfbClientPtr cl)
+{
+    Server* server = (Server*)cl->screen->screenData;
+
+    delete (ClientData*)cl->clientData;
+
+    if (server->numClients-- == 1)
+    {
+        rfbMarkRectAsModified(server->server, 0, 0, server->video.getWidth(),
+                              server->video.getHeight());
+    }
+}
+
+enum rfbNewClientAction Server::newClient(rfbClientPtr cl)
+{
+    Server* server = (Server*)cl->screen->screenData;
+
+    cl->clientData =
+        new ClientData(server->video.getFrameRate(), &server->input);
+    cl->clientGoneHook = clientGone;
+    if (!server->numClients++)
+    {
+        server->pendingResize = false;
+        server->frameCounter = 0;
+        server->video.start();
+    }
+
+    return RFB_CLIENT_ACCEPT;
+}
+
+void Server::doResize()
+{
+    rfbClientIteratorPtr it;
+    rfbClientPtr cl;
+
+    framebuffer.resize(
+        video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
+
+    rfbNewFramebuffer(server, framebuffer.data(), video.getWidth(),
+                      video.getHeight(), Video::bitsPerSample,
+                      Video::samplesPerPixel, Video::bytesPerPixel);
+    rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
+
+    it = rfbGetClientIterator(server);
+
+    while ((cl = rfbClientIteratorNext(it)))
+    {
+        ClientData* cd = (ClientData*)cl->clientData;
+
+        if (!cd)
+        {
+            continue;
+        }
+
+        // delay video updates to give the client time to resize
+        cd->skipFrame = video.getFrameRate();
+    }
+
+    rfbReleaseClientIterator(it);
 }
 
 } // namespace ikvm