blob: 7be99e4379d1277cd0a27ee1ece3b5943d03fd61 [file] [log] [blame]
#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) :
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;
server->cursor = rfbMakeXCursor(cursorWidth, cursorHeight, (char*)cursor,
(char*)cursorMask);
server->cursor->xhot = 1;
server->cursor->yhot = 1;
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)
{
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 (!cd->needUpdate)
{
continue;
}
cd->needUpdate = false;
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::clientFramebufferUpdateRequest(
rfbClientPtr cl, rfbFramebufferUpdateRequestMsg *furMsg)
{
ClientData *cd = (ClientData *)cl->clientData;
if (!cd)
return;
// Ignore the furMsg info. This service uses full frame update always.
(void)furMsg;
cd->needUpdate = true;
}
void Server::clientGone(rfbClientPtr cl)
{
Server* server = (Server*)cl->screen->screenData;
delete (ClientData*)cl->clientData;
cl->clientData = nullptr;
if (server->numClients-- == 1)
{
server->input.disconnect();
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;
cl->clientFramebufferUpdateRequestHook = clientFramebufferUpdateRequest;
if (!server->numClients++)
{
server->input.connect();
server->pendingResize = false;
server->frameCounter = 0;
}
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