1*f0687c8aSRaman Tenneti #include <chrono>
2*f0687c8aSRaman Tenneti #include <cstdio>
3*f0687c8aSRaman Tenneti #include <vector>
4*f0687c8aSRaman Tenneti #include <memory>
5*f0687c8aSRaman Tenneti #include <algorithm>
6*f0687c8aSRaman Tenneti #include <poll.h>
7*f0687c8aSRaman Tenneti
8*f0687c8aSRaman Tenneti #include <xf86drm.h>
9*f0687c8aSRaman Tenneti #include <xf86drmMode.h>
10*f0687c8aSRaman Tenneti #include <gbm.h>
11*f0687c8aSRaman Tenneti
12*f0687c8aSRaman Tenneti #include <kms++/kms++.h>
13*f0687c8aSRaman Tenneti #include <kms++util/kms++util.h>
14*f0687c8aSRaman Tenneti #include "cube-egl.h"
15*f0687c8aSRaman Tenneti #include "cube-gles2.h"
16*f0687c8aSRaman Tenneti
17*f0687c8aSRaman Tenneti using namespace kms;
18*f0687c8aSRaman Tenneti using namespace std;
19*f0687c8aSRaman Tenneti
20*f0687c8aSRaman Tenneti static int s_flip_pending;
21*f0687c8aSRaman Tenneti static bool s_need_exit;
22*f0687c8aSRaman Tenneti
23*f0687c8aSRaman Tenneti static bool s_support_planes;
24*f0687c8aSRaman Tenneti
25*f0687c8aSRaman Tenneti class GbmDevice
26*f0687c8aSRaman Tenneti {
27*f0687c8aSRaman Tenneti public:
GbmDevice(Card & card)28*f0687c8aSRaman Tenneti GbmDevice(Card& card)
29*f0687c8aSRaman Tenneti {
30*f0687c8aSRaman Tenneti m_dev = gbm_create_device(card.fd());
31*f0687c8aSRaman Tenneti FAIL_IF(!m_dev, "failed to create gbm device");
32*f0687c8aSRaman Tenneti }
33*f0687c8aSRaman Tenneti
~GbmDevice()34*f0687c8aSRaman Tenneti ~GbmDevice()
35*f0687c8aSRaman Tenneti {
36*f0687c8aSRaman Tenneti gbm_device_destroy(m_dev);
37*f0687c8aSRaman Tenneti }
38*f0687c8aSRaman Tenneti
39*f0687c8aSRaman Tenneti GbmDevice(const GbmDevice& other) = delete;
40*f0687c8aSRaman Tenneti GbmDevice& operator=(const GbmDevice& other) = delete;
41*f0687c8aSRaman Tenneti
handle() const42*f0687c8aSRaman Tenneti struct gbm_device* handle() const { return m_dev; }
43*f0687c8aSRaman Tenneti
44*f0687c8aSRaman Tenneti private:
45*f0687c8aSRaman Tenneti struct gbm_device* m_dev;
46*f0687c8aSRaman Tenneti };
47*f0687c8aSRaman Tenneti
48*f0687c8aSRaman Tenneti class GbmSurface
49*f0687c8aSRaman Tenneti {
50*f0687c8aSRaman Tenneti public:
GbmSurface(GbmDevice & gdev,int width,int height)51*f0687c8aSRaman Tenneti GbmSurface(GbmDevice& gdev, int width, int height)
52*f0687c8aSRaman Tenneti {
53*f0687c8aSRaman Tenneti m_surface = gbm_surface_create(gdev.handle(), width, height,
54*f0687c8aSRaman Tenneti GBM_FORMAT_XRGB8888,
55*f0687c8aSRaman Tenneti GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
56*f0687c8aSRaman Tenneti FAIL_IF(!m_surface, "failed to create gbm surface");
57*f0687c8aSRaman Tenneti }
58*f0687c8aSRaman Tenneti
~GbmSurface()59*f0687c8aSRaman Tenneti ~GbmSurface()
60*f0687c8aSRaman Tenneti {
61*f0687c8aSRaman Tenneti gbm_surface_destroy(m_surface);
62*f0687c8aSRaman Tenneti }
63*f0687c8aSRaman Tenneti
64*f0687c8aSRaman Tenneti GbmSurface(const GbmSurface& other) = delete;
65*f0687c8aSRaman Tenneti GbmSurface& operator=(const GbmSurface& other) = delete;
66*f0687c8aSRaman Tenneti
has_free()67*f0687c8aSRaman Tenneti bool has_free()
68*f0687c8aSRaman Tenneti {
69*f0687c8aSRaman Tenneti return gbm_surface_has_free_buffers(m_surface);
70*f0687c8aSRaman Tenneti }
71*f0687c8aSRaman Tenneti
lock_front_buffer()72*f0687c8aSRaman Tenneti gbm_bo* lock_front_buffer()
73*f0687c8aSRaman Tenneti {
74*f0687c8aSRaman Tenneti return gbm_surface_lock_front_buffer(m_surface);
75*f0687c8aSRaman Tenneti }
76*f0687c8aSRaman Tenneti
release_buffer(gbm_bo * bo)77*f0687c8aSRaman Tenneti void release_buffer(gbm_bo* bo)
78*f0687c8aSRaman Tenneti {
79*f0687c8aSRaman Tenneti gbm_surface_release_buffer(m_surface, bo);
80*f0687c8aSRaman Tenneti }
81*f0687c8aSRaman Tenneti
handle() const82*f0687c8aSRaman Tenneti struct gbm_surface* handle() const { return m_surface; }
83*f0687c8aSRaman Tenneti
84*f0687c8aSRaman Tenneti private:
85*f0687c8aSRaman Tenneti struct gbm_surface* m_surface;
86*f0687c8aSRaman Tenneti };
87*f0687c8aSRaman Tenneti
88*f0687c8aSRaman Tenneti class GbmEglSurface
89*f0687c8aSRaman Tenneti {
90*f0687c8aSRaman Tenneti public:
GbmEglSurface(Card & card,GbmDevice & gdev,const EglState & egl,int width,int height)91*f0687c8aSRaman Tenneti GbmEglSurface(Card& card, GbmDevice& gdev, const EglState& egl, int width, int height)
92*f0687c8aSRaman Tenneti : card(card), egl(egl), m_width(width), m_height(height),
93*f0687c8aSRaman Tenneti bo_prev(0), bo_next(0)
94*f0687c8aSRaman Tenneti {
95*f0687c8aSRaman Tenneti gsurface = unique_ptr<GbmSurface>(new GbmSurface(gdev, width, height));
96*f0687c8aSRaman Tenneti esurface = eglCreateWindowSurface(egl.display(), egl.config(), gsurface->handle(), NULL);
97*f0687c8aSRaman Tenneti FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface");
98*f0687c8aSRaman Tenneti }
99*f0687c8aSRaman Tenneti
~GbmEglSurface()100*f0687c8aSRaman Tenneti ~GbmEglSurface()
101*f0687c8aSRaman Tenneti {
102*f0687c8aSRaman Tenneti if (bo_next)
103*f0687c8aSRaman Tenneti gsurface->release_buffer(bo_next);
104*f0687c8aSRaman Tenneti eglDestroySurface(egl.display(), esurface);
105*f0687c8aSRaman Tenneti }
106*f0687c8aSRaman Tenneti
make_current()107*f0687c8aSRaman Tenneti void make_current()
108*f0687c8aSRaman Tenneti {
109*f0687c8aSRaman Tenneti FAIL_IF(!gsurface->has_free(), "No free buffers");
110*f0687c8aSRaman Tenneti
111*f0687c8aSRaman Tenneti eglMakeCurrent(egl.display(), esurface, esurface, egl.context());
112*f0687c8aSRaman Tenneti }
113*f0687c8aSRaman Tenneti
swap_buffers()114*f0687c8aSRaman Tenneti void swap_buffers()
115*f0687c8aSRaman Tenneti {
116*f0687c8aSRaman Tenneti eglSwapBuffers(egl.display(), esurface);
117*f0687c8aSRaman Tenneti }
118*f0687c8aSRaman Tenneti
drm_fb_destroy_callback(struct gbm_bo * bo,void * data)119*f0687c8aSRaman Tenneti static void drm_fb_destroy_callback(struct gbm_bo* bo, void* data)
120*f0687c8aSRaman Tenneti {
121*f0687c8aSRaman Tenneti auto fb = reinterpret_cast<Framebuffer*>(data);
122*f0687c8aSRaman Tenneti delete fb;
123*f0687c8aSRaman Tenneti }
124*f0687c8aSRaman Tenneti
drm_fb_get_from_bo(struct gbm_bo * bo,Card & card)125*f0687c8aSRaman Tenneti static Framebuffer* drm_fb_get_from_bo(struct gbm_bo* bo, Card& card)
126*f0687c8aSRaman Tenneti {
127*f0687c8aSRaman Tenneti auto fb = reinterpret_cast<Framebuffer*>(gbm_bo_get_user_data(bo));
128*f0687c8aSRaman Tenneti if (fb)
129*f0687c8aSRaman Tenneti return fb;
130*f0687c8aSRaman Tenneti
131*f0687c8aSRaman Tenneti uint32_t width = gbm_bo_get_width(bo);
132*f0687c8aSRaman Tenneti uint32_t height = gbm_bo_get_height(bo);
133*f0687c8aSRaman Tenneti uint32_t stride = gbm_bo_get_stride(bo);
134*f0687c8aSRaman Tenneti uint32_t handle = gbm_bo_get_handle(bo).u32;
135*f0687c8aSRaman Tenneti PixelFormat format = (PixelFormat)gbm_bo_get_format(bo);
136*f0687c8aSRaman Tenneti
137*f0687c8aSRaman Tenneti vector<uint32_t> handles{ handle };
138*f0687c8aSRaman Tenneti vector<uint32_t> strides{ stride };
139*f0687c8aSRaman Tenneti vector<uint32_t> offsets{ 0 };
140*f0687c8aSRaman Tenneti
141*f0687c8aSRaman Tenneti fb = new ExtFramebuffer(card, width, height, format, handles, strides, offsets);
142*f0687c8aSRaman Tenneti
143*f0687c8aSRaman Tenneti gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
144*f0687c8aSRaman Tenneti
145*f0687c8aSRaman Tenneti return fb;
146*f0687c8aSRaman Tenneti }
147*f0687c8aSRaman Tenneti
lock_next()148*f0687c8aSRaman Tenneti Framebuffer* lock_next()
149*f0687c8aSRaman Tenneti {
150*f0687c8aSRaman Tenneti bo_prev = bo_next;
151*f0687c8aSRaman Tenneti bo_next = gsurface->lock_front_buffer();
152*f0687c8aSRaman Tenneti FAIL_IF(!bo_next, "could not lock gbm buffer");
153*f0687c8aSRaman Tenneti return drm_fb_get_from_bo(bo_next, card);
154*f0687c8aSRaman Tenneti }
155*f0687c8aSRaman Tenneti
free_prev()156*f0687c8aSRaman Tenneti void free_prev()
157*f0687c8aSRaman Tenneti {
158*f0687c8aSRaman Tenneti if (bo_prev) {
159*f0687c8aSRaman Tenneti gsurface->release_buffer(bo_prev);
160*f0687c8aSRaman Tenneti bo_prev = 0;
161*f0687c8aSRaman Tenneti }
162*f0687c8aSRaman Tenneti }
163*f0687c8aSRaman Tenneti
width() const164*f0687c8aSRaman Tenneti uint32_t width() const { return m_width; }
height() const165*f0687c8aSRaman Tenneti uint32_t height() const { return m_height; }
166*f0687c8aSRaman Tenneti
167*f0687c8aSRaman Tenneti private:
168*f0687c8aSRaman Tenneti Card& card;
169*f0687c8aSRaman Tenneti const EglState& egl;
170*f0687c8aSRaman Tenneti
171*f0687c8aSRaman Tenneti unique_ptr<GbmSurface> gsurface;
172*f0687c8aSRaman Tenneti EGLSurface esurface;
173*f0687c8aSRaman Tenneti
174*f0687c8aSRaman Tenneti int m_width;
175*f0687c8aSRaman Tenneti int m_height;
176*f0687c8aSRaman Tenneti
177*f0687c8aSRaman Tenneti struct gbm_bo* bo_prev;
178*f0687c8aSRaman Tenneti struct gbm_bo* bo_next;
179*f0687c8aSRaman Tenneti };
180*f0687c8aSRaman Tenneti
181*f0687c8aSRaman Tenneti class OutputHandler : private PageFlipHandlerBase
182*f0687c8aSRaman Tenneti {
183*f0687c8aSRaman Tenneti public:
OutputHandler(Card & card,GbmDevice & gdev,const EglState & egl,Connector * connector,Crtc * crtc,Videomode & mode,Plane * root_plane,Plane * plane,float rotation_mult)184*f0687c8aSRaman Tenneti OutputHandler(Card& card, GbmDevice& gdev, const EglState& egl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* root_plane, Plane* plane, float rotation_mult)
185*f0687c8aSRaman Tenneti : m_frame_num(0), m_connector(connector), m_crtc(crtc), m_root_plane(root_plane), m_plane(plane), m_mode(mode),
186*f0687c8aSRaman Tenneti m_rotation_mult(rotation_mult)
187*f0687c8aSRaman Tenneti {
188*f0687c8aSRaman Tenneti m_surface1 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, mode.hdisplay, mode.vdisplay));
189*f0687c8aSRaman Tenneti m_scene1 = unique_ptr<GlScene>(new GlScene());
190*f0687c8aSRaman Tenneti m_scene1->set_viewport(m_surface1->width(), m_surface1->height());
191*f0687c8aSRaman Tenneti
192*f0687c8aSRaman Tenneti if (m_plane) {
193*f0687c8aSRaman Tenneti m_surface2 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, 400, 400));
194*f0687c8aSRaman Tenneti m_scene2 = unique_ptr<GlScene>(new GlScene());
195*f0687c8aSRaman Tenneti m_scene2->set_viewport(m_surface2->width(), m_surface2->height());
196*f0687c8aSRaman Tenneti }
197*f0687c8aSRaman Tenneti }
198*f0687c8aSRaman Tenneti
199*f0687c8aSRaman Tenneti OutputHandler(const OutputHandler& other) = delete;
200*f0687c8aSRaman Tenneti OutputHandler& operator=(const OutputHandler& other) = delete;
201*f0687c8aSRaman Tenneti
setup()202*f0687c8aSRaman Tenneti void setup()
203*f0687c8aSRaman Tenneti {
204*f0687c8aSRaman Tenneti int ret;
205*f0687c8aSRaman Tenneti
206*f0687c8aSRaman Tenneti m_surface1->make_current();
207*f0687c8aSRaman Tenneti m_surface1->swap_buffers();
208*f0687c8aSRaman Tenneti Framebuffer* fb = m_surface1->lock_next();
209*f0687c8aSRaman Tenneti
210*f0687c8aSRaman Tenneti Framebuffer* planefb = 0;
211*f0687c8aSRaman Tenneti
212*f0687c8aSRaman Tenneti if (m_plane) {
213*f0687c8aSRaman Tenneti m_surface2->make_current();
214*f0687c8aSRaman Tenneti m_surface2->swap_buffers();
215*f0687c8aSRaman Tenneti planefb = m_surface2->lock_next();
216*f0687c8aSRaman Tenneti }
217*f0687c8aSRaman Tenneti
218*f0687c8aSRaman Tenneti ret = m_crtc->set_mode(m_connector, *fb, m_mode);
219*f0687c8aSRaman Tenneti FAIL_IF(ret, "failed to set mode");
220*f0687c8aSRaman Tenneti
221*f0687c8aSRaman Tenneti if (m_plane) {
222*f0687c8aSRaman Tenneti ret = m_crtc->set_plane(m_plane, *planefb,
223*f0687c8aSRaman Tenneti 0, 0, planefb->width(), planefb->height(),
224*f0687c8aSRaman Tenneti 0, 0, planefb->width(), planefb->height());
225*f0687c8aSRaman Tenneti FAIL_IF(ret, "failed to set plane");
226*f0687c8aSRaman Tenneti }
227*f0687c8aSRaman Tenneti }
228*f0687c8aSRaman Tenneti
start_flipping()229*f0687c8aSRaman Tenneti void start_flipping()
230*f0687c8aSRaman Tenneti {
231*f0687c8aSRaman Tenneti m_t1 = chrono::steady_clock::now();
232*f0687c8aSRaman Tenneti queue_next();
233*f0687c8aSRaman Tenneti }
234*f0687c8aSRaman Tenneti
235*f0687c8aSRaman Tenneti private:
handle_page_flip(uint32_t frame,double time)236*f0687c8aSRaman Tenneti void handle_page_flip(uint32_t frame, double time)
237*f0687c8aSRaman Tenneti {
238*f0687c8aSRaman Tenneti ++m_frame_num;
239*f0687c8aSRaman Tenneti
240*f0687c8aSRaman Tenneti if (m_frame_num % 100 == 0) {
241*f0687c8aSRaman Tenneti auto t2 = chrono::steady_clock::now();
242*f0687c8aSRaman Tenneti chrono::duration<float> fsec = t2 - m_t1;
243*f0687c8aSRaman Tenneti printf("fps: %f\n", 100.0 / fsec.count());
244*f0687c8aSRaman Tenneti m_t1 = t2;
245*f0687c8aSRaman Tenneti }
246*f0687c8aSRaman Tenneti
247*f0687c8aSRaman Tenneti s_flip_pending--;
248*f0687c8aSRaman Tenneti
249*f0687c8aSRaman Tenneti m_surface1->free_prev();
250*f0687c8aSRaman Tenneti if (m_plane)
251*f0687c8aSRaman Tenneti m_surface2->free_prev();
252*f0687c8aSRaman Tenneti
253*f0687c8aSRaman Tenneti if (s_need_exit)
254*f0687c8aSRaman Tenneti return;
255*f0687c8aSRaman Tenneti
256*f0687c8aSRaman Tenneti queue_next();
257*f0687c8aSRaman Tenneti }
258*f0687c8aSRaman Tenneti
queue_next()259*f0687c8aSRaman Tenneti void queue_next()
260*f0687c8aSRaman Tenneti {
261*f0687c8aSRaman Tenneti m_surface1->make_current();
262*f0687c8aSRaman Tenneti m_scene1->draw(m_frame_num * m_rotation_mult);
263*f0687c8aSRaman Tenneti m_surface1->swap_buffers();
264*f0687c8aSRaman Tenneti Framebuffer* fb = m_surface1->lock_next();
265*f0687c8aSRaman Tenneti
266*f0687c8aSRaman Tenneti Framebuffer* planefb = 0;
267*f0687c8aSRaman Tenneti
268*f0687c8aSRaman Tenneti if (m_plane) {
269*f0687c8aSRaman Tenneti m_surface2->make_current();
270*f0687c8aSRaman Tenneti m_scene2->draw(m_frame_num * m_rotation_mult * 2);
271*f0687c8aSRaman Tenneti m_surface2->swap_buffers();
272*f0687c8aSRaman Tenneti planefb = m_surface2->lock_next();
273*f0687c8aSRaman Tenneti }
274*f0687c8aSRaman Tenneti
275*f0687c8aSRaman Tenneti int r;
276*f0687c8aSRaman Tenneti
277*f0687c8aSRaman Tenneti AtomicReq req(m_crtc->card());
278*f0687c8aSRaman Tenneti
279*f0687c8aSRaman Tenneti req.add(m_root_plane, "FB_ID", fb->id());
280*f0687c8aSRaman Tenneti if (m_plane)
281*f0687c8aSRaman Tenneti req.add(m_plane, "FB_ID", planefb->id());
282*f0687c8aSRaman Tenneti
283*f0687c8aSRaman Tenneti r = req.test();
284*f0687c8aSRaman Tenneti FAIL_IF(r, "atomic test failed");
285*f0687c8aSRaman Tenneti
286*f0687c8aSRaman Tenneti r = req.commit(this);
287*f0687c8aSRaman Tenneti FAIL_IF(r, "atomic commit failed");
288*f0687c8aSRaman Tenneti
289*f0687c8aSRaman Tenneti s_flip_pending++;
290*f0687c8aSRaman Tenneti }
291*f0687c8aSRaman Tenneti
292*f0687c8aSRaman Tenneti int m_frame_num;
293*f0687c8aSRaman Tenneti chrono::steady_clock::time_point m_t1;
294*f0687c8aSRaman Tenneti
295*f0687c8aSRaman Tenneti Connector* m_connector;
296*f0687c8aSRaman Tenneti Crtc* m_crtc;
297*f0687c8aSRaman Tenneti Plane* m_root_plane;
298*f0687c8aSRaman Tenneti Plane* m_plane;
299*f0687c8aSRaman Tenneti Videomode m_mode;
300*f0687c8aSRaman Tenneti
301*f0687c8aSRaman Tenneti unique_ptr<GbmEglSurface> m_surface1;
302*f0687c8aSRaman Tenneti unique_ptr<GbmEglSurface> m_surface2;
303*f0687c8aSRaman Tenneti
304*f0687c8aSRaman Tenneti unique_ptr<GlScene> m_scene1;
305*f0687c8aSRaman Tenneti unique_ptr<GlScene> m_scene2;
306*f0687c8aSRaman Tenneti
307*f0687c8aSRaman Tenneti float m_rotation_mult;
308*f0687c8aSRaman Tenneti };
309*f0687c8aSRaman Tenneti
main_gbm()310*f0687c8aSRaman Tenneti void main_gbm()
311*f0687c8aSRaman Tenneti {
312*f0687c8aSRaman Tenneti Card card;
313*f0687c8aSRaman Tenneti
314*f0687c8aSRaman Tenneti FAIL_IF(!card.has_atomic(), "No atomic modesetting");
315*f0687c8aSRaman Tenneti
316*f0687c8aSRaman Tenneti GbmDevice gdev(card);
317*f0687c8aSRaman Tenneti EglState egl(gdev.handle());
318*f0687c8aSRaman Tenneti
319*f0687c8aSRaman Tenneti ResourceManager resman(card);
320*f0687c8aSRaman Tenneti
321*f0687c8aSRaman Tenneti vector<unique_ptr<OutputHandler>> outputs;
322*f0687c8aSRaman Tenneti
323*f0687c8aSRaman Tenneti float rot_mult = 1;
324*f0687c8aSRaman Tenneti
325*f0687c8aSRaman Tenneti for (Connector* conn : card.get_connectors()) {
326*f0687c8aSRaman Tenneti if (!conn->connected())
327*f0687c8aSRaman Tenneti continue;
328*f0687c8aSRaman Tenneti
329*f0687c8aSRaman Tenneti resman.reserve_connector(conn);
330*f0687c8aSRaman Tenneti
331*f0687c8aSRaman Tenneti Crtc* crtc = resman.reserve_crtc(conn);
332*f0687c8aSRaman Tenneti auto mode = conn->get_default_mode();
333*f0687c8aSRaman Tenneti
334*f0687c8aSRaman Tenneti Plane* root_plane = resman.reserve_generic_plane(crtc);
335*f0687c8aSRaman Tenneti FAIL_IF(!root_plane, "Root plane not available");
336*f0687c8aSRaman Tenneti
337*f0687c8aSRaman Tenneti Plane* plane = nullptr;
338*f0687c8aSRaman Tenneti
339*f0687c8aSRaman Tenneti if (s_support_planes)
340*f0687c8aSRaman Tenneti plane = resman.reserve_generic_plane(crtc);
341*f0687c8aSRaman Tenneti
342*f0687c8aSRaman Tenneti auto out = new OutputHandler(card, gdev, egl, conn, crtc, mode, root_plane, plane, rot_mult);
343*f0687c8aSRaman Tenneti outputs.emplace_back(out);
344*f0687c8aSRaman Tenneti
345*f0687c8aSRaman Tenneti rot_mult *= 1.33;
346*f0687c8aSRaman Tenneti }
347*f0687c8aSRaman Tenneti
348*f0687c8aSRaman Tenneti for (auto& out : outputs)
349*f0687c8aSRaman Tenneti out->setup();
350*f0687c8aSRaman Tenneti
351*f0687c8aSRaman Tenneti for (auto& out : outputs)
352*f0687c8aSRaman Tenneti out->start_flipping();
353*f0687c8aSRaman Tenneti
354*f0687c8aSRaman Tenneti struct pollfd fds[2] = {};
355*f0687c8aSRaman Tenneti fds[0].fd = 0;
356*f0687c8aSRaman Tenneti fds[0].events = POLLIN;
357*f0687c8aSRaman Tenneti fds[1].fd = card.fd();
358*f0687c8aSRaman Tenneti fds[1].events = POLLIN;
359*f0687c8aSRaman Tenneti
360*f0687c8aSRaman Tenneti while (!s_need_exit || s_flip_pending) {
361*f0687c8aSRaman Tenneti int r = poll(fds, ARRAY_SIZE(fds), -1);
362*f0687c8aSRaman Tenneti FAIL_IF(r < 0, "poll error %d", r);
363*f0687c8aSRaman Tenneti
364*f0687c8aSRaman Tenneti if (fds[0].revents)
365*f0687c8aSRaman Tenneti s_need_exit = true;
366*f0687c8aSRaman Tenneti
367*f0687c8aSRaman Tenneti if (fds[1].revents)
368*f0687c8aSRaman Tenneti card.call_page_flip_handlers();
369*f0687c8aSRaman Tenneti }
370*f0687c8aSRaman Tenneti }
371