blob: a8faf47b62ce7fba26ce4fabc43dabd3289e116a [file] [log] [blame]
Jorge E. Moreiraccd57452017-09-29 15:19:07 -07001/*
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"
Jorge E. Moreira24307012017-09-22 14:35:21 -070018
Jorge E. Moreira24307012017-09-22 14:35:21 -070019#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
Greg Hartman70ac1d22017-10-18 12:30:36 -070033#include <gflags/gflags.h>
Greg Hartmanca7fb192017-09-28 16:14:12 -070034#include <glog/logging.h>
Ryan Haining0594a5c2018-01-25 13:03:06 -080035#include "common/libs/tcp_socket/tcp_socket.h"
Jorge E. Moreiraccd57452017-09-29 15:19:07 -070036#include "host/frontend/vnc_server/keysyms.h"
37#include "host/frontend/vnc_server/mocks.h"
Jorge E. Moreiraccd57452017-09-29 15:19:07 -070038#include "host/frontend/vnc_server/vnc_utils.h"
Ryan Haining8e9c7122018-07-27 11:55:46 -070039#include "host/libs/config/cuttlefish_config.h"
Jorge E. Moreira24307012017-09-22 14:35:21 -070040
Ryan Haining0594a5c2018-01-25 13:03:06 -080041using cvd::Message;
Greg Hartman153b1062017-11-11 12:09:21 -080042using cvd::vnc::Stripe;
43using cvd::vnc::StripePtrVec;
44using cvd::vnc::VncClientConnection;
Cody Schuffelen134ff032019-11-22 00:25:32 -080045using vsoc::screen::ScreenRegionView;
Jorge E. Moreira24307012017-09-22 14:35:21 -070046
Greg Hartman70ac1d22017-10-18 12:30:36 -070047DEFINE_bool(debug_client, false, "Turn on detailed logging for the client");
48
Greg Hartmand0f20d42017-12-19 23:47:26 -080049#define DLOG(LEVEL) \
Greg Hartman70ac1d22017-10-18 12:30:36 -070050 if (FLAGS_debug_client) LOG(LEVEL)
51
Jorge E. Moreira24307012017-09-22 14:35:21 -070052namespace {
53class BigEndianChecker {
54 public:
55 BigEndianChecker() {
56 uint32_t u = 1;
57 is_big_endian_ = *reinterpret_cast<const char*>(&u) == 0;
58 }
Greg Hartman70753782017-09-28 16:12:43 -070059 bool operator()() const { return is_big_endian_; }
Jorge E. Moreira24307012017-09-22 14:35:21 -070060
61 private:
62 bool is_big_endian_{};
63};
64
65const BigEndianChecker ImBigEndian;
66
67constexpr int32_t kDesktopSizeEncoding = -223;
68constexpr int32_t kTightEncoding = 7;
69
70// These are the lengths not counting the first byte. The first byte
71// indicates the message type.
72constexpr size_t kSetPixelFormatLength = 19;
73constexpr size_t kFramebufferUpdateRequestLength = 9;
74constexpr size_t kSetEncodingsLength = 3; // more bytes follow
75constexpr size_t kKeyEventLength = 7;
76constexpr size_t kPointerEventLength = 5;
77constexpr size_t kClientCutTextLength = 7; // more bytes follow
78
Jorge E. Moreira24307012017-09-22 14:35:21 -070079std::string HostName() {
Ryan Haining8e9c7122018-07-27 11:55:46 -070080 auto config = vsoc::CuttlefishConfig::Get();
81 return !config || config->device_title().empty() ? std::string{"localhost"}
82 : config->device_title();
Jorge E. Moreira24307012017-09-22 14:35:21 -070083}
84
85std::uint16_t uint16_tAt(const void* p) {
86 std::uint16_t u{};
87 std::memcpy(&u, p, sizeof u);
88 return ntohs(u);
89}
90
91std::uint32_t uint32_tAt(const void* p) {
92 std::uint32_t u{};
93 std::memcpy(&u, p, sizeof u);
94 return ntohl(u);
95}
96
97std::int32_t int32_tAt(const void* p) {
98 std::uint32_t u{};
99 std::memcpy(&u, p, sizeof u);
100 u = ntohl(u);
101 std::int32_t s{};
102 std::memcpy(&s, &u, sizeof s);
103 return s;
104}
105
106std::uint32_t RedVal(std::uint32_t pixel) {
Jorge E. Moreira57919e82018-02-13 11:50:34 -0800107 return (pixel >> ScreenRegionView::kRedShift) &
108 ((0x1 << ScreenRegionView::kRedBits) - 1);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700109}
110
111std::uint32_t BlueVal(std::uint32_t pixel) {
Jorge E. Moreira57919e82018-02-13 11:50:34 -0800112 return (pixel >> ScreenRegionView::kBlueShift) &
113 ((0x1 << ScreenRegionView::kBlueBits) - 1);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700114}
115
116std::uint32_t GreenVal(std::uint32_t pixel) {
Jorge E. Moreira57919e82018-02-13 11:50:34 -0800117 return (pixel >> ScreenRegionView::kGreenShift) &
118 ((0x1 << ScreenRegionView::kGreenBits) - 1);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700119}
Greg Hartman70753782017-09-28 16:12:43 -0700120} // namespace
Greg Hartman153b1062017-11-11 12:09:21 -0800121namespace cvd {
Jorge E. Moreira24307012017-09-22 14:35:21 -0700122namespace vnc {
123bool operator==(const VncClientConnection::FrameBufferUpdateRequest& lhs,
124 const VncClientConnection::FrameBufferUpdateRequest& rhs) {
125 return lhs.x_pos == rhs.x_pos && lhs.y_pos == rhs.y_pos &&
126 lhs.width == rhs.width && lhs.height == rhs.height;
127}
128
129bool operator!=(const VncClientConnection::FrameBufferUpdateRequest& lhs,
130 const VncClientConnection::FrameBufferUpdateRequest& rhs) {
131 return !(lhs == rhs);
132}
133} // namespace vnc
Greg Hartman153b1062017-11-11 12:09:21 -0800134} // namespace cvd
Jorge E. Moreira24307012017-09-22 14:35:21 -0700135
Jorge E. Moreirac87c2c72019-03-06 16:12:23 -0800136VncClientConnection::VncClientConnection(
137 ClientSocket client, std::shared_ptr<VirtualInputs> virtual_inputs,
138 BlackBoard* bb, bool aggressive)
Jorge E. Moreira4ca884e2018-01-25 16:57:48 -0800139 : client_{std::move(client)}, virtual_inputs_{virtual_inputs}, bb_{bb} {
Jorge E. Moreira24307012017-09-22 14:35:21 -0700140 frame_buffer_request_handler_tid_ = std::thread(
141 &VncClientConnection::FrameBufferUpdateRequestHandler, this, aggressive);
142}
143
144VncClientConnection::~VncClientConnection() {
145 {
146 std::lock_guard<std::mutex> guard(m_);
147 closed_ = true;
148 }
149 bb_->StopWaiting(this);
150 frame_buffer_request_handler_tid_.join();
151}
152
153void VncClientConnection::StartSession() {
Greg Hartmanca7fb192017-09-28 16:14:12 -0700154 LOG(INFO) << "Starting session";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700155 SetupProtocol();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700156 LOG(INFO) << "Protocol set up";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700157 if (client_.closed()) {
158 return;
159 }
160 SetupSecurityType();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700161 LOG(INFO) << "Security type set";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700162 if (client_.closed()) {
163 return;
164 }
165 GetClientInit();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700166 LOG(INFO) << "Gotten client init";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700167 if (client_.closed()) {
168 return;
169 }
170 SendServerInit();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700171 LOG(INFO) << "Sent server init";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700172 if (client_.closed()) {
173 return;
174 }
175 NormalSession();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700176 LOG(INFO) << "vnc session terminated";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700177}
178
179bool VncClientConnection::closed() {
180 std::lock_guard<std::mutex> guard(m_);
181 return closed_;
182}
183
184void VncClientConnection::SetupProtocol() {
185 static constexpr char kRFBVersion[] = "RFB 003.008\n";
186 static constexpr auto kVersionLen = (sizeof kRFBVersion) - 1;
Ryan Haining561e0012018-11-26 11:08:33 -0800187 client_.SendNoSignal(reinterpret_cast<const std::uint8_t*>(kRFBVersion),
188 kVersionLen);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700189 auto client_protocol = client_.Recv(kVersionLen);
190 if (std::memcmp(&client_protocol[0], kRFBVersion,
191 std::min(kVersionLen, client_protocol.size())) != 0) {
192 client_protocol.push_back('\0');
Greg Hartmanca7fb192017-09-28 16:14:12 -0700193 LOG(ERROR) << "vnc client wants a different protocol: "
194 << reinterpret_cast<const char*>(&client_protocol[0]);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700195 }
196}
197
198void VncClientConnection::SetupSecurityType() {
199 static constexpr std::uint8_t kNoneSecurity = 0x1;
200 // The first '0x1' indicates the number of items that follow
201 static constexpr std::uint8_t kOnlyNoneSecurity[] = {0x01, kNoneSecurity};
Ryan Haining561e0012018-11-26 11:08:33 -0800202 client_.SendNoSignal(kOnlyNoneSecurity);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700203 auto client_security = client_.Recv(1);
204 if (client_.closed()) {
205 return;
206 }
207 if (client_security.front() != kNoneSecurity) {
Greg Hartmanca7fb192017-09-28 16:14:12 -0700208 LOG(ERROR) << "vnc client is asking for security type "
209 << static_cast<int>(client_security.front());
Jorge E. Moreira24307012017-09-22 14:35:21 -0700210 }
211 static constexpr std::uint8_t kZero[4] = {};
Ryan Haining561e0012018-11-26 11:08:33 -0800212 client_.SendNoSignal(kZero);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700213}
214
215void VncClientConnection::GetClientInit() {
216 auto client_shared = client_.Recv(1);
217}
218
219void VncClientConnection::SendServerInit() {
220 const std::string server_name = HostName();
221 std::lock_guard<std::mutex> guard(m_);
Ryan Haining58ab4cd2018-11-26 10:42:53 -0800222 auto server_init = cvd::CreateMessage(
Jorge E. Moreira24307012017-09-22 14:35:21 -0700223 static_cast<std::uint16_t>(ScreenWidth()),
224 static_cast<std::uint16_t>(ScreenHeight()), pixel_format_.bits_per_pixel,
225 pixel_format_.depth, pixel_format_.big_endian, pixel_format_.true_color,
226 pixel_format_.red_max, pixel_format_.green_max, pixel_format_.blue_max,
227 pixel_format_.red_shift, pixel_format_.green_shift,
228 pixel_format_.blue_shift, std::uint16_t{}, // padding
229 std::uint8_t{}, // padding
230 static_cast<std::uint32_t>(server_name.size()), server_name);
Ryan Haining561e0012018-11-26 11:08:33 -0800231 client_.SendNoSignal(server_init);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700232}
233
234Message VncClientConnection::MakeFrameBufferUpdateHeader(
235 std::uint16_t num_stripes) {
Ryan Haining58ab4cd2018-11-26 10:42:53 -0800236 return cvd::CreateMessage(std::uint8_t{0}, // message-type
237 std::uint8_t{}, // padding
238 std::uint16_t{num_stripes});
Jorge E. Moreira24307012017-09-22 14:35:21 -0700239}
240
241void VncClientConnection::AppendRawStripeHeader(Message* frame_buffer_update,
242 const Stripe& stripe) {
243 static constexpr int32_t kRawEncoding = 0;
Ryan Haining58ab4cd2018-11-26 10:42:53 -0800244 cvd::AppendToMessage(frame_buffer_update, std::uint16_t{stripe.x},
245 std::uint16_t{stripe.y}, std::uint16_t{stripe.width},
246 std::uint16_t{stripe.height}, kRawEncoding);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700247}
248
249void VncClientConnection::AppendJpegSize(Message* frame_buffer_update,
250 size_t jpeg_size) {
251 constexpr size_t kJpegSizeOneByteMax = 127;
252 constexpr size_t kJpegSizeTwoByteMax = 16383;
253 constexpr size_t kJpegSizeThreeByteMax = 4194303;
254
255 if (jpeg_size <= kJpegSizeOneByteMax) {
Ryan Haining58ab4cd2018-11-26 10:42:53 -0800256 cvd::AppendToMessage(frame_buffer_update,
257 static_cast<std::uint8_t>(jpeg_size));
Jorge E. Moreira24307012017-09-22 14:35:21 -0700258 } else if (jpeg_size <= kJpegSizeTwoByteMax) {
259 auto sz = static_cast<std::uint32_t>(jpeg_size);
Ryan Haining58ab4cd2018-11-26 10:42:53 -0800260 cvd::AppendToMessage(frame_buffer_update,
261 static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
262 static_cast<std::uint8_t>((sz >> 7) & 0xFF));
Jorge E. Moreira24307012017-09-22 14:35:21 -0700263 } else {
264 if (jpeg_size > kJpegSizeThreeByteMax) {
Greg Hartmanca7fb192017-09-28 16:14:12 -0700265 LOG(FATAL) << "jpeg size is too big: " << jpeg_size << " must be under "
266 << kJpegSizeThreeByteMax;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700267 }
268 const auto sz = static_cast<std::uint32_t>(jpeg_size);
Ryan Haining58ab4cd2018-11-26 10:42:53 -0800269 cvd::AppendToMessage(frame_buffer_update,
270 static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
271 static_cast<std::uint8_t>(((sz >> 7) & 0x7F) | 0x80),
272 static_cast<std::uint8_t>((sz >> 14) & 0xFF));
Jorge E. Moreira24307012017-09-22 14:35:21 -0700273 }
274}
275
276void VncClientConnection::AppendRawStripe(Message* frame_buffer_update,
277 const Stripe& stripe) const {
Jorge E. Moreira57919e82018-02-13 11:50:34 -0800278 using Pixel = ScreenRegionView::Pixel;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700279 auto& fbu = *frame_buffer_update;
280 AppendRawStripeHeader(&fbu, stripe);
281 auto init_size = fbu.size();
282 fbu.insert(fbu.end(), stripe.raw_data.begin(), stripe.raw_data.end());
283 for (size_t i = init_size; i < fbu.size(); i += sizeof(Pixel)) {
Ryan Haininga4e8cd92018-09-14 14:54:10 -0700284 CHECK_LE(i + sizeof(Pixel), fbu.size());
Jorge E. Moreira24307012017-09-22 14:35:21 -0700285 Pixel raw_pixel{};
286 std::memcpy(&raw_pixel, &fbu[i], sizeof raw_pixel);
287 auto red = RedVal(raw_pixel);
288 auto green = GreenVal(raw_pixel);
289 auto blue = BlueVal(raw_pixel);
290 Pixel pixel = Pixel{red} << pixel_format_.red_shift |
291 Pixel{blue} << pixel_format_.blue_shift |
292 Pixel{green} << pixel_format_.green_shift;
293
294 if (bool(pixel_format_.big_endian) != ImBigEndian()) {
295 // flip them bits (refactor into function)
296 auto p = reinterpret_cast<char*>(&pixel);
297 std::swap(p[0], p[3]);
298 std::swap(p[1], p[2]);
299 }
Jorge E. Moreira24307012017-09-22 14:35:21 -0700300 std::memcpy(&fbu[i], &pixel, sizeof pixel);
301 }
302}
303
304Message VncClientConnection::MakeRawFrameBufferUpdate(
305 const StripePtrVec& stripes) const {
306 auto fbu =
307 MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size()));
308 for (auto& stripe : stripes) {
309 AppendRawStripe(&fbu, *stripe);
310 }
311 return fbu;
312}
313
314void VncClientConnection::AppendJpegStripeHeader(Message* frame_buffer_update,
315 const Stripe& stripe) {
316 static constexpr std::uint8_t kJpegEncoding = 0x90;
Ryan Haining58ab4cd2018-11-26 10:42:53 -0800317 cvd::AppendToMessage(frame_buffer_update, stripe.x, stripe.y, stripe.width,
318 stripe.height, kTightEncoding, kJpegEncoding);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700319 AppendJpegSize(frame_buffer_update, stripe.jpeg_data.size());
320}
321
322void VncClientConnection::AppendJpegStripe(Message* frame_buffer_update,
323 const Stripe& stripe) {
324 AppendJpegStripeHeader(frame_buffer_update, stripe);
325 frame_buffer_update->insert(frame_buffer_update->end(),
326 stripe.jpeg_data.begin(), stripe.jpeg_data.end());
327}
328
329Message VncClientConnection::MakeJpegFrameBufferUpdate(
330 const StripePtrVec& stripes) {
331 auto fbu =
332 MakeFrameBufferUpdateHeader(static_cast<std::uint16_t>(stripes.size()));
333 for (auto& stripe : stripes) {
334 AppendJpegStripe(&fbu, *stripe);
335 }
336 return fbu;
337}
338
339Message VncClientConnection::MakeFrameBufferUpdate(
340 const StripePtrVec& stripes) {
341 return use_jpeg_compression_ ? MakeJpegFrameBufferUpdate(stripes)
342 : MakeRawFrameBufferUpdate(stripes);
343}
344
345void VncClientConnection::FrameBufferUpdateRequestHandler(bool aggressive) {
346 BlackBoard::Registerer reg(bb_, this);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700347
348 while (!closed()) {
349 auto stripes = bb_->WaitForSenderWork(this);
350 if (closed()) {
351 break;
352 }
Greg Hartmanca7fb192017-09-28 16:14:12 -0700353 if (stripes.empty()) {
354 LOG(FATAL) << "Got 0 stripes";
355 }
Jorge E. Moreira24307012017-09-22 14:35:21 -0700356 {
357 // lock here so a portrait frame can't be sent after a landscape
358 // DesktopSize update, or vice versa.
359 std::lock_guard<std::mutex> guard(m_);
Greg Hartmanca7fb192017-09-28 16:14:12 -0700360 DLOG(INFO) << "Sending update in "
361 << (current_orientation_ == ScreenOrientation::Portrait
362 ? "portrait"
363 : "landscape")
364 << " mode";
Ryan Haining561e0012018-11-26 11:08:33 -0800365 client_.SendNoSignal(MakeFrameBufferUpdate(stripes));
Jorge E. Moreira24307012017-09-22 14:35:21 -0700366 }
367 if (aggressive) {
368 bb_->FrameBufferUpdateRequestReceived(this);
369 }
370 }
371}
372
373void VncClientConnection::SendDesktopSizeUpdate() {
374 static constexpr int32_t kDesktopSizeEncoding = -223;
Ryan Haining561e0012018-11-26 11:08:33 -0800375 client_.SendNoSignal(cvd::CreateMessage(
376 std::uint8_t{0}, // message-type,
377 std::uint8_t{}, // padding
378 std::uint16_t{1}, // one pseudo rectangle
379 std::uint16_t{0}, std::uint16_t{0},
380 static_cast<std::uint16_t>(ScreenWidth()),
381 static_cast<std::uint16_t>(ScreenHeight()), kDesktopSizeEncoding));
Jorge E. Moreira24307012017-09-22 14:35:21 -0700382}
383
384bool VncClientConnection::IsUrgent(
385 const FrameBufferUpdateRequest& update_request) const {
386 return !update_request.incremental ||
387 update_request != previous_update_request_;
388}
389
390void VncClientConnection::HandleFramebufferUpdateRequest() {
391 auto msg = client_.Recv(kFramebufferUpdateRequestLength);
392 if (msg.size() != kFramebufferUpdateRequestLength) {
393 return;
394 }
395 FrameBufferUpdateRequest fbur{msg[1] == 0, uint16_tAt(&msg[1]),
396 uint16_tAt(&msg[3]), uint16_tAt(&msg[5]),
397 uint16_tAt(&msg[7])};
398 if (IsUrgent(fbur)) {
399 bb_->SignalClientNeedsEntireScreen(this);
400 }
401 bb_->FrameBufferUpdateRequestReceived(this);
402 previous_update_request_ = fbur;
403}
404
405void VncClientConnection::HandleSetEncodings() {
406 auto msg = client_.Recv(kSetEncodingsLength);
407 if (msg.size() != kSetEncodingsLength) {
408 return;
409 }
410 auto count = uint16_tAt(&msg[1]);
411 auto encodings = client_.Recv(count * sizeof(int32_t));
412 if (encodings.size() % sizeof(int32_t) != 0) {
413 return;
414 }
415 {
416 std::lock_guard<std::mutex> guard(m_);
417 use_jpeg_compression_ = false;
418 }
419 for (size_t i = 0; i < encodings.size(); i += sizeof(int32_t)) {
420 auto enc = int32_tAt(&encodings[i]);
Greg Hartmanca7fb192017-09-28 16:14:12 -0700421 DLOG(INFO) << "client requesting encoding: " << enc;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700422 if (enc == kTightEncoding) {
423 // This is a deviation from the spec which says that if a jpeg quality
424 // level is not specified, tight encoding won't use jpeg.
425 std::lock_guard<std::mutex> guard(m_);
426 use_jpeg_compression_ = true;
427 }
428 if (kJpegMinQualityEncoding <= enc && enc <= kJpegMaxQualityEncoding) {
Greg Hartmanca7fb192017-09-28 16:14:12 -0700429 DLOG(INFO) << "jpeg compression level: " << enc;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700430 bb_->set_jpeg_quality_level(enc);
431 }
432 if (enc == kDesktopSizeEncoding) {
433 supports_desktop_size_encoding_ = true;
434 }
435 }
436}
437
438void VncClientConnection::HandleSetPixelFormat() {
439 std::lock_guard<std::mutex> guard(m_);
440 auto msg = client_.Recv(kSetPixelFormatLength);
441 if (msg.size() != kSetPixelFormatLength) {
442 return;
443 }
444 pixel_format_.bits_per_pixel = msg[3];
445 pixel_format_.depth = msg[4];
446 pixel_format_.big_endian = msg[5];
447 pixel_format_.true_color = msg[7];
448 pixel_format_.red_max = uint16_tAt(&msg[8]);
449 pixel_format_.green_max = uint16_tAt(&msg[10]);
450 pixel_format_.blue_max = uint16_tAt(&msg[12]);
451 pixel_format_.red_shift = msg[13];
452 pixel_format_.green_shift = msg[14];
453 pixel_format_.blue_shift = msg[15];
454}
455
456void VncClientConnection::HandlePointerEvent() {
457 auto msg = client_.Recv(kPointerEventLength);
458 if (msg.size() != kPointerEventLength) {
459 return;
460 }
461 std::uint8_t button_mask = msg[0];
462 auto x_pos = uint16_tAt(&msg[1]);
463 auto y_pos = uint16_tAt(&msg[3]);
464 {
465 std::lock_guard<std::mutex> guard(m_);
466 if (current_orientation_ == ScreenOrientation::Landscape) {
467 std::tie(x_pos, y_pos) =
468 std::make_pair(ActualScreenWidth() - y_pos, x_pos);
469 }
470 }
471 virtual_inputs_->HandlePointerEvent(button_mask, x_pos, y_pos);
472}
473
Greg Hartman7d5e0bf2017-12-19 23:48:34 -0800474void VncClientConnection::UpdateAccelerometer(float /*x*/, float /*y*/,
475 float /*z*/) {
Jorge E. Moreira4ca884e2018-01-25 16:57:48 -0800476 // TODO(jemoreira): Implement when vsoc sensor hal is updated
Jorge E. Moreira24307012017-09-22 14:35:21 -0700477}
478
479VncClientConnection::Coordinates VncClientConnection::CoordinatesForOrientation(
480 ScreenOrientation orientation) const {
481 // Compute the acceleration vector that we need to send to mimic
482 // this change.
483 constexpr float g = 9.81;
484 constexpr float angle = 20.0;
485 const float cos_angle = std::cos(angle / M_PI);
486 const float sin_angle = std::sin(angle / M_PI);
487 const float z = g * sin_angle;
488 switch (orientation) {
489 case ScreenOrientation::Portrait:
490 return {0, g * cos_angle, z};
491 case ScreenOrientation::Landscape:
492 return {g * cos_angle, 0, z};
493 }
494}
495
496int VncClientConnection::ScreenWidth() const {
497 return current_orientation_ == ScreenOrientation::Portrait
498 ? ActualScreenWidth()
499 : ActualScreenHeight();
500}
501
502int VncClientConnection::ScreenHeight() const {
503 return current_orientation_ == ScreenOrientation::Portrait
504 ? ActualScreenHeight()
505 : ActualScreenWidth();
506}
507
508void VncClientConnection::SetScreenOrientation(ScreenOrientation orientation) {
509 std::lock_guard<std::mutex> guard(m_);
510 auto coords = CoordinatesForOrientation(orientation);
511 UpdateAccelerometer(coords.x, coords.y, coords.z);
512 if (supports_desktop_size_encoding_) {
513 auto previous_orientation = current_orientation_;
514 current_orientation_ = orientation;
515 if (current_orientation_ != previous_orientation &&
516 supports_desktop_size_encoding_) {
517 SendDesktopSizeUpdate();
518 bb_->SetOrientation(this, current_orientation_);
519 // TODO not sure if I should be sending a frame update along with this,
520 // or just letting the next FBUR handle it. This seems to me like it's
521 // sending one more frame buffer update than was requested, which is
522 // maybe a violation of the spec?
523 }
524 }
525}
526
527bool VncClientConnection::RotateIfIsRotationCommand(std::uint32_t key) {
528 // Due to different configurations on different platforms we're supporting
529 // a set of options for rotating the screen. These are similar to what
530 // the emulator supports and has supported.
531 // ctrl+left and ctrl+right work on windows and linux
532 // command+left and command+right work on Mac
533 // ctrl+fn+F11 and ctrl+fn+F12 work when chromoting to ubuntu from a Mac
534 if (!control_key_down_ && !meta_key_down_) {
535 return false;
536 }
537 switch (key) {
Greg Hartman153b1062017-11-11 12:09:21 -0800538 case cvd::xk::Right:
539 case cvd::xk::F12:
Greg Hartmanca7fb192017-09-28 16:14:12 -0700540 DLOG(INFO) << "switching to portrait";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700541 SetScreenOrientation(ScreenOrientation::Portrait);
542 break;
Greg Hartman153b1062017-11-11 12:09:21 -0800543 case cvd::xk::Left:
544 case cvd::xk::F11:
Greg Hartmanca7fb192017-09-28 16:14:12 -0700545 DLOG(INFO) << "switching to landscape";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700546 SetScreenOrientation(ScreenOrientation::Landscape);
547 break;
548 default:
549 return false;
550 }
551 return true;
552}
553
554void VncClientConnection::HandleKeyEvent() {
555 auto msg = client_.Recv(kKeyEventLength);
556 if (msg.size() != kKeyEventLength) {
557 return;
558 }
559
560 auto key = uint32_tAt(&msg[3]);
561 bool key_down = msg[0];
562 switch (key) {
Greg Hartman153b1062017-11-11 12:09:21 -0800563 case cvd::xk::ControlLeft:
564 case cvd::xk::ControlRight:
Jorge E. Moreira24307012017-09-22 14:35:21 -0700565 control_key_down_ = key_down;
566 break;
Greg Hartman153b1062017-11-11 12:09:21 -0800567 case cvd::xk::MetaLeft:
568 case cvd::xk::MetaRight:
Jorge E. Moreira24307012017-09-22 14:35:21 -0700569 meta_key_down_ = key_down;
570 break;
Greg Hartman153b1062017-11-11 12:09:21 -0800571 case cvd::xk::F5:
572 key = cvd::xk::Menu;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700573 break;
Greg Hartman153b1062017-11-11 12:09:21 -0800574 case cvd::xk::F7:
Jorge E. Moreira24307012017-09-22 14:35:21 -0700575 virtual_inputs_->PressPowerButton(key_down);
576 return;
577 default:
578 break;
579 }
580
581 if (RotateIfIsRotationCommand(key)) {
582 return;
583 }
584
585 virtual_inputs_->GenerateKeyPressEvent(key, key_down);
586}
587
588void VncClientConnection::HandleClientCutText() {
589 auto msg = client_.Recv(kClientCutTextLength);
590 if (msg.size() != kClientCutTextLength) {
591 return;
592 }
593 auto len = uint32_tAt(&msg[3]);
594 client_.Recv(len);
595}
596
597void VncClientConnection::NormalSession() {
598 static constexpr std::uint8_t kSetPixelFormatMessage{0};
599 static constexpr std::uint8_t kSetEncodingsMessage{2};
600 static constexpr std::uint8_t kFramebufferUpdateRequestMessage{3};
601 static constexpr std::uint8_t kKeyEventMessage{4};
602 static constexpr std::uint8_t kPointerEventMessage{5};
603 static constexpr std::uint8_t kClientCutTextMessage{6};
604 while (true) {
605 if (client_.closed()) {
606 return;
607 }
608 auto msg = client_.Recv(1);
609 if (client_.closed()) {
610 return;
611 }
612 auto msg_type = msg.front();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700613 DLOG(INFO) << "Received message type " << msg_type;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700614
615 switch (msg_type) {
616 case kSetPixelFormatMessage:
617 HandleSetPixelFormat();
618 break;
619
620 case kSetEncodingsMessage:
621 HandleSetEncodings();
622 break;
623
624 case kFramebufferUpdateRequestMessage:
625 HandleFramebufferUpdateRequest();
626 break;
627
628 case kKeyEventMessage:
629 HandleKeyEvent();
630 break;
631
632 case kPointerEventMessage:
633 HandlePointerEvent();
634 break;
635
636 case kClientCutTextMessage:
637 HandleClientCutText();
638 break;
639
640 default:
Greg Hartmanca7fb192017-09-28 16:14:12 -0700641 LOG(WARNING) << "message type not handled: "
642 << static_cast<int>(msg_type);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700643 break;
644 }
645 }
646}