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

Side by Side Diff: media/base/user_input_monitor_linux.cc

Issue 21183002: Adding key press detection in the browser process. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 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 "remoting/host/local_input_monitor.h" 5 #include "media/base/user_input_monitor.h"
6 6
7 #include <sys/select.h> 7 #include <sys/select.h>
8 #include <unistd.h> 8 #include <unistd.h>
9 #define XK_MISCELLANY 9 #define XK_MISCELLANY
10 #include <X11/keysymdef.h> 10 #include <X11/keysymdef.h>
11 11
12 #include "base/basictypes.h" 12 #include "base/basictypes.h"
13 #include "base/bind.h" 13 #include "base/bind.h"
14 #include "base/callback.h" 14 #include "base/callback.h"
15 #include "base/compiler_specific.h" 15 #include "base/compiler_specific.h"
16 #include "base/location.h" 16 #include "base/location.h"
17 #include "base/logging.h" 17 #include "base/logging.h"
18 #include "base/message_loop/message_loop.h" 18 #include "base/message_loop/message_loop.h"
19 #include "base/message_loop/message_pump_libevent.h" 19 #include "base/message_loop/message_pump_libevent.h"
20 #include "base/posix/eintr_wrapper.h" 20 #include "base/posix/eintr_wrapper.h"
21 #include "base/single_thread_task_runner.h" 21 #include "base/single_thread_task_runner.h"
22 #include "base/threading/non_thread_safe.h" 22 #include "base/threading/non_thread_safe.h"
23 #include "remoting/host/client_session_control.h"
24 #include "third_party/skia/include/core/SkPoint.h" 23 #include "third_party/skia/include/core/SkPoint.h"
24 #include "ui/base/keycodes/keyboard_code_conversion_x.h"
25 25
26 // These includes need to be later than dictated by the style guide due to 26 // These includes need to be later than dictated by the style guide due to
27 // Xlib header pollution, specifically the min, max, and Status macros. 27 // Xlib header pollution, specifically the min, max, and Status macros.
28 #include <X11/XKBlib.h> 28 #include <X11/XKBlib.h>
29 #include <X11/Xlibint.h> 29 #include <X11/Xlibint.h>
30 #include <X11/extensions/record.h> 30 #include <X11/extensions/record.h>
31 31
32 namespace remoting { 32 namespace media {
33 33
34 namespace { 34 namespace {
35 35
36 class LocalInputMonitorLinux : public base::NonThreadSafe, 36 class UserInputMonitorLinux : public base::NonThreadSafe,
37 public LocalInputMonitor { 37 public UserInputMonitor {
38 public: 38 public:
39 LocalInputMonitorLinux( 39 UserInputMonitorLinux(
40 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, 40 const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner,
41 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, 41 const scoped_refptr<base::SingleThreadTaskRunner>& observer_task_runner,
42 base::WeakPtr<ClientSessionControl> client_session_control); 42 uint8 input_event_mask,
43 virtual ~LocalInputMonitorLinux(); 43 const base::WeakPtr<UserInputObserver>& observer);
44 virtual ~UserInputMonitorLinux();
44 45
45 private: 46 private:
46 // The actual implementation resides in LocalInputMonitorLinux::Core class. 47 // The actual implementation resides in UserInputMonitorLinux::Core class.
47 class Core 48 class Core : public base::RefCountedThreadSafe<Core>,
48 : public base::RefCountedThreadSafe<Core>, 49 public base::MessagePumpLibevent::Watcher {
49 public base::MessagePumpLibevent::Watcher {
50 public: 50 public:
51 Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, 51 Core(
52 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, 52 const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner,
53 base::WeakPtr<ClientSessionControl> client_session_control); 53 const scoped_refptr<base::SingleThreadTaskRunner>& observer_task_runner,
54 uint8 input_event_mask,
55 const base::WeakPtr<UserInputObserver>& observer);
54 56
55 void Start(); 57 void Start();
56 void Stop(); 58 void Stop();
57 59
58 private: 60 private:
59 friend class base::RefCountedThreadSafe<Core>; 61 friend class base::RefCountedThreadSafe<Core>;
60 virtual ~Core(); 62 virtual ~Core();
61 63
62 void StartOnInputThread(); 64 void StartOnInputThread();
63 void StopOnInputThread(); 65 void StopOnInputThread();
64 66
65 // base::MessagePumpLibevent::Watcher interface. 67 // base::MessagePumpLibevent::Watcher interface.
66 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; 68 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
67 virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; 69 virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
68 70
69 // Processes key and mouse events. 71 // Processes key and mouse events.
70 void ProcessXEvent(xEvent* event); 72 void ProcessXEvent(xEvent* event);
71 73
72 static void ProcessReply(XPointer self, XRecordInterceptData* data); 74 static void ProcessReply(XPointer self, XRecordInterceptData* data);
73 75
76 // Task runner on which X Window events are received.
77 const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner_;
Sergey Ulanov 2013/08/08 01:25:48 Ouch. This shouldn't be a const ref. It will crash
jiayl 2013/08/09 23:30:14 Oops, find&replace error. Fixed.
78
74 // Task runner on which public methods of this class must be called. 79 // Task runner on which public methods of this class must be called.
75 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_; 80 const scoped_refptr<base::SingleThreadTaskRunner>& observer_task_runner_;
76 81
77 // Task runner on which X Window events are received. 82 // Points to the object receiving notifications.
78 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_; 83 const base::WeakPtr<UserInputObserver>& observer_;
Sergey Ulanov 2013/08/08 01:25:48 same here. It should be weak ptr member. Not a con
jiayl 2013/08/09 23:30:14 Done.
79
80 // Points to the object receiving mouse event notifications and session
81 // disconnect requests.
82 base::WeakPtr<ClientSessionControl> client_session_control_;
83 84
84 // Used to receive base::MessagePumpLibevent::Watcher events. 85 // Used to receive base::MessagePumpLibevent::Watcher events.
85 base::MessagePumpLibevent::FileDescriptorWatcher controller_; 86 base::MessagePumpLibevent::FileDescriptorWatcher controller_;
86 87
87 // True when Alt is pressed.
88 bool alt_pressed_;
89
90 // True when Ctrl is pressed.
91 bool ctrl_pressed_;
92
93 Display* display_; 88 Display* display_;
94 Display* x_record_display_; 89 Display* x_record_display_;
95 XRecordRange* x_record_range_[2]; 90 XRecordRange* x_record_range_[2];
96 XRecordContext x_record_context_; 91 XRecordContext x_record_context_;
92 uint8 input_event_mask_;
97 93
98 DISALLOW_COPY_AND_ASSIGN(Core); 94 DISALLOW_COPY_AND_ASSIGN(Core);
99 }; 95 };
100 96
101 scoped_refptr<Core> core_; 97 scoped_refptr<Core> core_;
102 98
103 DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorLinux); 99 DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinux);
104 }; 100 };
105 101
106 LocalInputMonitorLinux::LocalInputMonitorLinux( 102 UserInputMonitorLinux::UserInputMonitorLinux(
107 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, 103 const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner,
108 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, 104 const scoped_refptr<base::SingleThreadTaskRunner>& observer_task_runner,
109 base::WeakPtr<ClientSessionControl> client_session_control) 105 uint8 input_event_mask,
110 : core_(new Core(caller_task_runner, 106 const base::WeakPtr<UserInputObserver>& observer)
111 input_task_runner, 107 : core_(new Core(input_task_runner,
112 client_session_control)) { 108 observer_task_runner,
109 input_event_mask,
110 observer)) {
113 core_->Start(); 111 core_->Start();
114 } 112 }
115 113
116 LocalInputMonitorLinux::~LocalInputMonitorLinux() { 114 UserInputMonitorLinux::~UserInputMonitorLinux() { core_->Stop(); }
117 core_->Stop();
118 }
119 115
120 LocalInputMonitorLinux::Core::Core( 116 UserInputMonitorLinux::Core::Core(
121 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, 117 const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner,
122 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, 118 const scoped_refptr<base::SingleThreadTaskRunner>& observer_task_runner,
123 base::WeakPtr<ClientSessionControl> client_session_control) 119 uint8 input_event_mask,
124 : caller_task_runner_(caller_task_runner), 120 const base::WeakPtr<UserInputObserver>& observer)
125 input_task_runner_(input_task_runner), 121 : input_task_runner_(input_task_runner),
126 client_session_control_(client_session_control), 122 observer_task_runner_(observer_task_runner),
127 alt_pressed_(false), 123 observer_(observer),
128 ctrl_pressed_(false),
129 display_(NULL), 124 display_(NULL),
130 x_record_display_(NULL), 125 x_record_display_(NULL),
131 x_record_context_(0) { 126 x_record_context_(0),
132 DCHECK(caller_task_runner_->BelongsToCurrentThread()); 127 input_event_mask_(input_event_mask) {
133 DCHECK(client_session_control_.get()); 128 DCHECK(observer_.get());
134 129
135 x_record_range_[0] = NULL; 130 x_record_range_[0] = NULL;
136 x_record_range_[1] = NULL; 131 x_record_range_[1] = NULL;
137 } 132 }
138 133
139 void LocalInputMonitorLinux::Core::Start() { 134 void UserInputMonitorLinux::Core::Start() {
140 DCHECK(caller_task_runner_->BelongsToCurrentThread());
141
142 input_task_runner_->PostTask(FROM_HERE, 135 input_task_runner_->PostTask(FROM_HERE,
143 base::Bind(&Core::StartOnInputThread, this)); 136 base::Bind(&Core::StartOnInputThread, this));
144 } 137 }
145 138
146 void LocalInputMonitorLinux::Core::Stop() { 139 void UserInputMonitorLinux::Core::Stop() {
147 DCHECK(caller_task_runner_->BelongsToCurrentThread());
148
149 input_task_runner_->PostTask(FROM_HERE, 140 input_task_runner_->PostTask(FROM_HERE,
150 base::Bind(&Core::StopOnInputThread, this)); 141 base::Bind(&Core::StopOnInputThread, this));
151 } 142 }
152 143
153 LocalInputMonitorLinux::Core::~Core() { 144 UserInputMonitorLinux::Core::~Core() {
154 DCHECK(!display_); 145 DCHECK(!display_);
155 DCHECK(!x_record_display_); 146 DCHECK(!x_record_display_);
156 DCHECK(!x_record_range_[0]); 147 DCHECK(!x_record_range_[0]);
157 DCHECK(!x_record_range_[1]); 148 DCHECK(!x_record_range_[1]);
158 DCHECK(!x_record_context_); 149 DCHECK(!x_record_context_);
159 } 150 }
160 151
161 void LocalInputMonitorLinux::Core::StartOnInputThread() { 152 void UserInputMonitorLinux::Core::StartOnInputThread() {
162 DCHECK(input_task_runner_->BelongsToCurrentThread()); 153 DCHECK(input_task_runner_->BelongsToCurrentThread());
163 DCHECK(!display_); 154 DCHECK(!display_);
164 DCHECK(!x_record_display_); 155 DCHECK(!x_record_display_);
165 DCHECK(!x_record_range_[0]); 156 DCHECK(!x_record_range_[0]);
166 DCHECK(!x_record_range_[1]); 157 DCHECK(!x_record_range_[1]);
167 DCHECK(!x_record_context_); 158 DCHECK(!x_record_context_);
168 159
169 // TODO(jamiewalch): We should pass the display in. At that point, since 160 // TODO(jamiewalch): We should pass the display in. At that point, since
170 // XRecord needs a private connection to the X Server for its data channel 161 // XRecord needs a private connection to the X Server for its data channel
171 // and both channels are used from a separate thread, we'll need to duplicate 162 // and both channels are used from a separate thread, we'll need to duplicate
(...skipping 11 matching lines...) Expand all
183 LOG(ERROR) << "X Record extension not available."; 174 LOG(ERROR) << "X Record extension not available.";
184 return; 175 return;
185 } 176 }
186 177
187 x_record_range_[0] = XRecordAllocRange(); 178 x_record_range_[0] = XRecordAllocRange();
188 x_record_range_[1] = XRecordAllocRange(); 179 x_record_range_[1] = XRecordAllocRange();
189 if (!x_record_range_[0] || !x_record_range_[1]) { 180 if (!x_record_range_[0] || !x_record_range_[1]) {
190 LOG(ERROR) << "XRecordAllocRange failed."; 181 LOG(ERROR) << "XRecordAllocRange failed.";
191 return; 182 return;
192 } 183 }
193 x_record_range_[0]->device_events.first = MotionNotify; 184 if (input_event_mask_ & MOUSE_MOVEMENT) {
194 x_record_range_[0]->device_events.last = MotionNotify; 185 x_record_range_[0]->device_events.first = MotionNotify;
195 x_record_range_[1]->device_events.first = KeyPress; 186 x_record_range_[0]->device_events.last = MotionNotify;
196 x_record_range_[1]->device_events.last = KeyRelease; 187 }
188 if (input_event_mask_ & KEYBOARD_EVENT) {
189 x_record_range_[1]->device_events.first = KeyPress;
190 x_record_range_[1]->device_events.last = KeyRelease;
191 }
192
197 XRecordClientSpec client_spec = XRecordAllClients; 193 XRecordClientSpec client_spec = XRecordAllClients;
198 194 x_record_context_ = XRecordCreateContext(x_record_display_,
Sergey Ulanov 2013/08/08 01:25:48 nit: IMO it's more readable when there are more th
jiayl 2013/08/09 23:30:14 This is auto formatted by "git-cl format".
199 x_record_context_ = XRecordCreateContext( 195 0,
200 x_record_display_, 0, &client_spec, 1, x_record_range_, 196 &client_spec,
201 arraysize(x_record_range_)); 197 1,
198 x_record_range_,
199 arraysize(x_record_range_));
202 if (!x_record_context_) { 200 if (!x_record_context_) {
203 LOG(ERROR) << "XRecordCreateContext failed."; 201 LOG(ERROR) << "XRecordCreateContext failed.";
204 return; 202 return;
205 } 203 }
206 204
207 if (!XRecordEnableContextAsync(x_record_display_, x_record_context_, 205 if (!XRecordEnableContextAsync(x_record_display_,
206 x_record_context_,
208 &Core::ProcessReply, 207 &Core::ProcessReply,
209 reinterpret_cast<XPointer>(this))) { 208 reinterpret_cast<XPointer>(this))) {
210 LOG(ERROR) << "XRecordEnableContextAsync failed."; 209 LOG(ERROR) << "XRecordEnableContextAsync failed.";
211 return; 210 return;
212 } 211 }
213 212
214 // Register OnFileCanReadWithoutBlocking() to be called every time there is 213 // Register OnFileCanReadWithoutBlocking() to be called every time there is
215 // something to read from |x_record_display_|. 214 // something to read from |x_record_display_|.
216 base::MessageLoopForIO* message_loop = base::MessageLoopForIO::current(); 215 base::MessageLoopForIO* message_loop = base::MessageLoopForIO::current();
217 int result = 216 int result =
218 message_loop->WatchFileDescriptor(ConnectionNumber(x_record_display_), 217 message_loop->WatchFileDescriptor(ConnectionNumber(x_record_display_),
219 true, 218 true,
220 base::MessageLoopForIO::WATCH_READ, 219 base::MessageLoopForIO::WATCH_READ,
221 &controller_, 220 &controller_,
222 this); 221 this);
223 if (!result) { 222 if (!result) {
224 LOG(ERROR) << "Failed to create X record task."; 223 LOG(ERROR) << "Failed to create X record task.";
225 return; 224 return;
226 } 225 }
227 226
228 // Fetch pending events if any. 227 // Fetch pending events if any.
229 while (XPending(x_record_display_)) { 228 while (XPending(x_record_display_)) {
230 XEvent ev; 229 XEvent ev;
231 XNextEvent(x_record_display_, &ev); 230 XNextEvent(x_record_display_, &ev);
232 } 231 }
233 } 232 }
234 233
235 void LocalInputMonitorLinux::Core::StopOnInputThread() { 234 void UserInputMonitorLinux::Core::StopOnInputThread() {
236 DCHECK(input_task_runner_->BelongsToCurrentThread()); 235 DCHECK(input_task_runner_->BelongsToCurrentThread());
237 236
238 // Context must be disabled via the control channel because we can't send 237 // Context must be disabled via the control channel because we can't send
239 // any X protocol traffic over the data channel while it's recording. 238 // any X protocol traffic over the data channel while it's recording.
240 if (x_record_context_) { 239 if (x_record_context_) {
241 XRecordDisableContext(display_, x_record_context_); 240 XRecordDisableContext(display_, x_record_context_);
242 XFlush(display_); 241 XFlush(display_);
243 } 242 }
244 243
245 controller_.StopWatchingFileDescriptor(); 244 controller_.StopWatchingFileDescriptor();
(...skipping 13 matching lines...) Expand all
259 if (x_record_display_) { 258 if (x_record_display_) {
260 XCloseDisplay(x_record_display_); 259 XCloseDisplay(x_record_display_);
261 x_record_display_ = NULL; 260 x_record_display_ = NULL;
262 } 261 }
263 if (display_) { 262 if (display_) {
264 XCloseDisplay(display_); 263 XCloseDisplay(display_);
265 display_ = NULL; 264 display_ = NULL;
266 } 265 }
267 } 266 }
268 267
269 void LocalInputMonitorLinux::Core::OnFileCanReadWithoutBlocking(int fd) { 268 void UserInputMonitorLinux::Core::OnFileCanReadWithoutBlocking(int fd) {
270 DCHECK(input_task_runner_->BelongsToCurrentThread()); 269 DCHECK(input_task_runner_->BelongsToCurrentThread());
271 270
272 // Fetch pending events if any. 271 // Fetch pending events if any.
273 while (XPending(x_record_display_)) { 272 while (XPending(x_record_display_)) {
274 XEvent ev; 273 XEvent ev;
275 XNextEvent(x_record_display_, &ev); 274 XNextEvent(x_record_display_, &ev);
276 } 275 }
277 } 276 }
278 277
279 void LocalInputMonitorLinux::Core::OnFileCanWriteWithoutBlocking(int fd) { 278 void UserInputMonitorLinux::Core::OnFileCanWriteWithoutBlocking(int fd) {
280 NOTREACHED(); 279 NOTREACHED();
281 } 280 }
282 281
283 void LocalInputMonitorLinux::Core::ProcessXEvent(xEvent* event) { 282 void UserInputMonitorLinux::Core::ProcessXEvent(xEvent* event) {
284 DCHECK(input_task_runner_->BelongsToCurrentThread()); 283 DCHECK(input_task_runner_->BelongsToCurrentThread());
285 284
286 if (event->u.u.type == MotionNotify) { 285 if (event->u.u.type == MotionNotify) {
287 SkIPoint position(SkIPoint::Make(event->u.keyButtonPointer.rootX, 286 SkIPoint position(SkIPoint::Make(event->u.keyButtonPointer.rootX,
288 event->u.keyButtonPointer.rootY)); 287 event->u.keyButtonPointer.rootY));
289 caller_task_runner_->PostTask( 288 observer_task_runner_->PostTask(
290 FROM_HERE, base::Bind(&ClientSessionControl::OnLocalMouseMoved, 289 FROM_HERE,
291 client_session_control_, 290 base::Bind(&UserInputObserver::OnMouseMoved, observer_, position));
292 position));
293 } else { 291 } else {
294 int key_code = event->u.u.detail; 292 ui::EventType type;
295 bool down = event->u.u.type == KeyPress; 293 if (event->u.u.type == KeyPress)
296 KeySym key_sym = XkbKeycodeToKeysym(display_, key_code, 0, 0); 294 type = ui::ET_KEY_PRESSED;
Sergey Ulanov 2013/08/08 01:25:48 add {} for multi-line if statement.
jiayl 2013/08/09 23:30:14 Done.
297 if (key_sym == XK_Control_L || key_sym == XK_Control_R) { 295 else if (event->u.u.type == KeyRelease)
298 ctrl_pressed_ = down; 296 type = ui::ET_KEY_RELEASED;
299 } else if (key_sym == XK_Alt_L || key_sym == XK_Alt_R) { 297 else
300 alt_pressed_ = down; 298 return;
301 } else if (key_sym == XK_Escape && down && alt_pressed_ && ctrl_pressed_) { 299 KeySym key_sym = XkbKeycodeToKeysym(display_, event->u.u.detail, 0, 0);
302 caller_task_runner_->PostTask( 300 ui::KeyboardCode key_code = ui::KeyboardCodeFromXKeysym(key_sym);
303 FROM_HERE, base::Bind(&ClientSessionControl::DisconnectSession, 301 observer_task_runner_->PostTask(
304 client_session_control_)); 302 FROM_HERE,
305 } 303 base::Bind(
304 &UserInputObserver::OnKeyboardEvent, observer_, type, key_code));
306 } 305 }
307 } 306 }
308 307
309 // static 308 // static
310 void LocalInputMonitorLinux::Core::ProcessReply(XPointer self, 309 void UserInputMonitorLinux::Core::ProcessReply(XPointer self,
311 XRecordInterceptData* data) { 310 XRecordInterceptData* data) {
312 if (data->category == XRecordFromServer) { 311 if (data->category == XRecordFromServer) {
313 xEvent* event = reinterpret_cast<xEvent*>(data->data); 312 xEvent* event = reinterpret_cast<xEvent*>(data->data);
314 reinterpret_cast<Core*>(self)->ProcessXEvent(event); 313 reinterpret_cast<Core*>(self)->ProcessXEvent(event);
315 } 314 }
316 XRecordFreeData(data); 315 XRecordFreeData(data);
317 } 316 }
318 317
319 } // namespace 318 } // namespace
320 319
321 scoped_ptr<LocalInputMonitor> LocalInputMonitor::Create( 320 scoped_ptr<UserInputMonitor> UserInputMonitor::Create(
322 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, 321 const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner,
323 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, 322 const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
324 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, 323 const scoped_refptr<base::SingleThreadTaskRunner>& observer_task_runner,
325 base::WeakPtr<ClientSessionControl> client_session_control) { 324 uint8 input_event_mask,
326 return scoped_ptr<LocalInputMonitor>( 325 const base::WeakPtr<UserInputObserver>& observer) {
327 new LocalInputMonitorLinux(caller_task_runner, 326 return scoped_ptr<UserInputMonitor>(new UserInputMonitorLinux(
328 input_task_runner, 327 input_task_runner, observer_task_runner, input_event_mask, observer));
329 client_session_control));
330 } 328 }
331 329
332 } // namespace remoting 330 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698