TYPE3
[iec.git] / src / type3_AndroidCloud / anbox-master / src / anbox / platform / sdl / platform.cpp
diff --git a/src/type3_AndroidCloud/anbox-master/src/anbox/platform/sdl/platform.cpp b/src/type3_AndroidCloud/anbox-master/src/anbox/platform/sdl/platform.cpp
new file mode 100644 (file)
index 0000000..4c5b3a5
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch-default"
+#include "anbox/platform/sdl/platform.h"
+#include "anbox/input/device.h"
+#include "anbox/input/manager.h"
+#include "anbox/logger.h"
+#include "anbox/platform/sdl/keycode_converter.h"
+#include "anbox/platform/sdl/window.h"
+#include "anbox/platform/sdl/audio_sink.h"
+#include "anbox/wm/manager.h"
+
+#include <boost/throw_exception.hpp>
+
+#include <signal.h>
+#include <sys/types.h>
+#pragma GCC diagnostic pop
+
+namespace anbox {
+namespace platform {
+namespace sdl {
+Platform::Platform(
+    const std::shared_ptr<input::Manager> &input_manager,
+    const Configuration &config)
+    : input_manager_(input_manager),
+      event_thread_running_(false),
+      config_(config) {
+
+  // Don't block the screensaver from kicking in. It will be blocked
+  // by the desktop shell already and we don't have to do this again.
+  // If we would leave this enabled it will prevent systems from
+  // suspending correctly.
+  SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
+
+#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
+  // Don't disable compositing
+  // Available since SDL 2.0.8
+  SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
+#endif
+
+  if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS) < 0) {
+    const auto message = utils::string_format("Failed to initialize SDL: %s", SDL_GetError());
+    BOOST_THROW_EXCEPTION(std::runtime_error(message));
+  }
+
+  auto display_frame = graphics::Rect::Invalid;
+  if (config_.display_frame == graphics::Rect::Invalid) {
+    for (auto n = 0; n < SDL_GetNumVideoDisplays(); n++) {
+      SDL_Rect r;
+      if (SDL_GetDisplayBounds(n, &r) != 0) continue;
+
+      graphics::Rect frame{r.x, r.y, r.x + r.w, r.y + r.h};
+
+      if (display_frame == graphics::Rect::Invalid)
+        display_frame = frame;
+      else
+        display_frame.merge(frame);
+    }
+
+    if (display_frame == graphics::Rect::Invalid)
+      BOOST_THROW_EXCEPTION(
+          std::runtime_error("No valid display configuration found"));
+  } else {
+    display_frame = config_.display_frame;
+    window_size_immutable_ = true;
+  }
+
+  graphics::emugl::DisplayInfo::get()->set_resolution(display_frame.width(), display_frame.height());
+  display_frame_ = display_frame;
+
+  pointer_ = input_manager->create_device();
+  pointer_->set_name("anbox-pointer");
+  pointer_->set_driver_version(1);
+  pointer_->set_input_id({BUS_VIRTUAL, 2, 2, 2});
+  pointer_->set_physical_location("none");
+  pointer_->set_key_bit(BTN_MOUSE);
+  // NOTE: We don't use REL_X/REL_Y in reality but have to specify them here
+  // to allow InputFlinger to detect we're a cursor device.
+  pointer_->set_rel_bit(REL_X);
+  pointer_->set_rel_bit(REL_Y);
+  pointer_->set_rel_bit(REL_HWHEEL);
+  pointer_->set_rel_bit(REL_WHEEL);
+  pointer_->set_prop_bit(INPUT_PROP_POINTER);
+
+  keyboard_ = input_manager->create_device();
+  keyboard_->set_name("anbox-keyboard");
+  keyboard_->set_driver_version(1);
+  keyboard_->set_input_id({BUS_VIRTUAL, 3, 3, 3});
+  keyboard_->set_physical_location("none");
+  keyboard_->set_key_bit(BTN_MISC);
+  keyboard_->set_key_bit(KEY_OK);
+
+  touch_ = input_manager->create_device();
+  touch_->set_name("anbox-touch");
+  touch_->set_driver_version(1);
+  touch_->set_input_id({BUS_VIRTUAL, 4, 4, 4});
+  touch_->set_physical_location("none");
+  touch_->set_abs_bit(ABS_MT_SLOT);
+  touch_->set_abs_max(ABS_MT_SLOT, 10);
+  touch_->set_abs_bit(ABS_MT_TOUCH_MAJOR);
+  touch_->set_abs_max(ABS_MT_TOUCH_MAJOR, 127);
+  touch_->set_abs_bit(ABS_MT_TOUCH_MINOR);
+  touch_->set_abs_max(ABS_MT_TOUCH_MINOR, 127);
+  touch_->set_abs_bit(ABS_MT_POSITION_X);
+  touch_->set_abs_max(ABS_MT_POSITION_X, display_frame.width());
+  touch_->set_abs_bit(ABS_MT_POSITION_Y);
+  touch_->set_abs_max(ABS_MT_POSITION_Y, display_frame.height());
+  touch_->set_abs_bit(ABS_MT_TRACKING_ID);
+  touch_->set_abs_max(ABS_MT_TRACKING_ID, MAX_TRACKING_ID);
+  touch_->set_prop_bit(INPUT_PROP_DIRECT);
+
+  for (int i = 0; i < MAX_FINGERS; i++)
+      touch_slots[i] = -1;
+
+  event_thread_ = std::thread(&Platform::process_events, this);
+}
+
+Platform::~Platform() {
+  if (event_thread_running_) {
+    event_thread_running_ = false;
+    event_thread_.join();
+  }
+}
+
+void Platform::set_renderer(const std::shared_ptr<Renderer> &renderer) {
+  renderer_ = renderer;
+}
+
+void Platform::set_window_manager(const std::shared_ptr<wm::Manager> &window_manager) {
+  window_manager_ = window_manager;
+}
+
+void Platform::process_events() {
+  event_thread_running_ = true;
+
+  while (event_thread_running_) {
+    SDL_Event event;
+    while (SDL_WaitEventTimeout(&event, 100)) {
+      switch (event.type) {
+        case SDL_QUIT:
+          break;
+        case SDL_WINDOWEVENT:
+          for (auto &iter : windows_) {
+            if (auto w = iter.second.lock()) {
+              if (w->window_id() == event.window.windowID) {
+                w->process_event(event);
+                break;
+              }
+            }
+          }
+          break;
+        case SDL_KEYDOWN:
+        case SDL_KEYUP:
+          if (keyboard_)
+            process_input_event(event);
+          break;
+        case SDL_MOUSEMOTION:
+        case SDL_MOUSEBUTTONDOWN:
+        case SDL_MOUSEBUTTONUP:
+        case SDL_MOUSEWHEEL:
+        case SDL_FINGERDOWN:
+        case SDL_FINGERUP:
+        case SDL_FINGERMOTION:
+          process_input_event(event);
+          break;
+        default:
+          break;
+      }
+    }
+  }
+}
+
+void Platform::process_input_event(const SDL_Event &event) {
+  std::vector<input::Event> mouse_events;
+  std::vector<input::Event> keyboard_events;
+  std::vector<input::Event> touch_events;
+
+  std::int32_t x = 0;
+  std::int32_t y = 0;
+
+  switch (event.type) {
+    // Mouse
+    case SDL_MOUSEBUTTONDOWN:
+      if (config_.no_touch_emulation) {
+        mouse_events.push_back({EV_KEY, BTN_LEFT, 1});
+      } else {
+        x = event.button.x;
+        y = event.button.y;
+        if (!adjust_coordinates(x, y))
+          break;
+        push_finger_down(x, y, emulated_touch_id_, touch_events);
+      }
+      break;
+    case SDL_MOUSEBUTTONUP:
+      if (config_.no_touch_emulation) {
+        mouse_events.push_back({EV_KEY, BTN_LEFT, 0});
+      } else {
+        push_finger_up(emulated_touch_id_, touch_events);
+      }
+      break;
+    case SDL_MOUSEMOTION:
+      x = event.motion.x;
+      y = event.motion.y;
+      if (!adjust_coordinates(x, y))
+        break;
+
+      if (config_.no_touch_emulation) {
+        // NOTE: Sending relative move events doesn't really work and we have
+        // changes in libinputflinger to take ABS_X/ABS_Y instead for absolute
+        // position events.
+        mouse_events.push_back({EV_ABS, ABS_X, x});
+        mouse_events.push_back({EV_ABS, ABS_Y, y});
+        // We're sending relative position updates here too but they will be only
+        // used by the Android side EventHub/InputReader to determine if the cursor
+        // was moved. They are not used to find out the exact position.
+        mouse_events.push_back({EV_REL, REL_X, event.motion.xrel});
+        mouse_events.push_back({EV_REL, REL_Y, event.motion.yrel});
+      } else {
+        push_finger_motion(x, y, emulated_touch_id_, touch_events);
+      }
+      break;
+    case SDL_MOUSEWHEEL:
+      if (!config_.no_touch_emulation) {
+        SDL_GetMouseState(&x, &y);
+        if (!adjust_coordinates(x, y))
+          break;
+
+        mouse_events.push_back({EV_ABS, ABS_X, x});
+        mouse_events.push_back({EV_ABS, ABS_Y, y});
+      }
+      mouse_events.push_back(
+          {EV_REL, REL_WHEEL, static_cast<std::int32_t>(event.wheel.y)});
+      break;
+    // Keyboard
+    case SDL_KEYDOWN: {
+      const auto code = KeycodeConverter::convert(event.key.keysym.scancode);
+      if (code == KEY_RESERVED) break;
+      keyboard_events.push_back({EV_KEY, code, 1});
+      break;
+    }
+    case SDL_KEYUP: {
+      const auto code = KeycodeConverter::convert(event.key.keysym.scancode);
+      if (code == KEY_RESERVED) break;
+      keyboard_events.push_back({EV_KEY, code, 0});
+      break;
+    }
+    // Touch screen
+    case SDL_FINGERDOWN: {
+      if (!calculate_touch_coordinates(event, x, y))
+        break;
+      push_finger_down(x, y, event.tfinger.fingerId, touch_events);
+
+      break;
+    }
+    case SDL_FINGERUP: {
+      push_finger_up(event.tfinger.fingerId, touch_events);
+      break;
+    }
+       case SDL_FINGERMOTION: {
+
+      if (!calculate_touch_coordinates(event, x, y))
+        break;
+      push_finger_motion(x, y, event.tfinger.fingerId, touch_events);
+      break;
+    }
+    default:
+      break;
+  }
+
+  if (mouse_events.size() > 0) {
+    mouse_events.push_back({EV_SYN, SYN_REPORT, 0});      
+    pointer_->send_events(mouse_events);
+  }
+
+  if (keyboard_events.size() > 0)
+    keyboard_->send_events(keyboard_events);
+
+  if (touch_events.size() > 0)
+    touch_->send_events(touch_events);
+}
+
+int Platform::find_touch_slot(int id){
+    for (int i = 0; i < MAX_FINGERS; i++) {
+        if (touch_slots[i] == id)
+          return i;
+    }
+    return -1;
+}
+
+void Platform::push_slot(std::vector<input::Event> &touch_events, int slot){
+    if (last_slot != slot) {
+        touch_events.push_back({EV_ABS, ABS_MT_SLOT, slot});
+        last_slot = slot;
+    }
+}
+
+void Platform::push_finger_down(int x, int y, int finger_id, std::vector<input::Event> &touch_events){
+    int slot = find_touch_slot(-1);
+    if (slot == -1) {
+        DEBUG("no free slot!");
+        return;
+    }
+    touch_slots[slot] = finger_id;
+    push_slot(touch_events, slot);
+    touch_events.push_back({EV_ABS, ABS_MT_TRACKING_ID, static_cast<std::int32_t>(finger_id % MAX_TRACKING_ID + 1)});
+    touch_events.push_back({EV_ABS, ABS_MT_POSITION_X, x});
+    touch_events.push_back({EV_ABS, ABS_MT_POSITION_Y, y});
+    touch_events.push_back({EV_SYN, SYN_REPORT, 0});
+}
+
+void Platform::push_finger_up(int finger_id, std::vector<input::Event> &touch_events){
+    int slot = find_touch_slot(finger_id);
+    if (slot == -1) 
+      return;
+    push_slot(touch_events, slot);
+    touch_events.push_back({EV_ABS, ABS_MT_TRACKING_ID, -1});
+    touch_events.push_back({EV_SYN, SYN_REPORT, 0});
+    touch_slots[slot] = -1;
+}
+
+void Platform::push_finger_motion(int x, int y, int finger_id, std::vector<input::Event> &touch_events){
+    int slot = find_touch_slot(finger_id);
+    if (slot == -1) 
+      return;
+    push_slot(touch_events, slot);
+    touch_events.push_back({EV_ABS, ABS_MT_POSITION_X, x});
+    touch_events.push_back({EV_ABS, ABS_MT_POSITION_Y, y});
+    touch_events.push_back({EV_SYN, SYN_REPORT, 0});
+}
+
+
+bool Platform::adjust_coordinates(std::int32_t &x, std::int32_t &y) {
+  SDL_Window *window = nullptr;
+
+  if (!config_.single_window) {
+    window = SDL_GetWindowFromID(focused_sdl_window_id_);
+    return adjust_coordinates(window, x, y);
+  } else {
+    // When running the whole Android system in a single window we don't
+    // need to reacalculate and the pointer position as they are already
+    // relative to our window.
+    return true;
+  }
+}
+
+bool Platform::adjust_coordinates(SDL_Window *window, std::int32_t &x, std::int32_t &y) {
+  std::int32_t rel_x = 0;
+  std::int32_t rel_y = 0;
+
+  if (!window) {
+    return false;
+  }
+  // As we get only absolute coordindates relative to our window we have to
+  // calculate the correct position based on the current focused window
+  SDL_GetWindowPosition(window, &rel_x, &rel_y);
+  x += rel_x;
+  y += rel_y;
+  return true;
+}
+
+bool Platform::calculate_touch_coordinates(const SDL_Event &event,
+                                           std::int32_t &x,
+                                           std::int32_t &y) {
+  SDL_Window *window = nullptr;
+
+  window = SDL_GetWindowFromID(focused_sdl_window_id_);
+  // before SDL 2.0.7 on X11 tfinger coordinates are not normalized
+  if (!SDL_VERSION_ATLEAST(2,0,7) && (event.tfinger.x > 1 || event.tfinger.y > 1)) {
+    x = event.tfinger.x;
+    y = event.tfinger.y;
+  } else {
+    if (window) {
+      SDL_GetWindowSize(window, &x, &y);
+      x *= event.tfinger.x;
+      y *= event.tfinger.y;
+    } else {
+      x = display_frame_.width() * event.tfinger.x;
+      y = display_frame_.height() * event.tfinger.y;
+    }
+  }
+
+  if (config_.single_window) {
+    // When running the whole Android system in a single window we don't
+    // need to reacalculate and the pointer position as they are already
+    // relative to our window.
+    return true;
+  } else {
+    return adjust_coordinates(window, x, y);
+  }
+}
+
+Window::Id Platform::next_window_id() {
+  static Window::Id next_id = 0;
+  return next_id++;
+}
+
+std::shared_ptr<wm::Window> Platform::create_window(
+    const anbox::wm::Task::Id &task, const anbox::graphics::Rect &frame, const std::string &title) {
+  if (!renderer_) {
+    ERROR("Can't create window without a renderer set");
+    return nullptr;
+  }
+
+  auto id = next_window_id();
+  auto w = std::make_shared<Window>(renderer_, id, task, shared_from_this(), frame, title,
+                 !window_size_immutable_, !config_.server_side_decoration);
+  focused_sdl_window_id_ = w->window_id();
+  windows_.insert({id, w});
+  return w;
+}
+
+void Platform::window_deleted(const Window::Id &id) {
+  auto w = windows_.find(id);
+  if (w == windows_.end()) {
+    WARNING("Got window removed event for unknown window (id %d)", id);
+    return;
+  }
+  if (auto window = w->second.lock())
+    window_manager_->remove_task(window->task());
+  windows_.erase(w);
+}
+
+void Platform::window_wants_focus(const Window::Id &id) {
+  auto w = windows_.find(id);
+  if (w == windows_.end()) return;
+
+  if (auto window = w->second.lock()) {
+    focused_sdl_window_id_ = window->window_id();
+    window_manager_->set_focused_task(window->task());
+  }
+}
+
+void Platform::window_moved(const Window::Id &id, const std::int32_t &x,
+                                  const std::int32_t &y) {
+  auto w = windows_.find(id);
+  if (w == windows_.end()) return;
+
+  if (auto window = w->second.lock()) {
+    auto new_frame = window->frame();
+    new_frame.translate(x, y);
+    window->update_frame(new_frame);
+    window_manager_->resize_task(window->task(), new_frame, 3);
+  }
+}
+
+void Platform::window_resized(const Window::Id &id,
+                                    const std::int32_t &width,
+                                    const std::int32_t &height) {
+  auto w = windows_.find(id);
+  if (w == windows_.end()) return;
+
+  if (auto window = w->second.lock()) {
+    auto new_frame = window->frame();
+    new_frame.resize(width, height);
+    // We need to update the window frame in advance here as otherwise we may
+    // get a movement event before we got an update of the actual layer
+    // representing this window and then we're back to the original size of
+    // the task.
+    window->update_frame(new_frame);
+    window_manager_->resize_task(window->task(), new_frame, 3);
+  }
+}
+
+void Platform::set_clipboard_data(const ClipboardData &data) {
+  if (data.text.empty())
+    return;
+  SDL_SetClipboardText(data.text.c_str());
+}
+
+Platform::ClipboardData Platform::get_clipboard_data() {
+  if (!SDL_HasClipboardText())
+    return ClipboardData{};
+
+  auto text = SDL_GetClipboardText();
+  if (!text)
+    return ClipboardData{};
+
+  auto data = ClipboardData{text};
+  SDL_free(text);
+  return data;
+}
+
+std::shared_ptr<audio::Sink> Platform::create_audio_sink() {
+  return std::make_shared<AudioSink>();
+}
+
+std::shared_ptr<audio::Source> Platform::create_audio_source() {
+  ERROR("Not implemented");
+  return nullptr;
+}
+
+bool Platform::supports_multi_window() const {
+  return true;
+}
+} // namespace sdl
+} // namespace platform
+} // namespace anbox