void Controller::ProcessMotionEvent()

in streaming/screen-sharing-agent/app/src/main/cpp/controller.cc [371:557]


void Controller::ProcessMotionEvent(const MotionEventMessage& message) {
  nanoseconds event_time = UptimeNanos();
  int32_t action = message.action();
  Log::V("Controller::ProcessMotionEvent action:%d", action);
  int32_t display_id = message.display_id();
  DisplayInfo display_info = Agent::GetDisplayInfo(display_id);
  if (!display_info.IsValid()) {
    return;
  }

  // Wake up the device if the display was turned off.
  if (action == AMOTION_EVENT_ACTION_DOWN) {
    WakeUpDevice();
  }

  int32_t tool = message.is_mouse() ? AMOTION_EVENT_TOOL_TYPE_STYLUS : AMOTION_EVENT_TOOL_TYPE_FINGER;

  if ((Agent::flags() & USE_UINPUT || input_event_injection_disabled_) && Agent::feature_level() > 36) {
    // Inject event using a virtual drawing tablet.
    auto& tablet = GetVirtualTablet(display_id, display_info.logical_size.width, display_info.logical_size.height);
    switch (action) {
      case AMOTION_EVENT_ACTION_HOVER_MOVE:
        for (auto& pointer : message.pointers()) {
          bool success = tablet.WriteMotionEvent(
              pointer.pointer_id, AMOTION_EVENT_TOOL_TYPE_STYLUS, AMOTION_EVENT_ACTION_MOVE, pointer.x, pointer.y, event_time);
          if (!success) {
            Log::E("Error writing hover move event");
          }
        }
        break;
      case AMOTION_EVENT_ACTION_HOVER_ENTER:
        tablet.StartHovering(event_time);
        break;
      case AMOTION_EVENT_ACTION_HOVER_EXIT:
        tablet.StopHovering(event_time);
        break;
      case AMOTION_EVENT_ACTION_DOWN:
      case AMOTION_EVENT_ACTION_UP:
      case AMOTION_EVENT_ACTION_MOVE:
        {
          int32_t pressure = action == AMOTION_EVENT_ACTION_UP ? 0 : VirtualInputDevice::MAX_PRESSURE;
          int32_t major_axis_size = pressure == 0 ? 0 : FINGER_TOUCH_SIZE;
          for (auto& pointer: message.pointers()) {
            bool success = tablet.WriteTouchEvent(pointer.pointer_id, AMOTION_EVENT_TOOL_TYPE_STYLUS, action, pointer.x, pointer.y,
                                                  pressure, major_axis_size, event_time);
            if (!success) {
              Log::E("Error writing touch event");
            }
          }
        }
        break;
      case AMOTION_EVENT_ACTION_SCROLL:
        if (!message.pointers().empty()) {
          auto& pointer = message.pointers()[0];
          for (const auto& entry: pointer.axis_values) {
            if (entry.first == AMOTION_EVENT_AXIS_VSCROLL) {
              float amount = entry.second;
              if (amount != 0) {
                bool success = tablet.WriteVerticalScrollEvent(amount, event_time);
                if (!success) {
                  Log::E("Error writing tablet vertical scroll event");
                }
              }
            } else if (entry.first == AMOTION_EVENT_AXIS_HSCROLL) {
              float amount = entry.second;
              if (amount != 0) {
                bool success = tablet.WriteHorizontalScrollEvent(amount, event_time);
                if (!success) {
                  Log::E("Error writing tablet horizontal scroll event");
                }
              }
            }
          }
        }
        break;
      default:
        {
          auto action_code = action & AMOTION_EVENT_ACTION_MASK;
          if (action_code == AMOTION_EVENT_ACTION_POINTER_DOWN || action_code == AMOTION_EVENT_ACTION_POINTER_UP) {
            auto pointer_id = action >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
            action = action_code == AMOTION_EVENT_ACTION_POINTER_DOWN ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
            int32_t pressure = action == AMOTION_EVENT_ACTION_UP ? 0 : VirtualInputDevice::MAX_PRESSURE;
            int32_t major_axis_size = pressure == 0 ? 0 : FINGER_TOUCH_SIZE;
            for (auto& pointer : message.pointers()) {
              if (pointer.pointer_id == pointer_id) {
                bool success = tablet.WriteTouchEvent(pointer_id, AMOTION_EVENT_TOOL_TYPE_STYLUS, action, pointer.x, pointer.y,
                                                      pressure, major_axis_size, event_time);
                if (!success) {
                  Log::E("Error writing touch event");
                }
                break;
              }
            }
          }
        }
      break;
    }
  } else {
    // Inject event using InputManager.
    MotionEvent event(jni_);
    event.display_id = display_id;
    event.action = action;
    event.button_state = message.button_state();
    event.event_time_millis = duration_cast<milliseconds>(event_time).count();;
    if (action != AMOTION_EVENT_ACTION_HOVER_MOVE && action != AMOTION_EVENT_ACTION_HOVER_ENTER &&
        action != AMOTION_EVENT_ACTION_HOVER_EXIT && action != AMOTION_EVENT_ACTION_SCROLL) {
      if (action == AMOTION_EVENT_ACTION_DOWN) {
        motion_event_start_time_ = event.event_time_millis;
      }
      if (motion_event_start_time_ == 0) {
        Log::E("Motion event started with action %d instead of expected %d", action, AMOTION_EVENT_ACTION_DOWN);
        motion_event_start_time_ = event.event_time_millis;
      }
      event.down_time_millis = motion_event_start_time_;
      if (action == AMOTION_EVENT_ACTION_UP) {
        motion_event_start_time_ = 0;
      }
    }
    if (message.is_mouse() || action == AMOTION_EVENT_ACTION_HOVER_MOVE || message.action_button() != 0 || message.button_state() != 0) {
      // AINPUT_SOURCE_MOUSE
      // - when action_button() is non-zero, as the Android framework has special handling for mouse in performButtonActionOnTouchDown(),
      //   which opens the context menu on right click.
      // - when message.button_state() is non-zero, otherwise drag operations initiated by touch down with AINPUT_SOURCE_MOUSE will not
      //   receive mouse move events.
      event.source = AINPUT_SOURCE_MOUSE;
    } else {
      event.source = AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_TOUCHSCREEN;
    }

    for (auto& pointer: message.pointers()) {
      JObject properties = pointer_properties_.GetElement(jni_, event.pointer_count);
      pointer_helper_->SetPointerId(properties, pointer.pointer_id);
      pointer_helper_->SetPointerToolType(properties, tool);
      JObject coordinates = pointer_coordinates_.GetElement(jni_, event.pointer_count);
      // We must clear first so that axis information from previous runs is not reused.
      pointer_helper_->ClearPointerCoords(coordinates);
      Point point = AdjustedDisplayCoordinates(pointer.x, pointer.y, display_info);
      pointer_helper_->SetPointerCoords(coordinates, point.x, point.y);
      float pressure = action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP ||
          action == AMOTION_EVENT_ACTION_BUTTON_PRESS || action == AMOTION_EVENT_ACTION_BUTTON_RELEASE ? 1 : 0;
      pointer_helper_->SetPointerPressure(coordinates, pressure);
      for (auto const& [axis, value]: pointer.axis_values) {
        pointer_helper_->SetAxisValue(coordinates, axis, value);
      }
      event.pointer_count++;
    }

    event.pointer_properties = pointer_properties_;
    event.pointer_coordinates = pointer_coordinates_;
    // InputManager doesn't allow ACTION_DOWN and ACTION_UP events with multiple pointers.
    // They have to be converted to a sequence of pointer-specific events.
    if (action == AMOTION_EVENT_ACTION_DOWN) {
      if (message.action_button()) {
        InjectMotionEvent(event);
        event.action = AMOTION_EVENT_ACTION_BUTTON_PRESS;
        event.action_button = message.action_button();
      } else {
        for (int i = 1; event.pointer_count = i, i < message.pointers().size(); i++) {
          InjectMotionEvent(event);
          event.action = AMOTION_EVENT_ACTION_POINTER_DOWN | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
        }
      }
    } else if (action == AMOTION_EVENT_ACTION_UP) {
      if (message.action_button()) {
        event.action = AMOTION_EVENT_ACTION_BUTTON_RELEASE;
        event.action_button = message.action_button();
        InjectMotionEvent(event);
        event.action = AMOTION_EVENT_ACTION_UP;
        event.action_button = 0;
      } else {
        for (int i = event.pointer_count; --i > 1;) {
          event.action = AMOTION_EVENT_ACTION_POINTER_UP | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
          pointer_helper_->SetPointerPressure(pointer_coordinates_.GetElement(jni_, i), 0);
          InjectMotionEvent(event);
          event.pointer_count = i;
        }
        event.action = AMOTION_EVENT_ACTION_UP;
      }
    }
    InjectMotionEvent(event);
  }

  if (action == AMOTION_EVENT_ACTION_UP) {
    // This event may have started an app. Update the app-level display orientation.
    Agent::SetVideoOrientation(display_id, DisplayStreamer::CURRENT_VIDEO_ORIENTATION);
  }
}