window.cpp 7.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * 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/>.
 *
 */

18
#include "anbox/platform/sdl/window.h"
19
#include "anbox/wm/window_state.h"
20 21
#include "anbox/graphics/density.h"
#include "anbox/logger.h"
22 23

#include <boost/throw_exception.hpp>
24

25 26 27 28
#if defined(MIR_SUPPORT)
#include <mir_toolkit/mir_client_library.h>
#endif

29 30 31 32 33 34 35 36
namespace {
constexpr const int window_resize_border{30};
constexpr const int top_drag_area{50};
constexpr const int button_size{32};
constexpr const int button_margin{5};
constexpr const int button_padding{4};
}

37
namespace anbox {
38 39
namespace platform {
namespace sdl {
40 41
Window::Id Window::Invalid{-1};

42
Window::Observer::~Observer() {}
43

S
Simon Fels 已提交
44 45
Window::Window(const std::shared_ptr<Renderer> &renderer,
               const Id &id, const wm::Task::Id &task,
46
               const std::shared_ptr<Observer> &observer,
47
               const graphics::Rect &frame,
48 49
               const std::string &title,
               bool resizable)
50
    : wm::Window(renderer, task, frame, title),
51 52 53 54
      id_(id),
      observer_(observer),
      native_display_(0),
      native_window_(0) {
55
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
56

57 58 59 60 61
  // NOTE: We don't furce GL initialization of the window as this will
  // be take care of by the Renderer when we attach to it. On EGL
  // initializing GL here will cause a surface to be created and the
  // renderer will attempt to create one too which will not work as
  // only a single surface per EGLNativeWindowType is supported.
62
  std::uint32_t flags = SDL_WINDOW_BORDERLESS;
63 64 65 66 67
  if (resizable)
    flags |= SDL_WINDOW_RESIZABLE;

  window_ = SDL_CreateWindow(title.c_str(),
                             frame.left(), frame.top(),
68
                             frame.width(), frame.height(),
69
                             flags);
70
  if (!window_) {
71
    const auto message = utils::string_format("Failed to create window: %s", SDL_GetError());
72 73 74
    BOOST_THROW_EXCEPTION(std::runtime_error(message));
  }

75 76 77
  if (SDL_SetWindowHitTest(window_, &Window::on_window_hit, this) < 0)
    BOOST_THROW_EXCEPTION(std::runtime_error("Failed to register for window hit test"));

78 79 80 81
  SDL_SysWMinfo info;
  SDL_VERSION(&info.version);
  SDL_GetWindowWMInfo(window_, &info);
  switch (info.subsystem) {
82
    case SDL_SYSWM_X11:
83
      native_display_ = static_cast<EGLNativeDisplayType>(info.info.x11.display);
84 85
      native_window_ = static_cast<EGLNativeWindowType>(info.info.x11.window);
      break;
86
#if defined(WAYLAND_SUPPORT)
87 88 89 90
    case SDL_SYSWM_WAYLAND:
      native_display_ = static_cast<EGLNativeDisplayType>(info.info.wl.display);
      native_window_ = reinterpret_cast<EGLNativeWindowType>(info.info.wl.surface);
      break;
91
#endif
92 93 94 95 96 97 98 99
#if defined(MIR_SUPPORT)
    case SDL_SYSWM_MIR: {
      native_display_ = static_cast<EGLNativeDisplayType>(mir_connection_get_egl_native_display(info.info.mir.connection));
      auto buffer_stream = mir_surface_get_buffer_stream(info.info.mir.surface);
      native_window_ = reinterpret_cast<EGLNativeWindowType>(mir_buffer_stream_get_egl_native_window(buffer_stream));
      break;
    }
#endif
100
    default:
101
      ERROR("Unknown subsystem (%d)", info.subsystem);
S
Shengjing Zhu 已提交
102
      BOOST_THROW_EXCEPTION(std::runtime_error("SDL subsystem not supported"));
103
  }
104 105

  SDL_ShowWindow(window_);
106 107
}

108
Window::~Window() {
109
  if (window_) SDL_DestroyWindow(window_);
110 111
}

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
SDL_HitTestResult Window::on_window_hit(SDL_Window *window, const SDL_Point *pt, void *data) {
  auto platform_window = reinterpret_cast<Window*>(data);

  int w = 0, h = 0;
  SDL_GetWindowSize(window, &w, &h);

  const auto border_size = graphics::dp_to_pixel(window_resize_border);
  const auto top_drag_area_height = graphics::dp_to_pixel(top_drag_area);
  const auto button_area_width = graphics::dp_to_pixel(button_size + button_padding * 2 + button_margin * 2);

  if (pt->y < top_drag_area_height) {
    if (pt->x > w - button_area_width && pt->x < w) {
      platform_window->close();
      return SDL_HITTEST_NORMAL;
    } else if (pt->x > w - button_area_width * 2 && pt->x < w - button_area_width) {
      platform_window->switch_window_state();
      return SDL_HITTEST_NORMAL;
    }
    return SDL_HITTEST_DRAGGABLE;
  } else if (pt->x < border_size && pt->y < border_size)
      return SDL_HITTEST_RESIZE_TOPLEFT;
  else if (pt->x > window_resize_border && pt->x < w - border_size && pt->y < border_size)
      return SDL_HITTEST_RESIZE_TOP;
  else if (pt->x > w - border_size && pt->y < border_size)
      return SDL_HITTEST_RESIZE_TOPRIGHT;
  else if (pt->x > w - border_size && pt->y > border_size && pt->y < h - border_size)
      return SDL_HITTEST_RESIZE_RIGHT;
  else if (pt->x > w - border_size && pt->y > h - border_size)
      return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
  else if (pt->x < w - border_size && pt->x > border_size && pt->y > h - border_size)
      return SDL_HITTEST_RESIZE_BOTTOM;
  else if (pt->x < border_size && pt->y > h - border_size)
      return SDL_HITTEST_RESIZE_BOTTOMLEFT;
  else if (pt->x < border_size && pt->y < h - border_size && pt->y > border_size)
      return SDL_HITTEST_RESIZE_LEFT;

  return SDL_HITTEST_NORMAL;
}

void Window::close() {
  if (observer_)
    observer_->window_deleted(id_);
}

void Window::switch_window_state() {
  const auto flags = SDL_GetWindowFlags(window_);
  if (flags & SDL_WINDOW_MAXIMIZED)
    SDL_RestoreWindow(window_);
  else
    SDL_MaximizeWindow(window_);
}

164
void Window::process_event(const SDL_Event &event) {
165
  switch (event.window.event) {
166
    case SDL_WINDOWEVENT_FOCUS_GAINED:
167 168
      if (observer_) observer_->window_wants_focus(id_);
      break;
169
    case SDL_WINDOWEVENT_FOCUS_LOST:
170
      break;
171 172
    // Not need to listen for SDL_WINDOWEVENT_RESIZED here as the
    // SDL_WINDOWEVENT_SIZE_CHANGED is always sent.
173
    case SDL_WINDOWEVENT_SIZE_CHANGED:
174 175
      if (observer_)
        observer_->window_resized(id_, event.window.data1, event.window.data2);
176
      break;
177
    case SDL_WINDOWEVENT_MOVED:
178 179
      if (observer_)
        observer_->window_moved(id_, event.window.data1, event.window.data2);
180
      break;
181
    case SDL_WINDOWEVENT_SHOWN:
182
      break;
183
    case SDL_WINDOWEVENT_HIDDEN:
184
      break;
185
    case SDL_WINDOWEVENT_CLOSE:
186 187 188 189
      if (observer_)
        observer_->window_deleted(id_);

      close();
190
      break;
T
Thomas Voß 已提交
191 192
    default:
      break;
193
  }
194 195
}

196
EGLNativeWindowType Window::native_handle() const { return native_window_; }
197

198
Window::Id Window::id() const { return id_; }
199

200
std::uint32_t Window::window_id() const { return SDL_GetWindowID(window_); }
201 202 203
} // namespace sdl
} // namespace platform
} // namespace anbox