summaryrefslogtreecommitdiffstats
path: root/remoting/host/local_input_monitor_thread_win.cc
blob: 8e08a2b91579317c2d6e9fef5a2f39098ee1ce91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/local_input_monitor_thread_win.h"

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/synchronization/waitable_event.h"
#include "remoting/host/mouse_move_observer.h"
#include "third_party/skia/include/core/SkPoint.h"

namespace remoting {

namespace {
LocalInputMonitorThread* g_local_input_monitor_thread = NULL;
base::LazyInstance<base::Lock>::Leaky g_thread_lock = LAZY_INSTANCE_INITIALIZER;
}  // namespace

LocalInputMonitorThread::LocalInputMonitorThread()
    : base::SimpleThread("LocalInputMonitor") {
}

LocalInputMonitorThread::~LocalInputMonitorThread() {
  DCHECK(observers_.empty());
}

void LocalInputMonitorThread::AddObserver(
    MouseMoveObserver* mouse_move_observer) {
  base::AutoLock lock(lock_);
  observers_.insert(mouse_move_observer);
}

bool LocalInputMonitorThread::RemoveObserver(
    MouseMoveObserver* mouse_move_observer) {
  base::AutoLock lock(lock_);
  observers_.erase(mouse_move_observer);
  return observers_.empty();
}

void LocalInputMonitorThread::Stop() {
  CHECK(PostThreadMessage(tid(), WM_QUIT, 0, 0));
  Join();
}

void LocalInputMonitorThread::Run() {
  extern HMODULE g_hModule;
  HHOOK win_event_hook = SetWindowsHookEx(WH_MOUSE_LL, HandleLowLevelMouseEvent,
                                          g_hModule, 0);
  if (!win_event_hook) {
    DWORD err = GetLastError();
    LOG(ERROR) << "SetWindowHookEx failed: " << err;
    return;
  }

  MSG msg;
  BOOL result;
  while ((result = GetMessage(&msg, NULL, 0, 0)) != 0) {
    if (result == -1) {
      DWORD err = GetLastError();
      LOG(ERROR) << "GetMessage failed: " << err;
      break;
    } else {
      DispatchMessage(&msg);
    }
  }

  if (win_event_hook) {
    CHECK(UnhookWindowsHookEx(win_event_hook));
  }
}

void LocalInputMonitorThread::LocalMouseMoved(const SkIPoint& mouse_position) {
  base::AutoLock lock(lock_);
  for (MouseMoveObservers::const_iterator i = observers_.begin();
       i != observers_.end(); ++i) {
    (*i)->OnLocalMouseMoved(mouse_position);
  }
}

LRESULT WINAPI LocalInputMonitorThread::HandleLowLevelMouseEvent(
    int code, WPARAM event_type, LPARAM event_data) {
  if (code == HC_ACTION) {
    if (event_type == WM_MOUSEMOVE) {
      PMSLLHOOKSTRUCT data = reinterpret_cast<PMSLLHOOKSTRUCT>(event_data);
      if ((data->flags & LLMHF_INJECTED) == 0) {
        SkIPoint mouse_position = SkIPoint::Make(data->pt.x, data->pt.y);
        // |g_local_input_monitor_thread| cannot be NULL because this function
        // is called from within the GetMessage call running on that thread.
        DCHECK(g_local_input_monitor_thread);
        g_local_input_monitor_thread->LocalMouseMoved(mouse_position);
      }
    }
  }
  return CallNextHookEx(NULL, code, event_type, event_data);
}


// static
void LocalInputMonitorThread::AddMouseMoveObserver(
    MouseMoveObserver* mouse_move_observer) {
  base::AutoLock lock(g_thread_lock.Get());
  if (!g_local_input_monitor_thread) {
    g_local_input_monitor_thread = new LocalInputMonitorThread;
    g_local_input_monitor_thread->Start();
  }
  g_local_input_monitor_thread->AddObserver(mouse_move_observer);
}

// static
void LocalInputMonitorThread::RemoveMouseMoveObserver(
    MouseMoveObserver* mouse_move_observer) {
  DCHECK(g_local_input_monitor_thread);
  base::AutoLock lock(g_thread_lock.Get());
  if (g_local_input_monitor_thread->RemoveObserver(mouse_move_observer)) {
    g_local_input_monitor_thread->Stop();
    delete g_local_input_monitor_thread;
    g_local_input_monitor_thread = NULL;
  }
}

}  // namespace remoting