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