Index: base/win/osk_display_manager.cc |
diff --git a/base/win/osk_display_manager.cc b/base/win/osk_display_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4b0017cf41c6eae42157d31d88718711673dc70b |
--- /dev/null |
+++ b/base/win/osk_display_manager.cc |
@@ -0,0 +1,256 @@ |
+// Copyright (c) 2016 The Chromium Authors. All rights reserved. |
grt (UTC plus 2)
2016/05/18 15:41:19
(c)
|
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "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" |
+ |
+namespace base { |
+namespace win { |
+ |
+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::OnScreenKeyboardDetector() |
+ : main_window_(nullptr), |
+ keyboard_detector_factory_(this), |
+ osk_visible_notification_received_(false) { |
+ memset(&osk_rect_, 0, sizeof(osk_rect_)); |
+} |
+ |
+OnScreenKeyboardDetector::~OnScreenKeyboardDetector() { |
+ ClearObservers(); |
grt (UTC plus 2)
2016/05/18 15:41:19
is this needed? can you just let the list self-des
ananta
2016/05/19 01:57:11
Done.
|
+} |
+ |
+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; |
+ |
+ ::GetWindowRect(osk, &osk_rect_); |
+ if (check_for_activation) { |
+ if (::IsWindowVisible(osk) && ::IsWindowEnabled(osk)) { |
+ if (!osk_visible_notification_received_) { |
grt (UTC plus 2)
2016/05/18 15:41:19
nit: omit braces
ananta
2016/05/19 01:57:11
Done.
|
+ HandleKeyboardVisible(); |
+ } |
+ } else { |
+ DVLOG(1) << "OSK did not come up in 1 second. Something wrong."; |
+ } |
+ } else { |
+ // Two cases here. |
grt (UTC plus 2)
2016/05/18 15:41:19
looks like the code handles three cases. could you
ananta
2016/05/19 01:57:12
Done.
|
+ // OSK was hidden because the user dismissed it. |
+ // We are no longer in the foreground. |
+ // 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(); |
+ ClearObservers(); |
+ } |
+ } 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_)); |
+ |
+ // 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_)); |
+} |
+ |
+void OnScreenKeyboardDetector::ClearObservers() { |
+ base::ObserverListBase<OnScreenKeyboardObserver>::Iterator iter(&observers_); |
+ for (OnScreenKeyboardObserver* observer = iter.GetNext(); observer; |
+ observer = iter.GetNext()) { |
+ RemoveObserver(observer); |
+ } |
+} |
+ |
+OnScreenKeyboardDisplayManager* OnScreenKeyboardDisplayManager::GetInstance() { |
+ return base::Singleton<OnScreenKeyboardDisplayManager, |
grt (UTC plus 2)
2016/05/18 15:41:19
is a leaky lazy instance appropriate here?
ananta
2016/05/19 01:57:11
That would work as well. Changed.
|
+ base::StaticMemorySingletonTraits< |
+ OnScreenKeyboardDisplayManager>>::get(); |
+} |
+ |
+bool OnScreenKeyboardDisplayManager::DisplayVirtualKeyboard( |
+ OnScreenKeyboardObserver* observer) { |
+ if (GetVersion() < VERSION_WIN8) |
+ return false; |
+ |
+ if (IsKeyboardPresentOnSlate(nullptr)) |
+ return false; |
+ |
+ static LazyInstance<string16>::Leaky osk_path = LAZY_INSTANCE_INITIALIZER; |
grt (UTC plus 2)
2016/05/18 15:41:19
OnScreenKeyboardDisplayManager is a singleton. why
ananta
2016/05/19 01:57:11
Yes. Moved this function into a class without insp
|
+ |
+ if (osk_path.Get().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. |
+ RegKey key(HKEY_LOCAL_MACHINE, kWindows8OSKRegPath, |
+ KEY_READ | KEY_WOW64_64KEY); |
+ DWORD osk_path_length = 1024; |
+ if (key.ReadValue(NULL, |
+ WriteInto(&osk_path.Get(), osk_path_length), |
grt (UTC plus 2)
2016/05/18 15:41:19
note that WriteInto doesn't update the size of the
ananta
2016/05/19 01:57:12
Done.
|
+ &osk_path_length, |
+ NULL) != ERROR_SUCCESS) { |
+ DLOG(WARNING) << "Failed to read on screen keyboard path from registry"; |
+ return false; |
+ } |
+ size_t common_program_files_offset = |
+ osk_path.Get().find(L"%CommonProgramFiles%"); |
grt (UTC plus 2)
2016/05/18 15:41:20
case-insensitive?
|
+ // 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 != string16::npos) { |
+ // Preserve the beginning quote in the path. |
+ osk_path.Get().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. |
+ string16 common_program_files_path; |
+ std::unique_ptr<wchar_t[]> common_program_files_wow6432; |
+ DWORD buffer_size = |
+ GetEnvironmentVariable(L"CommonProgramW6432", NULL, 0); |
grt (UTC plus 2)
2016/05/18 15:41:19
nullptr
ananta
2016/05/19 01:57:12
Done.
|
+ if (buffer_size) { |
+ common_program_files_wow6432.reset(new wchar_t[buffer_size]); |
+ GetEnvironmentVariable(L"CommonProgramW6432", |
+ common_program_files_wow6432.get(), |
grt (UTC plus 2)
2016/05/18 15:41:20
WriteInto(&common_program_files_path, buffer_size)
ananta
2016/05/19 01:57:11
Thanks. done.
|
+ buffer_size); |
+ common_program_files_path = common_program_files_wow6432.get(); |
+ DCHECK(!common_program_files_path.empty()); |
+ } |
+ else { |
+ ScopedCoMem<wchar_t> common_program_files; |
+ if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0, NULL, |
grt (UTC plus 2)
2016/05/18 15:41:19
nullptr
ananta
2016/05/19 01:57:11
Done.
|
+ &common_program_files))) { |
+ return false; |
+ } |
+ common_program_files_path = common_program_files; |
+ } |
+ |
+ osk_path.Get().insert(1, common_program_files_path); |
grt (UTC plus 2)
2016/05/18 15:41:19
1 -> common_program_files_offset?
ananta
2016/05/19 01:57:12
Yes. done.
|
+ } |
+ } |
+ |
+ HINSTANCE ret = ::ShellExecuteW(NULL, |
grt (UTC plus 2)
2016/05/18 15:41:19
nullptr all the things ;-)
ananta
2016/05/19 01:57:12
Done.
|
+ L"", |
+ osk_path.Get().c_str(), |
+ NULL, |
+ NULL, |
+ SW_SHOW); |
+ |
+ bool success = reinterpret_cast<intptr_t>(ret) > 32; |
+ if (success) { |
+ keyboard_detector_.reset(new OnScreenKeyboardDetector); |
grt (UTC plus 2)
2016/05/18 15:41:20
since this is a singleton, two callers to DisplayV
ananta
2016/05/19 01:57:11
It does not matter in this use case as the callers
|
+ if (observer) |
+ keyboard_detector_->AddObserver(observer); |
+ keyboard_detector_->DetectKeyboard(::GetForegroundWindow()); |
+ } |
+ return success; |
+} |
+ |
+bool OnScreenKeyboardDisplayManager::DismissVirtualKeyboard( |
+ OnScreenKeyboardObserver* observer) { |
+ if (GetVersion() < VERSION_WIN8) |
+ return false; |
+ |
+ DCHECK(keyboard_detector_.get()); |
+ bool ret = keyboard_detector_->DismissKeyboard(); |
+ if (observer) |
+ keyboard_detector_->RemoveObserver(observer); |
+ return ret; |
+} |
+ |
+} // namespace win |
+} // namespace base |
+ |