blob: acbdfaae06cf6fdc95972608e8967be99f5b41f9 [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"
Jorge E. Moreira24307012017-09-22 14:35:21 -070039
Ryan Haining0594a5c2018-01-25 13:03:06 -080040using cvd::Message;
Greg Hartman153b1062017-11-11 12:09:21 -080041using cvd::vnc::Stripe;
42using cvd::vnc::StripePtrVec;
43using cvd::vnc::VncClientConnection;
Jorge E. Moreira4ca884e2018-01-25 16:57:48 -080044using vsoc::framebuffer::FBBroadcastRegionView;
Jorge E. Moreira24307012017-09-22 14:35:21 -070045
Greg Hartman70ac1d22017-10-18 12:30:36 -070046DEFINE_bool(debug_client, false, "Turn on detailed logging for the client");
47
Greg Hartmand0f20d42017-12-19 23:47:26 -080048#define DLOG(LEVEL) \
Greg Hartman70ac1d22017-10-18 12:30:36 -070049 if (FLAGS_debug_client) LOG(LEVEL)
50
Jorge E. Moreira24307012017-09-22 14:35:21 -070051namespace {
52class BigEndianChecker {
53 public:
54 BigEndianChecker() {
55 uint32_t u = 1;
56 is_big_endian_ = *reinterpret_cast<const char*>(&u) == 0;
57 }
Greg Hartman70753782017-09-28 16:12:43 -070058 bool operator()() const { return is_big_endian_; }
Jorge E. Moreira24307012017-09-22 14:35:21 -070059
60 private:
61 bool is_big_endian_{};
62};
63
64const BigEndianChecker ImBigEndian;
65
66constexpr int32_t kDesktopSizeEncoding = -223;
67constexpr int32_t kTightEncoding = 7;
68
69// These are the lengths not counting the first byte. The first byte
70// indicates the message type.
71constexpr size_t kSetPixelFormatLength = 19;
72constexpr size_t kFramebufferUpdateRequestLength = 9;
73constexpr size_t kSetEncodingsLength = 3; // more bytes follow
74constexpr size_t kKeyEventLength = 7;
75constexpr size_t kPointerEventLength = 5;
76constexpr size_t kClientCutTextLength = 7; // more bytes follow
77
78void AppendInNetworkByteOrder(Message* msg, const std::uint8_t b) {
79 msg->push_back(b);
80}
81
82void AppendInNetworkByteOrder(Message* msg, const std::uint16_t s) {
83 const std::uint16_t n = htons(s);
84 auto p = reinterpret_cast<const std::uint8_t*>(&n);
85 msg->insert(msg->end(), p, p + sizeof n);
86}
87
88void AppendInNetworkByteOrder(Message* msg, const std::uint32_t w) {
89 const std::uint32_t n = htonl(w);
90 auto p = reinterpret_cast<const std::uint8_t*>(&n);
91 msg->insert(msg->end(), p, p + sizeof n);
92}
93
94void AppendInNetworkByteOrder(Message* msg, const int32_t w) {
95 std::uint32_t u{};
96 std::memcpy(&u, &w, sizeof u);
97 AppendInNetworkByteOrder(msg, u);
98}
99
100void AppendInNetworkByteOrder(Message* msg, const std::string& str) {
101 msg->insert(msg->end(), str.begin(), str.end());
102}
103
104void AppendToMessage(Message*) {}
105
106template <typename T, typename... Ts>
107void AppendToMessage(Message* msg, T v, Ts... vals) {
108 AppendInNetworkByteOrder(msg, v);
109 AppendToMessage(msg, vals...);
110}
111
112template <typename... Ts>
113Message CreateMessage(Ts... vals) {
114 Message m;
115 AppendToMessage(&m, vals...);
116 return m;
117}
118
119std::string HostName() {
Greg Hartmanca7fb192017-09-28 16:14:12 -0700120 // Localhost is good enough for local development and to connect through ssh
121 // tunneling, for something else this probably needs to change.
122 return "localhost";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700123}
124
125std::uint16_t uint16_tAt(const void* p) {
126 std::uint16_t u{};
127 std::memcpy(&u, p, sizeof u);
128 return ntohs(u);
129}
130
131std::uint32_t uint32_tAt(const void* p) {
132 std::uint32_t u{};
133 std::memcpy(&u, p, sizeof u);
134 return ntohl(u);
135}
136
137std::int32_t int32_tAt(const void* p) {
138 std::uint32_t u{};
139 std::memcpy(&u, p, sizeof u);
140 u = ntohl(u);
141 std::int32_t s{};
142 std::memcpy(&s, &u, sizeof s);
143 return s;
144}
145
146std::uint32_t RedVal(std::uint32_t pixel) {
Jorge E. Moreira4ca884e2018-01-25 16:57:48 -0800147 return (pixel >> FBBroadcastRegionView::kRedShift) &
148 ((0x1 << FBBroadcastRegionView::kRedBits) - 1);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700149}
150
151std::uint32_t BlueVal(std::uint32_t pixel) {
Jorge E. Moreira4ca884e2018-01-25 16:57:48 -0800152 return (pixel >> FBBroadcastRegionView::kBlueShift) &
153 ((0x1 << FBBroadcastRegionView::kBlueBits) - 1);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700154}
155
156std::uint32_t GreenVal(std::uint32_t pixel) {
Jorge E. Moreira4ca884e2018-01-25 16:57:48 -0800157 return (pixel >> FBBroadcastRegionView::kGreenShift) &
158 ((0x1 << FBBroadcastRegionView::kGreenBits) - 1);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700159}
Greg Hartman70753782017-09-28 16:12:43 -0700160} // namespace
Greg Hartman153b1062017-11-11 12:09:21 -0800161namespace cvd {
Jorge E. Moreira24307012017-09-22 14:35:21 -0700162namespace vnc {
163bool operator==(const VncClientConnection::FrameBufferUpdateRequest& lhs,
164 const VncClientConnection::FrameBufferUpdateRequest& rhs) {
165 return lhs.x_pos == rhs.x_pos && lhs.y_pos == rhs.y_pos &&
166 lhs.width == rhs.width && lhs.height == rhs.height;
167}
168
169bool operator!=(const VncClientConnection::FrameBufferUpdateRequest& lhs,
170 const VncClientConnection::FrameBufferUpdateRequest& rhs) {
171 return !(lhs == rhs);
172}
173} // namespace vnc
Greg Hartman153b1062017-11-11 12:09:21 -0800174} // namespace cvd
Jorge E. Moreira24307012017-09-22 14:35:21 -0700175
176VncClientConnection::VncClientConnection(ClientSocket client,
177 VirtualInputs* virtual_inputs,
178 BlackBoard* bb, bool aggressive)
Jorge E. Moreira4ca884e2018-01-25 16:57:48 -0800179 : client_{std::move(client)}, virtual_inputs_{virtual_inputs}, bb_{bb} {
Jorge E. Moreira24307012017-09-22 14:35:21 -0700180 frame_buffer_request_handler_tid_ = std::thread(
181 &VncClientConnection::FrameBufferUpdateRequestHandler, this, aggressive);
182}
183
184VncClientConnection::~VncClientConnection() {
185 {
186 std::lock_guard<std::mutex> guard(m_);
187 closed_ = true;
188 }
189 bb_->StopWaiting(this);
190 frame_buffer_request_handler_tid_.join();
191}
192
193void VncClientConnection::StartSession() {
Greg Hartmanca7fb192017-09-28 16:14:12 -0700194 LOG(INFO) << "Starting session";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700195 SetupProtocol();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700196 LOG(INFO) << "Protocol set up";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700197 if (client_.closed()) {
198 return;
199 }
200 SetupSecurityType();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700201 LOG(INFO) << "Security type set";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700202 if (client_.closed()) {
203 return;
204 }
205 GetClientInit();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700206 LOG(INFO) << "Gotten client init";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700207 if (client_.closed()) {
208 return;
209 }
210 SendServerInit();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700211 LOG(INFO) << "Sent server init";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700212 if (client_.closed()) {
213 return;
214 }
215 NormalSession();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700216 LOG(INFO) << "vnc session terminated";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700217}
218
219bool VncClientConnection::closed() {
220 std::lock_guard<std::mutex> guard(m_);
221 return closed_;
222}
223
224void VncClientConnection::SetupProtocol() {
225 static constexpr char kRFBVersion[] = "RFB 003.008\n";
226 static constexpr auto kVersionLen = (sizeof kRFBVersion) - 1;
227 client_.Send(reinterpret_cast<const std::uint8_t*>(kRFBVersion), kVersionLen);
228 auto client_protocol = client_.Recv(kVersionLen);
229 if (std::memcmp(&client_protocol[0], kRFBVersion,
230 std::min(kVersionLen, client_protocol.size())) != 0) {
231 client_protocol.push_back('\0');
Greg Hartmanca7fb192017-09-28 16:14:12 -0700232 LOG(ERROR) << "vnc client wants a different protocol: "
233 << reinterpret_cast<const char*>(&client_protocol[0]);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700234 }
235}
236
237void VncClientConnection::SetupSecurityType() {
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_.Send(kOnlyNoneSecurity);
242 auto client_security = client_.Recv(1);
243 if (client_.closed()) {
244 return;
245 }
246 if (client_security.front() != kNoneSecurity) {
Greg Hartmanca7fb192017-09-28 16:14:12 -0700247 LOG(ERROR) << "vnc client is asking for security type "
248 << static_cast<int>(client_security.front());
Jorge E. Moreira24307012017-09-22 14:35:21 -0700249 }
250 static constexpr std::uint8_t kZero[4] = {};
251 client_.Send(kZero);
252}
253
254void VncClientConnection::GetClientInit() {
255 auto client_shared = client_.Recv(1);
256}
257
258void VncClientConnection::SendServerInit() {
259 const std::string server_name = HostName();
260 std::lock_guard<std::mutex> guard(m_);
261 auto server_init = 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_.Send(server_init);
271}
272
273Message VncClientConnection::MakeFrameBufferUpdateHeader(
274 std::uint16_t num_stripes) {
275 return CreateMessage(std::uint8_t{0}, // message-type
276 std::uint8_t{}, // padding
277 std::uint16_t{num_stripes});
278}
279
280void VncClientConnection::AppendRawStripeHeader(Message* frame_buffer_update,
281 const Stripe& stripe) {
282 static constexpr int32_t kRawEncoding = 0;
283 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
288void 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 AppendToMessage(frame_buffer_update, static_cast<std::uint8_t>(jpeg_size));
296 } else if (jpeg_size <= kJpegSizeTwoByteMax) {
297 auto sz = static_cast<std::uint32_t>(jpeg_size);
298 AppendToMessage(frame_buffer_update,
299 static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
300 static_cast<std::uint8_t>((sz >> 7) & 0xFF));
301 } else {
302 if (jpeg_size > kJpegSizeThreeByteMax) {
Greg Hartmanca7fb192017-09-28 16:14:12 -0700303 LOG(FATAL) << "jpeg size is too big: " << jpeg_size << " must be under "
304 << kJpegSizeThreeByteMax;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700305 }
306 const auto sz = static_cast<std::uint32_t>(jpeg_size);
307 AppendToMessage(frame_buffer_update,
308 static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
309 static_cast<std::uint8_t>(((sz >> 7) & 0x7F) | 0x80),
310 static_cast<std::uint8_t>((sz >> 14) & 0xFF));
311 }
312}
313
314void VncClientConnection::AppendRawStripe(Message* frame_buffer_update,
315 const Stripe& stripe) const {
Jorge E. Moreira4ca884e2018-01-25 16:57:48 -0800316 using Pixel = FBBroadcastRegionView::Pixel;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700317 auto& fbu = *frame_buffer_update;
318 AppendRawStripeHeader(&fbu, stripe);
319 auto init_size = fbu.size();
320 fbu.insert(fbu.end(), stripe.raw_data.begin(), stripe.raw_data.end());
321 for (size_t i = init_size; i < fbu.size(); i += sizeof(Pixel)) {
Greg Hartmanca7fb192017-09-28 16:14:12 -0700322 CHECK((i + sizeof(Pixel)) < fbu.size());
Jorge E. Moreira24307012017-09-22 14:35:21 -0700323 Pixel raw_pixel{};
324 std::memcpy(&raw_pixel, &fbu[i], sizeof raw_pixel);
325 auto red = RedVal(raw_pixel);
326 auto green = GreenVal(raw_pixel);
327 auto blue = BlueVal(raw_pixel);
328 Pixel pixel = Pixel{red} << pixel_format_.red_shift |
329 Pixel{blue} << pixel_format_.blue_shift |
330 Pixel{green} << pixel_format_.green_shift;
331
332 if (bool(pixel_format_.big_endian) != ImBigEndian()) {
333 // flip them bits (refactor into function)
334 auto p = reinterpret_cast<char*>(&pixel);
335 std::swap(p[0], p[3]);
336 std::swap(p[1], p[2]);
337 }
Greg Hartmanca7fb192017-09-28 16:14:12 -0700338 CHECK(i + sizeof pixel <= fbu.size());
Jorge E. Moreira24307012017-09-22 14:35:21 -0700339 std::memcpy(&fbu[i], &pixel, sizeof pixel);
340 }
341}
342
343Message 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
353void VncClientConnection::AppendJpegStripeHeader(Message* frame_buffer_update,
354 const Stripe& stripe) {
355 static constexpr std::uint8_t kJpegEncoding = 0x90;
356 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
361void 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
368Message 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
378Message VncClientConnection::MakeFrameBufferUpdate(
379 const StripePtrVec& stripes) {
380 return use_jpeg_compression_ ? MakeJpegFrameBufferUpdate(stripes)
381 : MakeRawFrameBufferUpdate(stripes);
382}
383
384void VncClientConnection::FrameBufferUpdateRequestHandler(bool aggressive) {
385 BlackBoard::Registerer reg(bb_, this);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700386
387 while (!closed()) {
388 auto stripes = bb_->WaitForSenderWork(this);
389 if (closed()) {
390 break;
391 }
Greg Hartmanca7fb192017-09-28 16:14:12 -0700392 if (stripes.empty()) {
393 LOG(FATAL) << "Got 0 stripes";
394 }
Jorge E. Moreira24307012017-09-22 14:35:21 -0700395 {
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_);
Greg Hartmanca7fb192017-09-28 16:14:12 -0700399 DLOG(INFO) << "Sending update in "
400 << (current_orientation_ == ScreenOrientation::Portrait
401 ? "portrait"
402 : "landscape")
403 << " mode";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700404 client_.Send(MakeFrameBufferUpdate(stripes));
405 }
406 if (aggressive) {
407 bb_->FrameBufferUpdateRequestReceived(this);
408 }
409 }
410}
411
412void VncClientConnection::SendDesktopSizeUpdate() {
413 static constexpr int32_t kDesktopSizeEncoding = -223;
414 client_.Send(CreateMessage(std::uint8_t{0}, // message-type,
415 std::uint8_t{}, // padding
416 std::uint16_t{1}, // one pseudo rectangle
417 std::uint16_t{0}, std::uint16_t{0},
418 static_cast<std::uint16_t>(ScreenWidth()),
419 static_cast<std::uint16_t>(ScreenHeight()),
420 kDesktopSizeEncoding));
421}
422
423bool VncClientConnection::IsUrgent(
424 const FrameBufferUpdateRequest& update_request) const {
425 return !update_request.incremental ||
426 update_request != previous_update_request_;
427}
428
429void 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
444void 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]);
Greg Hartmanca7fb192017-09-28 16:14:12 -0700460 DLOG(INFO) << "client requesting encoding: " << enc;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700461 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) {
Greg Hartmanca7fb192017-09-28 16:14:12 -0700468 DLOG(INFO) << "jpeg compression level: " << enc;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700469 bb_->set_jpeg_quality_level(enc);
470 }
471 if (enc == kDesktopSizeEncoding) {
472 supports_desktop_size_encoding_ = true;
473 }
474 }
475}
476
477void 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
495void 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(ActualScreenWidth() - y_pos, x_pos);
508 }
509 }
510 virtual_inputs_->HandlePointerEvent(button_mask, x_pos, y_pos);
511}
512
Greg Hartman7d5e0bf2017-12-19 23:48:34 -0800513void VncClientConnection::UpdateAccelerometer(float /*x*/, float /*y*/,
514 float /*z*/) {
Jorge E. Moreira4ca884e2018-01-25 16:57:48 -0800515 // TODO(jemoreira): Implement when vsoc sensor hal is updated
Jorge E. Moreira24307012017-09-22 14:35:21 -0700516}
517
518VncClientConnection::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
535int VncClientConnection::ScreenWidth() const {
536 return current_orientation_ == ScreenOrientation::Portrait
537 ? ActualScreenWidth()
538 : ActualScreenHeight();
539}
540
541int VncClientConnection::ScreenHeight() const {
542 return current_orientation_ == ScreenOrientation::Portrait
543 ? ActualScreenHeight()
544 : ActualScreenWidth();
545}
546
547void 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
566bool 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) {
Greg Hartman153b1062017-11-11 12:09:21 -0800577 case cvd::xk::Right:
578 case cvd::xk::F12:
Greg Hartmanca7fb192017-09-28 16:14:12 -0700579 DLOG(INFO) << "switching to portrait";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700580 SetScreenOrientation(ScreenOrientation::Portrait);
581 break;
Greg Hartman153b1062017-11-11 12:09:21 -0800582 case cvd::xk::Left:
583 case cvd::xk::F11:
Greg Hartmanca7fb192017-09-28 16:14:12 -0700584 DLOG(INFO) << "switching to landscape";
Jorge E. Moreira24307012017-09-22 14:35:21 -0700585 SetScreenOrientation(ScreenOrientation::Landscape);
586 break;
587 default:
588 return false;
589 }
590 return true;
591}
592
593void 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) {
Greg Hartman153b1062017-11-11 12:09:21 -0800602 case cvd::xk::ControlLeft:
603 case cvd::xk::ControlRight:
Jorge E. Moreira24307012017-09-22 14:35:21 -0700604 control_key_down_ = key_down;
605 break;
Greg Hartman153b1062017-11-11 12:09:21 -0800606 case cvd::xk::MetaLeft:
607 case cvd::xk::MetaRight:
Jorge E. Moreira24307012017-09-22 14:35:21 -0700608 meta_key_down_ = key_down;
609 break;
Greg Hartman153b1062017-11-11 12:09:21 -0800610 case cvd::xk::F5:
611 key = cvd::xk::Menu;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700612 break;
Greg Hartman153b1062017-11-11 12:09:21 -0800613 case cvd::xk::F7:
Jorge E. Moreira24307012017-09-22 14:35:21 -0700614 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
627void 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
636void 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();
Greg Hartmanca7fb192017-09-28 16:14:12 -0700652 DLOG(INFO) << "Received message type " << msg_type;
Jorge E. Moreira24307012017-09-22 14:35:21 -0700653
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:
Greg Hartmanca7fb192017-09-28 16:14:12 -0700680 LOG(WARNING) << "message type not handled: "
681 << static_cast<int>(msg_type);
Jorge E. Moreira24307012017-09-22 14:35:21 -0700682 break;
683 }
684 }
685}