nested: Remove the surface from the surface list when destroyed
[profile/ivi/weston-ivi-shell.git] / clients / wscreensaver.c
1 /*
2  * Copyright © 2011 Collabora, Ltd.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22
23 #include "../config.h"
24
25 #include "wscreensaver.h"
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/time.h>
31
32 #include <GL/gl.h>
33 #include <EGL/eglext.h>
34
35 #include <wayland-client.h>
36
37 #include "desktop-shell-client-protocol.h"
38 #include "window.h"
39
40 extern struct wscreensaver_plugin glmatrix_screensaver;
41
42 static const struct wscreensaver_plugin * const plugins[] = {
43         &glmatrix_screensaver,
44         NULL
45 };
46
47 const char *progname = NULL;
48
49 static int demo_mode;
50
51 struct wscreensaver {
52         struct screensaver *interface;
53
54         struct display *display;
55
56         struct ModeInfo *demomode;
57
58         struct {
59                 EGLDisplay display;
60                 EGLConfig config;
61         } egl;
62
63         const struct wscreensaver_plugin *plugin;
64 };
65
66 static void
67 frame_callback(void *data, struct wl_callback *callback, uint32_t time)
68 {
69         struct ModeInfo *mi = data;
70
71         window_schedule_redraw(mi->window);
72         wl_callback_destroy(callback);
73 }
74
75 static const struct wl_callback_listener listener = {
76         frame_callback
77 };
78
79 static void
80 redraw_handler(struct widget *widget, void *data)
81 {
82         struct ModeInfo *mi = data;
83         struct wscreensaver *wscr = mi->priv;
84         struct rectangle drawarea;
85         struct rectangle winarea;
86         struct wl_callback *callback;
87         int bottom;
88
89         mi->swap_buffers = 0;
90
91         widget_get_allocation(mi->widget, &drawarea);
92         window_get_allocation(mi->window, &winarea);
93
94         if (display_acquire_window_surface(wscr->display,
95                                            mi->window,
96                                            mi->eglctx) < 0) {
97                 fprintf(stderr, "%s: unable to acquire window surface",
98                         progname);
99                 return;
100         }
101
102         bottom = winarea.height - (drawarea.height + drawarea.y);
103         glViewport(drawarea.x, bottom, drawarea.width, drawarea.height);
104         glScissor(drawarea.x, bottom, drawarea.width, drawarea.height);
105         glEnable(GL_SCISSOR_TEST);
106
107         if (mi->width != drawarea.width || mi->height != drawarea.height) {
108                 mi->width = drawarea.width;
109                 mi->height = drawarea.height;
110                 wscr->plugin->reshape(mi, mi->width, mi->height);
111         }
112
113         wscr->plugin->draw(mi);
114
115         if (mi->swap_buffers == 0)
116                 fprintf(stderr, "%s: swapBuffers not called\n", progname);
117
118         display_release_window_surface(wscr->display, mi->window);
119
120         callback = wl_surface_frame(window_get_wl_surface(mi->window));
121         wl_callback_add_listener(callback, &listener, mi);
122 }
123
124 static void
125 init_frand(void)
126 {
127         struct timeval tv;
128         gettimeofday(&tv, NULL);
129         srandom(tv.tv_sec * 100 + tv.tv_usec / 10000);
130 }
131
132 WL_EXPORT EGLContext *
133 init_GL(struct ModeInfo *mi)
134 {
135         struct wscreensaver *wscr = mi->priv;
136         EGLContext *pctx;
137
138         pctx = malloc(sizeof *pctx);
139         if (!pctx)
140                 return NULL;
141
142         if (mi->eglctx != EGL_NO_CONTEXT) {
143                 fprintf(stderr, "%s: multiple GL contexts are not supported",
144                         progname);
145                 goto errout;
146         }
147
148         mi->eglctx = eglCreateContext(wscr->egl.display, wscr->egl.config,
149                                       EGL_NO_CONTEXT, NULL);
150         if (mi->eglctx == EGL_NO_CONTEXT) {
151                 fprintf(stderr, "%s: init_GL failed to create EGL context\n",
152                         progname);
153                 goto errout;
154         }
155
156         if (!eglMakeCurrent(wscr->egl.display, NULL, NULL, mi->eglctx)) {
157                 fprintf(stderr, "%s: init_GL failed on eglMakeCurrent\n",
158                         progname);
159                 goto errout;
160         }
161
162         glClearColor(0.0, 0.0, 0.0, 1.0);
163
164         *pctx = mi->eglctx;
165         return pctx;
166
167 errout:
168         free(pctx);
169         return NULL;
170 }
171
172 static struct ModeInfo *
173 create_wscreensaver_instance(struct wscreensaver *screensaver,
174                              struct wl_output *output, int width, int height)
175 {
176         static int instance;
177         struct ModeInfo *mi;
178         struct rectangle drawarea;
179
180         mi = calloc(1, sizeof *mi);
181         if (!mi)
182                 return NULL;
183
184         if (demo_mode)
185                 mi->window = window_create(screensaver->display);
186         else
187                 mi->window = window_create_custom(screensaver->display);
188
189         if (!mi->window) {
190                 fprintf(stderr, "%s: creating a window failed.\n", progname);
191                 free(mi);
192                 return NULL;
193         }
194
195         window_set_title(mi->window, progname);
196
197         if (screensaver->interface && !demo_mode) {
198                 mi->widget = window_add_widget(mi->window, mi);
199                 screensaver_set_surface(screensaver->interface,
200                                         window_get_wl_surface(mi->window),
201                                         output);
202         } else {
203                 mi->widget = window_frame_create(mi->window, mi);
204         }
205         widget_set_redraw_handler(mi->widget, redraw_handler);
206
207         mi->priv = screensaver;
208         mi->eglctx = EGL_NO_CONTEXT;
209         mi->instance_number = instance++; /* XXX */
210
211         widget_get_allocation(mi->widget, &drawarea);
212         mi->width = drawarea.width;
213         mi->height = drawarea.height;
214
215         screensaver->plugin->init(mi);
216
217         window_schedule_resize(mi->window, width, height);
218         return mi;
219 }
220
221 static void
222 handle_output_destroy(struct output *output, void *data)
223 {
224         /* struct ModeInfo *mi = data;
225          * TODO */
226 }
227
228 static void
229 handle_output_configure(struct output *output, void *data)
230 {
231         struct wscreensaver *screensaver = data;
232         struct ModeInfo *mi;
233         struct rectangle area;
234
235         /* skip existing outputs */
236         if (output_get_user_data(output))
237                 return;
238
239         output_get_allocation(output, &area);
240         mi = create_wscreensaver_instance(screensaver,
241                                           output_get_wl_output(output),
242                                           area.width, area.height);
243         output_set_user_data(output, mi);
244         output_set_destroy_handler(output, handle_output_destroy);
245 }
246
247 static int
248 init_wscreensaver(struct wscreensaver *wscr, struct display *display)
249 {
250         int size;
251         const char prefix[] = "wscreensaver::";
252         char *str;
253
254         display_set_user_data(display, wscr);
255         wscr->display = display;
256         wscr->plugin = plugins[0];
257
258         size = sizeof(prefix) + strlen(wscr->plugin->name);
259         str = malloc(size);
260         if (!str) {
261                 fprintf(stderr, "init: out of memory\n");
262                 return -1;
263         }
264         snprintf(str, size, "%s%s", prefix, wscr->plugin->name);
265         progname = str;
266
267         wscr->egl.display = display_get_egl_display(wscr->display);
268         if (!wscr->egl.display) {
269                 fprintf(stderr, "init: no EGL display\n");
270                 return -1;
271         }
272
273         eglBindAPI(EGL_OPENGL_API);
274         wscr->egl.config = display_get_argb_egl_config(wscr->display);
275
276         if (demo_mode) {
277                 struct wl_output *o =
278                         output_get_wl_output(display_get_output(display));
279                 /* only one instance */
280                 wscr->demomode =
281                         create_wscreensaver_instance(wscr, o, 400, 300);
282                 return 0;
283         }
284
285         display_set_output_configure_handler(display, handle_output_configure);
286
287         return 0;
288 }
289
290 static void
291 global_handler(struct display *display, uint32_t name,
292                const char *interface, uint32_t version, void *data)
293 {
294         struct wscreensaver *screensaver = data;
295
296         if (!strcmp(interface, "screensaver")) {
297                 screensaver->interface =
298                         display_bind(display, name, &screensaver_interface, 1);
299         }
300 }
301
302 static const struct weston_option wscreensaver_options[] = {
303         { WESTON_OPTION_BOOLEAN, "demo", 0, &demo_mode },
304 };
305
306 int main(int argc, char *argv[])
307 {
308         struct display *d;
309         struct wscreensaver screensaver = { 0 };
310
311         init_frand();
312
313         parse_options(wscreensaver_options,
314                       ARRAY_LENGTH(wscreensaver_options), &argc, argv);
315
316         d = display_create(&argc, argv);
317         if (d == NULL) {
318                 fprintf(stderr, "failed to create display: %m\n");
319                 return EXIT_FAILURE;
320         }
321
322         if (!demo_mode) {
323                 /* iterates already known globals immediately */
324                 display_set_user_data(d, &screensaver);
325                 display_set_global_handler(d, global_handler);
326                 if (!screensaver.interface) {
327                         fprintf(stderr,
328                                 "Server did not offer screensaver interface,"
329                                 " exiting.\n");
330                         return EXIT_FAILURE;
331                 }
332         }
333
334         if (init_wscreensaver(&screensaver, d) < 0) {
335                 fprintf(stderr, "wscreensaver init failed.\n");
336                 return EXIT_FAILURE;
337         }
338
339         display_run(d);
340
341         free((void *)progname);
342
343         return EXIT_SUCCESS;
344 }