1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file.
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include <stddef.h>
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <windows.h>
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <mmsystem.h>
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
9c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/event_recorder.h"
10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/file_util.h"
11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h"
12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// A note about time.
14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// For perfect playback of events, you'd like a very accurate timer
15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// so that events are played back at exactly the same time that
16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// they were recorded.  However, windows has a clock which is only
17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// granular to ~15ms.  We see more consistent event playback when
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// using a higher resolution timer.  To do this, we use the
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// timeGetTime API instead of the default GetTickCount() API.
20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace base {
22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottEventRecorder* EventRecorder::current_ = NULL;
24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottLRESULT CALLBACK StaticRecordWndProc(int nCode, WPARAM wParam,
26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     LPARAM lParam) {
27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  CHECK(EventRecorder::current());
28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam);
29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottLRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam,
32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                       LPARAM lParam) {
33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  CHECK(EventRecorder::current());
34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return EventRecorder::current()->PlaybackWndProc(nCode, wParam, lParam);
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottEventRecorder::~EventRecorder() {
38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Try to assert early if the caller deletes the recorder
39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // while it is still in use.
40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(!journal_hook_);
41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(!is_recording_ && !is_playing_);
42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool EventRecorder::StartRecording(const FilePath& filename) {
45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (journal_hook_ != NULL)
46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (is_recording_ || is_playing_)
48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Open the recording file.
51dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  DCHECK(!file_);
52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  file_ = file_util::OpenFile(filename, "wb+");
53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!file_) {
54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DLOG(ERROR) << "EventRecorder could not open log file";
55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Set the faster clock, if possible.
59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  ::timeBeginPeriod(1);
60c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Set the recording hook.  JOURNALRECORD can only be used as a global hook.
62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc,
63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     GetModuleHandle(NULL), 0);
64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!journal_hook_) {
65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DLOG(ERROR) << "EventRecorder Record Hook failed";
66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    file_util::CloseFile(file_);
67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  is_recording_ = true;
71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid EventRecorder::StopRecording() {
75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (is_recording_) {
76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DCHECK(journal_hook_ != NULL);
77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!::UnhookWindowsHookEx(journal_hook_)) {
79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      DLOG(ERROR) << "EventRecorder Unhook failed";
80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Nothing else we can really do here.
81c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return;
82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ::timeEndPeriod(1);
85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
86c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DCHECK(file_ != NULL);
87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    file_util::CloseFile(file_);
88c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    file_ = NULL;
89c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    journal_hook_ = NULL;
91c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    is_recording_ = false;
92c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool EventRecorder::StartPlayback(const FilePath& filename) {
96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (journal_hook_ != NULL)
97c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
98c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (is_recording_ || is_playing_)
99c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Open the recording file.
102dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  DCHECK(!file_);
103c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  file_ = file_util::OpenFile(filename, "rb");
104c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!file_) {
105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DLOG(ERROR) << "EventRecorder Playback could not open log file";
106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
108c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Read the first event from the record.
109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) {
110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DLOG(ERROR) << "EventRecorder Playback has no records!";
111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    file_util::CloseFile(file_);
112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
115c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Set the faster clock, if possible.
116c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  ::timeBeginPeriod(1);
117c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
118c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Playback time is tricky.  When playing back, we read a series of events,
119c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // each with timeouts.  Simply subtracting the delta between two timers will
120c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // lead to fast playback (about 2x speed).  The API has two events, one
121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // which advances to the next event (HC_SKIP), and another that requests the
122c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // event (HC_GETNEXT).  The same event will be requested multiple times.
123c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Each time the event is requested, we must calculate the new delay.
124c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // To do this, we track the start time of the playback, and constantly
125c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // re-compute the delay.   I mention this only because I saw two examples
126c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // of how to use this code on the net, and both were broken :-)
127c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  playback_start_time_ = timeGetTime();
128c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  playback_first_msg_time_ = playback_msg_.time;
129c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
130c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Set the hook.  JOURNALPLAYBACK can only be used as a global hook.
131c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  journal_hook_ = ::SetWindowsHookEx(WH_JOURNALPLAYBACK, StaticPlaybackWndProc,
132c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     GetModuleHandle(NULL), 0);
133c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!journal_hook_) {
134c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DLOG(ERROR) << "EventRecorder Playback Hook failed";
135c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
136c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
137c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
138c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  is_playing_ = true;
139c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
140c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
141c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
142c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
143c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid EventRecorder::StopPlayback() {
144c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (is_playing_) {
145c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DCHECK(journal_hook_ != NULL);
146c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
147c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!::UnhookWindowsHookEx(journal_hook_)) {
148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      DLOG(ERROR) << "EventRecorder Unhook failed";
149c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Nothing else we can really do here.
150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DCHECK(file_ != NULL);
153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    file_util::CloseFile(file_);
154c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    file_ = NULL;
155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ::timeEndPeriod(1);
157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    journal_hook_ = NULL;
159c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    is_playing_ = false;
160c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
161c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Windows callback hook for the recorder.
164c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottLRESULT EventRecorder::RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  static bool recording_enabled = true;
166c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  EVENTMSG* msg_ptr = NULL;
167c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // The API says we have to do this.
169c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // See http://msdn2.microsoft.com/en-us/library/ms644983(VS.85).aspx
170c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (nCode < 0)
171c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
172c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
173c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Check for the break key being pressed and stop recording.
174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (::GetKeyState(VK_CANCEL) & 0x8000) {
175c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    StopRecording();
176c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
177c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
178c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // The Journal Recorder must stop recording events when system modal
180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // dialogs are present. (see msdn link above)
181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  switch (nCode) {
182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case HC_SYSMODALON:
183c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      recording_enabled = false;
184c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
185c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case HC_SYSMODALOFF:
186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      recording_enabled = true;
187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
189c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (nCode == HC_ACTION && recording_enabled) {
191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Aha - we have an event to record.
192c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    msg_ptr = reinterpret_cast<EVENTMSG*>(lParam);
193c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    msg_ptr->time = timeGetTime();
194c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    fwrite(msg_ptr, sizeof(EVENTMSG), 1, file_);
195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    fflush(file_);
196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
199c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
200c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
201c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Windows callback for the playback mode.
202c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottLRESULT EventRecorder::PlaybackWndProc(int nCode, WPARAM wParam,
203c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                       LPARAM lParam) {
204c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  static bool playback_enabled = true;
205c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int delay = 0;
206c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
207c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  switch (nCode) {
208c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // A system modal dialog box is being displayed.  Stop playing back
209c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // messages.
210c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case HC_SYSMODALON:
211c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      playback_enabled = false;
212c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
214c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // A system modal dialog box is destroyed.  We can start playing back
215c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // messages again.
216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case HC_SYSMODALOFF:
217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      playback_enabled = true;
218c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
219c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
220c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Prepare to copy the next mouse or keyboard event to playback.
221c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case HC_SKIP:
222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (!playback_enabled)
223c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        break;
224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
225c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Read the next event from the record.
226c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1)
227c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        this->StopPlayback();
228c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
229c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
230c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Copy the mouse or keyboard event to the EVENTMSG structure in lParam.
231c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case HC_GETNEXT:
232c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (!playback_enabled)
233c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        break;
234c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
235c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      memcpy(reinterpret_cast<void*>(lParam), &playback_msg_,
236c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott             sizeof(playback_msg_));
237c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
238c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // The return value is the amount of time (in milliseconds) to wait
239c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // before playing back the next message in the playback queue.  Each
240c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // time this is called, we recalculate the delay relative to our current
241c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // wall clock.
242c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      delay = (playback_msg_.time - playback_first_msg_time_) -
243c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott              (timeGetTime() - playback_start_time_);
244c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (delay < 0)
245c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        delay = 0;
246c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return delay;
247c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
248c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // An application has called PeekMessage with wRemoveMsg set to PM_NOREMOVE
249c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // indicating that the message is not removed from the message queue after
250c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // PeekMessage processing.
251c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case HC_NOREMOVE:
252c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
253c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
254c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
255c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
256c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
257c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
258c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}  // namespace base
259