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 #include "common/debug.h"
24
25 namespace
26 {
27
GetDefaultLoop()28 async::Loop *GetDefaultLoop()
29 {
30 static async::Loop *defaultLoop = new async::Loop(&kAsyncLoopConfigNeverAttachToThread);
31 return defaultLoop;
32 }
33
ConnectToServiceRoot()34 zx::channel ConnectToServiceRoot()
35 {
36 zx::channel clientChannel;
37 zx::channel serverChannel;
38 zx_status_t result = zx::channel::create(0, &clientChannel, &serverChannel);
39 ASSERT(result == ZX_OK);
40 result = fdio_service_connect("/svc/.", serverChannel.release());
41 ASSERT(result == ZX_OK);
42 return clientChannel;
43 }
44
45 template <typename Interface>
ConnectToService(zx_handle_t serviceRoot,fidl::InterfaceRequest<Interface> request)46 zx_status_t ConnectToService(zx_handle_t serviceRoot, fidl::InterfaceRequest<Interface> request)
47 {
48 ASSERT(request.is_valid());
49 return fdio_service_connect_at(serviceRoot, Interface::Name_, request.TakeChannel().release());
50 }
51
52 template <typename Interface>
ConnectToService(zx_handle_t serviceRoot,async_dispatcher_t * dispatcher)53 fidl::InterfacePtr<Interface> ConnectToService(zx_handle_t serviceRoot,
54 async_dispatcher_t *dispatcher)
55 {
56 fidl::InterfacePtr<Interface> result;
57 ConnectToService(serviceRoot, result.NewRequest(dispatcher));
58 return result;
59 }
60
61 } // namespace
62
ScenicWindow()63 ScenicWindow::ScenicWindow()
64 : mLoop(GetDefaultLoop()),
65 mServiceRoot(ConnectToServiceRoot()),
66 mScenic(
67 ConnectToService<fuchsia::ui::scenic::Scenic>(mServiceRoot.get(), mLoop->dispatcher())),
68 mPresenter(ConnectToService<fuchsia::ui::policy::Presenter>(mServiceRoot.get(),
69 mLoop->dispatcher())),
70 mScenicSession(mScenic.get(), mLoop->dispatcher()),
71 mShape(&mScenicSession),
72 mMaterial(&mScenicSession)
73 {
74 mScenicSession.set_error_handler(fit::bind_member(this, &ScenicWindow::onScenicError));
75 mScenicSession.set_event_handler(fit::bind_member(this, &ScenicWindow::onScenicEvents));
76 mScenicSession.set_on_frame_presented_handler(
77 fit::bind_member(this, &ScenicWindow::onFramePresented));
78 }
79
~ScenicWindow()80 ScenicWindow::~ScenicWindow()
81 {
82 destroy();
83 }
84
initialize(const std::string & name,int width,int height)85 bool ScenicWindow::initialize(const std::string &name, int width, int height)
86 {
87 // Set up scenic resources.
88 mShape.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
89 mShape.SetMaterial(mMaterial);
90
91 fuchsia::ui::views::ViewToken viewToken;
92 fuchsia::ui::views::ViewHolderToken viewHolderToken;
93 std::tie(viewToken, viewHolderToken) = scenic::NewViewTokenPair();
94
95 // Create view.
96 mView = std::make_unique<scenic::View>(&mScenicSession, std::move(viewToken), name);
97 mView->AddChild(mShape);
98
99 // Present view.
100 mPresenter->PresentOrReplaceView(std::move(viewHolderToken), nullptr);
101
102 mWidth = width;
103 mHeight = height;
104
105 resetNativeWindow();
106
107 // Block until initial view dimensions are known.
108 while (!mHasViewMetrics && !mHasViewProperties && !mLostSession)
109 {
110 mLoop->ResetQuit();
111 mLoop->Run(zx::time::infinite(), true /* once */);
112 }
113
114 return true;
115 }
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
setPosition(int x,int y)166 bool ScenicWindow::setPosition(int x, int y)
167 {
168 UNIMPLEMENTED();
169 return false;
170 }
171
resize(int width,int height)172 bool ScenicWindow::resize(int width, int height)
173 {
174 mWidth = width;
175 mHeight = height;
176
177 fuchsia_egl_window_resize(mFuchsiaEGLWindow.get(), width, height);
178
179 mViewSizeDirty = true;
180
181 updateViewSize();
182
183 return true;
184 }
185
setVisible(bool isVisible)186 void ScenicWindow::setVisible(bool isVisible) {}
187
signalTestEvent()188 void ScenicWindow::signalTestEvent() {}
189
present()190 void ScenicWindow::present()
191 {
192 while (mInFlightPresents >= kMaxInFlightPresents && !mLostSession)
193 {
194 mLoop->ResetQuit();
195 mLoop->Run();
196 }
197
198 if (mLostSession)
199 {
200 return;
201 }
202
203 ASSERT(mInFlightPresents < kMaxInFlightPresents);
204
205 ++mInFlightPresents;
206 mScenicSession.Present2(0, 0, [](fuchsia::scenic::scheduling::FuturePresentationTimes info) {});
207 }
208
onFramePresented(fuchsia::scenic::scheduling::FramePresentedInfo info)209 void ScenicWindow::onFramePresented(fuchsia::scenic::scheduling::FramePresentedInfo info)
210 {
211 mInFlightPresents -= info.presentation_infos.size();
212 ASSERT(mInFlightPresents >= 0);
213 mLoop->Quit();
214 }
215
onScenicEvents(std::vector<fuchsia::ui::scenic::Event> events)216 void ScenicWindow::onScenicEvents(std::vector<fuchsia::ui::scenic::Event> events)
217 {
218 for (const auto &event : events)
219 {
220 if (event.is_gfx())
221 {
222 if (event.gfx().is_metrics())
223 {
224 if (event.gfx().metrics().node_id != mShape.id())
225 continue;
226 onViewMetrics(event.gfx().metrics().metrics);
227 }
228 else if (event.gfx().is_view_properties_changed())
229 {
230 if (event.gfx().view_properties_changed().view_id != mView->id())
231 continue;
232 onViewProperties(event.gfx().view_properties_changed().properties);
233 }
234 }
235 }
236
237 if (mViewSizeDirty)
238 {
239 updateViewSize();
240 }
241 }
242
onScenicError(zx_status_t status)243 void ScenicWindow::onScenicError(zx_status_t status)
244 {
245 WARN() << "OnScenicError: " << zx_status_get_string(status);
246 mLostSession = true;
247 mLoop->Quit();
248 }
249
onViewMetrics(const fuchsia::ui::gfx::Metrics & metrics)250 void ScenicWindow::onViewMetrics(const fuchsia::ui::gfx::Metrics &metrics)
251 {
252 mDisplayScaleX = metrics.scale_x;
253 mDisplayScaleY = metrics.scale_y;
254
255 mHasViewMetrics = true;
256 mViewSizeDirty = true;
257 }
258
onViewProperties(const fuchsia::ui::gfx::ViewProperties & properties)259 void ScenicWindow::onViewProperties(const fuchsia::ui::gfx::ViewProperties &properties)
260 {
261 float width = properties.bounding_box.max.x - properties.bounding_box.min.x -
262 properties.inset_from_min.x - properties.inset_from_max.x;
263 float height = properties.bounding_box.max.y - properties.bounding_box.min.y -
264 properties.inset_from_min.y - properties.inset_from_max.y;
265
266 mDisplayWidthDips = width;
267 mDisplayHeightDips = height;
268
269 mHasViewProperties = true;
270 mViewSizeDirty = true;
271 }
272
updateViewSize()273 void ScenicWindow::updateViewSize()
274 {
275 if (!mViewSizeDirty || !mHasViewMetrics || !mHasViewProperties)
276 {
277 return;
278 }
279
280 mViewSizeDirty = false;
281
282 // Surface size in pixels is
283 // (mWidth, mHeight)
284 //
285 // View size in pixels is
286 // (mDisplayWidthDips * mDisplayScaleX) x (mDisplayHeightDips * mDisplayScaleY)
287
288 float widthDips = mWidth / mDisplayScaleX;
289 float heightDips = mHeight / mDisplayScaleY;
290
291 mShape.SetShape(scenic::Rectangle(&mScenicSession, widthDips, heightDips));
292 mShape.SetTranslation(0.5f * widthDips, 0.5f * heightDips, 0.f);
293 present();
294 }
295
296 // static
New()297 OSWindow *OSWindow::New()
298 {
299 return new ScenicWindow;
300 }
301