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