blob: 141f6c60886f5340ef0819b49fa05ff51bca24a9 [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
George Liuf79f6f52022-07-06 09:32:35 +08005#include <boost/crc.hpp>
Eddie James29f775a2018-12-11 13:22:54 -06006#include <phosphor-logging/elog-errors.hpp>
7#include <phosphor-logging/elog.hpp>
8#include <phosphor-logging/log.hpp>
9#include <xyz/openbmc_project/Common/error.hpp>
10
Eddie James21b177e2018-12-11 13:14:46 -060011namespace ikvm
12{
13
Eddie James29f775a2018-12-11 13:22:54 -060014using namespace phosphor::logging;
15using namespace sdbusplus::xyz::openbmc_project::Common::Error;
16
Eddie James21b177e2018-12-11 13:14:46 -060017Server::Server(const Args& args, Input& i, Video& v) :
Eddie James29f775a2018-12-11 13:22:54 -060018 pendingResize(false), frameCounter(0), numClients(0), input(i), video(v)
Eddie James21b177e2018-12-11 13:14:46 -060019{
Eddie James29f775a2018-12-11 13:22:54 -060020 std::string ip("localhost");
21 const Args::CommandLine& commandLine = args.getCommandLine();
22 int argc = commandLine.argc;
23
24 server = rfbGetScreen(&argc, commandLine.argv, video.getWidth(),
25 video.getHeight(), Video::bitsPerSample,
26 Video::samplesPerPixel, Video::bytesPerPixel);
27
28 if (!server)
29 {
30 log<level::ERR>("Failed to get VNC screen due to invalid arguments");
31 elog<InvalidArgument>(
32 xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_NAME(""),
33 xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_VALUE(""));
34 }
35
36 framebuffer.resize(
37 video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
38
39 server->screenData = this;
40 server->desktopName = "OpenBMC IKVM";
41 server->frameBuffer = framebuffer.data();
42 server->newClientHook = newClient;
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -080043 server->cursor = rfbMakeXCursor(cursorWidth, cursorHeight, (char*)cursor,
44 (char*)cursorMask);
45 server->cursor->xhot = 1;
46 server->cursor->yhot = 1;
Eddie James29f775a2018-12-11 13:22:54 -060047
48 rfbStringToAddr(&ip[0], &server->listenInterface);
49
50 rfbInitServer(server);
51
52 rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
53
54 server->kbdAddEvent = Input::keyEvent;
55 server->ptrAddEvent = Input::pointerEvent;
56
57 processTime = (1000000 / video.getFrameRate()) - 100;
Paul Fertser2d2f3da2021-06-18 11:16:43 +000058
59 calcFrameCRC = args.getCalcFrameCRC();
Eddie James21b177e2018-12-11 13:14:46 -060060}
61
62Server::~Server()
63{
Eddie James29f775a2018-12-11 13:22:54 -060064 rfbScreenCleanup(server);
65}
66
67void Server::resize()
68{
69 if (frameCounter > video.getFrameRate())
70 {
71 doResize();
72 }
73 else
74 {
75 pendingResize = true;
76 }
77}
78
79void Server::run()
80{
81 rfbProcessEvents(server, processTime);
82
83 if (server->clientHead)
84 {
Eddie James29f775a2018-12-11 13:22:54 -060085 frameCounter++;
86 if (pendingResize && frameCounter > video.getFrameRate())
87 {
88 doResize();
89 pendingResize = false;
90 }
91 }
92}
93
94void Server::sendFrame()
95{
96 char* data = video.getData();
97 rfbClientIteratorPtr it;
98 rfbClientPtr cl;
Paul Fertser2d2f3da2021-06-18 11:16:43 +000099 int64_t frame_crc = -1;
Eddie James29f775a2018-12-11 13:22:54 -0600100
101 if (!data || pendingResize)
102 {
103 return;
104 }
105
106 it = rfbGetClientIterator(server);
107
108 while ((cl = rfbClientIteratorNext(it)))
109 {
110 ClientData* cd = (ClientData*)cl->clientData;
111 rfbFramebufferUpdateMsg* fu = (rfbFramebufferUpdateMsg*)cl->updateBuf;
112
113 if (!cd)
114 {
115 continue;
116 }
117
118 if (cd->skipFrame)
119 {
120 cd->skipFrame--;
121 continue;
122 }
123
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700124 if (!cd->needUpdate)
125 {
126 continue;
127 }
Paul Fertser2d2f3da2021-06-18 11:16:43 +0000128
129 if (calcFrameCRC)
130 {
131 if (frame_crc == -1)
132 {
133 /* JFIF header contains some varying data so skip it for
134 * checksum calculation */
George Liuf79f6f52022-07-06 09:32:35 +0800135 frame_crc =
136 boost::crc<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true,
137 true>(data + 0x30, video.getFrameSize() - 0x30);
Paul Fertser2d2f3da2021-06-18 11:16:43 +0000138 }
139
140 if (cd->last_crc == frame_crc)
141 {
142 continue;
143 }
144
145 cd->last_crc = frame_crc;
146 }
147
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700148 cd->needUpdate = false;
149
Eddie James29f775a2018-12-11 13:22:54 -0600150 if (cl->enableLastRectEncoding)
151 {
152 fu->nRects = 0xFFFF;
153 }
154 else
155 {
156 fu->nRects = Swap16IfLE(1);
157 }
158
159 fu->type = rfbFramebufferUpdate;
160 cl->ublen = sz_rfbFramebufferUpdateMsg;
161 rfbSendUpdateBuf(cl);
162
163 cl->tightEncoding = rfbEncodingTight;
164 rfbSendTightHeader(cl, 0, 0, video.getWidth(), video.getHeight());
165
166 cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
167 rfbSendCompressedDataTight(cl, data, video.getFrameSize());
168
169 if (cl->enableLastRectEncoding)
170 {
171 rfbSendLastRectMarker(cl);
172 }
173
174 rfbSendUpdateBuf(cl);
175 }
176
177 rfbReleaseClientIterator(it);
178}
179
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700180void Server::clientFramebufferUpdateRequest(
George Liuf79f6f52022-07-06 09:32:35 +0800181 rfbClientPtr cl, rfbFramebufferUpdateRequestMsg* furMsg)
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700182{
George Liuf79f6f52022-07-06 09:32:35 +0800183 ClientData* cd = (ClientData*)cl->clientData;
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700184
185 if (!cd)
186 return;
187
188 // Ignore the furMsg info. This service uses full frame update always.
189 (void)furMsg;
190
191 cd->needUpdate = true;
192}
193
Eddie James29f775a2018-12-11 13:22:54 -0600194void Server::clientGone(rfbClientPtr cl)
195{
196 Server* server = (Server*)cl->screen->screenData;
197
198 delete (ClientData*)cl->clientData;
Jae Hyun Yoo0049bfa2019-03-06 15:39:58 -0800199 cl->clientData = nullptr;
Eddie James29f775a2018-12-11 13:22:54 -0600200
201 if (server->numClients-- == 1)
202 {
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700203 server->input.disconnect();
Eddie James29f775a2018-12-11 13:22:54 -0600204 rfbMarkRectAsModified(server->server, 0, 0, server->video.getWidth(),
205 server->video.getHeight());
206 }
207}
208
209enum rfbNewClientAction Server::newClient(rfbClientPtr cl)
210{
211 Server* server = (Server*)cl->screen->screenData;
212
213 cl->clientData =
214 new ClientData(server->video.getFrameRate(), &server->input);
215 cl->clientGoneHook = clientGone;
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700216 cl->clientFramebufferUpdateRequestHook = clientFramebufferUpdateRequest;
Eddie James29f775a2018-12-11 13:22:54 -0600217 if (!server->numClients++)
218 {
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700219 server->input.connect();
Eddie James29f775a2018-12-11 13:22:54 -0600220 server->pendingResize = false;
221 server->frameCounter = 0;
Eddie James29f775a2018-12-11 13:22:54 -0600222 }
223
224 return RFB_CLIENT_ACCEPT;
225}
226
227void Server::doResize()
228{
229 rfbClientIteratorPtr it;
230 rfbClientPtr cl;
231
232 framebuffer.resize(
233 video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
234
235 rfbNewFramebuffer(server, framebuffer.data(), video.getWidth(),
236 video.getHeight(), Video::bitsPerSample,
237 Video::samplesPerPixel, Video::bytesPerPixel);
238 rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
239
240 it = rfbGetClientIterator(server);
241
242 while ((cl = rfbClientIteratorNext(it)))
243 {
244 ClientData* cd = (ClientData*)cl->clientData;
245
246 if (!cd)
247 {
248 continue;
249 }
250
251 // delay video updates to give the client time to resize
252 cd->skipFrame = video.getFrameRate();
253 }
254
255 rfbReleaseClientIterator(it);
Eddie James21b177e2018-12-11 13:14:46 -0600256}
257
258} // namespace ikvm