6e16811e5e
Add the screensaver interface to the desktop-shell protocol file. Also add stubs for it in the compositor, and make wscreensaver to bind to the screensaver interface. Wscreensaver gets a new option --demo to retain the current behaviour as a regular wayland client. When a screensaver application starts, it should bind to the screensaver interface, enumerate all outputs, create a surface per output, and register those surfaces via screensaver::set_surface request. Then it continues with the usual animation loop, waiting for frame events. The compositor will decide, when the given screensaver surfaces are displayed. A screensaver application should respond to outputs coming and going away by creating and destroying surfaces. The compositor is supposed to activate a screensaver by exec'ing it, and stop the screensaver by killing the client process. Only one client may be bound to the screensaver interface at a time. If there already is a client, the compositor could either kill it first, or not exec a new one. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
310 lines
7.1 KiB
C
310 lines
7.1 KiB
C
/*
|
|
* Copyright © 2011 Collabora, Ltd.
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that copyright
|
|
* notice and this permission notice appear in supporting documentation, and
|
|
* that the name of the copyright holders not be used in advertising or
|
|
* publicity pertaining to distribution of the software without specific,
|
|
* written prior permission. The copyright holders make no representations
|
|
* about the suitability of this software for any purpose. It is provided "as
|
|
* is" without express or implied warranty.
|
|
*
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
* OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include "../config.h"
|
|
|
|
#include "wscreensaver.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <GL/gl.h>
|
|
#include <EGL/eglext.h>
|
|
|
|
#include <wayland-client.h>
|
|
|
|
#include "desktop-shell-client-protocol.h"
|
|
#include "window.h"
|
|
|
|
extern struct wscreensaver_plugin glmatrix_screensaver;
|
|
|
|
static const struct wscreensaver_plugin * const plugins[] = {
|
|
&glmatrix_screensaver,
|
|
NULL
|
|
};
|
|
|
|
const char *progname = NULL;
|
|
|
|
static int demo_mode;
|
|
|
|
struct wscreensaver {
|
|
struct screensaver *interface;
|
|
|
|
struct display *display;
|
|
|
|
/* per output, if fullscreen mode */
|
|
struct ModeInfo *modeinfo;
|
|
|
|
struct {
|
|
EGLDisplay display;
|
|
EGLConfig config;
|
|
} egl;
|
|
|
|
const struct wscreensaver_plugin *plugin;
|
|
};
|
|
|
|
static void
|
|
draw_instance(struct ModeInfo *mi)
|
|
{
|
|
struct wscreensaver *wscr = mi->priv;
|
|
struct rectangle drawarea;
|
|
struct rectangle winarea;
|
|
int bottom;
|
|
|
|
mi->swap_buffers = 0;
|
|
|
|
window_draw(mi->window);
|
|
|
|
window_get_child_allocation(mi->window, &drawarea);
|
|
window_get_allocation(mi->window, &winarea);
|
|
|
|
if (display_acquire_window_surface(wscr->display,
|
|
mi->window,
|
|
mi->eglctx) < 0) {
|
|
fprintf(stderr, "%s: unable to acquire window surface",
|
|
progname);
|
|
return;
|
|
}
|
|
|
|
bottom = winarea.height - (drawarea.height + drawarea.y);
|
|
glViewport(drawarea.x, bottom, drawarea.width, drawarea.height);
|
|
glScissor(drawarea.x, bottom, drawarea.width, drawarea.height);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
|
|
if (mi->width != drawarea.width || mi->height != drawarea.height) {
|
|
mi->width = drawarea.width;
|
|
mi->height = drawarea.height;
|
|
wscr->plugin->reshape(mi, mi->width, mi->height);
|
|
}
|
|
|
|
wscr->plugin->draw(mi);
|
|
|
|
if (mi->swap_buffers == 0)
|
|
fprintf(stderr, "%s: swapBuffers not called\n", progname);
|
|
|
|
display_release_window_surface(wscr->display, mi->window);
|
|
window_flush(mi->window);
|
|
}
|
|
|
|
static void
|
|
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
|
|
{
|
|
struct ModeInfo *mi = data;
|
|
static const struct wl_callback_listener listener = {
|
|
frame_callback
|
|
};
|
|
|
|
draw_instance(mi);
|
|
|
|
if (callback)
|
|
wl_callback_destroy(callback);
|
|
|
|
callback = wl_surface_frame(window_get_wl_surface(mi->window));
|
|
wl_callback_add_listener(callback, &listener, mi);
|
|
}
|
|
|
|
static void
|
|
init_frand(void)
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
srandom(tv.tv_sec * 100 + tv.tv_usec / 10000);
|
|
}
|
|
|
|
WL_EXPORT EGLContext *
|
|
init_GL(struct ModeInfo *mi)
|
|
{
|
|
struct wscreensaver *wscr = mi->priv;
|
|
EGLContext *pctx;
|
|
|
|
pctx = malloc(sizeof *pctx);
|
|
if (!pctx)
|
|
return NULL;
|
|
|
|
if (mi->eglctx != EGL_NO_CONTEXT) {
|
|
fprintf(stderr, "%s: multiple GL contexts are not supported",
|
|
progname);
|
|
goto errout;
|
|
}
|
|
|
|
mi->eglctx = eglCreateContext(wscr->egl.display, wscr->egl.config,
|
|
EGL_NO_CONTEXT, NULL);
|
|
if (mi->eglctx == EGL_NO_CONTEXT) {
|
|
fprintf(stderr, "%s: init_GL failed to create EGL context\n",
|
|
progname);
|
|
goto errout;
|
|
}
|
|
|
|
if (!eglMakeCurrent(wscr->egl.display, NULL, NULL, mi->eglctx)) {
|
|
fprintf(stderr, "%s: init_GL failed on eglMakeCurrent\n",
|
|
progname);
|
|
goto errout;
|
|
}
|
|
|
|
glClearColor(0.0, 0.0, 0.0, 1.0);
|
|
|
|
*pctx = mi->eglctx;
|
|
return pctx;
|
|
|
|
errout:
|
|
free(pctx);
|
|
return NULL;
|
|
}
|
|
|
|
static struct ModeInfo *
|
|
create_modeinfo(struct wscreensaver *wscr, struct window *window)
|
|
{
|
|
struct ModeInfo *mi;
|
|
struct rectangle drawarea;
|
|
|
|
mi = calloc(1, sizeof *mi);
|
|
if (!mi)
|
|
return NULL;
|
|
|
|
window_get_child_allocation(window, &drawarea);
|
|
|
|
mi->priv = wscr;
|
|
mi->eglctx = EGL_NO_CONTEXT;
|
|
|
|
mi->window = window;
|
|
|
|
mi->instance_number = 0; /* XXX */
|
|
mi->width = drawarea.width;
|
|
mi->height = drawarea.height;
|
|
|
|
return mi;
|
|
}
|
|
|
|
static struct ModeInfo *
|
|
create_wscreensaver_instance(struct wscreensaver *wscr)
|
|
{
|
|
struct ModeInfo *mi;
|
|
struct window *window;
|
|
|
|
window = window_create(wscr->display, 400, 300);
|
|
if (!window) {
|
|
fprintf(stderr, "%s: creating a window failed.\n", progname);
|
|
return NULL;
|
|
}
|
|
|
|
window_set_transparent(window, 0);
|
|
window_set_title(window, progname);
|
|
|
|
mi = create_modeinfo(wscr, window);
|
|
if (!mi)
|
|
return NULL;
|
|
|
|
wscr->plugin->init(mi);
|
|
|
|
frame_callback(mi, NULL, 0);
|
|
return mi;
|
|
}
|
|
|
|
/* returns error message, or NULL if success */
|
|
static const char *
|
|
init_wscreensaver(struct wscreensaver *wscr, struct display *display)
|
|
{
|
|
int size;
|
|
const char prefix[] = "wscreensaver::";
|
|
char *str;
|
|
|
|
wscr->display = display;
|
|
wscr->plugin = plugins[0];
|
|
|
|
size = sizeof(prefix) + strlen(wscr->plugin->name);
|
|
str = malloc(size);
|
|
if (!str)
|
|
return "out of memory";
|
|
snprintf(str, size, "%s%s", prefix, wscr->plugin->name);
|
|
progname = str;
|
|
|
|
wscr->egl.display = display_get_egl_display(wscr->display);
|
|
if (!wscr->egl.display)
|
|
return "no EGL display";
|
|
|
|
eglBindAPI(EGL_OPENGL_API);
|
|
wscr->egl.config = display_get_rgb_egl_config(wscr->display);
|
|
|
|
wscr->modeinfo = create_wscreensaver_instance(wscr);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
global_handler(struct wl_display *display, uint32_t id,
|
|
const char *interface, uint32_t version, void *data)
|
|
{
|
|
struct wscreensaver *screensaver = data;
|
|
|
|
if (!strcmp(interface, "screensaver")) {
|
|
screensaver->interface =
|
|
wl_display_bind(display, id, &screensaver_interface);
|
|
}
|
|
}
|
|
|
|
static const GOptionEntry option_entries[] = {
|
|
{ "demo", 0, 0, G_OPTION_ARG_NONE, &demo_mode,
|
|
"Run as a regular application, not a screensaver.", NULL },
|
|
{ NULL }
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct display *d;
|
|
struct wscreensaver screensaver = { 0 };
|
|
const char *msg;
|
|
|
|
init_frand();
|
|
|
|
d = display_create(&argc, &argv, option_entries);
|
|
if (d == NULL) {
|
|
fprintf(stderr, "failed to create display: %m\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (!demo_mode) {
|
|
wl_display_add_global_listener(display_get_display(d),
|
|
global_handler, &screensaver);
|
|
wl_display_roundtrip(display_get_display(d));
|
|
if (!screensaver.interface) {
|
|
fprintf(stderr,
|
|
"Server did not offer screensaver interface,"
|
|
" exiting.\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
msg = init_wscreensaver(&screensaver, d);
|
|
if (msg) {
|
|
fprintf(stderr, "wscreensaver init failed: %s\n", msg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
display_run(d);
|
|
|
|
free((void *)progname);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|