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

Side by Side Diff: base/message_pump_win.cc

Issue 10826223: Replace PeekMessage for TSF awareness (Closed) Base URL: http://git.chromium.org/chromium/src.git@yukawa
Patch Set: update names Created 8 years, 4 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
« no previous file with comments | « base/message_pump_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/message_pump_win.h" 5 #include "base/message_pump_win.h"
6 6
7 #include <imm.h>
7 #include <math.h> 8 #include <math.h>
9 #include <msctf.h>
10 #include <Windows.h>
8 11
9 #include "base/debug/trace_event.h" 12 #include "base/debug/trace_event.h"
10 #include "base/message_loop.h" 13 #include "base/message_loop.h"
11 #include "base/metrics/histogram.h" 14 #include "base/metrics/histogram.h"
12 #include "base/process_util.h" 15 #include "base/process_util.h"
16 #include "base/win/metro.h"
17 #include "base/win/scoped_comptr.h"
13 #include "base/win/wrapped_window_proc.h" 18 #include "base/win/wrapped_window_proc.h"
14 19
20 namespace base {
21
22 static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow";
23
24 // Message sent to get an additional time slice for pumping (processing) another
25 // task (a series of such messages creates a continuous task pump).
26 static const int kMsgHaveWork = WM_USER + 1;
27
28 class TextServicesBridge {
29 public:
30 TfClientId client_id;
31 bool is_initialized;
32 base::win::ScopedComPtr<ITfThreadMgr> thread_mgr;
33 base::win::ScopedComPtr<ITfMessagePump> message_pump;
34 base::win::ScopedComPtr<ITfKeystrokeMgr> keystroke_mgr;
35 TextServicesBridge()
36 : client_id(TF_CLIENTID_NULL), is_initialized(true) {
37 is_initialized &= SUCCEEDED(thread_mgr.CreateInstance(CLSID_TF_ThreadMgr));
38 if (!thread_mgr)
39 return;
40
41 is_initialized &= SUCCEEDED(message_pump.QueryFrom(thread_mgr));
42 is_initialized &= SUCCEEDED(keystroke_mgr.QueryFrom(thread_mgr));
43 // When activate succeeded, |client_id| is set valid value.
44 is_initialized &= SUCCEEDED(thread_mgr->Activate(&client_id));
45 }
46 ~TextServicesBridge() {
47 if (thread_mgr && client_id != TF_CLIENTID_NULL)
48 thread_mgr->Deactivate();
49 }
50
51 bool ForwardKeyMessageForTSF(const MSG& msg) {
52 if (!is_initialized)
53 return false;
54
55 if(msg.message == WM_KEYDOWN) {
56 BOOL eaten = FALSE;
57 HRESULT hr = keystroke_mgr->TestKeyDown(msg.wParam, msg.lParam, &eaten);
58 if (FAILED(hr) && !eaten)
59 return false;
60 eaten = FALSE;
61 hr = keystroke_mgr->KeyDown(msg.wParam, msg.lParam, &eaten);
62 return (SUCCEEDED(hr) && eaten);
63 }
64
65 if(msg.message == WM_KEYUP) {
66 BOOL eaten = FALSE;
67 HRESULT hr = keystroke_mgr->TestKeyUp(msg.wParam, msg.lParam, &eaten);
68 if (FAILED(hr) && !eaten)
69 return false;
70 eaten = FALSE;
71 hr = keystroke_mgr->KeyUp(msg.wParam, msg.lParam, &eaten);
72 return (SUCCEEDED(hr) && eaten);
73 }
74
75 return false;
76 }
77
78 bool PeekMessageForTSF(MSG* msg, HWND hwnd, UINT wMsgFilterMin,
79 UINT wMsgFilterMax, UINT wRemoveMsg) {
80 if (!is_initialized)
81 // FallBack.
82 return !!::PeekMessage(msg, hwnd, wMsgFilterMin, wMsgFilterMax,
83 wRemoveMsg);
84
85 BOOL result = FALSE;
86 if(FAILED(message_pump->PeekMessage(msg, hwnd, wMsgFilterMin, wMsgFilterMax,
87 wRemoveMsg, &result)))
88 result = FALSE;
89 return !!result;
90 }
91 };
92
15 namespace { 93 namespace {
16 94
17 enum MessageLoopProblems { 95 enum MessageLoopProblems {
18 MESSAGE_POST_ERROR, 96 MESSAGE_POST_ERROR,
19 COMPLETION_POST_ERROR, 97 COMPLETION_POST_ERROR,
20 SET_TIMER_ERROR, 98 SET_TIMER_ERROR,
21 MESSAGE_LOOP_PROBLEM_MAX, 99 MESSAGE_LOOP_PROBLEM_MAX,
22 }; 100 };
23 101
24 } // namespace 102 } // namespace
25 103
26 namespace base {
27
28 static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow";
29
30 // Message sent to get an additional time slice for pumping (processing) another
31 // task (a series of such messages creates a continuous task pump).
32 static const int kMsgHaveWork = WM_USER + 1;
33
34 //----------------------------------------------------------------------------- 104 //-----------------------------------------------------------------------------
35 // MessagePumpWin public: 105 // MessagePumpWin public:
36 106
37 void MessagePumpWin::AddObserver(MessagePumpObserver* observer) { 107 void MessagePumpWin::AddObserver(MessagePumpObserver* observer) {
38 observers_.AddObserver(observer); 108 observers_.AddObserver(observer);
39 } 109 }
40 110
41 void MessagePumpWin::RemoveObserver(MessagePumpObserver* observer) { 111 void MessagePumpWin::RemoveObserver(MessagePumpObserver* observer) {
42 observers_.RemoveObserver(observer); 112 observers_.RemoveObserver(observer);
43 } 113 }
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
90 delay = 0; 160 delay = 0;
91 161
92 return delay; 162 return delay;
93 } 163 }
94 164
95 //----------------------------------------------------------------------------- 165 //-----------------------------------------------------------------------------
96 // MessagePumpForUI public: 166 // MessagePumpForUI public:
97 167
98 MessagePumpForUI::MessagePumpForUI() : instance_(NULL) { 168 MessagePumpForUI::MessagePumpForUI() : instance_(NULL) {
99 InitMessageWnd(); 169 InitMessageWnd();
170
171 if (base::win::IsTsfAwareRequired())
172 text_services_bridge_.reset(new TextServicesBridge);
100 } 173 }
101 174
102 MessagePumpForUI::~MessagePumpForUI() { 175 MessagePumpForUI::~MessagePumpForUI() {
176 if (base::win::IsTsfAwareRequired())
177 text_services_bridge_.reset(NULL);
103 DestroyWindow(message_hwnd_); 178 DestroyWindow(message_hwnd_);
104 UnregisterClass(kWndClass, instance_); 179 UnregisterClass(kWndClass, instance_);
105 } 180 }
106 181
107 void MessagePumpForUI::ScheduleWork() { 182 void MessagePumpForUI::ScheduleWork() {
108 if (InterlockedExchange(&have_work_, 1)) 183 if (InterlockedExchange(&have_work_, 1))
109 return; // Someone else continued the pumping. 184 return; // Someone else continued the pumping.
110 185
111 // Make sure the MessagePump does some work for us. 186 // Make sure the MessagePump does some work for us.
112 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork, 187 BOOL ret = PostMessage(message_hwnd_, kMsgHaveWork,
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 return; 250 return;
176 251
177 // Create a mini-message-pump to force immediate processing of only Windows 252 // Create a mini-message-pump to force immediate processing of only Windows
178 // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking 253 // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking
179 // to get the job done. Actual common max is 4 peeks, but we'll be a little 254 // to get the job done. Actual common max is 4 peeks, but we'll be a little
180 // safe here. 255 // safe here.
181 const int kMaxPeekCount = 20; 256 const int kMaxPeekCount = 20;
182 int peek_count; 257 int peek_count;
183 for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) { 258 for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
184 MSG msg; 259 MSG msg;
185 if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT)) 260 if (!ProcessPeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
186 break; 261 break;
187 ProcessMessageHelper(msg); 262 ProcessMessageHelper(msg);
188 if (state_->should_quit) // Handle WM_QUIT. 263 if (state_->should_quit) // Handle WM_QUIT.
189 break; 264 break;
190 } 265 }
191 // Histogram what was really being used, to help to adjust kMaxPeekCount. 266 // Histogram what was really being used, to help to adjust kMaxPeekCount.
192 DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count); 267 DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count);
193 } 268 }
194 269
195 //----------------------------------------------------------------------------- 270 //-----------------------------------------------------------------------------
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
297 // This causes the MsgWaitForMultipleObjectsEx API to return indicating 372 // This causes the MsgWaitForMultipleObjectsEx API to return indicating
298 // that messages are ready for processing (specifically mouse messages 373 // that messages are ready for processing (specifically mouse messages
299 // intended for the child window. Occurs if the child window has capture) 374 // intended for the child window. Occurs if the child window has capture)
300 // The subsequent PeekMessages call fails to return any messages thus 375 // The subsequent PeekMessages call fails to return any messages thus
301 // causing us to enter a tight loop at times. 376 // causing us to enter a tight loop at times.
302 // The WaitMessage call below is a workaround to give the child window 377 // The WaitMessage call below is a workaround to give the child window
303 // sometime to process its input messages. 378 // sometime to process its input messages.
304 MSG msg = {0}; 379 MSG msg = {0};
305 DWORD queue_status = GetQueueStatus(QS_MOUSE); 380 DWORD queue_status = GetQueueStatus(QS_MOUSE);
306 if (HIWORD(queue_status) & QS_MOUSE && 381 if (HIWORD(queue_status) & QS_MOUSE &&
307 !PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) { 382 !ProcessPeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST,
383 PM_NOREMOVE)) {
308 WaitMessage(); 384 WaitMessage();
309 } 385 }
310 return; 386 return;
311 } 387 }
312 388
313 DCHECK_NE(WAIT_FAILED, result) << GetLastError(); 389 DCHECK_NE(WAIT_FAILED, result) << GetLastError();
314 } 390 }
315 391
316 void MessagePumpForUI::HandleWorkMessage() { 392 void MessagePumpForUI::HandleWorkMessage() {
317 // If we are being called outside of the context of Run, then don't try to do 393 // If we are being called outside of the context of Run, then don't try to do
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 // If there are sent messages in the queue then PeekMessage internally 430 // If there are sent messages in the queue then PeekMessage internally
355 // dispatches the message and returns false. We return true in this 431 // dispatches the message and returns false. We return true in this
356 // case to ensure that the message loop peeks again instead of calling 432 // case to ensure that the message loop peeks again instead of calling
357 // MsgWaitForMultipleObjectsEx again. 433 // MsgWaitForMultipleObjectsEx again.
358 bool sent_messages_in_queue = false; 434 bool sent_messages_in_queue = false;
359 DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE); 435 DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE);
360 if (HIWORD(queue_status) & QS_SENDMESSAGE) 436 if (HIWORD(queue_status) & QS_SENDMESSAGE)
361 sent_messages_in_queue = true; 437 sent_messages_in_queue = true;
362 438
363 MSG msg; 439 MSG msg;
364 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 440 if (ProcessPeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
365 return ProcessMessageHelper(msg); 441 return ProcessMessageHelper(msg);
366 442
367 return sent_messages_in_queue; 443 return sent_messages_in_queue;
368 } 444 }
369 445
370 bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { 446 bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
371 TRACE_EVENT1("base", "MessagePumpForUI::ProcessMessageHelper", 447 TRACE_EVENT1("base", "MessagePumpForUI::ProcessMessageHelper",
372 "message", msg.message); 448 "message", msg.message);
373 if (WM_QUIT == msg.message) { 449 if (WM_QUIT == msg.message) {
374 // Repost the QUIT message so that it will be retrieved by the primary 450 // Repost the QUIT message so that it will be retrieved by the primary
375 // GetMessage() loop. 451 // GetMessage() loop.
376 state_->should_quit = true; 452 state_->should_quit = true;
377 PostQuitMessage(static_cast<int>(msg.wParam)); 453 PostQuitMessage(static_cast<int>(msg.wParam));
378 return false; 454 return false;
379 } 455 }
380 456
381 // While running our main message pump, we discard kMsgHaveWork messages. 457 // While running our main message pump, we discard kMsgHaveWork messages.
382 if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) 458 if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
383 return ProcessPumpReplacementMessage(); 459 return ProcessPumpReplacementMessage();
384 460
385 if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) 461 if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode))
386 return true; 462 return true;
387 463
388 WillProcessMessage(msg); 464 WillProcessMessage(msg);
389 465
390 if (state_->dispatcher) { 466 bool is_key_dispatched_by_tsf = false;
391 if (!state_->dispatcher->Dispatch(msg)) 467 if (base::win::IsTsfAwareRequired()) {
392 state_->should_quit = true; 468 is_key_dispatched_by_tsf =
393 } else { 469 text_services_bridge_->ForwardKeyMessageForTSF(msg);
394 TranslateMessage(&msg); 470 }
395 DispatchMessage(&msg); 471
472 if (!is_key_dispatched_by_tsf) {
473 if (state_->dispatcher) {
474 if (!state_->dispatcher->Dispatch(msg))
475 state_->should_quit = true;
476 } else {
477 TranslateMessage(&msg);
478 DispatchMessage(&msg);
479 }
396 } 480 }
397 481
398 DidProcessMessage(msg); 482 DidProcessMessage(msg);
399 return true; 483 return true;
400 } 484 }
401 485
402 bool MessagePumpForUI::ProcessPumpReplacementMessage() { 486 bool MessagePumpForUI::ProcessPumpReplacementMessage() {
403 // When we encounter a kMsgHaveWork message, this method is called to peek 487 // When we encounter a kMsgHaveWork message, this method is called to peek
404 // and process a replacement message, such as a WM_PAINT or WM_TIMER. The 488 // and process a replacement message, such as a WM_PAINT or WM_TIMER. The
405 // goal is to make the kMsgHaveWork as non-intrusive as possible, even though 489 // goal is to make the kMsgHaveWork as non-intrusive as possible, even though
406 // a continuous stream of such messages are posted. This method carefully 490 // a continuous stream of such messages are posted. This method carefully
407 // peeks a message while there is no chance for a kMsgHaveWork to be pending, 491 // peeks a message while there is no chance for a kMsgHaveWork to be pending,
408 // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to 492 // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
409 // possibly be posted), and finally dispatches that peeked replacement. Note 493 // possibly be posted), and finally dispatches that peeked replacement. Note
410 // that the re-post of kMsgHaveWork may be asynchronous to this thread!! 494 // that the re-post of kMsgHaveWork may be asynchronous to this thread!!
411 495
412 bool have_message = false; 496 bool have_message = false;
413 MSG msg; 497 MSG msg;
414 // We should not process all window messages if we are in the context of an 498 // We should not process all window messages if we are in the context of an
415 // OS modal loop, i.e. in the context of a windows API call like MessageBox. 499 // OS modal loop, i.e. in the context of a windows API call like MessageBox.
416 // This is to ensure that these messages are peeked out by the OS modal loop. 500 // This is to ensure that these messages are peeked out by the OS modal loop.
417 if (MessageLoop::current()->os_modal_loop()) { 501 if (MessageLoop::current()->os_modal_loop()) {
418 // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above. 502 // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above.
419 have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || 503 have_message =
420 PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); 504 ProcessPeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
Yohei Yukawa 2012/08/16 02:54:46 nit: indent.
yoichio 2012/08/16 06:14:13 Done.
505 ProcessPeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
421 } else { 506 } else {
422 have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); 507 have_message = ProcessPeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
423 } 508 }
424 509
425 DCHECK(!have_message || kMsgHaveWork != msg.message || 510 DCHECK(!have_message || kMsgHaveWork != msg.message ||
426 msg.hwnd != message_hwnd_); 511 msg.hwnd != message_hwnd_);
427 512
428 // Since we discarded a kMsgHaveWork message, we must update the flag. 513 // Since we discarded a kMsgHaveWork message, we must update the flag.
429 int old_have_work = InterlockedExchange(&have_work_, 0); 514 int old_have_work = InterlockedExchange(&have_work_, 0);
430 DCHECK(old_have_work); 515 DCHECK(old_have_work);
431 516
432 // We don't need a special time slice if we didn't have_message to process. 517 // We don't need a special time slice if we didn't have_message to process.
433 if (!have_message) 518 if (!have_message)
434 return false; 519 return false;
435 520
436 // Guarantee we'll get another time slice in the case where we go into native 521 // Guarantee we'll get another time slice in the case where we go into native
437 // windows code. This ScheduleWork() may hurt performance a tiny bit when 522 // windows code. This ScheduleWork() may hurt performance a tiny bit when
438 // tasks appear very infrequently, but when the event queue is busy, the 523 // tasks appear very infrequently, but when the event queue is busy, the
439 // kMsgHaveWork events get (percentage wise) rarer and rarer. 524 // kMsgHaveWork events get (percentage wise) rarer and rarer.
440 ScheduleWork(); 525 ScheduleWork();
441 return ProcessMessageHelper(msg); 526 return ProcessMessageHelper(msg);
442 } 527 }
443 528
529 bool MessagePumpForUI::ProcessPeekMessage(
530 MSG* pMsg, HWND hwnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg)
531 {
532 if (base::win::IsTsfAwareRequired())
533 return text_services_bridge_->PeekMessageForTSF(
534 pMsg, hwnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
535
536 return !!::PeekMessage(pMsg, hwnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
537 }
538
444 //----------------------------------------------------------------------------- 539 //-----------------------------------------------------------------------------
445 // MessagePumpForIO public: 540 // MessagePumpForIO public:
446 541
447 MessagePumpForIO::MessagePumpForIO() { 542 MessagePumpForIO::MessagePumpForIO() {
448 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); 543 port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1));
449 DCHECK(port_.IsValid()); 544 DCHECK(port_.IsValid());
450 } 545 }
451 546
452 void MessagePumpForIO::ScheduleWork() { 547 void MessagePumpForIO::ScheduleWork() {
453 if (InterlockedExchange(&have_work_, 1)) 548 if (InterlockedExchange(&have_work_, 1))
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
617 712
618 void MessagePumpForIO::WillProcessIOEvent() { 713 void MessagePumpForIO::WillProcessIOEvent() {
619 FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent()); 714 FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent());
620 } 715 }
621 716
622 void MessagePumpForIO::DidProcessIOEvent() { 717 void MessagePumpForIO::DidProcessIOEvent() {
623 FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent()); 718 FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent());
624 } 719 }
625 720
626 } // namespace base 721 } // namespace base
OLDNEW
« no previous file with comments | « base/message_pump_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698