window.cpp 12.9 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
#include "anbox/system_configuration.h"
23 24

#include <boost/throw_exception.hpp>
25
#include <SDL2/SDL_image.h>
26

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

31
namespace {
Y
yu_qinfei 已提交
32
constexpr const int window_resize_border{5};
33 34 35
constexpr const int button_size{42};
constexpr const int button_margin{0};
constexpr const int button_padding{0};
36 37
}

38
namespace anbox {
39 40
namespace platform {
namespace sdl {
41 42 43 44 45 46

static const std::uint32_t HIDE_BACK     = 0x01;
static const std::uint32_t HIDE_MINIMIZE = 0x02;
static const std::uint32_t HIDE_MAXIMIZE = 0x04;
static const std::uint32_t HIDE_CLOSE    = 0x08;
static const std::uint32_t SHOW_ALL      = 0x00;
47 48
static const std::uint32_t MINI_WIDTH    = 540;
static const std::uint32_t MINI_HEIGHT   = 700;
49
static const std::uint32_t WX_MINI_WIDTH = 540;
50 51

const std::map<std::string, std::uint32_t> Window::property_map = {
52
  {"腾讯视频", SHOW_ALL},
Y
yu_qinfei 已提交
53
  {"爱奇艺HD", SHOW_ALL}
54
};
55

56 57 58 59
const std::map<std::string, Window::mini_size>Window::custom_window_map = {
  {"微信", {WX_MINI_WIDTH, MINI_HEIGHT}}
};

60
Window::Id Window::Invalid{ -1 };
61

62
Window::Observer::~Observer() {}
63

S
Simon Fels 已提交
64 65
Window::Window(const std::shared_ptr<Renderer> &renderer,
               const Id &id, const wm::Task::Id &task,
66
               const std::shared_ptr<Observer> &observer,
67
               const graphics::Rect &frame,
68 69
               const std::string &title,
               bool resizable)
70
    : wm::Window(renderer, task, frame, title),
71
      id_(id),
72
      lastClickTime(0),
73
      observer_(observer),
74 75
      native_display_(0),
      visible_property(SHOW_ALL) {
76
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
77

78 79 80 81 82
  // 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.
83
  std::uint32_t flags = SDL_WINDOW_BORDERLESS;
Y
yu_qinfei 已提交
84 85 86
  auto property_itr = property_map.find(title);
  if (property_itr != property_map.end()) {
    visible_property = property_itr->second;
87 88
  } else {
    visible_property = HIDE_MAXIMIZE;
89 90
  }
  if (!(visible_property & HIDE_MAXIMIZE) && resizable) {
91
    flags |= SDL_WINDOW_RESIZABLE;
Y
yu_qinfei 已提交
92
  }
93

94 95
  window_ = SDL_CreateWindow(title.c_str(),
                             frame.left(), frame.top(),
96
                             frame.width(), frame.height(),
97
                             flags);
98
  if (!window_) {
99
    const auto message = utils::string_format("Failed to create window: %s", SDL_GetError());
100 101 102
    BOOST_THROW_EXCEPTION(std::runtime_error(message));
  }

103 104 105
  if (utils::get_env_value("ANBOX_NO_SDL_WINDOW_HIT_TEST", "false") == "false")
    if (SDL_SetWindowHitTest(window_, &Window::on_window_hit, this) < 0)
      BOOST_THROW_EXCEPTION(std::runtime_error("Failed to register for window hit test"));
106

107
  std::string strPath = SystemConfiguration::instance().resource_dir() + "/ui/logo.png";
108 109 110

  SDL_Surface *icon = IMG_Load(strPath.c_str());
  SDL_SetWindowIcon(window_, icon);
111
  SDL_FreeSurface(icon);
112

113 114 115 116
  SDL_SysWMinfo info;
  SDL_VERSION(&info.version);
  SDL_GetWindowWMInfo(window_, &info);
  switch (info.subsystem) {
D
David Heidelberg 已提交
117
#if defined(X11_SUPPORT)
118
    case SDL_SYSWM_X11:
119
      native_display_ = static_cast<EGLNativeDisplayType>(info.info.x11.display);
120
      set_native_handle(static_cast<EGLNativeWindowType>(info.info.x11.window));
121
      break;
D
David Heidelberg 已提交
122
#endif
123
#if defined(WAYLAND_SUPPORT)
124
    case SDL_SYSWM_WAYLAND:
125
      native_display_ = reinterpret_cast<EGLNativeDisplayType>(info.info.wl.display);
126
      set_native_handle(reinterpret_cast<EGLNativeWindowType>(info.info.wl.surface));
127
      break;
128
#endif
129 130 131 132
#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);
133
      set_native_handle(reinterpret_cast<EGLNativeWindowType>(mir_buffer_stream_get_egl_native_window(buffer_stream)));
134 135 136
      break;
    }
#endif
137
    default:
138
      ERROR("Unknown subsystem (%d)", info.subsystem);
S
Shengjing Zhu 已提交
139
      BOOST_THROW_EXCEPTION(std::runtime_error("SDL subsystem not supported"));
140
  }
141

142 143
  struct timeval now = (struct timeval) { 0 };
  gettimeofday(&now, NULL);
144 145
  last_update_time = USEC_PER_SEC * (now.tv_sec) + now.tv_usec;
  lastClickTime = last_update_time;
146 147 148 149 150 151
  auto window_size_ptr = custom_window_map.find(title);
  if (window_size_ptr != custom_window_map.end()) {
    SDL_SetWindowMinimumSize(window_, window_size_ptr->second.minimum_width, window_size_ptr->second.minimum_height);
  } else {
    SDL_SetWindowMinimumSize(window_, MINI_WIDTH, MINI_HEIGHT);
  }
152
  SDL_ShowWindow(window_);
153 154
}

155
Window::~Window() {
156
  if (window_) SDL_DestroyWindow(window_);
157 158
}

159
bool Window::title_event_filter(int x, int y) {
160 161
  const auto top_drag_area_height = graphics::dp_to_pixel(button_size + (button_margin << 1));
  return !fullscreen_ && y <= top_drag_area_height;
162 163
}

164 165 166
SDL_HitTestResult Window::on_window_hit(SDL_Window *window, const SDL_Point *pt, void *data) {
  auto platform_window = reinterpret_cast<Window*>(data);

T
twwang 已提交
167 168 169 170
  if (platform_window->get_fullscreen()) {
    return SDL_HITTEST_NORMAL;
  }

171 172 173 174
  int w = 0, h = 0;
  SDL_GetWindowSize(window, &w, &h);

  const auto border_size = graphics::dp_to_pixel(window_resize_border);
175 176 177 178 179 180 181
  // top and bottom, two margins
  const auto top_drag_area_height = graphics::dp_to_pixel(button_size + (button_margin << 1));
  // left and right, two margins
  const auto button_area_width = graphics::dp_to_pixel(button_size + (button_margin << 1));

  SDL_HitTestResult result = SDL_HITTEST_NORMAL;

182 183 184 185
  if (!platform_window->initialized.load()) {
    INFO("window initialized by resize");
    platform_window->initialized = true;
  }
186
  if (platform_window->title_event_filter(pt->x, pt->y)) {
187 188 189 190 191
    if (!platform_window->initialized.load()) {
      INFO("window initialized by click top");
      platform_window->initialized = true;
    }

192 193 194 195 196 197 198
    if (pt->x > 0 && pt->x < button_area_width &&
            ((platform_window->get_property() & HIDE_BACK) != HIDE_BACK)) {
      std::shared_ptr<anbox::platform::sdl::Window::Observer> observer = platform_window->observer_;
      if (observer ) {
        observer->window_wants_focus(platform_window->id());
        observer->input_key_event(SDL_SCANCODE_AC_BACK, 1);
        observer->input_key_event(SDL_SCANCODE_AC_BACK, 0);
199
      }
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
      return SDL_HITTEST_NORMAL;
    }
    int btn_cnt = 1;  //button count from right of the titlebar
    if ((platform_window->get_property() & HIDE_CLOSE) != HIDE_CLOSE) {
      if (pt->x > w - button_area_width * btn_cnt &&
              pt->x < w - button_area_width * (btn_cnt - 1)) {
        platform_window->close();
        return SDL_HITTEST_NORMAL;
      }
      ++btn_cnt;
    }
    if ((platform_window->get_property() & HIDE_MAXIMIZE) != HIDE_MAXIMIZE) {
      if (pt->x > w - button_area_width * btn_cnt &&
              pt->x < w - button_area_width * (btn_cnt - 1)) {
        platform_window->switch_window_state();
        return SDL_HITTEST_NORMAL;
      }
      ++btn_cnt;
    }
    if ((platform_window->get_property() & HIDE_MINIMIZE) != HIDE_MINIMIZE) {
      if (pt->x > w - button_area_width * btn_cnt &&
              pt->x < w - button_area_width * (btn_cnt - 1)) {
        SDL_MinimizeWindow(platform_window->window_);
        return SDL_HITTEST_NORMAL;
      }
    }
    if (((platform_window->get_property() & HIDE_MAXIMIZE) != HIDE_MAXIMIZE) &&
               platform_window->check_db_clicked(pt->x, pt->y)) {
228
      platform_window->switch_window_state();
229
      return SDL_HITTEST_NORMAL;
230
    } else {
231
      return SDL_HITTEST_DRAGGABLE;
232
    }
233
  }
234 235 236 237

  return SDL_HITTEST_NORMAL;
}

238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
bool Window::check_db_clicked(int x, int y) {
  bool result = false;
  int wnd_x = 0;
  int wnd_y = 0;
  SDL_GetWindowPosition(window_, &wnd_x, &wnd_y);
  struct timeval now;
  gettimeofday(&now, NULL);
  long long current_time = USEC_PER_SEC * (now.tv_sec) + now.tv_usec;
  if (x == last_point_x && y == last_point_y && 
      wnd_x == last_wnd_x && wnd_y == last_wnd_y) {
    if (current_time - lastClickTime <= timespan_db_click) {
      lastClickTime = current_time - timespan_db_click;
      result = true;
    }
  }
  if (!result){
    lastClickTime = current_time;
  }

  last_wnd_x = wnd_x;
  last_wnd_y = wnd_y;
  last_point_x = x;
  last_point_y = y;
  return result;
}

264 265 266 267 268 269 270 271 272 273 274 275 276
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_);
}

277
void Window::process_event(const SDL_Event &event) {
278
  switch (event.window.event) {
279
    case SDL_WINDOWEVENT_FOCUS_GAINED:
280 281 282
      if (observer_) {
        observer_->window_wants_focus(id_);
      }
283
      break;
284
    case SDL_WINDOWEVENT_FOCUS_LOST:
285
      break;
286 287
    // Not need to listen for SDL_WINDOWEVENT_RESIZED here as the
    // SDL_WINDOWEVENT_SIZE_CHANGED is always sent.
288
    case SDL_WINDOWEVENT_SIZE_CHANGED:
289
      if (observer_) {
290 291 292 293
        struct timeval now = (struct timeval) { 0 };
        gettimeofday(&now, NULL);
        last_resize_time_ = USEC_PER_SEC * (now.tv_sec) + now.tv_usec;
        resizing_ = true;
294
        observer_->window_resized(id_, event.window.data1, event.window.data2);
295
      }
296
      break;
297
    case SDL_WINDOWEVENT_MOVED:
298
      if (observer_) {
299 300 301 302
        struct timeval now = (struct timeval) { 0 };
        gettimeofday(&now, NULL);
        last_resize_time_ = USEC_PER_SEC * (now.tv_sec) + now.tv_usec;
        resizing_ = true;
303
        observer_->window_moved(id_, event.window.data1, event.window.data2);
304
      }
305
      break;
306
    case SDL_WINDOWEVENT_SHOWN:
307
      break;
308
    case SDL_WINDOWEVENT_HIDDEN:
309
      break;
310
    case SDL_WINDOWEVENT_CLOSE:
311 312 313
      if (observer_)
        observer_->window_deleted(id_);
      close();
314
      break;
T
Thomas Voß 已提交
315 316
    default:
      break;
317
  }
318 319
}

320
Window::Id Window::id() const { return id_; }
321

322
std::uint32_t Window::window_id() const { return SDL_GetWindowID(window_); }
323

324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
bool Window::checkResizeable() {
  struct timeval now = (struct timeval) { 0 };
  gettimeofday(&now, NULL);
  long long time_now = USEC_PER_SEC * (now.tv_sec) + now.tv_usec;
  if (resizing_ && time_now - last_resize_time_ > RESIZE_TIMESPAN) {
    last_frame_ = frame();
    resizing_ = false;
  }
  return resizing_;
}

void Window::setResizing(bool resizing) {
  last_frame_ = frame();
  resizing_ = resizing;
}

340
void Window::update_state(const wm::WindowState::List &states) {
T
twwang 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353
  for (auto ws : states)
  {
    if (!fullscreen_ && ws.videofullscreen()) {
      SDL_SetWindowFullscreen(window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
      fullscreen_ = true;
    } else if (fullscreen_ && !ws.videofullscreen()) {
      SDL_SetWindowFullscreen(window_, 0);
      fullscreen_ = false;
    }
    if (fullscreen_) {
      return;
    }
  }
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
  if (!initialized.load() && !states.empty()) {
    int w = 0;
    int h = 0;
    int x = 0;
    int y = 0;
    SDL_GetWindowSize(window_, &w, &h);
    SDL_GetWindowPosition(window_, &x, &y);

    graphics::Rect rect;
    int area = w * h;
    for (auto ws : states)
    {
      int temp = ws.frame().width() * ws.frame().height();
      if (temp >= area) {
        rect = ws.frame();
      }
    }

    if (w == rect.width() &&
        h == rect.height() &&
        x == rect.left() &&
        y == rect.top()) {
      return;
    }
    struct timeval now = (struct timeval) { 0 };
    gettimeofday(&now, NULL);
    long long current_time = USEC_PER_SEC * (now.tv_sec) + now.tv_usec;
    if (current_time - last_update_time >= APP_START_MAX_TIME) {
      INFO("window initialized by timeout");
      initialized = true;
      return;
    }

    last_update_time = current_time;
    SDL_SetWindowSize(window_, rect.width(), rect.height());
    SDL_SetWindowPosition(window_, rect.left(), rect.top());
    update_frame(rect);
  }
}
393 394 395 396 397 398 399 400 401

void Window::restore_window() {
  SDL_ShowWindow(window_);
  SDL_RaiseWindow(window_);
  auto flags = SDL_GetWindowFlags(window_);
  if (flags & SDL_WINDOW_MINIMIZED) {
    SDL_RestoreWindow(window_);
  }
}
402

403 404 405
} // namespace sdl
} // namespace platform
} // namespace anbox