15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stddef.h>
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h>
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <mmsystem.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/event_recorder.h"
106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/files/file_util.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A note about time.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For perfect playback of events, you'd like a very accurate timer
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// so that events are played back at exactly the same time that
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// they were recorded.  However, windows has a clock which is only
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// granular to ~15ms.  We see more consistent event playback when
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// using a higher resolution timer.  To do this, we use the
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// timeGetTime API instead of the default GetTickCount() API.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace base {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EventRecorder* EventRecorder::current_ = NULL;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LRESULT CALLBACK StaticRecordWndProc(int nCode, WPARAM wParam,
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     LPARAM lParam) {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(EventRecorder::current());
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam);
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam,
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       LPARAM lParam) {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(EventRecorder::current());
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return EventRecorder::current()->PlaybackWndProc(nCode, wParam, lParam);
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EventRecorder::~EventRecorder() {
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try to assert early if the caller deletes the recorder
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // while it is still in use.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!journal_hook_);
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!is_recording_ && !is_playing_);
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool EventRecorder::StartRecording(const FilePath& filename) {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (journal_hook_ != NULL)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_recording_ || is_playing_)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Open the recording file.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!file_);
52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  file_ = OpenFile(filename, "wb+");
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!file_) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(ERROR) << "EventRecorder could not open log file";
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the faster clock, if possible.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::timeBeginPeriod(1);
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the recording hook.  JOURNALRECORD can only be used as a global hook.
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc,
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     GetModuleHandle(NULL), 0);
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!journal_hook_) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(ERROR) << "EventRecorder Record Hook failed";
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    CloseFile(file_);
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_recording_ = true;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void EventRecorder::StopRecording() {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_recording_) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(journal_hook_ != NULL);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!::UnhookWindowsHookEx(journal_hook_)) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DLOG(ERROR) << "EventRecorder Unhook failed";
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Nothing else we can really do here.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::timeEndPeriod(1);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(file_ != NULL);
87a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    CloseFile(file_);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_ = NULL;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    journal_hook_ = NULL;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is_recording_ = false;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool EventRecorder::StartPlayback(const FilePath& filename) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (journal_hook_ != NULL)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_recording_ || is_playing_)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Open the recording file.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!file_);
103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  file_ = OpenFile(filename, "rb");
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!file_) {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(ERROR) << "EventRecorder Playback could not open log file";
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Read the first event from the record.
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(ERROR) << "EventRecorder Playback has no records!";
111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    CloseFile(file_);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the faster clock, if possible.
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ::timeBeginPeriod(1);
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Playback time is tricky.  When playing back, we read a series of events,
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // each with timeouts.  Simply subtracting the delta between two timers will
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // lead to fast playback (about 2x speed).  The API has two events, one
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // which advances to the next event (HC_SKIP), and another that requests the
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // event (HC_GETNEXT).  The same event will be requested multiple times.
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Each time the event is requested, we must calculate the new delay.
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // To do this, we track the start time of the playback, and constantly
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // re-compute the delay.   I mention this only because I saw two examples
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // of how to use this code on the net, and both were broken :-)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  playback_start_time_ = timeGetTime();
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  playback_first_msg_time_ = playback_msg_.time;
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the hook.  JOURNALPLAYBACK can only be used as a global hook.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  journal_hook_ = ::SetWindowsHookEx(WH_JOURNALPLAYBACK, StaticPlaybackWndProc,
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     GetModuleHandle(NULL), 0);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!journal_hook_) {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DLOG(ERROR) << "EventRecorder Playback Hook failed";
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_playing_ = true;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void EventRecorder::StopPlayback() {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (is_playing_) {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(journal_hook_ != NULL);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!::UnhookWindowsHookEx(journal_hook_)) {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DLOG(ERROR) << "EventRecorder Unhook failed";
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Nothing else we can really do here.
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(file_ != NULL);
153a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    CloseFile(file_);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_ = NULL;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::timeEndPeriod(1);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    journal_hook_ = NULL;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is_playing_ = false;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Windows callback hook for the recorder.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LRESULT EventRecorder::RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static bool recording_enabled = true;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EVENTMSG* msg_ptr = NULL;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The API says we have to do this.
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // See http://msdn2.microsoft.com/en-us/library/ms644983(VS.85).aspx
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (nCode < 0)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check for the break key being pressed and stop recording.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (::GetKeyState(VK_CANCEL) & 0x8000) {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StopRecording();
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The Journal Recorder must stop recording events when system modal
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // dialogs are present. (see msdn link above)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (nCode) {
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case HC_SYSMODALON:
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      recording_enabled = false;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case HC_SYSMODALOFF:
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      recording_enabled = true;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (nCode == HC_ACTION && recording_enabled) {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Aha - we have an event to record.
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    msg_ptr = reinterpret_cast<EVENTMSG*>(lParam);
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    msg_ptr->time = timeGetTime();
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fwrite(msg_ptr, sizeof(EVENTMSG), 1, file_);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fflush(file_);
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Windows callback for the playback mode.
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LRESULT EventRecorder::PlaybackWndProc(int nCode, WPARAM wParam,
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       LPARAM lParam) {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static bool playback_enabled = true;
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int delay = 0;
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (nCode) {
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // A system modal dialog box is being displayed.  Stop playing back
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // messages.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case HC_SYSMODALON:
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      playback_enabled = false;
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // A system modal dialog box is destroyed.  We can start playing back
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // messages again.
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case HC_SYSMODALOFF:
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      playback_enabled = true;
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Prepare to copy the next mouse or keyboard event to playback.
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case HC_SKIP:
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!playback_enabled)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Read the next event from the record.
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this->StopPlayback();
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Copy the mouse or keyboard event to the EVENTMSG structure in lParam.
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case HC_GETNEXT:
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!playback_enabled)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      memcpy(reinterpret_cast<void*>(lParam), &playback_msg_,
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             sizeof(playback_msg_));
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The return value is the amount of time (in milliseconds) to wait
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // before playing back the next message in the playback queue.  Each
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // time this is called, we recalculate the delay relative to our current
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // wall clock.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delay = (playback_msg_.time - playback_first_msg_time_) -
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              (timeGetTime() - playback_start_time_);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (delay < 0)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delay = 0;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return delay;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // An application has called PeekMessage with wRemoveMsg set to PM_NOREMOVE
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // indicating that the message is not removed from the message queue after
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // PeekMessage processing.
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case HC_NOREMOVE:
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace base
259