1 /*
2  *  Copyright (c) 2015 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 "platform_uithread.h"
12 
13 namespace rtc {
14 
15 #if defined(WEBRTC_WIN)
16 
17 // timer id used in delayed callbacks
18 static const UINT_PTR kTimerId = 1;
19 static const wchar_t kThisProperty[] = L"ThreadWindowsUIPtr";
20 static const wchar_t kThreadWindow[] = L"WebrtcWindowsUIThread";
21 
InternalInit()22 bool PlatformUIThread::InternalInit() {
23   // Create an event window for use in generating callbacks to capture
24   // objects.
25   CritScope scoped_lock(&cs_);
26   if (hwnd_ == NULL) {
27     WNDCLASSW wc;
28     HMODULE hModule = GetModuleHandle(NULL);
29     if (!GetClassInfoW(hModule, kThreadWindow, &wc)) {
30       ZeroMemory(&wc, sizeof(WNDCLASSW));
31       wc.hInstance = hModule;
32       wc.lpfnWndProc = EventWindowProc;
33       wc.lpszClassName = kThreadWindow;
34       RegisterClassW(&wc);
35     }
36     hwnd_ = CreateWindowW(kThreadWindow, L"", 0, 0, 0, 0, 0, NULL, NULL,
37                           hModule, NULL);
38     RTC_DCHECK(hwnd_);
39     SetPropW(hwnd_, kThisProperty, this);
40 
41     if (timeout_) {
42       // if someone set the timer before we started
43       RequestCallbackTimer(timeout_);
44     }
45   }
46   return !!hwnd_;
47 }
48 
RequestCallbackTimer(unsigned int milliseconds)49 bool PlatformUIThread::RequestCallbackTimer(unsigned int milliseconds) {
50   CritScope scoped_lock(&cs_);
51   if (!hwnd_) {
52     // There is a condition that thread_ (PlatformUIThread) has been
53     // created but PlatformUIThread::Run() hasn't been run yet (hwnd_ is
54     // null while thread_ is not). If we do RTC_DCHECK(!thread_) here,
55     // it would lead to crash in this condition.
56 
57     // set timer once thread starts
58   } else {
59     if (timerid_) {
60       KillTimer(hwnd_, timerid_);
61     }
62     timerid_ = SetTimer(hwnd_, kTimerId, milliseconds, NULL);
63   }
64   timeout_ = milliseconds;
65   return !!timerid_;
66 }
67 
Stop()68 void PlatformUIThread::Stop() {
69   RTC_DCHECK(thread_checker_.CalledOnValidThread());
70   // Shut down the dispatch loop and let the background thread exit.
71   if (timerid_) {
72     KillTimer(hwnd_, timerid_);
73     timerid_ = 0;
74   }
75 
76   PostMessage(hwnd_, WM_CLOSE, 0, 0);
77 
78   hwnd_ = NULL;
79 
80   PlatformThread::Stop();
81 }
82 
Run()83 void PlatformUIThread::Run() {
84   RTC_CHECK(InternalInit());  // always evaluates
85   // The interface contract of Start/Stop is that for a successful call to
86   // Start, there should be at least one call to the run function.  So we
87   // call the function before checking |stop_|.
88   run_function_deprecated_(obj_);
89 
90   do {
91     // Alertable sleep to permit RaiseFlag to run and update |stop_|.
92     if (MsgWaitForMultipleObjectsEx(0, nullptr, INFINITE, QS_ALLINPUT,
93                                     MWMO_ALERTABLE | MWMO_INPUTAVAILABLE) ==
94         WAIT_OBJECT_0) {
95       MSG msg;
96       if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
97         if (msg.message == WM_QUIT) {
98           stop_ = true;
99           break;
100         }
101         TranslateMessage(&msg);
102         DispatchMessage(&msg);
103       }
104     }
105 
106   } while (!stop_);
107 }
108 
NativeEventCallback()109 void PlatformUIThread::NativeEventCallback() {
110   if (!run_function_deprecated_) {
111     stop_ = true;
112     return;
113   }
114   run_function_deprecated_(obj_);
115 }
116 
117 /* static */
EventWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)118 LRESULT CALLBACK PlatformUIThread::EventWindowProc(HWND hwnd, UINT uMsg,
119                                                    WPARAM wParam,
120                                                    LPARAM lParam) {
121   if (uMsg == WM_DESTROY) {
122     RemovePropW(hwnd, kThisProperty);
123     PostQuitMessage(0);
124     return 0;
125   }
126 
127   PlatformUIThread* twui =
128       static_cast<PlatformUIThread*>(GetPropW(hwnd, kThisProperty));
129   if (!twui) {
130     return DefWindowProc(hwnd, uMsg, wParam, lParam);
131   }
132 
133   if (uMsg == WM_TIMER && wParam == kTimerId) {
134     twui->NativeEventCallback();
135     return 0;
136   }
137 
138   return DefWindowProc(hwnd, uMsg, wParam, lParam);
139 }
140 #endif
141 
142 }  // namespace rtc
143