blob: 804eb39af6701490d8374443f587b637d7c852e8 [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
Paul Fertser2d2f3da2021-06-18 11:16:43 +000010#include <boost/crc.hpp>
11
Eddie James21b177e2018-12-11 13:14:46 -060012namespace ikvm
13{
14
Eddie James29f775a2018-12-11 13:22:54 -060015using namespace phosphor::logging;
16using namespace sdbusplus::xyz::openbmc_project::Common::Error;
17
Eddie James21b177e2018-12-11 13:14:46 -060018Server::Server(const Args& args, Input& i, Video& v) :
Eddie James29f775a2018-12-11 13:22:54 -060019 pendingResize(false), frameCounter(0), numClients(0), input(i), video(v)
Eddie James21b177e2018-12-11 13:14:46 -060020{
Eddie James29f775a2018-12-11 13:22:54 -060021 std::string ip("localhost");
22 const Args::CommandLine& commandLine = args.getCommandLine();
23 int argc = commandLine.argc;
24
25 server = rfbGetScreen(&argc, commandLine.argv, video.getWidth(),
26 video.getHeight(), Video::bitsPerSample,
27 Video::samplesPerPixel, Video::bytesPerPixel);
28
29 if (!server)
30 {
31 log<level::ERR>("Failed to get VNC screen due to invalid arguments");
32 elog<InvalidArgument>(
33 xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_NAME(""),
34 xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_VALUE(""));
35 }
36
37 framebuffer.resize(
38 video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
39
40 server->screenData = this;
41 server->desktopName = "OpenBMC IKVM";
42 server->frameBuffer = framebuffer.data();
43 server->newClientHook = newClient;
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -080044 server->cursor = rfbMakeXCursor(cursorWidth, cursorHeight, (char*)cursor,
45 (char*)cursorMask);
46 server->cursor->xhot = 1;
47 server->cursor->yhot = 1;
Eddie James29f775a2018-12-11 13:22:54 -060048
49 rfbStringToAddr(&ip[0], &server->listenInterface);
50
51 rfbInitServer(server);
52
53 rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
54
55 server->kbdAddEvent = Input::keyEvent;
56 server->ptrAddEvent = Input::pointerEvent;
57
58 processTime = (1000000 / video.getFrameRate()) - 100;
Paul Fertser2d2f3da2021-06-18 11:16:43 +000059
60 calcFrameCRC = args.getCalcFrameCRC();
Eddie James21b177e2018-12-11 13:14:46 -060061}
62
63Server::~Server()
64{
Eddie James29f775a2018-12-11 13:22:54 -060065 rfbScreenCleanup(server);
66}
67
68void Server::resize()
69{
70 if (frameCounter > video.getFrameRate())
71 {
72 doResize();
73 }
74 else
75 {
76 pendingResize = true;
77 }
78}
79
80void Server::run()
81{
82 rfbProcessEvents(server, processTime);
83
84 if (server->clientHead)
85 {
Eddie James29f775a2018-12-11 13:22:54 -060086 frameCounter++;
87 if (pendingResize && frameCounter > video.getFrameRate())
88 {
89 doResize();
90 pendingResize = false;
91 }
92 }
93}
94
95void Server::sendFrame()
96{
97 char* data = video.getData();
98 rfbClientIteratorPtr it;
99 rfbClientPtr cl;
Paul Fertser2d2f3da2021-06-18 11:16:43 +0000100 int64_t frame_crc = -1;
Eddie James29f775a2018-12-11 13:22:54 -0600101
102 if (!data || pendingResize)
103 {
104 return;
105 }
106
107 it = rfbGetClientIterator(server);
108
109 while ((cl = rfbClientIteratorNext(it)))
110 {
111 ClientData* cd = (ClientData*)cl->clientData;
112 rfbFramebufferUpdateMsg* fu = (rfbFramebufferUpdateMsg*)cl->updateBuf;
113
114 if (!cd)
115 {
116 continue;
117 }
118
119 if (cd->skipFrame)
120 {
121 cd->skipFrame--;
122 continue;
123 }
124
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700125 if (!cd->needUpdate)
126 {
127 continue;
128 }
Paul Fertser2d2f3da2021-06-18 11:16:43 +0000129
130 if (calcFrameCRC)
131 {
132 if (frame_crc == -1)
133 {
134 /* JFIF header contains some varying data so skip it for
135 * checksum calculation */
136 frame_crc = boost::crc<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF,
137 true, true>(data + 0x30,
138 video.getFrameSize() - 0x30);
139 }
140
141 if (cd->last_crc == frame_crc)
142 {
143 continue;
144 }
145
146 cd->last_crc = frame_crc;
147 }
148
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700149 cd->needUpdate = false;
150
Eddie James29f775a2018-12-11 13:22:54 -0600151 if (cl->enableLastRectEncoding)
152 {
153 fu->nRects = 0xFFFF;
154 }
155 else
156 {
157 fu->nRects = Swap16IfLE(1);
158 }
159
160 fu->type = rfbFramebufferUpdate;
161 cl->ublen = sz_rfbFramebufferUpdateMsg;
162 rfbSendUpdateBuf(cl);
163
164 cl->tightEncoding = rfbEncodingTight;
165 rfbSendTightHeader(cl, 0, 0, video.getWidth(), video.getHeight());
166
167 cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
168 rfbSendCompressedDataTight(cl, data, video.getFrameSize());
169
170 if (cl->enableLastRectEncoding)
171 {
172 rfbSendLastRectMarker(cl);
173 }
174
175 rfbSendUpdateBuf(cl);
176 }
177
178 rfbReleaseClientIterator(it);
179}
180
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700181void Server::clientFramebufferUpdateRequest(
182 rfbClientPtr cl, rfbFramebufferUpdateRequestMsg *furMsg)
183{
184 ClientData *cd = (ClientData *)cl->clientData;
185
186 if (!cd)
187 return;
188
189 // Ignore the furMsg info. This service uses full frame update always.
190 (void)furMsg;
191
192 cd->needUpdate = true;
193}
194
Eddie James29f775a2018-12-11 13:22:54 -0600195void Server::clientGone(rfbClientPtr cl)
196{
197 Server* server = (Server*)cl->screen->screenData;
198
199 delete (ClientData*)cl->clientData;
Jae Hyun Yoo0049bfa2019-03-06 15:39:58 -0800200 cl->clientData = nullptr;
Eddie James29f775a2018-12-11 13:22:54 -0600201
202 if (server->numClients-- == 1)
203 {
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700204 server->input.disconnect();
Eddie James29f775a2018-12-11 13:22:54 -0600205 rfbMarkRectAsModified(server->server, 0, 0, server->video.getWidth(),
206 server->video.getHeight());
207 }
208}
209
210enum rfbNewClientAction Server::newClient(rfbClientPtr cl)
211{
212 Server* server = (Server*)cl->screen->screenData;
213
214 cl->clientData =
215 new ClientData(server->video.getFrameRate(), &server->input);
216 cl->clientGoneHook = clientGone;
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700217 cl->clientFramebufferUpdateRequestHook = clientFramebufferUpdateRequest;
Eddie James29f775a2018-12-11 13:22:54 -0600218 if (!server->numClients++)
219 {
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700220 server->input.connect();
Eddie James29f775a2018-12-11 13:22:54 -0600221 server->pendingResize = false;
222 server->frameCounter = 0;
Eddie James29f775a2018-12-11 13:22:54 -0600223 }
224
225 return RFB_CLIENT_ACCEPT;
226}
227
228void Server::doResize()
229{
230 rfbClientIteratorPtr it;
231 rfbClientPtr cl;
232
233 framebuffer.resize(
234 video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
235
236 rfbNewFramebuffer(server, framebuffer.data(), video.getWidth(),
237 video.getHeight(), Video::bitsPerSample,
238 Video::samplesPerPixel, Video::bytesPerPixel);
239 rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
240
241 it = rfbGetClientIterator(server);
242
243 while ((cl = rfbClientIteratorNext(it)))
244 {
245 ClientData* cd = (ClientData*)cl->clientData;
246
247 if (!cd)
248 {
249 continue;
250 }
251
252 // delay video updates to give the client time to resize
253 cd->skipFrame = video.getFrameRate();
254 }
255
256 rfbReleaseClientIterator(it);
Eddie James21b177e2018-12-11 13:14:46 -0600257}
258
259} // namespace ikvm