diff --git a/src/anbox/graphics/multi_window_composer_strategy.cpp b/src/anbox/graphics/multi_window_composer_strategy.cpp index 5023891dbf239efc350bab18492c7a765f8642d3..c660bd71e51f0c136139359746ba1ad8a9519e8c 100644 --- a/src/anbox/graphics/multi_window_composer_strategy.cpp +++ b/src/anbox/graphics/multi_window_composer_strategy.cpp @@ -48,51 +48,60 @@ std::map, RenderableList> MultiWindowComposerStrateg for (auto &w : win_layers) { const auto &renderables = w.second; RenderableList final_renderables; - int top = 1080; - int left = 1920; - - for (auto &r : renderables) { - // We always prioritize layers which are lower in the list we got - // from SurfaceFlinger as they are already ordered. - int current_left = r.screen_position().left(); - int current_top = r.screen_position().top(); - int half_diff = 2; - if (r.screen_position().width() > w.first->frame().width()) { - auto diff = r.screen_position().width() - w.first->frame().width(); - current_left += diff / half_diff; - } - if (r.screen_position().height() > w.first->frame().height()) { - auto diff = r.screen_position().height() - w.first->frame().height(); - current_top += diff / half_diff; - } - - if (current_left < left) { - left = current_left; - } - if (current_top < top) { - top = current_top; - } - } - + auto window_frame = w.first->frame(); + bool resizeable = w.first->checkResizeable(); + auto old_frame = w.first->last_frame(); + int max_area = 0; + Rect max_rect; for (auto &r : renderables) { // As we get absolute display coordinates from the Android hwcomposer we // need to recalculate all layer coordinates into relatives ones to the // window they are drawn into. auto rect = Rect{ - r.screen_position().left() - left + r.crop().left(), - r.screen_position().top() - top + r.crop().top(), - r.screen_position().right() - left + r.crop().left(), - r.screen_position().bottom() - top + r.crop().top()}; + r.screen_position().left() - window_frame.left(), + r.screen_position().top() - window_frame.top(), + r.screen_position().right() - window_frame.left(), + r.screen_position().bottom() - window_frame.top()}; + if (rect.width() * rect.height() > max_area) { + max_area = rect.width() * rect.height(); + max_rect = Rect(r.screen_position().left() - old_frame.left(), + r.screen_position().top() - old_frame.top(), + r.screen_position().right() - old_frame.left(), + r.screen_position().bottom() - old_frame.top()); + } auto new_renderable = r; new_renderable.set_screen_position(rect); final_renderables.push_back(new_renderable); } - w.second = final_renderables; - } + bool changed = false; + if (resizeable) { + int max_old_area = 0; + Rect max_old_rect; + auto it = last_renderables.find(w.first); + if (it != last_renderables.end()) { + for (auto &rt : it->second) { + if (max_old_area < rt.screen_position().width() * rt.screen_position().height()) { + max_old_area = rt.screen_position().width() * rt.screen_position().height(); + max_old_rect = rt.screen_position(); + } + } + } + if (max_old_rect == max_rect) { + w.second = it->second; + changed = true; + } else { + w.first->setResizing(false); + } + } - return win_layers; + if(!changed) { + w.second = final_renderables; + } + } + last_renderables.swap(win_layers); + return last_renderables; } } // namespace graphics } // namespace anbox diff --git a/src/anbox/graphics/multi_window_composer_strategy.h b/src/anbox/graphics/multi_window_composer_strategy.h index e7dbdf96d1770fecd50fc0e2548e88f5cfac7b06..0205df52e30f76a0b35d096573205991e68a247e 100644 --- a/src/anbox/graphics/multi_window_composer_strategy.h +++ b/src/anbox/graphics/multi_window_composer_strategy.h @@ -33,6 +33,7 @@ class MultiWindowComposerStrategy : public LayerComposer::Strategy { private: std::shared_ptr wm_; + WindowRenderableList last_renderables; }; } // namespace graphics } // namespace anbox diff --git a/src/anbox/platform/sdl/window.cpp b/src/anbox/platform/sdl/window.cpp index f380e00866c93c61d8b84087f4e5b7794f986fe1..b3b14f75e1ff99b3aa2d3feb4c8565a13b51c780 100755 --- a/src/anbox/platform/sdl/window.cpp +++ b/src/anbox/platform/sdl/window.cpp @@ -279,11 +279,19 @@ void Window::process_event(const SDL_Event &event) { // SDL_WINDOWEVENT_SIZE_CHANGED is always sent. case SDL_WINDOWEVENT_SIZE_CHANGED: if (observer_) { + struct timeval now = (struct timeval) { 0 }; + gettimeofday(&now, NULL); + last_resize_time_ = USEC_PER_SEC * (now.tv_sec) + now.tv_usec; + resizing_ = true; observer_->window_resized(id_, event.window.data1, event.window.data2); } break; case SDL_WINDOWEVENT_MOVED: if (observer_) { + struct timeval now = (struct timeval) { 0 }; + gettimeofday(&now, NULL); + last_resize_time_ = USEC_PER_SEC * (now.tv_sec) + now.tv_usec; + resizing_ = true; observer_->window_moved(id_, event.window.data1, event.window.data2); } break; @@ -305,6 +313,22 @@ Window::Id Window::id() const { return id_; } std::uint32_t Window::window_id() const { return SDL_GetWindowID(window_); } +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; +} + void Window::update_state(const wm::WindowState::List &states) { if (!initialized.load() && !states.empty()) { int w = 0; @@ -330,7 +354,6 @@ void Window::update_state(const wm::WindowState::List &states) { 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; diff --git a/src/anbox/platform/sdl/window.h b/src/anbox/platform/sdl/window.h index 6a6d90431f97ec9f129658cfc6e68df84fd3900c..05af468e495bed0e180864c5b738a44dbd91bcb5 100755 --- a/src/anbox/platform/sdl/window.h +++ b/src/anbox/platform/sdl/window.h @@ -41,6 +41,7 @@ class Window : public std::enable_shared_from_this, public wm::Window { static const long long USEC_PER_SEC = 1000000; static const long long APP_START_MAX_TIME = 15 * USEC_PER_SEC; static const long long timespan_db_click = 500000; + static const long long RESIZE_TIMESPAN = USEC_PER_SEC / 2; struct mini_size { int minimum_width; @@ -77,6 +78,8 @@ class Window : public std::enable_shared_from_this, public wm::Window { void update_state(const wm::WindowState::List &states) override; bool check_db_clicked(int x, int y); + bool checkResizeable() override; + void setResizing(bool resizing) override; void restore_window(); Id id() const; @@ -93,6 +96,7 @@ class Window : public std::enable_shared_from_this, public wm::Window { std::atomic initialized{false}; long long last_update_time{ 0 }; + long long last_resize_time_{ 0 }; Id id_; std::shared_ptr observer_; EGLNativeDisplayType native_display_; diff --git a/src/anbox/wm/window.cpp b/src/anbox/wm/window.cpp index 7169e643addb8f372b3b007d2d53945fb4138ab2..05c6a48cdbd755a8161115a26d94ea3c09323d4b 100644 --- a/src/anbox/wm/window.cpp +++ b/src/anbox/wm/window.cpp @@ -21,8 +21,9 @@ namespace anbox { namespace wm { -Window::Window(const std::shared_ptr &renderer, const Task::Id &task, const graphics::Rect &frame, const std::string &title) - : renderer_(renderer), task_(task), frame_(frame), title_(title), native_window_(0) {} +Window::Window(const std::shared_ptr &renderer, const Task::Id &task, + const graphics::Rect &frame, const std::string &title) + : renderer_(renderer), task_(task), frame_(frame), title_(title), native_window_(0), last_frame_(frame) {} Window::~Window() { release(); @@ -44,6 +45,8 @@ Task::Id Window::task() const { return task_; } graphics::Rect Window::frame() const { return frame_; } +graphics::Rect Window::last_frame() const { return last_frame_; } + EGLNativeWindowType Window::native_handle() const { return native_window_; } std::string Window::title() const { return title_; } diff --git a/src/anbox/wm/window.h b/src/anbox/wm/window.h index 1788621dbd3a65b59321acfd13038e739597f2c9..f149bb170a71d315f242b5c2f482408e5c7254ac 100644 --- a/src/anbox/wm/window.h +++ b/src/anbox/wm/window.h @@ -53,15 +53,21 @@ class Window { virtual void update_state(const WindowState::List &states); void update_frame(const graphics::Rect &frame); + void update_last_frame(const graphics::Rect &frame); virtual bool title_event_filter(int y); void set_native_handle(const EGLNativeWindowType &handle); EGLNativeWindowType native_handle() const; graphics::Rect frame() const; + graphics::Rect last_frame() const; Task::Id task() const; std::string title() const; - + virtual bool checkResizeable() { return resizing_; } + virtual void setResizing(bool resizing) { resizing_ = resizing; } + protected: + graphics::Rect last_frame_; + bool resizing_{false}; private: EGLNativeWindowType native_window_; std::shared_ptr renderer_; diff --git a/tests/anbox/graphics/layer_composer_tests.cpp b/tests/anbox/graphics/layer_composer_tests.cpp index 98342971b323bd670068ee528f9d1f916346f760..8fe5957e273dbd9c13c936e3f4f1bcb43b3f1662 100644 --- a/tests/anbox/graphics/layer_composer_tests.cpp +++ b/tests/anbox/graphics/layer_composer_tests.cpp @@ -125,7 +125,7 @@ TEST(LayerComposer, MapsLayersToWindows) { }; RenderableList second_window_renderables{ - {"org.anbox.surface.2", 1, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, + {"org.anbox.surface.2", 1, 1.0f, {-300, -400, 724, 368}, {0, 0, 1024, 768}}, }; EXPECT_CALL(*renderer, draw(_, Rect{0, 0, first_window.frame().width(), @@ -171,12 +171,12 @@ TEST(LayerComposer, WindowPartiallyOffscreen) { // but the layer covering the whole window is placed with its top left // origin outside of the visible display area. RenderableList renderables = { - {"org.anbox.surface.1", 0, 1.0f, {-100, -100, 924, 668}, {0, 0, 1024, 768}}, + {"org.anbox.surface.1", 0, 1.0f, {0, 0, 924, 668}, {100, 100, 1024, 768}}, {"org.anbox.surface.1", 1, 1.0f, {0, 0, 100, 200}, {0, 0, 100, 200}}, }; RenderableList expected_renderables{ - {"org.anbox.surface.1", 0, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, + {"org.anbox.surface.1", 0, 1.0f, {100, 100, 1024, 768}, {100, 100, 1024, 768}}, {"org.anbox.surface.1", 1, 1.0f, {100, 100, 200, 300}, {0, 0, 100, 200}}, }; @@ -227,8 +227,8 @@ TEST(LayerComposer, PopupShouldNotCauseWindowLayerOffset) { }; RenderableList expected_renderables{ - {"org.anbox.surface.3", 0, 1.0f, {0, 24, 1024, 792}, {0, 0, 1024, 768}}, - {"org.anbox.surface.3", 1, 1.0f, {784, 0, 1044, 160}, {0, 0, 260, 160}}, + {"org.anbox.surface.3", 0, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, + {"org.anbox.surface.3", 1, 1.0f, {784, -24, 1044, 136}, {0, 0, 260, 160}}, }; EXPECT_CALL(*renderer, draw(_, Rect{0, 0, @@ -241,5 +241,103 @@ TEST(LayerComposer, PopupShouldNotCauseWindowLayerOffset) { composer.submit_layers(renderables); } +TEST(LayerComposer, ResizingShouldUseOldRender) { + auto renderer = std::make_shared(); + + platform::Configuration config; + // The default policy will create a dumb window instance when requested + // from the manager. + auto platform = platform::create(std::string(), nullptr, config); + auto app_db = std::make_shared(); + auto wm = std::make_shared(platform, nullptr, app_db); + + auto single_window = wm::WindowState{ + wm::Display::Id{1}, + true, + graphics::Rect{1120, 270, 2144, 1038}, + "org.anbox.foo", + wm::Task::Id{3}, + wm::Stack::Id::Freeform, + }; + + auto window = platform->create_window(single_window.task(), single_window.frame(), single_window.package_name()); + window->attach(); + wm->insert_task(single_window.task(), window); + + LayerComposer composer(renderer, std::make_shared(wm)); + + RenderableList renderables_first = { + {"org.anbox.surface.3", 0, 1.0f, {1120,270,2144,1038}, {0, 0, 1024, 768}}, + }; + + composer.submit_layers(renderables_first); + window->update_frame(graphics::Rect{0, 0, 1024, 768}); + window->setResizing(true); + composer.submit_layers(renderables_first); + RenderableList expected_renderables{ + {"org.anbox.surface.3", 0, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, + }; + + EXPECT_CALL(*renderer, draw(_, Rect{0, 0, + single_window.frame().width(), + single_window.frame().height()}, + expected_renderables)) + .Times(1) + .WillOnce(Return(true)); + + composer.submit_layers(renderables_first); +} + +TEST(LayerComposer, ResizeOverShouldUseNewRender) { + auto renderer = std::make_shared(); + + platform::Configuration config; + // The default policy will create a dumb window instance when requested + // from the manager. + auto platform = platform::create(std::string(), nullptr, config); + auto app_db = std::make_shared(); + auto wm = std::make_shared(platform, nullptr, app_db); + + auto single_window = wm::WindowState{ + wm::Display::Id{1}, + true, + graphics::Rect{1120, 270, 2144, 1038}, + "org.anbox.foo", + wm::Task::Id{3}, + wm::Stack::Id::Freeform, + }; + + auto window = platform->create_window(single_window.task(), single_window.frame(), single_window.package_name()); + window->attach(); + wm->insert_task(single_window.task(), window); + + LayerComposer composer(renderer, std::make_shared(wm)); + + RenderableList renderables_first = { + {"org.anbox.surface.3", 0, 1.0f, {1120,270,2144,1038}, {0, 0, 1024, 768}}, + }; + + RenderableList renderables_second = { + {"org.anbox.surface.3", 0, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, + }; + + composer.submit_layers(renderables_first); + window->update_frame(graphics::Rect{0, 0, 1024, 768}); + window->setResizing(true); + composer.submit_layers(renderables_first); + RenderableList expected_renderables{ + {"org.anbox.surface.3", 0, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, + }; + + EXPECT_CALL(*renderer, draw(_, Rect{0, 0, + single_window.frame().width(), + single_window.frame().height()}, + expected_renderables)) + .Times(1) + .WillOnce(Return(true)); + + composer.submit_layers(renderables_second); + EXPECT_TRUE(window->checkResizeable() == false); +} } // namespace graphics } // namespace anbox