1 //
2 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // ScenicWindow.cpp:
7 //    Implements methods from ScenicWindow
8 //
9 
10 #include "util/fuchsia/ScenicWindow.h"
11 
12 #include <fuchsia/images/cpp/fidl.h>
13 #include <fuchsia/ui/views/cpp/fidl.h>
14 #include <lib/async-loop/cpp/loop.h>
15 #include <lib/async-loop/default.h>
16 #include <lib/fdio/directory.h>
17 #include <lib/fidl/cpp/interface_ptr.h>
18 #include <lib/fidl/cpp/interface_request.h>
19 #include <lib/ui/scenic/cpp/view_token_pair.h>
20 #include <lib/zx/channel.h>
21 #include <zircon/status.h>
22 
23 namespace
24 {
25 
GetDefaultLoop()26 async::Loop *GetDefaultLoop()
27 {
28     static async::Loop *defaultLoop = new async::Loop(&kAsyncLoopConfigNeverAttachToThread);
29     return defaultLoop;
30 }
31 
ConnectToServiceRoot()32 zx::channel ConnectToServiceRoot()
33 {
34     zx::channel clientChannel;
35     zx::channel serverChannel;
36     zx_status_t result = zx::channel::create(0, &clientChannel, &serverChannel);
37     ASSERT(result == ZX_OK);
38     result = fdio_service_connect("/svc/.", serverChannel.release());
39     ASSERT(result == ZX_OK);
40     return clientChannel;
41 }
42 
43 template <typename Interface>
ConnectToService(zx_handle_t serviceRoot,fidl::InterfaceRequest<Interface> request)44 zx_status_t ConnectToService(zx_handle_t serviceRoot, fidl::InterfaceRequest<Interface> request)
45 {
46     ASSERT(request.is_valid());
47     return fdio_service_connect_at(serviceRoot, Interface::Name_, request.TakeChannel().release());
48 }
49 
50 template <typename Interface>
ConnectToService(zx_handle_t serviceRoot,async_dispatcher_t * dispatcher)51 fidl::InterfacePtr<Interface> ConnectToService(zx_handle_t serviceRoot,
52                                                async_dispatcher_t *dispatcher)
53 {
54     fidl::InterfacePtr<Interface> result;
55     ConnectToService(serviceRoot, result.NewRequest(dispatcher));
56     return result;
57 }
58 
59 }  // namespace
60 
ScenicWindow()61 ScenicWindow::ScenicWindow()
62     : mLoop(GetDefaultLoop()),
63       mServiceRoot(ConnectToServiceRoot()),
64       mScenic(
65           ConnectToService<fuchsia::ui::scenic::Scenic>(mServiceRoot.get(), mLoop->dispatcher())),
66       mPresenter(ConnectToService<fuchsia::ui::policy::Presenter>(mServiceRoot.get(),
67                                                                   mLoop->dispatcher())),
68       mScenicSession(mScenic.get(), mLoop->dispatcher()),
69       mShape(&mScenicSession),
70       mMaterial(&mScenicSession)
71 {
72     mScenicSession.set_error_handler(fit::bind_member(this, &ScenicWindow::onScenicError));
73     mScenicSession.set_event_handler(fit::bind_member(this, &ScenicWindow::onScenicEvents));
74     mScenicSession.set_on_frame_presented_handler(
75         fit::bind_member(this, &ScenicWindow::onFramePresented));
76 }
77 
~ScenicWindow()78 ScenicWindow::~ScenicWindow()
79 {
80     destroy();
81 }
82 
initializeImpl(const std::string & name,int width,int height)83 bool ScenicWindow::initializeImpl(const std::string &name, int width, int height)
84 {
85     // Set up scenic resources.
86     mShape.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
87     mShape.SetMaterial(mMaterial);
88 
89     fuchsia::ui::views::ViewToken viewToken;
90     fuchsia::ui::views::ViewHolderToken viewHolderToken;
91     std::tie(viewToken, viewHolderToken) = scenic::NewViewTokenPair();
92 
93     // Create view.
94     mView = std::make_unique<scenic::View>(&mScenicSession, std::move(viewToken), name);
95     mView->AddChild(mShape);
96 
97     // Present view.
98     mPresenter->PresentOrReplaceView(std::move(viewHolderToken), nullptr);
99 
100     mWidth  = width;
101     mHeight = height;
102 
103     resetNativeWindow();
104 
105     // Block until initial view dimensions are known.
106     while (!mHasViewMetrics && !mHasViewProperties && !mLostSession)
107     {
108         mLoop->ResetQuit();
109         mLoop->Run(zx::time::infinite(), true /* once */);
110     }
111 
112     return true;
113 }
114 
disableErrorMessageDialog()115 void ScenicWindow::disableErrorMessageDialog() {}
116 
destroy()117 void ScenicWindow::destroy()
118 {
119     while (mInFlightPresents != 0 && !mLostSession)
120     {
121         mLoop->ResetQuit();
122         mLoop->Run();
123     }
124 
125     ASSERT(mInFlightPresents == 0 || mLostSession);
126 
127     mFuchsiaEGLWindow.reset();
128 }
129 
resetNativeWindow()130 void ScenicWindow::resetNativeWindow()
131 {
132     fuchsia::images::ImagePipe2Ptr imagePipe;
133     uint32_t imagePipeId = mScenicSession.AllocResourceId();
134     mScenicSession.Enqueue(
135         scenic::NewCreateImagePipe2Cmd(imagePipeId, imagePipe.NewRequest(mLoop->dispatcher())));
136     zx_handle_t imagePipeHandle = imagePipe.Unbind().TakeChannel().release();
137 
138     mMaterial.SetTexture(imagePipeId);
139     mScenicSession.ReleaseResource(imagePipeId);
140     present();
141 
142     mFuchsiaEGLWindow.reset(fuchsia_egl_window_create(imagePipeHandle, mWidth, mHeight));
143 }
144 
getNativeWindow() const145 EGLNativeWindowType ScenicWindow::getNativeWindow() const
146 {
147     return reinterpret_cast<EGLNativeWindowType>(mFuchsiaEGLWindow.get());
148 }
149 
getNativeDisplay() const150 EGLNativeDisplayType ScenicWindow::getNativeDisplay() const
151 {
152     return EGL_DEFAULT_DISPLAY;
153 }
154 
messageLoop()155 void ScenicWindow::messageLoop()
156 {
157     mLoop->ResetQuit();
158     mLoop->RunUntilIdle();
159 }
160 
setMousePosition(int x,int y)161 void ScenicWindow::setMousePosition(int x, int y)
162 {
163     UNIMPLEMENTED();
164 }
165 
setOrientation(int width,int height)166 bool ScenicWindow::setOrientation(int width, int height)
167 {
168     UNIMPLEMENTED();
169     return false;
170 }
171 
setPosition(int x,int y)172 bool ScenicWindow::setPosition(int x, int y)
173 {
174     UNIMPLEMENTED();
175     return false;
176 }
177 
resize(int width,int height)178 bool ScenicWindow::resize(int width, int height)
179 {
180     mWidth  = width;
181     mHeight = height;
182 
183     fuchsia_egl_window_resize(mFuchsiaEGLWindow.get(), width, height);
184 
185     mViewSizeDirty = true;
186 
187     updateViewSize();
188 
189     return true;
190 }
191 
setVisible(bool isVisible)192 void ScenicWindow::setVisible(bool isVisible) {}
193 
signalTestEvent()194 void ScenicWindow::signalTestEvent() {}
195 
present()196 void ScenicWindow::present()
197 {
198     while (mInFlightPresents >= kMaxInFlightPresents && !mLostSession)
199     {
200         mLoop->ResetQuit();
201         mLoop->Run();
202     }
203 
204     if (mLostSession)
205     {
206         return;
207     }
208 
209     ASSERT(mInFlightPresents < kMaxInFlightPresents);
210 
211     ++mInFlightPresents;
212     mScenicSession.Present2(0, 0, [](fuchsia::scenic::scheduling::FuturePresentationTimes info) {});
213 }
214 
onFramePresented(fuchsia::scenic::scheduling::FramePresentedInfo info)215 void ScenicWindow::onFramePresented(fuchsia::scenic::scheduling::FramePresentedInfo info)
216 {
217     mInFlightPresents -= info.presentation_infos.size();
218     ASSERT(mInFlightPresents >= 0);
219     mLoop->Quit();
220 }
221 
onScenicEvents(std::vector<fuchsia::ui::scenic::Event> events)222 void ScenicWindow::onScenicEvents(std::vector<fuchsia::ui::scenic::Event> events)
223 {
224     for (const auto &event : events)
225     {
226         if (event.is_gfx())
227         {
228             if (event.gfx().is_metrics())
229             {
230                 if (event.gfx().metrics().node_id != mShape.id())
231                     continue;
232                 onViewMetrics(event.gfx().metrics().metrics);
233             }
234             else if (event.gfx().is_view_properties_changed())
235             {
236                 if (event.gfx().view_properties_changed().view_id != mView->id())
237                     continue;
238                 onViewProperties(event.gfx().view_properties_changed().properties);
239             }
240         }
241     }
242 
243     if (mViewSizeDirty)
244     {
245         updateViewSize();
246     }
247 }
248 
onScenicError(zx_status_t status)249 void ScenicWindow::onScenicError(zx_status_t status)
250 {
251     WARN() << "OnScenicError: " << zx_status_get_string(status);
252     mLostSession = true;
253     mLoop->Quit();
254 }
255 
onViewMetrics(const fuchsia::ui::gfx::Metrics & metrics)256 void ScenicWindow::onViewMetrics(const fuchsia::ui::gfx::Metrics &metrics)
257 {
258     mDisplayScaleX = metrics.scale_x;
259     mDisplayScaleY = metrics.scale_y;
260 
261     mHasViewMetrics = true;
262     mViewSizeDirty  = true;
263 }
264 
onViewProperties(const fuchsia::ui::gfx::ViewProperties & properties)265 void ScenicWindow::onViewProperties(const fuchsia::ui::gfx::ViewProperties &properties)
266 {
267     float width = properties.bounding_box.max.x - properties.bounding_box.min.x -
268                   properties.inset_from_min.x - properties.inset_from_max.x;
269     float height = properties.bounding_box.max.y - properties.bounding_box.min.y -
270                    properties.inset_from_min.y - properties.inset_from_max.y;
271 
272     mDisplayWidthDips  = width;
273     mDisplayHeightDips = height;
274 
275     mHasViewProperties = true;
276     mViewSizeDirty     = true;
277 }
278 
updateViewSize()279 void ScenicWindow::updateViewSize()
280 {
281     if (!mViewSizeDirty || !mHasViewMetrics || !mHasViewProperties)
282     {
283         return;
284     }
285 
286     mViewSizeDirty = false;
287 
288     // Surface size in pixels is
289     //   (mWidth, mHeight)
290     //
291     // View size in pixels is
292     //   (mDisplayWidthDips * mDisplayScaleX) x (mDisplayHeightDips * mDisplayScaleY)
293 
294     float widthDips  = mWidth / mDisplayScaleX;
295     float heightDips = mHeight / mDisplayScaleY;
296 
297     mShape.SetShape(scenic::Rectangle(&mScenicSession, widthDips, heightDips));
298     mShape.SetTranslation(0.5f * widthDips, 0.5f * heightDips, 0.f);
299     present();
300 }
301 
302 // static
New()303 OSWindow *OSWindow::New()
304 {
305     return new ScenicWindow;
306 }
307