1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "host/frontend/vnc_server/vnc_client_connection.h"
18
19 #include <netinet/in.h>
20 #include <sys/time.h>
21
22 #include <algorithm>
23 #include <cmath>
24 #include <cstdint>
25 #include <cstring>
26 #include <memory>
27 #include <mutex>
28 #include <string>
29 #include <thread>
30 #include <utility>
31 #include <vector>
32
33 #include <gflags/gflags.h>
34 #include <android-base/logging.h>
35 #include "common/libs/tcp_socket/tcp_socket.h"
36 #include "host/frontend/vnc_server/keysyms.h"
37 #include "host/frontend/vnc_server/mocks.h"
38 #include "host/frontend/vnc_server/vnc_utils.h"
39 #include "host/libs/config/cuttlefish_config.h"
40 #include "host/libs/screen_connector/screen_connector.h"
41
42 using cuttlefish::Message;
43 using cuttlefish::vnc::Stripe;
44 using cuttlefish::vnc::StripePtrVec;
45 using cuttlefish::vnc::VncClientConnection;
46
47 struct ScreenRegionView {
48 using Pixel = uint32_t;
49 static constexpr int kSwiftShaderPadding = 4;
50 static constexpr int kRedShift = 0;
51 static constexpr int kGreenShift = 8;
52 static constexpr int kBlueShift = 16;
53 static constexpr int kRedBits = 8;
54 static constexpr int kGreenBits = 8;
55 static constexpr int kBlueBits = 8;
56 };
57
58 DEFINE_bool(debug_client, false, "Turn on detailed logging for the client");
59
60 #define DLOG(LEVEL) \
61 if (FLAGS_debug_client) LOG(LEVEL)
62
63 namespace {
64 class BigEndianChecker {
65 public:
BigEndianChecker()66 BigEndianChecker() {
67 uint32_t u = 1;
68 is_big_endian_ = *reinterpret_cast<const char*>(&u) == 0;
69 }
operator ()() const70 bool operator()() const { return is_big_endian_; }
71
72 private:
73 bool is_big_endian_{};
74 };
75
76 const BigEndianChecker ImBigEndian;
77
78 constexpr int32_t kDesktopSizeEncoding = -223;
79 constexpr int32_t kTightEncoding = 7;
80
81 // These are the lengths not counting the first byte. The first byte
82 // indicates the message type.
83 constexpr size_t kSetPixelFormatLength = 19;
84 constexpr size_t kFramebufferUpdateRequestLength = 9;
85 constexpr size_t kSetEncodingsLength = 3; // more bytes follow
86 constexpr size_t kKeyEventLength = 7;
87 constexpr size_t kPointerEventLength = 5;
88 constexpr size_t kClientCutTextLength = 7; // more bytes follow
89
HostName()90 std::string HostName() {
91 auto config = cuttlefish::CuttlefishConfig::Get();
92 auto instance = config->ForDefaultInstance();
93 return !config || instance.device_title().empty() ? std::string{"localhost"}
94 : instance.device_title();
95 }
96
uint16_tAt(const void * p)97 std::uint16_t uint16_tAt(const void* p) {
98 std::uint16_t u{};
99 std::memcpy(&u, p, sizeof u);
100 return ntohs(u);
101 }
102
uint32_tAt(const void * p)103 std::uint32_t uint32_tAt(const void* p) {
104 std::uint32_t u{};
105 std::memcpy(&u, p, sizeof u);
106 return ntohl(u);
107 }
108
int32_tAt(const void * p)109 std::int32_t int32_tAt(const void* p) {
110 std::uint32_t u{};
111 std::memcpy(&u, p, sizeof u);
112 u = ntohl(u);
113 std::int32_t s{};
114 std::memcpy(&s, &u, sizeof s);
115 return s;
116 }
117
RedVal(std::uint32_t pixel)118 std::uint32_t RedVal(std::uint32_t pixel) {
119 return (pixel >> ScreenRegionView::kRedShift) &
120 ((0x1 << ScreenRegionView::kRedBits) - 1);
121 }
122
BlueVal(std::uint32_t pixel)123 std::uint32_t BlueVal(std::uint32_t pixel) {
124 return (pixel >> ScreenRegionView::kBlueShift) &
125 ((0x1 << ScreenRegionView::kBlueBits) - 1);
126 }
127
GreenVal(std::uint32_t pixel)128 std::uint32_t GreenVal(std::uint32_t pixel) {
129 return (pixel >> ScreenRegionView::kGreenShift) &
130 ((0x1 << ScreenRegionView::kGreenBits) - 1);
131 }
132 } // namespace
133 namespace cuttlefish {
134 namespace vnc {
operator ==(const VncClientConnection::FrameBufferUpdateRequest & lhs,const VncClientConnection::FrameBufferUpdateRequest & rhs)135 bool operator==(const VncClientConnection::FrameBufferUpdateRequest& lhs,
136 const VncClientConnection::FrameBufferUpdateRequest& rhs) {
137 return lhs.x_pos == rhs.x_pos && lhs.y_pos == rhs.y_pos &&
138 lhs.width == rhs.width && lhs.height == rhs.height;
139 }
140
operator !=(const VncClientConnection::FrameBufferUpdateRequest & lhs,const VncClientConnection::FrameBufferUpdateRequest & rhs)141 bool operator!=(const VncClientConnection::FrameBufferUpdateRequest& lhs,
142 const VncClientConnection::FrameBufferUpdateRequest& rhs) {
143 return !(lhs == rhs);
144 }
145 } // namespace vnc
146 } // namespace cuttlefish
147
VncClientConnection(ClientSocket client,std::shared_ptr<VirtualInputs> virtual_inputs,BlackBoard * bb,bool aggressive)148 VncClientConnection::VncClientConnection(
149 ClientSocket client, std::shared_ptr<VirtualInputs> virtual_inputs,
150 BlackBoard* bb, bool aggressive)
151 : client_{std::move(client)}, virtual_inputs_{virtual_inputs}, bb_{bb} {
152 frame_buffer_request_handler_tid_ = std::thread(
153 &VncClientConnection::FrameBufferUpdateRequestHandler, this, aggressive);
154 }
155
~VncClientConnection()156 VncClientConnection::~VncClientConnection() {
157 {
158 std::lock_guard<std::mutex> guard(m_);
159 closed_ = true;
160 }
161 bb_->StopWaiting(this);
162 frame_buffer_request_handler_tid_.join();
163 }
164
StartSession()165 void VncClientConnection::StartSession() {
166 LOG(INFO) << "Starting session";
167 SetupProtocol();
168 LOG(INFO) << "Protocol set up";
169 if (client_.closed()) {
170 return;
171 }
172 SetupSecurityType();
173 LOG(INFO) << "Security type set";
174 if (client_.closed()) {
175 return;
176 }
177 GetClientInit();
178 LOG(INFO) << "Gotten client init";
179 if (client_.closed()) {
180 return;
181 }
182 SendServerInit();
183 LOG(INFO) << "Sent server init";
184 if (client_.closed()) {
185 return;
186 }
187 NormalSession();
188 LOG(INFO) << "vnc session terminated";
189 }
190
closed()191 bool VncClientConnection::closed() {
192 std::lock_guard<std::mutex> guard(m_);
193 return closed_;
194 }
195
SetupProtocol()196 void VncClientConnection::SetupProtocol() {
197 static constexpr char kRFBVersion[] = "RFB 003.008\n";
198 static constexpr char kRFBVersionOld[] = "RFB 003.003\n";
199 static constexpr auto kVersionLen = (sizeof kRFBVersion) - 1;
200 client_.SendNoSignal(reinterpret_cast<const std::uint8_t*>(kRFBVersion),
201 kVersionLen);
202 auto client_protocol = client_.Recv(kVersionLen);
203 if (std::memcmp(&client_protocol[0], kRFBVersion,
204 std::min(kVersionLen, client_protocol.size())) != 0) {
205 if (!std::memcmp(
206 &client_protocol[0],
207 kRFBVersionOld,
208 std::min(kVersionLen, client_protocol.size()))) {
209 // We'll deal with V3.3 as well.
210 client_is_old_ = true;
211 return;
212 }
213
214 client_protocol.push_back('\0');
215 LOG(ERROR) << "vnc client wants a different protocol: "
216 << reinterpret_cast<const char*>(&client_protocol[0]);
217 }
218 }
219
SetupSecurityType()220 void VncClientConnection::SetupSecurityType() {
221 if (client_is_old_) {
222 static constexpr std::uint8_t kVNCSecurity[4] = { 0x00, 0x00, 0x00, 0x02 };
223 client_.SendNoSignal(kVNCSecurity);
224
225 static constexpr std::uint8_t kChallenge[16] =
226 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
227
228 client_.SendNoSignal(kChallenge);
229
230 auto clientResponse = client_.Recv(16);
231 (void)clientResponse; // Accept any response, we're not interested in actual security.
232
233 static constexpr std::uint8_t kSuccess[4] = { 0x00, 0x00, 0x00, 0x00 };
234 client_.SendNoSignal(kSuccess);
235 return;
236 }
237
238 static constexpr std::uint8_t kNoneSecurity = 0x1;
239 // The first '0x1' indicates the number of items that follow
240 static constexpr std::uint8_t kOnlyNoneSecurity[] = {0x01, kNoneSecurity};
241 client_.SendNoSignal(kOnlyNoneSecurity);
242 auto client_security = client_.Recv(1);
243 if (client_.closed()) {
244 return;
245 }
246 if (client_security.front() != kNoneSecurity) {
247 LOG(ERROR) << "vnc client is asking for security type "
248 << static_cast<int>(client_security.front());
249 }
250 static constexpr std::uint8_t kZero[4] = {};
251 client_.SendNoSignal(kZero);
252 }
253
GetClientInit()254 void VncClientConnection::GetClientInit() {
255 auto client_shared = client_.Recv(1);
256 }
257
SendServerInit()258 void VncClientConnection::SendServerInit() {
259 const std::string server_name = HostName();
260 std::lock_guard<std::mutex> guard(m_);
261 auto server_init = cuttlefish::CreateMessage(
262 static_cast<std::uint16_t>(ScreenWidth()),
263 static_cast<std::uint16_t>(ScreenHeight()), pixel_format_.bits_per_pixel,
264 pixel_format_.depth, pixel_format_.big_endian, pixel_format_.true_color,
265 pixel_format_.red_max, pixel_format_.green_max, pixel_format_.blue_max,
266 pixel_format_.red_shift, pixel_format_.green_shift,
267 pixel_format_.blue_shift, std::uint16_t{}, // padding
268 std::uint8_t{}, // padding
269 static_cast<std::uint32_t>(server_name.size()), server_name);
270 client_.SendNoSignal(server_init);
271 }
272
MakeFrameBufferUpdateHeader(std::uint16_t num_stripes)273 Message VncClientConnection::MakeFrameBufferUpdateHeader(
274 std::uint16_t num_stripes) {
275 return cuttlefish::CreateMessage(std::uint8_t{0}, // message-type
276 std::uint8_t{}, // padding
277 std::uint16_t{num_stripes});
278 }
279
AppendRawStripeHeader(Message * frame_buffer_update,const Stripe & stripe)280 void VncClientConnection::AppendRawStripeHeader(Message* frame_buffer_update,
281 const Stripe& stripe) {
282 static constexpr int32_t kRawEncoding = 0;
283 cuttlefish::AppendToMessage(frame_buffer_update, std::uint16_t{stripe.x},
284 std::uint16_t{stripe.y}, std::uint16_t{stripe.width},
285 std::uint16_t{stripe.height}, kRawEncoding);
286 }
287
AppendJpegSize(Message * frame_buffer_update,size_t jpeg_size)288 void VncClientConnection::AppendJpegSize(Message* frame_buffer_update,
289 size_t jpeg_size) {
290 constexpr size_t kJpegSizeOneByteMax = 127;
291 constexpr size_t kJpegSizeTwoByteMax = 16383;
292 constexpr size_t kJpegSizeThreeByteMax = 4194303;
293
294 if (jpeg_size <= kJpegSizeOneByteMax) {
295 cuttlefish::AppendToMessage(frame_buffer_update,
296 static_cast<std::uint8_t>(jpeg_size));
297 } else if (jpeg_size <= kJpegSizeTwoByteMax) {
298 auto sz = static_cast<std::uint32_t>(jpeg_size);
299 cuttlefish::AppendToMessage(frame_buffer_update,
300 static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
301 static_cast<std::uint8_t>((sz >> 7) & 0xFF));
302 } else {
303 if (jpeg_size > kJpegSizeThreeByteMax) {
304 LOG(FATAL) << "jpeg size is too big: " << jpeg_size << " must be under "
305 << kJpegSizeThreeByteMax;
306 }
307 const auto sz = static_cast<std::uint32_t>(jpeg_size);
308 cuttlefish::AppendToMessage(frame_buffer_update,
309 static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
310 static_cast<std::uint8_t>(((sz >> 7) & 0x7F) | 0x80),
311 static_cast<std::uint8_t>((sz >> 14) & 0xFF));
312 }
313 }
314
AppendRawStripe(Message * frame_buffer_update,const Stripe & stripe) const315 void VncClientConnection::AppendRawStripe(Message* frame_buffer_update,
316 const Stripe& stripe) const {
317 using Pixel = ScreenRegionView::Pixel;
318 auto& fbu = *frame_buffer_update;
319 AppendRawStripeHeader(&fbu, stripe);
320 auto init_size = fbu.size();
321 fbu.insert(fbu.end(), stripe.raw_data.begin(), stripe.raw_data.end());
322 for (size_t i = init_size; i < fbu.size(); i += sizeof(Pixel)) {
323 CHECK_LE(i + sizeof(Pixel), fbu.size());
324 Pixel raw_pixel{};
325 std::memcpy(&raw_pixel, &fbu[i], sizeof raw_pixel);
326 auto red = RedVal(raw_pixel);
327 auto green = GreenVal(raw_pixel);
328 auto blue = BlueVal(raw_pixel);
329 Pixel pixel = Pixel{red} << pixel_format_.red_shift |
330 Pixel{blue} << pixel_format_.blue_shift |
331 Pixel{green} << pixel_format_.green_shift;
332
333 if (bool(pixel_format_.big_endian) != ImBigEndian()) {
334 // flip them bits (refactor into function)
335 auto p = reinterpret_cast<char*>(&pixel);
336 std::swap(p[0], p[3]);
337 std::swap(p[1], p[2]);
338 }
339 std::memcpy(&fbu[i], &pixel, sizeof pixel);
340 }
341 }
342
MakeRawFrameBufferUpdate(const StripePtrVec & stripes) const343 Message VncClientConnection::MakeRawFrameBufferUpdate(
344 const StripePtrVec& stripes) const {
345 auto fbu =
346 MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size()));
347 for (auto& stripe : stripes) {
348 AppendRawStripe(&fbu, *stripe);
349 }
350 return fbu;
351 }
352
AppendJpegStripeHeader(Message * frame_buffer_update,const Stripe & stripe)353 void VncClientConnection::AppendJpegStripeHeader(Message* frame_buffer_update,
354 const Stripe& stripe) {
355 static constexpr std::uint8_t kJpegEncoding = 0x90;
356 cuttlefish::AppendToMessage(frame_buffer_update, stripe.x, stripe.y, stripe.width,
357 stripe.height, kTightEncoding, kJpegEncoding);
358 AppendJpegSize(frame_buffer_update, stripe.jpeg_data.size());
359 }
360
AppendJpegStripe(Message * frame_buffer_update,const Stripe & stripe)361 void VncClientConnection::AppendJpegStripe(Message* frame_buffer_update,
362 const Stripe& stripe) {
363 AppendJpegStripeHeader(frame_buffer_update, stripe);
364 frame_buffer_update->insert(frame_buffer_update->end(),
365 stripe.jpeg_data.begin(), stripe.jpeg_data.end());
366 }
367
MakeJpegFrameBufferUpdate(const StripePtrVec & stripes)368 Message VncClientConnection::MakeJpegFrameBufferUpdate(
369 const StripePtrVec& stripes) {
370 auto fbu =
371 MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size()));
372 for (auto& stripe : stripes) {
373 AppendJpegStripe(&fbu, *stripe);
374 }
375 return fbu;
376 }
377
MakeFrameBufferUpdate(const StripePtrVec & stripes)378 Message VncClientConnection::MakeFrameBufferUpdate(
379 const StripePtrVec& stripes) {
380 return use_jpeg_compression_ ? MakeJpegFrameBufferUpdate(stripes)
381 : MakeRawFrameBufferUpdate(stripes);
382 }
383
FrameBufferUpdateRequestHandler(bool aggressive)384 void VncClientConnection::FrameBufferUpdateRequestHandler(bool aggressive) {
385 BlackBoard::Registerer reg(bb_, this);
386
387 while (!closed()) {
388 auto stripes = bb_->WaitForSenderWork(this);
389 if (closed()) {
390 break;
391 }
392 if (stripes.empty()) {
393 LOG(FATAL) << "Got 0 stripes";
394 }
395 {
396 // lock here so a portrait frame can't be sent after a landscape
397 // DesktopSize update, or vice versa.
398 std::lock_guard<std::mutex> guard(m_);
399 DLOG(INFO) << "Sending update in "
400 << (current_orientation_ == ScreenOrientation::Portrait
401 ? "portrait"
402 : "landscape")
403 << " mode";
404 client_.SendNoSignal(MakeFrameBufferUpdate(stripes));
405 }
406 if (aggressive) {
407 bb_->FrameBufferUpdateRequestReceived(this);
408 }
409 }
410 }
411
SendDesktopSizeUpdate()412 void VncClientConnection::SendDesktopSizeUpdate() {
413 static constexpr int32_t kDesktopSizeEncoding = -223;
414 client_.SendNoSignal(cuttlefish::CreateMessage(
415 std::uint8_t{0}, // message-type,
416 std::uint8_t{}, // padding
417 std::uint16_t{1}, // one pseudo rectangle
418 std::uint16_t{0}, std::uint16_t{0},
419 static_cast<std::uint16_t>(ScreenWidth()),
420 static_cast<std::uint16_t>(ScreenHeight()), kDesktopSizeEncoding));
421 }
422
IsUrgent(const FrameBufferUpdateRequest & update_request) const423 bool VncClientConnection::IsUrgent(
424 const FrameBufferUpdateRequest& update_request) const {
425 return !update_request.incremental ||
426 update_request != previous_update_request_;
427 }
428
HandleFramebufferUpdateRequest()429 void VncClientConnection::HandleFramebufferUpdateRequest() {
430 auto msg = client_.Recv(kFramebufferUpdateRequestLength);
431 if (msg.size() != kFramebufferUpdateRequestLength) {
432 return;
433 }
434 FrameBufferUpdateRequest fbur{msg[1] == 0, uint16_tAt(&msg[1]),
435 uint16_tAt(&msg[3]), uint16_tAt(&msg[5]),
436 uint16_tAt(&msg[7])};
437 if (IsUrgent(fbur)) {
438 bb_->SignalClientNeedsEntireScreen(this);
439 }
440 bb_->FrameBufferUpdateRequestReceived(this);
441 previous_update_request_ = fbur;
442 }
443
HandleSetEncodings()444 void VncClientConnection::HandleSetEncodings() {
445 auto msg = client_.Recv(kSetEncodingsLength);
446 if (msg.size() != kSetEncodingsLength) {
447 return;
448 }
449 auto count = uint16_tAt(&msg[1]);
450 auto encodings = client_.Recv(count * sizeof(int32_t));
451 if (encodings.size() % sizeof(int32_t) != 0) {
452 return;
453 }
454 {
455 std::lock_guard<std::mutex> guard(m_);
456 use_jpeg_compression_ = false;
457 }
458 for (size_t i = 0; i < encodings.size(); i += sizeof(int32_t)) {
459 auto enc = int32_tAt(&encodings[i]);
460 DLOG(INFO) << "client requesting encoding: " << enc;
461 if (enc == kTightEncoding) {
462 // This is a deviation from the spec which says that if a jpeg quality
463 // level is not specified, tight encoding won't use jpeg.
464 std::lock_guard<std::mutex> guard(m_);
465 use_jpeg_compression_ = true;
466 }
467 if (kJpegMinQualityEncoding <= enc && enc <= kJpegMaxQualityEncoding) {
468 DLOG(INFO) << "jpeg compression level: " << enc;
469 bb_->set_jpeg_quality_level(enc);
470 }
471 if (enc == kDesktopSizeEncoding) {
472 supports_desktop_size_encoding_ = true;
473 }
474 }
475 }
476
HandleSetPixelFormat()477 void VncClientConnection::HandleSetPixelFormat() {
478 std::lock_guard<std::mutex> guard(m_);
479 auto msg = client_.Recv(kSetPixelFormatLength);
480 if (msg.size() != kSetPixelFormatLength) {
481 return;
482 }
483 pixel_format_.bits_per_pixel = msg[3];
484 pixel_format_.depth = msg[4];
485 pixel_format_.big_endian = msg[5];
486 pixel_format_.true_color = msg[7];
487 pixel_format_.red_max = uint16_tAt(&msg[8]);
488 pixel_format_.green_max = uint16_tAt(&msg[10]);
489 pixel_format_.blue_max = uint16_tAt(&msg[12]);
490 pixel_format_.red_shift = msg[13];
491 pixel_format_.green_shift = msg[14];
492 pixel_format_.blue_shift = msg[15];
493 }
494
HandlePointerEvent()495 void VncClientConnection::HandlePointerEvent() {
496 auto msg = client_.Recv(kPointerEventLength);
497 if (msg.size() != kPointerEventLength) {
498 return;
499 }
500 std::uint8_t button_mask = msg[0];
501 auto x_pos = uint16_tAt(&msg[1]);
502 auto y_pos = uint16_tAt(&msg[3]);
503 {
504 std::lock_guard<std::mutex> guard(m_);
505 if (current_orientation_ == ScreenOrientation::Landscape) {
506 std::tie(x_pos, y_pos) =
507 std::make_pair(ScreenConnector::ScreenWidth() - y_pos, x_pos);
508 }
509 }
510 virtual_inputs_->HandlePointerEvent(button_mask, x_pos, y_pos);
511 }
512
UpdateAccelerometer(float,float,float)513 void VncClientConnection::UpdateAccelerometer(float /*x*/, float /*y*/,
514 float /*z*/) {
515 // TODO(jemoreira): Implement when vsoc sensor hal is updated
516 }
517
CoordinatesForOrientation(ScreenOrientation orientation) const518 VncClientConnection::Coordinates VncClientConnection::CoordinatesForOrientation(
519 ScreenOrientation orientation) const {
520 // Compute the acceleration vector that we need to send to mimic
521 // this change.
522 constexpr float g = 9.81;
523 constexpr float angle = 20.0;
524 const float cos_angle = std::cos(angle / M_PI);
525 const float sin_angle = std::sin(angle / M_PI);
526 const float z = g * sin_angle;
527 switch (orientation) {
528 case ScreenOrientation::Portrait:
529 return {0, g * cos_angle, z};
530 case ScreenOrientation::Landscape:
531 return {g * cos_angle, 0, z};
532 }
533 }
534
ScreenWidth() const535 int VncClientConnection::ScreenWidth() const {
536 return current_orientation_ == ScreenOrientation::Portrait
537 ? ScreenConnector::ScreenWidth()
538 : ScreenConnector::ScreenHeight();
539 }
540
ScreenHeight() const541 int VncClientConnection::ScreenHeight() const {
542 return current_orientation_ == ScreenOrientation::Portrait
543 ? ScreenConnector::ScreenHeight()
544 : ScreenConnector::ScreenWidth();
545 }
546
SetScreenOrientation(ScreenOrientation orientation)547 void VncClientConnection::SetScreenOrientation(ScreenOrientation orientation) {
548 std::lock_guard<std::mutex> guard(m_);
549 auto coords = CoordinatesForOrientation(orientation);
550 UpdateAccelerometer(coords.x, coords.y, coords.z);
551 if (supports_desktop_size_encoding_) {
552 auto previous_orientation = current_orientation_;
553 current_orientation_ = orientation;
554 if (current_orientation_ != previous_orientation &&
555 supports_desktop_size_encoding_) {
556 SendDesktopSizeUpdate();
557 bb_->SetOrientation(this, current_orientation_);
558 // TODO not sure if I should be sending a frame update along with this,
559 // or just letting the next FBUR handle it. This seems to me like it's
560 // sending one more frame buffer update than was requested, which is
561 // maybe a violation of the spec?
562 }
563 }
564 }
565
RotateIfIsRotationCommand(std::uint32_t key)566 bool VncClientConnection::RotateIfIsRotationCommand(std::uint32_t key) {
567 // Due to different configurations on different platforms we're supporting
568 // a set of options for rotating the screen. These are similar to what
569 // the emulator supports and has supported.
570 // ctrl+left and ctrl+right work on windows and linux
571 // command+left and command+right work on Mac
572 // ctrl+fn+F11 and ctrl+fn+F12 work when chromoting to ubuntu from a Mac
573 if (!control_key_down_ && !meta_key_down_) {
574 return false;
575 }
576 switch (key) {
577 case cuttlefish::xk::Right:
578 case cuttlefish::xk::F12:
579 DLOG(INFO) << "switching to portrait";
580 SetScreenOrientation(ScreenOrientation::Portrait);
581 break;
582 case cuttlefish::xk::Left:
583 case cuttlefish::xk::F11:
584 DLOG(INFO) << "switching to landscape";
585 SetScreenOrientation(ScreenOrientation::Landscape);
586 break;
587 default:
588 return false;
589 }
590 return true;
591 }
592
HandleKeyEvent()593 void VncClientConnection::HandleKeyEvent() {
594 auto msg = client_.Recv(kKeyEventLength);
595 if (msg.size() != kKeyEventLength) {
596 return;
597 }
598
599 auto key = uint32_tAt(&msg[3]);
600 bool key_down = msg[0];
601 switch (key) {
602 case cuttlefish::xk::ControlLeft:
603 case cuttlefish::xk::ControlRight:
604 control_key_down_ = key_down;
605 break;
606 case cuttlefish::xk::MetaLeft:
607 case cuttlefish::xk::MetaRight:
608 meta_key_down_ = key_down;
609 break;
610 case cuttlefish::xk::F5:
611 key = cuttlefish::xk::Menu;
612 break;
613 case cuttlefish::xk::F7:
614 virtual_inputs_->PressPowerButton(key_down);
615 return;
616 default:
617 break;
618 }
619
620 if (RotateIfIsRotationCommand(key)) {
621 return;
622 }
623
624 virtual_inputs_->GenerateKeyPressEvent(key, key_down);
625 }
626
HandleClientCutText()627 void VncClientConnection::HandleClientCutText() {
628 auto msg = client_.Recv(kClientCutTextLength);
629 if (msg.size() != kClientCutTextLength) {
630 return;
631 }
632 auto len = uint32_tAt(&msg[3]);
633 client_.Recv(len);
634 }
635
NormalSession()636 void VncClientConnection::NormalSession() {
637 static constexpr std::uint8_t kSetPixelFormatMessage{0};
638 static constexpr std::uint8_t kSetEncodingsMessage{2};
639 static constexpr std::uint8_t kFramebufferUpdateRequestMessage{3};
640 static constexpr std::uint8_t kKeyEventMessage{4};
641 static constexpr std::uint8_t kPointerEventMessage{5};
642 static constexpr std::uint8_t kClientCutTextMessage{6};
643 while (true) {
644 if (client_.closed()) {
645 return;
646 }
647 auto msg = client_.Recv(1);
648 if (client_.closed()) {
649 return;
650 }
651 auto msg_type = msg.front();
652 DLOG(INFO) << "Received message type " << msg_type;
653
654 switch (msg_type) {
655 case kSetPixelFormatMessage:
656 HandleSetPixelFormat();
657 break;
658
659 case kSetEncodingsMessage:
660 HandleSetEncodings();
661 break;
662
663 case kFramebufferUpdateRequestMessage:
664 HandleFramebufferUpdateRequest();
665 break;
666
667 case kKeyEventMessage:
668 HandleKeyEvent();
669 break;
670
671 case kPointerEventMessage:
672 HandlePointerEvent();
673 break;
674
675 case kClientCutTextMessage:
676 HandleClientCutText();
677 break;
678
679 default:
680 LOG(WARNING) << "message type not handled: "
681 << static_cast<int>(msg_type);
682 break;
683 }
684 }
685 }
686