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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
grt (UTC plus 2) 2016/05/18 15:41:19 (c)
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/win/osk_display_manager.h"
6
7 #include <windows.h>
8 #include <shellapi.h>
9 #include <shlobj.h>
10 #include <shobjidl.h> // Must be before propkey.
11
12 #include "base/bind.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/strings/string_util.h"
17 #include "base/win/registry.h"
18 #include "base/win/scoped_co_mem.h"
19 #include "base/win/win_util.h"
20 #include "base/win/windows_version.h"
21
22 namespace base {
23 namespace win {
24
25 static const int kCheckOSKDelayMs = 1000;
26 static const wchar_t kOSKClassName[] = L"IPTip_Main_Window";
27
28 const wchar_t kWindows8OSKRegPath[] =
29 L"Software\\Classes\\CLSID\\{054AAE20-4BEA-4347-8A35-64A533254A9D}"
30 L"\\LocalServer32";
31
32 OnScreenKeyboardDetector::OnScreenKeyboardDetector()
33 : main_window_(nullptr),
34 keyboard_detector_factory_(this),
35 osk_visible_notification_received_(false) {
36 memset(&osk_rect_, 0, sizeof(osk_rect_));
37 }
38
39 OnScreenKeyboardDetector::~OnScreenKeyboardDetector() {
40 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.
41 }
42
43 void OnScreenKeyboardDetector::DetectKeyboard(HWND main_window) {
44 main_window_ = main_window;
45 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
46 base::Bind(&OnScreenKeyboardDetector::CheckOSKState,
47 keyboard_detector_factory_.GetWeakPtr(), true),
48 base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs));
49 }
50
51 bool OnScreenKeyboardDetector::DismissKeyboard() {
52 // We dismiss the virtual keyboard by generating the ESC keystroke
53 // programmatically.
54 HWND osk = ::FindWindow(kOSKClassName, nullptr);
55 if (::IsWindow(osk) && ::IsWindowEnabled(osk)) {
56 HandleKeyboardHidden();
57 PostMessage(osk, WM_SYSCOMMAND, SC_CLOSE, 0);
58 return true;
59 }
60 return false;
61 }
62
63 void OnScreenKeyboardDetector::AddObserver(
64 OnScreenKeyboardObserver* observer) {
65 observers_.AddObserver(observer);
66 }
67
68 void OnScreenKeyboardDetector::RemoveObserver(
69 OnScreenKeyboardObserver* observer) {
70 observers_.RemoveObserver(observer);
71 }
72
73 void OnScreenKeyboardDetector::CheckOSKState(bool check_for_activation) {
74 HWND osk = ::FindWindow(kOSKClassName, nullptr);
75 if (!::IsWindow(osk))
76 return;
77
78 ::GetWindowRect(osk, &osk_rect_);
79 if (check_for_activation) {
80 if (::IsWindowVisible(osk) && ::IsWindowEnabled(osk)) {
81 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.
82 HandleKeyboardVisible();
83 }
84 } else {
85 DVLOG(1) << "OSK did not come up in 1 second. Something wrong.";
86 }
87 } else {
88 // 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.
89 // OSK was hidden because the user dismissed it.
90 // We are no longer in the foreground.
91 // In the first case we just have to notify the observers that the OSK was
92 // hidden.
93 // In the second case we need to dismiss the OSK which internally will
94 // notify the observers about the OSK being hidden.
95 if (!::IsWindowEnabled(osk)) {
96 if (osk_visible_notification_received_) {
97 if (main_window_ == ::GetForegroundWindow()) {
98 DVLOG(1) << "OSK window hidden while we are in the foreground.";
99 HandleKeyboardHidden();
100 }
101 }
102 } else if (main_window_ != ::GetForegroundWindow()) {
103 if (osk_visible_notification_received_) {
104 DVLOG(1) << "We are no longer in the foreground. Dismising OSK.";
105 DismissKeyboard();
106 ClearObservers();
107 }
108 } else {
109 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
110 base::Bind(&OnScreenKeyboardDetector::CheckOSKState,
111 keyboard_detector_factory_.GetWeakPtr(), false),
112 base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs));
113 }
114 }
115 }
116
117 void OnScreenKeyboardDetector::HandleKeyboardVisible() {
118 DCHECK(!osk_visible_notification_received_);
119 osk_visible_notification_received_ = true;
120
121 FOR_EACH_OBSERVER(OnScreenKeyboardObserver, observers_,
122 OnKeyboardVisible(osk_rect_));
123
124 // Now that the keyboard is visible, run the task to detect if it was hidden.
125 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
126 base::Bind(&OnScreenKeyboardDetector::CheckOSKState,
127 keyboard_detector_factory_.GetWeakPtr(), false),
128 base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs));
129 }
130
131 void OnScreenKeyboardDetector::HandleKeyboardHidden() {
132 osk_visible_notification_received_ = false;
133 FOR_EACH_OBSERVER(OnScreenKeyboardObserver, observers_,
134 OnKeyboardHidden(osk_rect_));
135 }
136
137 void OnScreenKeyboardDetector::ClearObservers() {
138 base::ObserverListBase<OnScreenKeyboardObserver>::Iterator iter(&observers_);
139 for (OnScreenKeyboardObserver* observer = iter.GetNext(); observer;
140 observer = iter.GetNext()) {
141 RemoveObserver(observer);
142 }
143 }
144
145 OnScreenKeyboardDisplayManager* OnScreenKeyboardDisplayManager::GetInstance() {
146 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.
147 base::StaticMemorySingletonTraits<
148 OnScreenKeyboardDisplayManager>>::get();
149 }
150
151 bool OnScreenKeyboardDisplayManager::DisplayVirtualKeyboard(
152 OnScreenKeyboardObserver* observer) {
153 if (GetVersion() < VERSION_WIN8)
154 return false;
155
156 if (IsKeyboardPresentOnSlate(nullptr))
157 return false;
158
159 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
160
161 if (osk_path.Get().empty()) {
162 // We need to launch TabTip.exe from the location specified under the
163 // LocalServer32 key for the {{054AAE20-4BEA-4347-8A35-64A533254A9D}}
164 // CLSID.
165 // TabTip.exe is typically found at
166 // c:\program files\common files\microsoft shared\ink on English Windows.
167 // We don't want to launch TabTip.exe from
168 // c:\program files (x86)\common files\microsoft shared\ink. This path is
169 // normally found on 64 bit Windows.
170 RegKey key(HKEY_LOCAL_MACHINE, kWindows8OSKRegPath,
171 KEY_READ | KEY_WOW64_64KEY);
172 DWORD osk_path_length = 1024;
173 if (key.ReadValue(NULL,
174 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.
175 &osk_path_length,
176 NULL) != ERROR_SUCCESS) {
177 DLOG(WARNING) << "Failed to read on screen keyboard path from registry";
178 return false;
179 }
180 size_t common_program_files_offset =
181 osk_path.Get().find(L"%CommonProgramFiles%");
grt (UTC plus 2) 2016/05/18 15:41:20 case-insensitive?
182 // Typically the path to TabTip.exe read from the registry will start with
183 // %CommonProgramFiles% which needs to be replaced with the corrsponding
184 // expanded string.
185 // If the path does not begin with %CommonProgramFiles% we use it as is.
186 if (common_program_files_offset != string16::npos) {
187 // Preserve the beginning quote in the path.
188 osk_path.Get().erase(common_program_files_offset,
189 wcslen(L"%CommonProgramFiles%"));
190 // The path read from the registry contains the %CommonProgramFiles%
191 // environment variable prefix. On 64 bit Windows the SHGetKnownFolderPath
192 // function returns the common program files path with the X86 suffix for
193 // the FOLDERID_ProgramFilesCommon value.
194 // To get the correct path to TabTip.exe we first read the environment
195 // variable CommonProgramW6432 which points to the desired common
196 // files path. Failing that we fallback to the SHGetKnownFolderPath API.
197
198 // We then replace the %CommonProgramFiles% value with the actual common
199 // files path found in the process.
200 string16 common_program_files_path;
201 std::unique_ptr<wchar_t[]> common_program_files_wow6432;
202 DWORD buffer_size =
203 GetEnvironmentVariable(L"CommonProgramW6432", NULL, 0);
grt (UTC plus 2) 2016/05/18 15:41:19 nullptr
ananta 2016/05/19 01:57:12 Done.
204 if (buffer_size) {
205 common_program_files_wow6432.reset(new wchar_t[buffer_size]);
206 GetEnvironmentVariable(L"CommonProgramW6432",
207 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.
208 buffer_size);
209 common_program_files_path = common_program_files_wow6432.get();
210 DCHECK(!common_program_files_path.empty());
211 }
212 else {
213 ScopedCoMem<wchar_t> common_program_files;
214 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.
215 &common_program_files))) {
216 return false;
217 }
218 common_program_files_path = common_program_files;
219 }
220
221 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.
222 }
223 }
224
225 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.
226 L"",
227 osk_path.Get().c_str(),
228 NULL,
229 NULL,
230 SW_SHOW);
231
232 bool success = reinterpret_cast<intptr_t>(ret) > 32;
233 if (success) {
234 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
235 if (observer)
236 keyboard_detector_->AddObserver(observer);
237 keyboard_detector_->DetectKeyboard(::GetForegroundWindow());
238 }
239 return success;
240 }
241
242 bool OnScreenKeyboardDisplayManager::DismissVirtualKeyboard(
243 OnScreenKeyboardObserver* observer) {
244 if (GetVersion() < VERSION_WIN8)
245 return false;
246
247 DCHECK(keyboard_detector_.get());
248 bool ret = keyboard_detector_->DismissKeyboard();
249 if (observer)
250 keyboard_detector_->RemoveObserver(observer);
251 return ret;
252 }
253
254 } // namespace win
255 } // namespace base
256
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698