| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/system_window/win/system_window.h" |
| |
| #include <csignal> |
| |
| #if !defined(WIN32_LEAN_AND_MEAN) |
| #define WIN32_LEAN_AND_MEAN |
| #endif |
| #include <windows.h> |
| |
| #include "base/bind.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/threading/thread_local.h" |
| #include "cobalt/system_window/application_event.h" |
| #include "cobalt/system_window/keyboard_event.h" |
| |
| namespace cobalt { |
| namespace system_window { |
| |
| namespace { |
| const wchar_t* kClassName = L"CobaltMainWindow"; |
| const wchar_t* kWindowTitle = L"Cobalt"; |
| const char kThreadName[] = "Win32 Message Loop"; |
| |
| // Lazily created thread local storage to access the SystemWindowWin object |
| // defined in the current thread. The WndProc function starts getting called |
| // with messages before CreateWindowEx even returns, so this allows us to |
| // access the corresponding SystemWindowWin object in that function. |
| // This assumes that the function is always called on the same thread that the |
| // window was created on and the messages are processed on |
| // (the thread running Win32WindowThread). |
| base::LazyInstance<base::ThreadLocalPointer<SystemWindowWin> > lazy_tls_ptr = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| inline bool IsKeyDown(int keycode) { |
| // Key down state is indicated by high-order bit of SHORT. |
| return (GetKeyState(keycode) & 0x8000) != 0; |
| } |
| |
| unsigned int GetModifierKeyState() { |
| unsigned int modifiers = KeyboardEvent::kNoModifier; |
| if (IsKeyDown(VK_MENU)) { |
| modifiers |= KeyboardEvent::kAltKey; |
| } |
| if (IsKeyDown(VK_CONTROL)) { |
| modifiers |= KeyboardEvent::kCtrlKey; |
| } |
| if (IsKeyDown(VK_SHIFT)) { |
| modifiers |= KeyboardEvent::kShiftKey; |
| } |
| return modifiers; |
| } |
| } // namespace |
| |
| SystemWindowWin::SystemWindowWin(base::EventDispatcher* event_dispatcher) |
| : SystemWindow(event_dispatcher), |
| thread(kThreadName), |
| window_initialized(true, false), |
| window_handle_(NULL), |
| window_size_(math::Size(1920, 1080)) { |
| StartWindow(); |
| } |
| |
| SystemWindowWin::SystemWindowWin(base::EventDispatcher* event_dispatcher, |
| const math::Size& window_size) |
| : SystemWindow(event_dispatcher), |
| thread(kThreadName), |
| window_initialized(true, false), |
| window_handle_(NULL), |
| window_size_(window_size) { |
| StartWindow(); |
| } |
| |
| SystemWindowWin::~SystemWindowWin() { EndWindow(); } |
| |
| void SystemWindowWin::StartWindow() { |
| thread.Start(); |
| thread.message_loop()->PostTask( |
| FROM_HERE, |
| base::Bind(&SystemWindowWin::Win32WindowThread, base::Unretained(this))); |
| |
| window_initialized.Wait(); |
| } |
| |
| LRESULT CALLBACK SystemWindowWin::WndProc(HWND window_handle, UINT message, |
| WPARAM w_param, LPARAM l_param) { |
| // Get the associated SystemWindowWin instance from thread local storage. |
| // This assumes this function is always called on the same thread. |
| SystemWindowWin* system_window = lazy_tls_ptr.Pointer()->Get(); |
| DCHECK(system_window); |
| |
| switch (message) { |
| case WM_CLOSE: { |
| DLOG(INFO) << "User clicked X button on window " << system_window; |
| scoped_ptr<ApplicationEvent> quit_event( |
| new ApplicationEvent(ApplicationEvent::kQuit)); |
| system_window->event_dispatcher()->DispatchEvent( |
| quit_event.PassAs<base::Event>()); |
| return 0; |
| } break; |
| case WM_KEYDOWN: { |
| int key_code = w_param; |
| bool is_repeat = IsKeyRepeat(l_param); |
| scoped_ptr<KeyboardEvent> keyboard_event(new KeyboardEvent( |
| KeyboardEvent::kKeyDown, key_code, GetModifierKeyState(), is_repeat)); |
| system_window->event_dispatcher()->DispatchEvent( |
| keyboard_event.PassAs<base::Event>()); |
| } break; |
| case WM_KEYUP: { |
| // The user has released a key on the keyboard. |
| int key_code = w_param; |
| scoped_ptr<KeyboardEvent> keyboard_event(new KeyboardEvent( |
| KeyboardEvent::kKeyUp, key_code, GetModifierKeyState(), false)); |
| system_window->event_dispatcher()->DispatchEvent( |
| keyboard_event.PassAs<base::Event>()); |
| } break; |
| } |
| |
| return DefWindowProc(window_handle, message, w_param, l_param); |
| } |
| |
| bool SystemWindowWin::IsKeyRepeat(LPARAM l_param) { |
| const LPARAM kPreviousStateMask = 1 << 30; |
| return (l_param & kPreviousStateMask) != 0L; |
| } |
| |
| void SystemWindowWin::EndWindow() { |
| PostMessage(window_handle_, WM_QUIT, 0, 0); |
| } |
| |
| void SystemWindowWin::Win32WindowThread() { |
| // Store the pointer to this object in thread-local storage. |
| // This lets us access this object in the WndProc function, assuming that |
| // function is always called on this thread. |
| DCHECK(lazy_tls_ptr.Pointer()->Get() == NULL); |
| lazy_tls_ptr.Pointer()->Set(this); |
| DCHECK(lazy_tls_ptr.Pointer()->Get() == this); |
| |
| HINSTANCE module_instance = GetModuleHandle(NULL); |
| |
| WNDCLASS window_class; |
| window_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; |
| window_class.lpfnWndProc = (WNDPROC)WndProc; |
| window_class.cbClsExtra = 0; |
| window_class.cbWndExtra = 0; |
| window_class.hInstance = module_instance; |
| window_class.hIcon = LoadIcon(NULL, IDI_WINLOGO); |
| window_class.hCursor = LoadCursor(NULL, IDC_ARROW); |
| window_class.hbrBackground = NULL; |
| window_class.lpszMenuName = NULL; |
| window_class.lpszClassName = kClassName; |
| CHECK(RegisterClass(&window_class)); |
| |
| window_handle_ = CreateWindowEx( |
| WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, kClassName, kWindowTitle, |
| WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW, 0, 0, |
| window_size_.width(), window_size_.height(), NULL, NULL, module_instance, |
| NULL); |
| CHECK(window_handle_); |
| |
| ShowWindow(window_handle_, SW_SHOW); |
| |
| window_initialized.Signal(); |
| |
| MSG message; |
| while (GetMessage(&message, NULL, 0, 0)) { |
| TranslateMessage(&message); |
| DispatchMessage(&message); |
| } |
| |
| CHECK(DestroyWindow(window_handle())); |
| CHECK(UnregisterClass(kClassName, module_instance)); |
| lazy_tls_ptr.Pointer()->Set(NULL); |
| DCHECK(lazy_tls_ptr.Pointer()->Get() == NULL); |
| } |
| |
| scoped_ptr<SystemWindow> CreateSystemWindow( |
| base::EventDispatcher* event_dispatcher, |
| const base::optional<math::Size>& window_size) { |
| if (window_size) { |
| return scoped_ptr<SystemWindow>( |
| new SystemWindowWin(event_dispatcher, *window_size)); |
| } else { |
| return scoped_ptr<SystemWindow>(new SystemWindowWin(event_dispatcher)); |
| } |
| } |
| |
| } // namespace system_window |
| } // namespace cobalt |