Index: ui/base/win/osk_display_manager.cc |
diff --git a/ui/base/win/osk_display_manager.cc b/ui/base/win/osk_display_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1889551f4eb9ea4727f37badac30b21f4cf51b30 |
--- /dev/null |
+++ b/ui/base/win/osk_display_manager.cc |
@@ -0,0 +1,276 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/base/win/osk_display_manager.h" |
+ |
+#include <windows.h> |
+#include <shellapi.h> |
+#include <shlobj.h> |
+#include <shobjidl.h> // Must be before propkey. |
+ |
+#include "base/bind.h" |
+#include "base/lazy_instance.h" |
+#include "base/logging.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/strings/string_util.h" |
+#include "base/win/registry.h" |
+#include "base/win/scoped_co_mem.h" |
+#include "base/win/win_util.h" |
+#include "base/win/windows_version.h" |
+#include "ui/display/win/dpi.h" |
+#include "ui/gfx/geometry/dip_util.h" |
+ |
+namespace ui { |
+ |
+static const int kCheckOSKDelayMs = 1000; |
+static const wchar_t kOSKClassName[] = L"IPTip_Main_Window"; |
+ |
+const wchar_t kWindows8OSKRegPath[] = |
+ L"Software\\Classes\\CLSID\\{054AAE20-4BEA-4347-8A35-64A533254A9D}" |
+ L"\\LocalServer32"; |
+ |
+// OnScreenKeyboardDetector member definitions. |
+OnScreenKeyboardDetector::OnScreenKeyboardDetector() |
+ : main_window_(nullptr), |
+ osk_visible_notification_received_(false), |
+ keyboard_detector_factory_(this) { |
+} |
+ |
+OnScreenKeyboardDetector::~OnScreenKeyboardDetector() { |
+} |
+ |
+void OnScreenKeyboardDetector::DetectKeyboard(HWND main_window) { |
+ main_window_ = main_window; |
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE, |
+ base::Bind(&OnScreenKeyboardDetector::CheckOSKState, |
+ keyboard_detector_factory_.GetWeakPtr(), true), |
+ base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); |
+} |
+ |
+bool OnScreenKeyboardDetector::DismissKeyboard() { |
+ // We dismiss the virtual keyboard by generating the ESC keystroke |
+ // programmatically. |
+ HWND osk = ::FindWindow(kOSKClassName, nullptr); |
+ if (::IsWindow(osk) && ::IsWindowEnabled(osk)) { |
+ HandleKeyboardHidden(); |
+ PostMessage(osk, WM_SYSCOMMAND, SC_CLOSE, 0); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+void OnScreenKeyboardDetector::AddObserver( |
+ OnScreenKeyboardObserver* observer) { |
+ observers_.AddObserver(observer); |
+} |
+ |
+void OnScreenKeyboardDetector::RemoveObserver( |
+ OnScreenKeyboardObserver* observer) { |
+ observers_.RemoveObserver(observer); |
+} |
+ |
+void OnScreenKeyboardDetector::CheckOSKState(bool check_for_activation) { |
+ HWND osk = ::FindWindow(kOSKClassName, nullptr); |
+ if (!::IsWindow(osk)) |
+ return; |
+ |
+ RECT osk_rect = {}; |
+ ::GetWindowRect(osk, &osk_rect); |
+ |
+ osk_rect_dip_ = gfx::ConvertRectToDIP(display::win::GetDPIScale(), |
+ gfx::Rect(osk_rect)); |
+ |
+ if (check_for_activation) { |
+ if (::IsWindowVisible(osk) && ::IsWindowEnabled(osk)) { |
+ if (!osk_visible_notification_received_) |
+ HandleKeyboardVisible(); |
+ } else { |
+ DVLOG(1) << "OSK did not come up in 1 second. Something wrong."; |
+ } |
+ } else { |
+ // Three cases here. |
+ // 1. OSK was hidden because the user dismissed it. |
+ // 2. We are no longer in the foreground. |
+ // 3. The OSK is still visible. |
+ // In the first case we just have to notify the observers that the OSK was |
+ // hidden. |
+ // In the second case we need to dismiss the OSK which internally will |
+ // notify the observers about the OSK being hidden. |
+ if (!::IsWindowEnabled(osk)) { |
+ if (osk_visible_notification_received_) { |
+ if (main_window_ == ::GetForegroundWindow()) { |
+ DVLOG(1) << "OSK window hidden while we are in the foreground."; |
+ HandleKeyboardHidden(); |
+ } |
+ } |
+ } else if (main_window_ != ::GetForegroundWindow()) { |
+ if (osk_visible_notification_received_) { |
+ DVLOG(1) << "We are no longer in the foreground. Dismising OSK."; |
+ DismissKeyboard(); |
+ } |
+ } else { |
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE, |
+ base::Bind(&OnScreenKeyboardDetector::CheckOSKState, |
+ keyboard_detector_factory_.GetWeakPtr(), false), |
+ base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); |
+ } |
+ } |
+} |
+ |
+void OnScreenKeyboardDetector::HandleKeyboardVisible() { |
+ DCHECK(!osk_visible_notification_received_); |
+ osk_visible_notification_received_ = true; |
+ |
+ FOR_EACH_OBSERVER(OnScreenKeyboardObserver, observers_, |
+ OnKeyboardVisible(osk_rect_dip_)); |
+ |
+ // Now that the keyboard is visible, run the task to detect if it was hidden. |
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE, |
+ base::Bind(&OnScreenKeyboardDetector::CheckOSKState, |
+ keyboard_detector_factory_.GetWeakPtr(), false), |
+ base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); |
+} |
+ |
+void OnScreenKeyboardDetector::HandleKeyboardHidden() { |
+ osk_visible_notification_received_ = false; |
+ FOR_EACH_OBSERVER(OnScreenKeyboardObserver, observers_, |
+ OnKeyboardHidden(osk_rect_dip_)); |
+ ClearObservers(); |
+} |
+ |
+void OnScreenKeyboardDetector::ClearObservers() { |
+ base::ObserverListBase<OnScreenKeyboardObserver>::Iterator iter(&observers_); |
+ for (OnScreenKeyboardObserver* observer = iter.GetNext(); observer; |
+ observer = iter.GetNext()) { |
+ RemoveObserver(observer); |
+ } |
+} |
+ |
+// OnScreenKeyboardDisplayManager member definitions. |
+OnScreenKeyboardDisplayManager::OnScreenKeyboardDisplayManager() { |
+} |
+ |
+OnScreenKeyboardDisplayManager::~OnScreenKeyboardDisplayManager() { |
+} |
+ |
+OnScreenKeyboardDisplayManager* OnScreenKeyboardDisplayManager::GetInstance() { |
+ return base::Singleton<OnScreenKeyboardDisplayManager, |
+ base::LeakySingletonTraits<OnScreenKeyboardDisplayManager>>::get(); |
+} |
+ |
+bool OnScreenKeyboardDisplayManager::DisplayVirtualKeyboard( |
+ OnScreenKeyboardObserver* observer) { |
+ if (base::win::GetVersion() < base::win::VERSION_WIN8) |
+ return false; |
+ |
+ if (base::win::IsKeyboardPresentOnSlate(nullptr)) |
+ return false; |
+ |
+ if (osk_path_.empty()) { |
+ // We need to launch TabTip.exe from the location specified under the |
+ // LocalServer32 key for the {{054AAE20-4BEA-4347-8A35-64A533254A9D}} |
+ // CLSID. |
+ // TabTip.exe is typically found at |
+ // c:\program files\common files\microsoft shared\ink on English Windows. |
+ // We don't want to launch TabTip.exe from |
+ // c:\program files (x86)\common files\microsoft shared\ink. This path is |
+ // normally found on 64 bit Windows. |
+ base::win::RegKey key(HKEY_LOCAL_MACHINE, kWindows8OSKRegPath, |
+ KEY_READ | KEY_WOW64_64KEY); |
+ DWORD osk_path_length = 1024; |
+ if (key.ReadValue(NULL, |
+ base::WriteInto(&osk_path_, osk_path_length), |
+ &osk_path_length, |
+ NULL) != ERROR_SUCCESS) { |
+ DLOG(WARNING) << "Failed to read on screen keyboard path from registry"; |
+ return false; |
+ } |
+ |
+ osk_path_.resize(base::string16::traits_type::length(osk_path_.c_str())); |
+ |
+ osk_path_ = base::ToLowerASCII(osk_path_); |
+ |
+ size_t common_program_files_offset = |
+ osk_path_.find(L"%commonprogramfiles%"); |
+ // Typically the path to TabTip.exe read from the registry will start with |
+ // %CommonProgramFiles% which needs to be replaced with the corrsponding |
+ // expanded string. |
+ // If the path does not begin with %CommonProgramFiles% we use it as is. |
+ if (common_program_files_offset != base::string16::npos) { |
+ // Preserve the beginning quote in the path. |
+ osk_path_.erase(common_program_files_offset, |
+ wcslen(L"%commonprogramfiles%")); |
+ // The path read from the registry contains the %CommonProgramFiles% |
+ // environment variable prefix. On 64 bit Windows the SHGetKnownFolderPath |
+ // function returns the common program files path with the X86 suffix for |
+ // the FOLDERID_ProgramFilesCommon value. |
+ // To get the correct path to TabTip.exe we first read the environment |
+ // variable CommonProgramW6432 which points to the desired common |
+ // files path. Failing that we fallback to the SHGetKnownFolderPath API. |
+ |
+ // We then replace the %CommonProgramFiles% value with the actual common |
+ // files path found in the process. |
+ base::string16 common_program_files_path; |
+ DWORD buffer_size = |
+ GetEnvironmentVariable(L"CommonProgramW6432", nullptr, 0); |
+ if (buffer_size) { |
+ GetEnvironmentVariable(L"CommonProgramW6432", |
+ base::WriteInto(&common_program_files_path, |
+ buffer_size), |
+ buffer_size); |
+ DCHECK(!common_program_files_path.empty()); |
+ } else { |
+ base::win::ScopedCoMem<wchar_t> common_program_files; |
+ if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, |
+ 0, |
+ nullptr, |
+ &common_program_files))) { |
+ return false; |
+ } |
+ common_program_files_path = common_program_files; |
+ } |
+ |
+ osk_path_.insert(common_program_files_offset, common_program_files_path); |
+ } |
+ } |
+ |
+ HINSTANCE ret = ::ShellExecuteW(nullptr, |
+ L"", |
+ osk_path_.c_str(), |
+ nullptr, |
+ nullptr, |
+ SW_SHOW); |
+ |
+ bool success = reinterpret_cast<intptr_t>(ret) > 32; |
+ if (success) { |
+ keyboard_detector_.reset(new OnScreenKeyboardDetector); |
ncarter (slow)
2016/05/19 17:45:55
Is this the right behavior, if keyboard_detector_
ananta
2016/05/19 19:33:38
In the ideal case we want a list of detectors. How
|
+ if (observer) |
+ keyboard_detector_->AddObserver(observer); |
+ keyboard_detector_->DetectKeyboard(::GetForegroundWindow()); |
+ } |
+ return success; |
+} |
+ |
+bool OnScreenKeyboardDisplayManager::DismissVirtualKeyboard() { |
+ if (base::win::GetVersion() < base::win::VERSION_WIN8) |
+ return false; |
+ |
+ DCHECK(keyboard_detector_.get()); |
ncarter (slow)
2016/05/19 17:45:55
It seems like we'll crash if DisplayVirtualKeyboar
|
+ bool ret = keyboard_detector_->DismissKeyboard(); |
+ return ret; |
+} |
+ |
+bool DisplayVirtualKeyboard() { |
+ DCHECK(OnScreenKeyboardDisplayManager::GetInstance()); |
+ return OnScreenKeyboardDisplayManager:: |
+ GetInstance()->DisplayVirtualKeyboard(nullptr); // !keyboard_observer. |
+} |
+ |
+bool DismissVirtualKeyboard() { |
+ DCHECK(OnScreenKeyboardDisplayManager::GetInstance()); |
+ return OnScreenKeyboardDisplayManager:: |
+ GetInstance()->DismissVirtualKeyboard(); // !keyboard_observer. |
ncarter (slow)
2016/05/19 17:45:55
This comment seems out of date.
ananta
2016/05/19 19:33:38
Done.
|
+} |
+ |
+} // namespace ui |