blob: 0736d1f55f73eceaad45c6805a069de34d579626 [file] [log] [blame]
Eddie James21b177e2018-12-11 13:14:46 -06001#include "ikvm_server.hpp"
2
Eddie James29f775a2018-12-11 13:22:54 -06003#include <rfb/rfbproto.h>
4
5#include <phosphor-logging/elog-errors.hpp>
6#include <phosphor-logging/elog.hpp>
7#include <phosphor-logging/log.hpp>
8#include <xyz/openbmc_project/Common/error.hpp>
9
Eddie James21b177e2018-12-11 13:14:46 -060010namespace ikvm
11{
12
Eddie James29f775a2018-12-11 13:22:54 -060013using namespace phosphor::logging;
14using namespace sdbusplus::xyz::openbmc_project::Common::Error;
15
Eddie James21b177e2018-12-11 13:14:46 -060016Server::Server(const Args& args, Input& i, Video& v) :
Eddie James29f775a2018-12-11 13:22:54 -060017 pendingResize(false), frameCounter(0), numClients(0), input(i), video(v)
Eddie James21b177e2018-12-11 13:14:46 -060018{
Eddie James29f775a2018-12-11 13:22:54 -060019 std::string ip("localhost");
20 const Args::CommandLine& commandLine = args.getCommandLine();
21 int argc = commandLine.argc;
22
23 server = rfbGetScreen(&argc, commandLine.argv, video.getWidth(),
24 video.getHeight(), Video::bitsPerSample,
25 Video::samplesPerPixel, Video::bytesPerPixel);
26
27 if (!server)
28 {
29 log<level::ERR>("Failed to get VNC screen due to invalid arguments");
30 elog<InvalidArgument>(
31 xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_NAME(""),
32 xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_VALUE(""));
33 }
34
35 framebuffer.resize(
36 video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
37
38 server->screenData = this;
39 server->desktopName = "OpenBMC IKVM";
40 server->frameBuffer = framebuffer.data();
41 server->newClientHook = newClient;
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -080042 server->cursor = rfbMakeXCursor(cursorWidth, cursorHeight, (char*)cursor,
43 (char*)cursorMask);
44 server->cursor->xhot = 1;
45 server->cursor->yhot = 1;
Eddie James29f775a2018-12-11 13:22:54 -060046
47 rfbStringToAddr(&ip[0], &server->listenInterface);
48
49 rfbInitServer(server);
50
51 rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
52
53 server->kbdAddEvent = Input::keyEvent;
54 server->ptrAddEvent = Input::pointerEvent;
55
56 processTime = (1000000 / video.getFrameRate()) - 100;
Eddie James21b177e2018-12-11 13:14:46 -060057}
58
59Server::~Server()
60{
Eddie James29f775a2018-12-11 13:22:54 -060061 rfbScreenCleanup(server);
62}
63
64void Server::resize()
65{
66 if (frameCounter > video.getFrameRate())
67 {
68 doResize();
69 }
70 else
71 {
72 pendingResize = true;
73 }
74}
75
76void Server::run()
77{
78 rfbProcessEvents(server, processTime);
79
80 if (server->clientHead)
81 {
82 input.sendReport();
83
84 frameCounter++;
85 if (pendingResize && frameCounter > video.getFrameRate())
86 {
87 doResize();
88 pendingResize = false;
89 }
90 }
91}
92
93void Server::sendFrame()
94{
95 char* data = video.getData();
96 rfbClientIteratorPtr it;
97 rfbClientPtr cl;
98
99 if (!data || pendingResize)
100 {
101 return;
102 }
103
104 it = rfbGetClientIterator(server);
105
106 while ((cl = rfbClientIteratorNext(it)))
107 {
108 ClientData* cd = (ClientData*)cl->clientData;
109 rfbFramebufferUpdateMsg* fu = (rfbFramebufferUpdateMsg*)cl->updateBuf;
110
111 if (!cd)
112 {
113 continue;
114 }
115
116 if (cd->skipFrame)
117 {
118 cd->skipFrame--;
119 continue;
120 }
121
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700122 if (!cd->needUpdate)
123 {
124 continue;
125 }
126 cd->needUpdate = false;
127
Eddie James29f775a2018-12-11 13:22:54 -0600128 if (cl->enableLastRectEncoding)
129 {
130 fu->nRects = 0xFFFF;
131 }
132 else
133 {
134 fu->nRects = Swap16IfLE(1);
135 }
136
137 fu->type = rfbFramebufferUpdate;
138 cl->ublen = sz_rfbFramebufferUpdateMsg;
139 rfbSendUpdateBuf(cl);
140
141 cl->tightEncoding = rfbEncodingTight;
142 rfbSendTightHeader(cl, 0, 0, video.getWidth(), video.getHeight());
143
144 cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
145 rfbSendCompressedDataTight(cl, data, video.getFrameSize());
146
147 if (cl->enableLastRectEncoding)
148 {
149 rfbSendLastRectMarker(cl);
150 }
151
152 rfbSendUpdateBuf(cl);
153 }
154
155 rfbReleaseClientIterator(it);
156}
157
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700158void Server::clientFramebufferUpdateRequest(
159 rfbClientPtr cl, rfbFramebufferUpdateRequestMsg *furMsg)
160{
161 ClientData *cd = (ClientData *)cl->clientData;
162
163 if (!cd)
164 return;
165
166 // Ignore the furMsg info. This service uses full frame update always.
167 (void)furMsg;
168
169 cd->needUpdate = true;
170}
171
Eddie James29f775a2018-12-11 13:22:54 -0600172void Server::clientGone(rfbClientPtr cl)
173{
174 Server* server = (Server*)cl->screen->screenData;
175
176 delete (ClientData*)cl->clientData;
Jae Hyun Yoo0049bfa2019-03-06 15:39:58 -0800177 cl->clientData = nullptr;
Eddie James29f775a2018-12-11 13:22:54 -0600178
179 if (server->numClients-- == 1)
180 {
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700181 server->input.disconnect();
Eddie James29f775a2018-12-11 13:22:54 -0600182 rfbMarkRectAsModified(server->server, 0, 0, server->video.getWidth(),
183 server->video.getHeight());
184 }
185}
186
187enum rfbNewClientAction Server::newClient(rfbClientPtr cl)
188{
189 Server* server = (Server*)cl->screen->screenData;
190
191 cl->clientData =
192 new ClientData(server->video.getFrameRate(), &server->input);
193 cl->clientGoneHook = clientGone;
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700194 cl->clientFramebufferUpdateRequestHook = clientFramebufferUpdateRequest;
Eddie James29f775a2018-12-11 13:22:54 -0600195 if (!server->numClients++)
196 {
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700197 server->input.connect();
Eddie James29f775a2018-12-11 13:22:54 -0600198 server->pendingResize = false;
199 server->frameCounter = 0;
Eddie James29f775a2018-12-11 13:22:54 -0600200 }
201
202 return RFB_CLIENT_ACCEPT;
203}
204
205void Server::doResize()
206{
207 rfbClientIteratorPtr it;
208 rfbClientPtr cl;
209
210 framebuffer.resize(
211 video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
212
213 rfbNewFramebuffer(server, framebuffer.data(), video.getWidth(),
214 video.getHeight(), Video::bitsPerSample,
215 Video::samplesPerPixel, Video::bytesPerPixel);
216 rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
217
218 it = rfbGetClientIterator(server);
219
220 while ((cl = rfbClientIteratorNext(it)))
221 {
222 ClientData* cd = (ClientData*)cl->clientData;
223
224 if (!cd)
225 {
226 continue;
227 }
228
229 // delay video updates to give the client time to resize
230 cd->skipFrame = video.getFrameRate();
231 }
232
233 rfbReleaseClientIterator(it);
Eddie James21b177e2018-12-11 13:14:46 -0600234}
235
236} // namespace ikvm