tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

main_wnd.cc (19414B)


      1 /*
      2 *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "examples/peerconnection/client/main_wnd.h"
     12 
     13 #include <math.h>
     14 
     15 #include <cstddef>
     16 #include <cstdint>
     17 #include <cstdio>
     18 #include <cstdlib>
     19 #include <iterator>
     20 #include <string>
     21 
     22 #include "api/media_stream_interface.h"
     23 #include "api/scoped_refptr.h"
     24 #include "api/video/i420_buffer.h"
     25 #include "api/video/video_frame.h"
     26 #include "api/video/video_frame_buffer.h"
     27 #include "api/video/video_rotation.h"
     28 #include "api/video/video_source_interface.h"
     29 #include "examples/peerconnection/client/peer_connection_client.h"
     30 #include "rtc_base/checks.h"
     31 #include "third_party/libyuv/include/libyuv/convert_argb.h"
     32 
     33 ATOM MainWnd::wnd_class_ = 0;
     34 const wchar_t MainWnd::kClassName[] = L"WebRTC_MainWnd";
     35 
     36 namespace {
     37 
     38 const char kConnecting[] = "Connecting... ";
     39 const char kNoVideoStreams[] = "(no video streams either way)";
     40 const char kNoIncomingStream[] = "(no incoming video)";
     41 
     42 void CalculateWindowSizeForText(HWND wnd,
     43                                const wchar_t* text,
     44                                size_t* width,
     45                                size_t* height) {
     46  HDC dc = ::GetDC(wnd);
     47  RECT text_rc = {0};
     48  ::DrawTextW(dc, text, -1, &text_rc, DT_CALCRECT | DT_SINGLELINE);
     49  ::ReleaseDC(wnd, dc);
     50  RECT client, window;
     51  ::GetClientRect(wnd, &client);
     52  ::GetWindowRect(wnd, &window);
     53 
     54  *width = text_rc.right - text_rc.left;
     55  *width += (window.right - window.left) - (client.right - client.left);
     56  *height = text_rc.bottom - text_rc.top;
     57  *height += (window.bottom - window.top) - (client.bottom - client.top);
     58 }
     59 
     60 HFONT GetDefaultFont() {
     61  static HFONT font = reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
     62  return font;
     63 }
     64 
     65 std::string GetWindowText(HWND wnd) {
     66  char text[MAX_PATH] = {0};
     67  ::GetWindowTextA(wnd, &text[0], std::size(text));
     68  return text;
     69 }
     70 
     71 void AddListBoxItem(HWND listbox, const std::string& str, LPARAM item_data) {
     72  LRESULT index = ::SendMessageA(listbox, LB_ADDSTRING, 0,
     73                                 reinterpret_cast<LPARAM>(str.c_str()));
     74  ::SendMessageA(listbox, LB_SETITEMDATA, index, item_data);
     75 }
     76 
     77 }  // namespace
     78 
     79 MainWnd::MainWnd(const char* server,
     80                 int port,
     81                 bool auto_connect,
     82                 bool auto_call)
     83    : ui_(CONNECT_TO_SERVER),
     84      wnd_(NULL),
     85      edit1_(NULL),
     86      edit2_(NULL),
     87      label1_(NULL),
     88      label2_(NULL),
     89      button_(NULL),
     90      listbox_(NULL),
     91      destroyed_(false),
     92      nested_msg_(NULL),
     93      callback_(NULL),
     94      server_(server),
     95      auto_connect_(auto_connect),
     96      auto_call_(auto_call) {
     97  char buffer[10];
     98  snprintf(buffer, sizeof(buffer), "%i", port);
     99  port_ = buffer;
    100 }
    101 
    102 MainWnd::~MainWnd() {
    103  RTC_DCHECK(!IsWindow());
    104 }
    105 
    106 bool MainWnd::Create() {
    107  RTC_DCHECK(wnd_ == NULL);
    108  if (!RegisterWindowClass())
    109    return false;
    110 
    111  ui_thread_id_ = ::GetCurrentThreadId();
    112  wnd_ =
    113      ::CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, kClassName, L"WebRTC",
    114                        WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
    115                        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    116                        CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), this);
    117 
    118  ::SendMessage(wnd_, WM_SETFONT, reinterpret_cast<WPARAM>(GetDefaultFont()),
    119                TRUE);
    120 
    121  CreateChildWindows();
    122  SwitchToConnectUI();
    123 
    124  return wnd_ != NULL;
    125 }
    126 
    127 bool MainWnd::Destroy() {
    128  BOOL ret = FALSE;
    129  if (IsWindow()) {
    130    ret = ::DestroyWindow(wnd_);
    131  }
    132 
    133  return ret != FALSE;
    134 }
    135 
    136 void MainWnd::RegisterObserver(MainWndCallback* callback) {
    137  callback_ = callback;
    138 }
    139 
    140 bool MainWnd::IsWindow() {
    141  return wnd_ && ::IsWindow(wnd_) != FALSE;
    142 }
    143 
    144 bool MainWnd::PreTranslateMessage(MSG* msg) {
    145  bool ret = false;
    146  if (msg->message == WM_CHAR) {
    147    if (msg->wParam == VK_TAB) {
    148      HandleTabbing();
    149      ret = true;
    150    } else if (msg->wParam == VK_RETURN) {
    151      OnDefaultAction();
    152      ret = true;
    153    } else if (msg->wParam == VK_ESCAPE) {
    154      if (callback_) {
    155        if (ui_ == STREAMING) {
    156          callback_->DisconnectFromCurrentPeer();
    157        } else {
    158          callback_->DisconnectFromServer();
    159        }
    160      }
    161    }
    162  } else if (msg->hwnd == NULL && msg->message == UI_THREAD_CALLBACK) {
    163    callback_->UIThreadCallback(static_cast<int>(msg->wParam),
    164                                reinterpret_cast<void*>(msg->lParam));
    165    ret = true;
    166  }
    167  return ret;
    168 }
    169 
    170 void MainWnd::SwitchToConnectUI() {
    171  RTC_DCHECK(IsWindow());
    172  LayoutPeerListUI(false);
    173  ui_ = CONNECT_TO_SERVER;
    174  LayoutConnectUI(true);
    175  ::SetFocus(edit1_);
    176 
    177  if (auto_connect_)
    178    ::PostMessage(button_, BM_CLICK, 0, 0);
    179 }
    180 
    181 void MainWnd::SwitchToPeerList(const Peers& peers) {
    182  LayoutConnectUI(false);
    183 
    184  ::SendMessage(listbox_, LB_RESETCONTENT, 0, 0);
    185 
    186  AddListBoxItem(listbox_, "List of currently connected peers:", -1);
    187  Peers::const_iterator i = peers.begin();
    188  for (; i != peers.end(); ++i)
    189    AddListBoxItem(listbox_, i->second.c_str(), i->first);
    190 
    191  ui_ = LIST_PEERS;
    192  LayoutPeerListUI(true);
    193  ::SetFocus(listbox_);
    194 
    195  if (auto_call_ && peers.begin() != peers.end()) {
    196    // Get the number of items in the list
    197    LRESULT count = ::SendMessage(listbox_, LB_GETCOUNT, 0, 0);
    198    if (count != LB_ERR) {
    199      // Select the last item in the list
    200      LRESULT selection = ::SendMessage(listbox_, LB_SETCURSEL, count - 1, 0);
    201      if (selection != LB_ERR)
    202        ::PostMessage(wnd_, WM_COMMAND,
    203                      MAKEWPARAM(GetDlgCtrlID(listbox_), LBN_DBLCLK),
    204                      reinterpret_cast<LPARAM>(listbox_));
    205    }
    206  }
    207 }
    208 
    209 void MainWnd::SwitchToStreamingUI() {
    210  LayoutConnectUI(false);
    211  LayoutPeerListUI(false);
    212  ui_ = STREAMING;
    213 }
    214 
    215 void MainWnd::MessageBox(const char* caption, const char* text, bool is_error) {
    216  DWORD flags = MB_OK;
    217  if (is_error)
    218    flags |= MB_ICONERROR;
    219 
    220  ::MessageBoxA(handle(), text, caption, flags);
    221 }
    222 
    223 void MainWnd::StartLocalRenderer(webrtc::VideoTrackInterface* local_video) {
    224  local_renderer_.reset(new VideoRenderer(handle(), 1, 1, local_video));
    225 }
    226 
    227 void MainWnd::StopLocalRenderer() {
    228  local_renderer_.reset();
    229 }
    230 
    231 void MainWnd::StartRemoteRenderer(webrtc::VideoTrackInterface* remote_video) {
    232  remote_renderer_.reset(new VideoRenderer(handle(), 1, 1, remote_video));
    233 }
    234 
    235 void MainWnd::StopRemoteRenderer() {
    236  remote_renderer_.reset();
    237 }
    238 
    239 void MainWnd::QueueUIThreadCallback(int msg_id, void* data) {
    240  ::PostThreadMessage(ui_thread_id_, UI_THREAD_CALLBACK,
    241                      static_cast<WPARAM>(msg_id),
    242                      reinterpret_cast<LPARAM>(data));
    243 }
    244 
    245 void MainWnd::OnPaint() {
    246  PAINTSTRUCT ps;
    247  ::BeginPaint(handle(), &ps);
    248 
    249  RECT rc;
    250  ::GetClientRect(handle(), &rc);
    251 
    252  VideoRenderer* local_renderer = local_renderer_.get();
    253  VideoRenderer* remote_renderer = remote_renderer_.get();
    254  if (ui_ == STREAMING && remote_renderer && local_renderer) {
    255    AutoLock<VideoRenderer> local_lock(local_renderer);
    256    AutoLock<VideoRenderer> remote_lock(remote_renderer);
    257 
    258    const BITMAPINFO& bmi = remote_renderer->bmi();
    259    int height = abs(bmi.bmiHeader.biHeight);
    260    int width = bmi.bmiHeader.biWidth;
    261 
    262    const uint8_t* image = remote_renderer->image();
    263    if (image != NULL) {
    264      HDC dc_mem = ::CreateCompatibleDC(ps.hdc);
    265      ::SetStretchBltMode(dc_mem, HALFTONE);
    266 
    267      // Set the map mode so that the ratio will be maintained for us.
    268      for (HDC dc : {ps.hdc, dc_mem}) {
    269        SetMapMode(dc, MM_ISOTROPIC);
    270        SetWindowExtEx(dc, width, height, nullptr);
    271        SetViewportExtEx(dc, rc.right, rc.bottom, nullptr);
    272      }
    273 
    274      HBITMAP bmp_mem = ::CreateCompatibleBitmap(ps.hdc, rc.right, rc.bottom);
    275      HGDIOBJ bmp_old = ::SelectObject(dc_mem, bmp_mem);
    276 
    277      POINT logical_area = {rc.right, rc.bottom};
    278      DPtoLP(ps.hdc, &logical_area, 1);
    279 
    280      HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
    281      RECT logical_rect = {0, 0, logical_area.x, logical_area.y};
    282      ::FillRect(dc_mem, &logical_rect, brush);
    283      ::DeleteObject(brush);
    284 
    285      int x = (logical_area.x / 2) - (width / 2);
    286      int y = (logical_area.y / 2) - (height / 2);
    287 
    288      StretchDIBits(dc_mem, x, y, width, height, 0, 0, width, height, image,
    289                    &bmi, DIB_RGB_COLORS, SRCCOPY);
    290 
    291      if ((rc.right - rc.left) > 200 && (rc.bottom - rc.top) > 200) {
    292        const BITMAPINFO& local_bmi = local_renderer->bmi();
    293        image = local_renderer->image();
    294        int thumb_width = local_bmi.bmiHeader.biWidth / 4;
    295        int thumb_height = abs(local_bmi.bmiHeader.biHeight) / 4;
    296        StretchDIBits(dc_mem, logical_area.x - thumb_width - 10,
    297                      logical_area.y - thumb_height - 10, thumb_width,
    298                      thumb_height, 0, 0, local_bmi.bmiHeader.biWidth,
    299                      -local_bmi.bmiHeader.biHeight, image, &local_bmi,
    300                      DIB_RGB_COLORS, SRCCOPY);
    301      }
    302 
    303      BitBlt(ps.hdc, 0, 0, logical_area.x, logical_area.y, dc_mem, 0, 0,
    304             SRCCOPY);
    305 
    306      // Cleanup.
    307      ::SelectObject(dc_mem, bmp_old);
    308      ::DeleteObject(bmp_mem);
    309      ::DeleteDC(dc_mem);
    310    } else {
    311      // We're still waiting for the video stream to be initialized.
    312      HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
    313      ::FillRect(ps.hdc, &rc, brush);
    314      ::DeleteObject(brush);
    315 
    316      HGDIOBJ old_font = ::SelectObject(ps.hdc, GetDefaultFont());
    317      ::SetTextColor(ps.hdc, RGB(0xff, 0xff, 0xff));
    318      ::SetBkMode(ps.hdc, TRANSPARENT);
    319 
    320      std::string text(kConnecting);
    321      if (!local_renderer->image()) {
    322        text += kNoVideoStreams;
    323      } else {
    324        text += kNoIncomingStream;
    325      }
    326      ::DrawTextA(ps.hdc, text.c_str(), -1, &rc,
    327                  DT_SINGLELINE | DT_CENTER | DT_VCENTER);
    328      ::SelectObject(ps.hdc, old_font);
    329    }
    330  } else {
    331    HBRUSH brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
    332    ::FillRect(ps.hdc, &rc, brush);
    333    ::DeleteObject(brush);
    334  }
    335 
    336  ::EndPaint(handle(), &ps);
    337 }
    338 
    339 void MainWnd::OnDestroyed() {
    340  PostQuitMessage(0);
    341 }
    342 
    343 void MainWnd::OnDefaultAction() {
    344  if (!callback_)
    345    return;
    346  if (ui_ == CONNECT_TO_SERVER) {
    347    std::string server(GetWindowText(edit1_));
    348    std::string port_str(GetWindowText(edit2_));
    349    int port = port_str.length() ? atoi(port_str.c_str()) : 0;
    350    callback_->StartLogin(server, port);
    351  } else if (ui_ == LIST_PEERS) {
    352    LRESULT sel = ::SendMessage(listbox_, LB_GETCURSEL, 0, 0);
    353    if (sel != LB_ERR) {
    354      LRESULT peer_id = ::SendMessage(listbox_, LB_GETITEMDATA, sel, 0);
    355      if (peer_id != -1 && callback_) {
    356        callback_->ConnectToPeer(peer_id);
    357      }
    358    }
    359  } else {
    360    ::MessageBoxA(wnd_, "OK!", "Yeah", MB_OK);
    361  }
    362 }
    363 
    364 bool MainWnd::OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT* result) {
    365  switch (msg) {
    366    case WM_ERASEBKGND:
    367      *result = TRUE;
    368      return true;
    369 
    370    case WM_PAINT:
    371      OnPaint();
    372      return true;
    373 
    374    case WM_SETFOCUS:
    375      if (ui_ == CONNECT_TO_SERVER) {
    376        SetFocus(edit1_);
    377      } else if (ui_ == LIST_PEERS) {
    378        SetFocus(listbox_);
    379      }
    380      return true;
    381 
    382    case WM_SIZE:
    383      if (ui_ == CONNECT_TO_SERVER) {
    384        LayoutConnectUI(true);
    385      } else if (ui_ == LIST_PEERS) {
    386        LayoutPeerListUI(true);
    387      }
    388      break;
    389 
    390    case WM_CTLCOLORSTATIC:
    391      *result = reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_WINDOW));
    392      return true;
    393 
    394    case WM_COMMAND:
    395      if (button_ == reinterpret_cast<HWND>(lp)) {
    396        if (BN_CLICKED == HIWORD(wp))
    397          OnDefaultAction();
    398      } else if (listbox_ == reinterpret_cast<HWND>(lp)) {
    399        if (LBN_DBLCLK == HIWORD(wp)) {
    400          OnDefaultAction();
    401        }
    402      }
    403      return true;
    404 
    405    case WM_CLOSE:
    406      if (callback_)
    407        callback_->Close();
    408      break;
    409  }
    410  return false;
    411 }
    412 
    413 // static
    414 LRESULT CALLBACK MainWnd::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    415  MainWnd* me =
    416      reinterpret_cast<MainWnd*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
    417  if (!me && WM_CREATE == msg) {
    418    CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lp);
    419    me = reinterpret_cast<MainWnd*>(cs->lpCreateParams);
    420    me->wnd_ = hwnd;
    421    ::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(me));
    422  }
    423 
    424  LRESULT result = 0;
    425  if (me) {
    426    void* prev_nested_msg = me->nested_msg_;
    427    me->nested_msg_ = &msg;
    428 
    429    bool handled = me->OnMessage(msg, wp, lp, &result);
    430    if (WM_NCDESTROY == msg) {
    431      me->destroyed_ = true;
    432    } else if (!handled) {
    433      result = ::DefWindowProc(hwnd, msg, wp, lp);
    434    }
    435 
    436    if (me->destroyed_ && prev_nested_msg == NULL) {
    437      me->OnDestroyed();
    438      me->wnd_ = NULL;
    439      me->destroyed_ = false;
    440    }
    441 
    442    me->nested_msg_ = prev_nested_msg;
    443  } else {
    444    result = ::DefWindowProc(hwnd, msg, wp, lp);
    445  }
    446 
    447  return result;
    448 }
    449 
    450 // static
    451 bool MainWnd::RegisterWindowClass() {
    452  if (wnd_class_)
    453    return true;
    454 
    455  WNDCLASSEXW wcex = {sizeof(WNDCLASSEX)};
    456  wcex.style = CS_DBLCLKS;
    457  wcex.hInstance = GetModuleHandle(NULL);
    458  wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
    459  wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    460  wcex.lpfnWndProc = &WndProc;
    461  wcex.lpszClassName = kClassName;
    462  wnd_class_ = ::RegisterClassExW(&wcex);
    463  RTC_DCHECK(wnd_class_ != 0);
    464  return wnd_class_ != 0;
    465 }
    466 
    467 void MainWnd::CreateChildWindow(HWND* wnd,
    468                                MainWnd::ChildWindowID id,
    469                                const wchar_t* class_name,
    470                                DWORD control_style,
    471                                DWORD ex_style) {
    472  if (::IsWindow(*wnd))
    473    return;
    474 
    475  // Child windows are invisible at first, and shown after being resized.
    476  DWORD style = WS_CHILD | control_style;
    477  *wnd = ::CreateWindowExW(ex_style, class_name, L"", style, 100, 100, 100, 100,
    478                           wnd_, reinterpret_cast<HMENU>(id),
    479                           GetModuleHandle(NULL), NULL);
    480  RTC_DCHECK(::IsWindow(*wnd) != FALSE);
    481  ::SendMessage(*wnd, WM_SETFONT, reinterpret_cast<WPARAM>(GetDefaultFont()),
    482                TRUE);
    483 }
    484 
    485 void MainWnd::CreateChildWindows() {
    486  // Create the child windows in tab order.
    487  CreateChildWindow(&label1_, LABEL1_ID, L"Static", ES_CENTER | ES_READONLY, 0);
    488  CreateChildWindow(&edit1_, EDIT_ID, L"Edit",
    489                    ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, WS_EX_CLIENTEDGE);
    490  CreateChildWindow(&label2_, LABEL2_ID, L"Static", ES_CENTER | ES_READONLY, 0);
    491  CreateChildWindow(&edit2_, EDIT_ID, L"Edit",
    492                    ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, WS_EX_CLIENTEDGE);
    493  CreateChildWindow(&button_, BUTTON_ID, L"Button", BS_CENTER | WS_TABSTOP, 0);
    494 
    495  CreateChildWindow(&listbox_, LISTBOX_ID, L"ListBox",
    496                    LBS_HASSTRINGS | LBS_NOTIFY, WS_EX_CLIENTEDGE);
    497 
    498  ::SetWindowTextA(edit1_, server_.c_str());
    499  ::SetWindowTextA(edit2_, port_.c_str());
    500 }
    501 
    502 void MainWnd::LayoutConnectUI(bool show) {
    503  struct Windows {
    504    HWND wnd;
    505    const wchar_t* text;
    506    size_t width;
    507    size_t height;
    508  } windows[] = {
    509      {label1_, L"Server"},  {edit1_, L"XXXyyyYYYgggXXXyyyYYYggg"},
    510      {label2_, L":"},       {edit2_, L"XyXyX"},
    511      {button_, L"Connect"},
    512  };
    513 
    514  if (show) {
    515    const size_t kSeparator = 5;
    516    size_t total_width = (std::size(windows) - 1) * kSeparator;
    517 
    518    for (Windows& window : windows) {
    519      CalculateWindowSizeForText(window.wnd, window.text, &window.width,
    520                                 &window.height);
    521      total_width += window.width;
    522    }
    523 
    524    RECT rc;
    525    ::GetClientRect(wnd_, &rc);
    526    size_t x = (rc.right / 2) - (total_width / 2);
    527    size_t y = rc.bottom / 2;
    528    for (Windows& window : windows) {
    529      size_t top = y - (window.height / 2);
    530      ::MoveWindow(window.wnd, static_cast<int>(x), static_cast<int>(top),
    531                   static_cast<int>(window.width),
    532                   static_cast<int>(window.height), TRUE);
    533      x += kSeparator + window.width;
    534      if (window.text[0] != 'X')
    535        ::SetWindowTextW(window.wnd, window.text);
    536      ::ShowWindow(window.wnd, SW_SHOWNA);
    537    }
    538  } else {
    539    for (Windows& window : windows) {
    540      ::ShowWindow(window.wnd, SW_HIDE);
    541    }
    542  }
    543 }
    544 
    545 void MainWnd::LayoutPeerListUI(bool show) {
    546  if (show) {
    547    RECT rc;
    548    ::GetClientRect(wnd_, &rc);
    549    ::MoveWindow(listbox_, 0, 0, rc.right, rc.bottom, TRUE);
    550    ::ShowWindow(listbox_, SW_SHOWNA);
    551  } else {
    552    ::ShowWindow(listbox_, SW_HIDE);
    553    InvalidateRect(wnd_, NULL, TRUE);
    554  }
    555 }
    556 
    557 void MainWnd::HandleTabbing() {
    558  bool shift = ((::GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0);
    559  UINT next_cmd = shift ? GW_HWNDPREV : GW_HWNDNEXT;
    560  UINT loop_around_cmd = shift ? GW_HWNDLAST : GW_HWNDFIRST;
    561  HWND focus = GetFocus(), next;
    562  do {
    563    next = ::GetWindow(focus, next_cmd);
    564    if (IsWindowVisible(next) &&
    565        (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP)) {
    566      break;
    567    }
    568 
    569    if (!next) {
    570      next = ::GetWindow(focus, loop_around_cmd);
    571      if (IsWindowVisible(next) &&
    572          (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP)) {
    573        break;
    574      }
    575    }
    576    focus = next;
    577  } while (true);
    578  ::SetFocus(next);
    579 }
    580 
    581 //
    582 // MainWnd::VideoRenderer
    583 //
    584 
    585 MainWnd::VideoRenderer::VideoRenderer(
    586    HWND wnd,
    587    int width,
    588    int height,
    589    webrtc::VideoTrackInterface* track_to_render)
    590    : wnd_(wnd), rendered_track_(track_to_render) {
    591  ::InitializeCriticalSection(&buffer_lock_);
    592  ZeroMemory(&bmi_, sizeof(bmi_));
    593  bmi_.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    594  bmi_.bmiHeader.biPlanes = 1;
    595  bmi_.bmiHeader.biBitCount = 32;
    596  bmi_.bmiHeader.biCompression = BI_RGB;
    597  bmi_.bmiHeader.biWidth = width;
    598  bmi_.bmiHeader.biHeight = -height;
    599  bmi_.bmiHeader.biSizeImage =
    600      width * height * (bmi_.bmiHeader.biBitCount >> 3);
    601  rendered_track_->AddOrUpdateSink(this, webrtc::VideoSinkWants());
    602 }
    603 
    604 MainWnd::VideoRenderer::~VideoRenderer() {
    605  rendered_track_->RemoveSink(this);
    606  ::DeleteCriticalSection(&buffer_lock_);
    607 }
    608 
    609 void MainWnd::VideoRenderer::SetSize(int width, int height) {
    610  AutoLock<VideoRenderer> lock(this);
    611 
    612  if (width == bmi_.bmiHeader.biWidth && height == bmi_.bmiHeader.biHeight) {
    613    return;
    614  }
    615 
    616  bmi_.bmiHeader.biWidth = width;
    617  bmi_.bmiHeader.biHeight = -height;
    618  bmi_.bmiHeader.biSizeImage =
    619      width * height * (bmi_.bmiHeader.biBitCount >> 3);
    620  image_.reset(new uint8_t[bmi_.bmiHeader.biSizeImage]);
    621 }
    622 
    623 void MainWnd::VideoRenderer::OnFrame(const webrtc::VideoFrame& video_frame) {
    624  {
    625    AutoLock<VideoRenderer> lock(this);
    626 
    627    webrtc::scoped_refptr<webrtc::I420BufferInterface> buffer(
    628        video_frame.video_frame_buffer()->ToI420());
    629    if (video_frame.rotation() != webrtc::kVideoRotation_0) {
    630      buffer = webrtc::I420Buffer::Rotate(*buffer, video_frame.rotation());
    631    }
    632 
    633    SetSize(buffer->width(), buffer->height());
    634 
    635    RTC_DCHECK(image_.get() != NULL);
    636    libyuv::I420ToARGB(buffer->DataY(), buffer->StrideY(), buffer->DataU(),
    637                       buffer->StrideU(), buffer->DataV(), buffer->StrideV(),
    638                       image_.get(),
    639                       bmi_.bmiHeader.biWidth * bmi_.bmiHeader.biBitCount / 8,
    640                       buffer->width(), buffer->height());
    641  }
    642  InvalidateRect(wnd_, NULL, TRUE);
    643 }