Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3172)

Unified Diff: base/win/osk_display_manager.cc

Issue 1986153005: The on screen keyboard on Windows 8+ should not obscure the input field. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove include Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
+

Powered by Google App Engine
This is Rietveld 408576698