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