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

Side by Side Diff: ui/events/ozone/evdev/touch_event_converter_evdev.cc

Issue 2263693003: Add palm suppression feature to EventConverterEvdev (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@stylus
Patch Set: Use enable/disable instead of palm suppression filter Created 4 years, 3 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 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 "ui/events/ozone/evdev/touch_event_converter_evdev.h" 5 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
6 6
7 #include <errno.h> 7 #include <errno.h>
8 #include <fcntl.h> 8 #include <fcntl.h>
9 #include <linux/input.h> 9 #include <linux/input.h>
10 #include <poll.h> 10 #include <poll.h>
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 touch_noise_finder_.reset(new TouchNoiseFinder); 119 touch_noise_finder_.reset(new TouchNoiseFinder);
120 } 120 }
121 touch_evdev_debug_buffer_.Initialize(devinfo); 121 touch_evdev_debug_buffer_.Initialize(devinfo);
122 } 122 }
123 123
124 TouchEventConverterEvdev::~TouchEventConverterEvdev() { 124 TouchEventConverterEvdev::~TouchEventConverterEvdev() {
125 } 125 }
126 126
127 void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) { 127 void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) {
128 has_mt_ = info.HasMultitouch(); 128 has_mt_ = info.HasMultitouch();
129 has_pen_ = info.HasKeyEvent(BTN_TOOL_PEN);
129 130
130 if (has_mt_) { 131 if (has_mt_) {
131 pressure_min_ = info.GetAbsMinimum(ABS_MT_PRESSURE); 132 pressure_min_ = info.GetAbsMinimum(ABS_MT_PRESSURE);
132 pressure_max_ = info.GetAbsMaximum(ABS_MT_PRESSURE); 133 pressure_max_ = info.GetAbsMaximum(ABS_MT_PRESSURE);
133 x_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_X); 134 x_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_X);
134 x_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_X) - x_min_tuxels_ + 1; 135 x_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_X) - x_min_tuxels_ + 1;
135 y_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_Y); 136 y_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_Y);
136 y_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_Y) - y_min_tuxels_ + 1; 137 y_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_Y) - y_min_tuxels_ + 1;
137 touch_points_ = 138 touch_points_ =
138 std::min<int>(info.GetAbsMaximum(ABS_MT_SLOT) + 1, kNumTouchEvdevSlots); 139 std::min<int>(info.GetAbsMaximum(ABS_MT_SLOT) + 1, kNumTouchEvdevSlots);
140 major_max_ = info.GetAbsMaximum(ABS_MT_TOUCH_MAJOR);
139 current_slot_ = info.GetAbsValue(ABS_MT_SLOT); 141 current_slot_ = info.GetAbsValue(ABS_MT_SLOT);
140 } else { 142 } else {
141 pressure_min_ = info.GetAbsMinimum(ABS_PRESSURE); 143 pressure_min_ = info.GetAbsMinimum(ABS_PRESSURE);
142 pressure_max_ = info.GetAbsMaximum(ABS_PRESSURE); 144 pressure_max_ = info.GetAbsMaximum(ABS_PRESSURE);
143 x_min_tuxels_ = info.GetAbsMinimum(ABS_X); 145 x_min_tuxels_ = info.GetAbsMinimum(ABS_X);
144 x_num_tuxels_ = info.GetAbsMaximum(ABS_X) - x_min_tuxels_ + 1; 146 x_num_tuxels_ = info.GetAbsMaximum(ABS_X) - x_min_tuxels_ + 1;
145 y_min_tuxels_ = info.GetAbsMinimum(ABS_Y); 147 y_min_tuxels_ = info.GetAbsMinimum(ABS_Y);
146 y_num_tuxels_ = info.GetAbsMaximum(ABS_Y) - y_min_tuxels_ + 1; 148 y_num_tuxels_ = info.GetAbsMaximum(ABS_Y) - y_min_tuxels_ + 1;
147 touch_points_ = 1; 149 touch_points_ = 1;
150 major_max_ = 0;
148 current_slot_ = 0; 151 current_slot_ = 0;
149 } 152 }
150 153
151 quirk_left_mouse_button_ = 154 quirk_left_mouse_button_ =
152 !has_mt_ && !info.HasKeyEvent(BTN_TOUCH) && info.HasKeyEvent(BTN_LEFT); 155 !has_mt_ && !info.HasKeyEvent(BTN_TOUCH) && info.HasKeyEvent(BTN_LEFT);
153 156
154 // Apply --touch-calibration. 157 // Apply --touch-calibration.
155 if (type() == INPUT_DEVICE_INTERNAL) { 158 if (type() == INPUT_DEVICE_INTERNAL) {
156 TouchCalibration cal = {}; 159 TouchCalibration cal = {};
157 GetTouchCalibration(&cal); 160 GetTouchCalibration(&cal);
(...skipping 20 matching lines...) Expand all
178 ABS_MT_TRACKING_ID, i, kTrackingIdForUnusedSlot); 181 ABS_MT_TRACKING_ID, i, kTrackingIdForUnusedSlot);
179 events_[i].touching = (events_[i].tracking_id >= 0); 182 events_[i].touching = (events_[i].tracking_id >= 0);
180 events_[i].slot = i; 183 events_[i].slot = i;
181 184
182 // Dirty the slot so we'll update the consumer at the first opportunity. 185 // Dirty the slot so we'll update the consumer at the first opportunity.
183 // We can't dispatch here as this is currently called on the worker pool. 186 // We can't dispatch here as this is currently called on the worker pool.
184 // TODO(spang): Move initialization off worker pool. 187 // TODO(spang): Move initialization off worker pool.
185 events_[i].altered = true; 188 events_[i].altered = true;
186 189
187 // Optional bits. 190 // Optional bits.
188 events_[i].radius_x = 191 int touch_major =
189 info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MAJOR, i, 0) / 2.0f; 192 info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MAJOR, i, 0) / 2.0f;
193 events_[i].radius_x = touch_major / 2.0f;
190 events_[i].radius_y = 194 events_[i].radius_y =
191 info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MINOR, i, 0) / 2.0f; 195 info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MINOR, i, 0) / 2.0f;
192 events_[i].pressure = ScalePressure( 196 events_[i].pressure = ScalePressure(
193 info.GetAbsMtSlotValueWithDefault(ABS_MT_PRESSURE, i, 0)); 197 info.GetAbsMtSlotValueWithDefault(ABS_MT_PRESSURE, i, 0));
198 events_[i].will_cancel = (major_max_ > 0 && touch_major == major_max_);
194 } 199 }
195 } else { 200 } else {
196 // TODO(spang): Add key state to EventDeviceInfo to allow initial contact. 201 // TODO(spang): Add key state to EventDeviceInfo to allow initial contact.
197 // (and make sure to take into account quirk_left_mouse_button_) 202 // (and make sure to take into account quirk_left_mouse_button_)
198 events_[0].x = 0; 203 events_[0].x = 0;
199 events_[0].y = 0; 204 events_[0].y = 0;
200 events_[0].tracking_id = kTrackingIdForUnusedSlot; 205 events_[0].tracking_id = kTrackingIdForUnusedSlot;
201 events_[0].touching = false; 206 events_[0].touching = false;
202 events_[0].slot = 0; 207 events_[0].slot = 0;
203 events_[0].radius_x = 0; 208 events_[0].radius_x = 0;
204 events_[0].radius_y = 0; 209 events_[0].radius_y = 0;
205 events_[0].pressure = 0; 210 events_[0].pressure = 0;
206 events_[0].tool_code = 0; 211 events_[0].tool_code = 0;
212 events_[0].will_cancel = false;
207 } 213 }
208 } 214 }
209 215
210 void TouchEventConverterEvdev::Reinitialize() { 216 void TouchEventConverterEvdev::Reinitialize() {
211 ReleaseButtons(); 217 ReleaseButtons();
212 218
213 EventDeviceInfo info; 219 EventDeviceInfo info;
214 if (!info.Initialize(fd_, path_)) { 220 if (!info.Initialize(fd_, path_)) {
215 LOG(ERROR) << "Failed to synchronize state for touch device: " 221 LOG(ERROR) << "Failed to synchronize state for touch device: "
216 << path_.value(); 222 << path_.value();
217 Stop(); 223 Stop();
218 return; 224 return;
219 } 225 }
220 Initialize(info); 226 Initialize(info);
221 } 227 }
222 228
223 bool TouchEventConverterEvdev::HasTouchscreen() const { 229 bool TouchEventConverterEvdev::HasTouchscreen() const {
224 return true; 230 return true;
225 } 231 }
226 232
233 bool TouchEventConverterEvdev::HasPen() const {
234 return has_pen_;
235 }
236
227 gfx::Size TouchEventConverterEvdev::GetTouchscreenSize() const { 237 gfx::Size TouchEventConverterEvdev::GetTouchscreenSize() const {
228 return gfx::Size(x_num_tuxels_, y_num_tuxels_); 238 return gfx::Size(x_num_tuxels_, y_num_tuxels_);
229 } 239 }
230 240
231 int TouchEventConverterEvdev::GetTouchPoints() const { 241 int TouchEventConverterEvdev::GetTouchPoints() const {
232 return touch_points_; 242 return touch_points_;
233 } 243 }
234 244
235 void TouchEventConverterEvdev::OnEnabled() { 245 void TouchEventConverterEvdev::OnEnabled() {
236 ReportEvents(EventTimeForNow());
237 } 246 }
238 247
239 void TouchEventConverterEvdev::OnDisabled() { 248 void TouchEventConverterEvdev::OnDisabled() {
240 ReleaseTouches(); 249 ReleaseTouches();
241 ReleaseButtons(); 250 ReleaseButtons();
251 if (enable_palm_suppression_callback_) {
252 enable_palm_suppression_callback_.Run(false);
253 }
242 } 254 }
243 255
244 void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) { 256 void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
245 TRACE_EVENT1("evdev", 257 TRACE_EVENT1("evdev",
246 "TouchEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd", 258 "TouchEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd",
247 fd); 259 fd);
248 260
249 input_event inputs[kNumTouchEvdevSlots * 6 + 1]; 261 input_event inputs[kNumTouchEvdevSlots * 6 + 1];
250 ssize_t read_size = read(fd, inputs, sizeof(inputs)); 262 ssize_t read_size = read(fd, inputs, sizeof(inputs));
251 if (read_size < 0) { 263 if (read_size < 0) {
252 if (errno == EINTR || errno == EAGAIN) 264 if (errno == EINTR || errno == EAGAIN)
253 return; 265 return;
254 if (errno != ENODEV) 266 if (errno != ENODEV)
255 PLOG(ERROR) << "error reading device " << path_.value(); 267 PLOG(ERROR) << "error reading device " << path_.value();
256 Stop(); 268 Stop();
257 return; 269 return;
258 } 270 }
259 271
260 if (!enabled_) {
261 dropped_events_ = true;
262 return;
263 }
264
265 for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) { 272 for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
266 if (!has_mt_) { 273 if (!has_mt_) {
267 // Emulate the device as an MT device with only 1 slot by inserting extra 274 // Emulate the device as an MT device with only 1 slot by inserting extra
268 // MT protocol events in the stream. 275 // MT protocol events in the stream.
269 EmulateMultitouchEvent(inputs[i]); 276 EmulateMultitouchEvent(inputs[i]);
270 } 277 }
271 278
272 ProcessMultitouchEvent(inputs[i]); 279 ProcessMultitouchEvent(inputs[i]);
273 } 280 }
274 } 281 }
275 282
276 void TouchEventConverterEvdev::DumpTouchEventLog(const char* filename) { 283 void TouchEventConverterEvdev::DumpTouchEventLog(const char* filename) {
277 touch_evdev_debug_buffer_.DumpLog(filename); 284 touch_evdev_debug_buffer_.DumpLog(filename);
278 } 285 }
279 286
280 void TouchEventConverterEvdev::SetTouchEventLoggingEnabled(bool enabled) { 287 void TouchEventConverterEvdev::SetTouchEventLoggingEnabled(bool enabled) {
281 touch_logging_enabled_ = enabled; 288 touch_logging_enabled_ = enabled;
282 } 289 }
283 290
291 void TouchEventConverterEvdev::SetPalmSuppressionCallback(
292 const base::Callback<void(bool)>& callback) {
293 enable_palm_suppression_callback_ = callback;
294 }
295
284 void TouchEventConverterEvdev::ProcessMultitouchEvent( 296 void TouchEventConverterEvdev::ProcessMultitouchEvent(
285 const input_event& input) { 297 const input_event& input) {
286 if (touch_logging_enabled_) 298 if (touch_logging_enabled_)
287 touch_evdev_debug_buffer_.ProcessEvent(current_slot_, &input); 299 touch_evdev_debug_buffer_.ProcessEvent(current_slot_, &input);
288 300
289 if (input.type == EV_SYN) { 301 if (input.type == EV_SYN) {
290 ProcessSyn(input); 302 ProcessSyn(input);
291 } else if (dropped_events_) { 303 } else if (dropped_events_) {
292 // Do nothing. This branch indicates we have lost sync with the driver. 304 // Do nothing. This branch indicates we have lost sync with the driver.
293 } else if (input.type == EV_ABS) { 305 } else if (input.type == EV_ABS) {
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
345 // Do not change tool types while touching to prevent inconsistencies 357 // Do not change tool types while touching to prevent inconsistencies
346 // from switching between Mouse and TouchEvents. 358 // from switching between Mouse and TouchEvents.
347 if (events_[current_slot_].was_touching) 359 if (events_[current_slot_].was_touching)
348 break; 360 break;
349 361
350 if (input.value > 0) { 362 if (input.value > 0) {
351 events_[current_slot_].tool_code = input.code; 363 events_[current_slot_].tool_code = input.code;
352 } else { 364 } else {
353 events_[current_slot_].tool_code = 0; 365 events_[current_slot_].tool_code = 0;
354 } 366 }
367 events_[current_slot_].altered = true;
355 break; 368 break;
356 default: 369 default:
357 NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code; 370 NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code;
358 } 371 }
359 } 372 }
360 373
361 void TouchEventConverterEvdev::ProcessAbs(const input_event& input) { 374 void TouchEventConverterEvdev::ProcessAbs(const input_event& input) {
362 switch (input.code) { 375 switch (input.code) {
363 case ABS_MT_TOUCH_MAJOR: 376 case ABS_MT_TOUCH_MAJOR:
364 // TODO(spang): If we have all of major, minor, and orientation, 377 // TODO(spang): If we have all of major, minor, and orientation,
365 // we can scale the ellipse correctly. However on the Pixel we get 378 // we can scale the ellipse correctly. However on the Pixel we get
366 // neither minor nor orientation, so this is all we can do. 379 // neither minor nor orientation, so this is all we can do.
367 events_[current_slot_].radius_x = input.value / 2.0f; 380 events_[current_slot_].radius_x = input.value / 2.0f;
381
382 // The MT protocol cannot communicate cancelled touches, so some kernel
383 // drivers will identify palms by setting touch major to max.
384 if (major_max_ > 0 && input.value == major_max_)
385 events_[current_slot_].will_cancel = true;
368 break; 386 break;
369 case ABS_MT_TOUCH_MINOR: 387 case ABS_MT_TOUCH_MINOR:
370 events_[current_slot_].radius_y = input.value / 2.0f; 388 events_[current_slot_].radius_y = input.value / 2.0f;
371 break; 389 break;
372 case ABS_MT_POSITION_X: 390 case ABS_MT_POSITION_X:
373 events_[current_slot_].x = input.value; 391 events_[current_slot_].x = input.value;
374 break; 392 break;
375 case ABS_MT_POSITION_Y: 393 case ABS_MT_POSITION_Y:
376 events_[current_slot_].y = input.value; 394 events_[current_slot_].y = input.value;
377 break; 395 break;
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
410 default: 428 default:
411 NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code; 429 NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code;
412 } 430 }
413 } 431 }
414 432
415 EventType TouchEventConverterEvdev::GetEventTypeForTouch( 433 EventType TouchEventConverterEvdev::GetEventTypeForTouch(
416 const InProgressTouchEvdev& touch) { 434 const InProgressTouchEvdev& touch) {
417 if (touch.cancelled) 435 if (touch.cancelled)
418 return ET_UNKNOWN; 436 return ET_UNKNOWN;
419 437
438 if (touch.will_cancel) {
439 if (touch.touching && !touch.was_touching)
440 return ET_UNKNOWN;
441 return ET_TOUCH_CANCELLED;
442 }
443
420 if (touch_noise_finder_ && touch_noise_finder_->SlotHasNoise(touch.slot)) { 444 if (touch_noise_finder_ && touch_noise_finder_->SlotHasNoise(touch.slot)) {
spang 2016/08/26 00:58:34 Can we move this earlier and just set |will_cancel
denniskempin 2016/08/26 21:56:02 Done. I have moved it to ReportTouches, since this
421 if (touch.touching && !touch.was_touching) 445 if (touch.touching && !touch.was_touching)
422 return ET_UNKNOWN; 446 return ET_UNKNOWN;
423 return ET_TOUCH_CANCELLED; 447 return ET_TOUCH_CANCELLED;
424 } 448 }
425 449
426 if (touch.touching) 450 if (touch.touching)
427 return touch.was_touching ? ET_TOUCH_MOVED : ET_TOUCH_PRESSED; 451 return touch.was_touching ? ET_TOUCH_MOVED : ET_TOUCH_PRESSED;
428 return touch.was_touching ? ET_TOUCH_RELEASED : ET_UNKNOWN; 452 return touch.was_touching ? ET_TOUCH_RELEASED : ET_UNKNOWN;
429 } 453 }
430 454
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
468 } 492 }
469 493
470 if (touch_noise_finder_) 494 if (touch_noise_finder_)
471 touch_noise_finder_->HandleTouches(events_, timestamp); 495 touch_noise_finder_->HandleTouches(events_, timestamp);
472 496
473 for (size_t i = 0; i < events_.size(); i++) { 497 for (size_t i = 0; i < events_.size(); i++) {
474 InProgressTouchEvdev* event = &events_[i]; 498 InProgressTouchEvdev* event = &events_[i];
475 if (!event->altered) 499 if (!event->altered)
476 continue; 500 continue;
477 501
502 if (enable_palm_suppression_callback_) {
503 enable_palm_suppression_callback_.Run(event->tool_code > 0);
504 }
505
478 if (event->tool_code > 0) { 506 if (event->tool_code > 0) {
479 ReportStylusEvent(*event, timestamp); 507 ReportStylusEvent(*event, timestamp);
480 } else { 508 } else {
481 EventType event_type = GetEventTypeForTouch(*event); 509 EventType event_type = GetEventTypeForTouch(*event);
482 if (event_type == ET_UNKNOWN || event_type == ET_TOUCH_CANCELLED) 510 if (event_type == ET_UNKNOWN || event_type == ET_TOUCH_CANCELLED)
483 event->cancelled = true; 511 event->cancelled = true;
spang 2016/08/26 00:58:34 Can we move this down next to the assignment to |w
denniskempin 2016/08/26 21:56:02 done
484 512
485 if (event_type != ET_UNKNOWN) 513 if (event_type != ET_UNKNOWN)
486 ReportTouchEvent(*event, event_type, timestamp); 514 ReportTouchEvent(*event, event_type, timestamp);
487 } 515 }
488 516
489 event->was_touching = event->touching; 517 event->was_touching = event->touching;
490 event->altered = false; 518 event->altered = false;
491 event->btn_left.changed = false; 519 event->btn_left.changed = false;
492 event->btn_right.changed = false; 520 event->btn_right.changed = false;
493 event->btn_middle.changed = false; 521 event->btn_middle.changed = false;
494 } 522 }
495 } 523 }
496 524
497 void TouchEventConverterEvdev::UpdateTrackingId(int slot, int tracking_id) { 525 void TouchEventConverterEvdev::UpdateTrackingId(int slot, int tracking_id) {
498 InProgressTouchEvdev* event = &events_[slot]; 526 InProgressTouchEvdev* event = &events_[slot];
499 527
500 if (event->tracking_id == tracking_id) 528 if (event->tracking_id == tracking_id)
501 return; 529 return;
502 530
503 event->tracking_id = tracking_id; 531 event->tracking_id = tracking_id;
504 event->touching = (tracking_id >= 0); 532 event->touching = (tracking_id >= 0);
505 event->altered = true; 533 event->altered = true;
506 534
507 if (tracking_id >= 0) 535 if (tracking_id >= 0) {
508 event->cancelled = false; 536 event->cancelled = false;
537 event->will_cancel = !enabled_;
538 }
509 } 539 }
510 540
511 void TouchEventConverterEvdev::ReleaseTouches() { 541 void TouchEventConverterEvdev::ReleaseTouches() {
512 for (size_t slot = 0; slot < events_.size(); slot++) 542 for (size_t slot = 0; slot < events_.size(); slot++)
513 UpdateTrackingId(slot, kTrackingIdForUnusedSlot); 543 events_[slot].will_cancel = true;
514 544
515 ReportEvents(EventTimeForNow()); 545 ReportEvents(EventTimeForNow());
516 } 546 }
517 547
518 void TouchEventConverterEvdev::ReleaseButtons() { 548 void TouchEventConverterEvdev::ReleaseButtons() {
519 for (size_t slot = 0; slot < events_.size(); slot++) { 549 for (size_t slot = 0; slot < events_.size(); slot++) {
520 InProgressTouchEvdev* event = &events_[slot]; 550 InProgressTouchEvdev* event = &events_[slot];
521 551
522 if (event->btn_left.down) { 552 if (event->btn_left.down) {
523 event->btn_left.down = false; 553 event->btn_left.down = false;
(...skipping 17 matching lines...) Expand all
541 if (pressure_max_ - pressure_min_) 571 if (pressure_max_ - pressure_min_)
542 pressure /= pressure_max_ - pressure_min_; 572 pressure /= pressure_max_ - pressure_min_;
543 return pressure; 573 return pressure;
544 } 574 }
545 575
546 int TouchEventConverterEvdev::NextTrackingId() { 576 int TouchEventConverterEvdev::NextTrackingId() {
547 return next_tracking_id_++ & kMaxTrackingId; 577 return next_tracking_id_++ & kMaxTrackingId;
548 } 578 }
549 579
550 } // namespace ui 580 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698