#include "ikvm_server.hpp" #include #include #include #include #include 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("Failed to get VNC screen due to invalid arguments"); elog( 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) { 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 (!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