wscreensaver: implement screensaver interface
[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 draw_instance(struct ModeInfo *mi)
68 {
69         struct wscreensaver *wscr = mi->priv;
70         struct rectangle drawarea;
71         struct rectangle winarea;
72         int bottom;
73
74         mi->swap_buffers = 0;
75
76         window_draw(mi->window);
77
78         window_get_child_allocation(mi->window, &drawarea);
79         window_get_allocation(mi->window, &winarea);
80
81         if (display_acquire_window_surface(wscr->display,
82                                            mi->window,
83                                            mi->eglctx) < 0) {
84                 fprintf(stderr, "%s: unable to acquire window surface",
85                         progname);
86                 return;
87         }
88
89         bottom = winarea.height - (drawarea.height + drawarea.y);
90         glViewport(drawarea.x, bottom, drawarea.width, drawarea.height);
91         glScissor(drawarea.x, bottom, drawarea.width, drawarea.height);
92         glEnable(GL_SCISSOR_TEST);
93
94         if (mi->width != drawarea.width || mi->height != drawarea.height) {
95                 mi->width = drawarea.width;
96                 mi->height = drawarea.height;
97                 wscr->plugin->reshape(mi, mi->width, mi->height);
98         }
99
100         wscr->plugin->draw(mi);
101
102         if (mi->swap_buffers == 0)
103                 fprintf(stderr, "%s: swapBuffers not called\n", progname);
104
105         display_release_window_surface(wscr->display, mi->window);
106         window_flush(mi->window);
107 }
108
109 static void
110 frame_callback(void *data, struct wl_callback *callback, uint32_t time)
111 {
112         struct ModeInfo *mi = data;
113         static const struct wl_callback_listener listener = {
114                 frame_callback
115         };
116
117         draw_instance(mi);
118
119         if (callback)
120                 wl_callback_destroy(callback);
121
122         callback = wl_surface_frame(window_get_wl_surface(mi->window));
123         wl_callback_add_listener(callback, &listener, mi);
124 }
125
126 static void
127 init_frand(void)
128 {
129         struct timeval tv;
130         gettimeofday(&tv, NULL);
131         srandom(tv.tv_sec * 100 + tv.tv_usec / 10000);
132 }
133
134 WL_EXPORT EGLContext *
135 init_GL(struct ModeInfo *mi)
136 {
137         struct wscreensaver *wscr = mi->priv;
138         EGLContext *pctx;
139
140         pctx = malloc(sizeof *pctx);
141         if (!pctx)
142                 return NULL;
143
144         if (mi->eglctx != EGL_NO_CONTEXT) {
145                 fprintf(stderr, "%s: multiple GL contexts are not supported",
146                         progname);
147                 goto errout;
148         }
149
150         mi->eglctx = eglCreateContext(wscr->egl.display, wscr->egl.config,
151                                       EGL_NO_CONTEXT, NULL);
152         if (mi->eglctx == EGL_NO_CONTEXT) {
153                 fprintf(stderr, "%s: init_GL failed to create EGL context\n",
154                         progname);
155                 goto errout;
156         }
157
158         if (!eglMakeCurrent(wscr->egl.display, NULL, NULL, mi->eglctx)) {
159                 fprintf(stderr, "%s: init_GL failed on eglMakeCurrent\n",
160                         progname);
161                 goto errout;
162         }
163
164         glClearColor(0.0, 0.0, 0.0, 1.0);
165
166         *pctx = mi->eglctx;
167         return pctx;
168
169 errout:
170         free(pctx);
171         return NULL;
172 }
173
174 static struct ModeInfo *
175 create_modeinfo(struct wscreensaver *wscr, struct window *window)
176 {
177         struct ModeInfo *mi;
178         struct rectangle drawarea;
179         static int instance;
180
181         mi = calloc(1, sizeof *mi);
182         if (!mi)
183                 return NULL;
184
185         window_get_child_allocation(window, &drawarea);
186
187         mi->priv = wscr;
188         mi->eglctx = EGL_NO_CONTEXT;
189
190         mi->window = window;
191
192         mi->instance_number = instance++; /* XXX */
193         mi->width = drawarea.width;
194         mi->height = drawarea.height;
195
196         return mi;
197 }
198
199 static struct ModeInfo *
200 create_wscreensaver_instance(struct wscreensaver *screensaver,
201                              struct wl_output *output, int width, int height)
202 {
203         struct ModeInfo *mi;
204         struct window *window;
205         
206         window = window_create(screensaver->display, width, height);
207         if (!window) {
208                 fprintf(stderr, "%s: creating a window failed.\n", progname);
209                 return NULL;
210         }
211
212         window_set_transparent(window, 0);
213         window_set_title(window, progname);
214
215         if (screensaver->interface) {
216                 window_set_custom(window);
217                 window_set_decoration(window, 0);
218                 screensaver_set_surface(screensaver->interface,
219                                         window_get_wl_shell_surface(window),
220                                         output);
221         }
222
223         mi = create_modeinfo(screensaver, window);
224         if (!mi)
225                 return NULL;
226
227         screensaver->plugin->init(mi);
228
229         frame_callback(mi, NULL, 0);
230         return mi;
231 }
232
233 static void
234 handle_output_destroy(struct output *output, void *data)
235 {
236         /* struct ModeInfo *mi = data;
237          * TODO */
238 }
239
240 static void
241 handle_output_configure(struct output *output, void *data)
242 {
243         struct wscreensaver *screensaver = data;
244         struct ModeInfo *mi;
245         struct rectangle area;
246
247         /* skip existing outputs */
248         if (output_get_user_data(output))
249                 return;
250
251         output_get_allocation(output, &area);
252         mi = create_wscreensaver_instance(screensaver,
253                                           output_get_wl_output(output),
254                                           area.width, area.height);
255         output_set_user_data(output, mi);
256         output_set_destroy_handler(output, handle_output_destroy);
257 }
258
259 static int
260 init_wscreensaver(struct wscreensaver *wscr, struct display *display)
261 {
262         int size;
263         const char prefix[] = "wscreensaver::";
264         char *str;
265
266         display_set_user_data(display, wscr);
267         wscr->display = display;
268         wscr->plugin = plugins[0];
269
270         size = sizeof(prefix) + strlen(wscr->plugin->name);
271         str = malloc(size);
272         if (!str) {
273                 fprintf(stderr, "init: out of memory\n");
274                 return -1;
275         }
276         snprintf(str, size, "%s%s", prefix, wscr->plugin->name);
277         progname = str;
278
279         wscr->egl.display = display_get_egl_display(wscr->display);
280         if (!wscr->egl.display) {
281                 fprintf(stderr, "init: no EGL display\n");
282                 return -1;
283         }
284
285         eglBindAPI(EGL_OPENGL_API);
286         wscr->egl.config = display_get_rgb_egl_config(wscr->display);
287
288         if (demo_mode) {
289                 struct wl_output *o =
290                         output_get_wl_output(display_get_output(display));
291                 /* only one instance */
292                 wscr->demomode =
293                         create_wscreensaver_instance(wscr, o, 400, 300);
294                 return 0;
295         }
296
297         display_set_output_configure_handler(display, handle_output_configure);
298
299         return 0;
300 }
301
302 static void
303 global_handler(struct wl_display *display, uint32_t id,
304                const char *interface, uint32_t version, void *data)
305 {
306         struct wscreensaver *screensaver = data;
307
308         if (!strcmp(interface, "screensaver")) {
309                 screensaver->interface =
310                         wl_display_bind(display, id, &screensaver_interface);
311         }
312 }
313
314 static const GOptionEntry option_entries[] = {
315         { "demo", 0, 0, G_OPTION_ARG_NONE, &demo_mode,
316                 "Run as a regular application, not a screensaver.", NULL },
317         { NULL }
318 };
319
320 int main(int argc, char *argv[])
321 {
322         struct display *d;
323         struct wscreensaver screensaver = { 0 };
324
325         init_frand();
326
327         d = display_create(&argc, &argv, option_entries);
328         if (d == NULL) {
329                 fprintf(stderr, "failed to create display: %m\n");
330                 return EXIT_FAILURE;
331         }
332
333         if (!demo_mode) {
334                 /* iterates already known globals immediately */
335                 wl_display_add_global_listener(display_get_display(d),
336                                                global_handler, &screensaver);
337                 if (!screensaver.interface) {
338                         fprintf(stderr,
339                                 "Server did not offer screensaver interface,"
340                                 " exiting.\n");
341                         return EXIT_FAILURE;
342                 }
343         }
344
345         if (init_wscreensaver(&screensaver, d) < 0) {
346                 fprintf(stderr, "wscreensaver init failed.\n");
347                 return EXIT_FAILURE;
348         }
349
350         display_run(d);
351
352         free((void *)progname);
353
354         return EXIT_SUCCESS;
355 }