71182aed84
The experimental versioning has not been updated when it was supposed to. Let's try to be better at it now, as xdg-shell is close to have its first stable version. Bump the version now to bring the world into the same exact version. There may be some protocol changes still coming, but we try to land them before 1.6 gets out. Those changes will bump the experimental version again as needed. When 1.6.0 is released, the experimental version will no longer be bumped, and no incompatible protocol changes will be made. Xdg-shell.xml file will move to Wayland in 1.7.0, drop the experimental versioning, and become stable. Cc: Jasper St. Pierre <jstpierre@mecheye.net> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
461 lines
11 KiB
C
461 lines
11 KiB
C
/*
|
|
* Copyright © 2011 Benjamin Franzke
|
|
* Copyright © 2010 Intel Corporation
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#include <signal.h>
|
|
|
|
#include <wayland-client.h>
|
|
#include "../shared/os-compatibility.h"
|
|
#include "xdg-shell-client-protocol.h"
|
|
#include "fullscreen-shell-client-protocol.h"
|
|
|
|
struct display {
|
|
struct wl_display *display;
|
|
struct wl_registry *registry;
|
|
struct wl_compositor *compositor;
|
|
struct xdg_shell *shell;
|
|
struct _wl_fullscreen_shell *fshell;
|
|
struct wl_shm *shm;
|
|
uint32_t formats;
|
|
};
|
|
|
|
struct buffer {
|
|
struct wl_buffer *buffer;
|
|
void *shm_data;
|
|
int busy;
|
|
};
|
|
|
|
struct window {
|
|
struct display *display;
|
|
int width, height;
|
|
struct wl_surface *surface;
|
|
struct xdg_surface *xdg_surface;
|
|
struct buffer buffers[2];
|
|
struct buffer *prev_buffer;
|
|
struct wl_callback *callback;
|
|
};
|
|
|
|
static int running = 1;
|
|
|
|
static void
|
|
buffer_release(void *data, struct wl_buffer *buffer)
|
|
{
|
|
struct buffer *mybuf = data;
|
|
|
|
mybuf->busy = 0;
|
|
}
|
|
|
|
static const struct wl_buffer_listener buffer_listener = {
|
|
buffer_release
|
|
};
|
|
|
|
static int
|
|
create_shm_buffer(struct display *display, struct buffer *buffer,
|
|
int width, int height, uint32_t format)
|
|
{
|
|
struct wl_shm_pool *pool;
|
|
int fd, size, stride;
|
|
void *data;
|
|
|
|
stride = width * 4;
|
|
size = stride * height;
|
|
|
|
fd = os_create_anonymous_file(size);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
|
|
size);
|
|
return -1;
|
|
}
|
|
|
|
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (data == MAP_FAILED) {
|
|
fprintf(stderr, "mmap failed: %m\n");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
pool = wl_shm_create_pool(display->shm, fd, size);
|
|
buffer->buffer = wl_shm_pool_create_buffer(pool, 0,
|
|
width, height,
|
|
stride, format);
|
|
wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer);
|
|
wl_shm_pool_destroy(pool);
|
|
close(fd);
|
|
|
|
buffer->shm_data = data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
handle_configure(void *data, struct xdg_surface *surface,
|
|
int32_t width, int32_t height,
|
|
struct wl_array *states, uint32_t serial)
|
|
{
|
|
xdg_surface_ack_configure(surface, serial);
|
|
}
|
|
|
|
static void
|
|
handle_delete(void *data, struct xdg_surface *xdg_surface)
|
|
{
|
|
running = 0;
|
|
}
|
|
|
|
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
handle_configure,
|
|
handle_delete,
|
|
};
|
|
|
|
static struct window *
|
|
create_window(struct display *display, int width, int height)
|
|
{
|
|
struct window *window;
|
|
|
|
window = calloc(1, sizeof *window);
|
|
if (!window)
|
|
return NULL;
|
|
|
|
window->callback = NULL;
|
|
window->display = display;
|
|
window->width = width;
|
|
window->height = height;
|
|
window->surface = wl_compositor_create_surface(display->compositor);
|
|
|
|
if (display->shell) {
|
|
window->xdg_surface =
|
|
xdg_shell_get_xdg_surface(display->shell,
|
|
window->surface);
|
|
|
|
assert(window->xdg_surface);
|
|
|
|
xdg_surface_add_listener(window->xdg_surface,
|
|
&xdg_surface_listener, window);
|
|
|
|
xdg_surface_set_title(window->xdg_surface, "simple-shm");
|
|
} else if (display->fshell) {
|
|
_wl_fullscreen_shell_present_surface(display->fshell,
|
|
window->surface,
|
|
_WL_FULLSCREEN_SHELL_PRESENT_METHOD_DEFAULT,
|
|
NULL);
|
|
} else {
|
|
assert(0);
|
|
}
|
|
|
|
return window;
|
|
}
|
|
|
|
static void
|
|
destroy_window(struct window *window)
|
|
{
|
|
if (window->callback)
|
|
wl_callback_destroy(window->callback);
|
|
|
|
if (window->buffers[0].buffer)
|
|
wl_buffer_destroy(window->buffers[0].buffer);
|
|
if (window->buffers[1].buffer)
|
|
wl_buffer_destroy(window->buffers[1].buffer);
|
|
|
|
if (window->xdg_surface)
|
|
xdg_surface_destroy(window->xdg_surface);
|
|
wl_surface_destroy(window->surface);
|
|
free(window);
|
|
}
|
|
|
|
static struct buffer *
|
|
window_next_buffer(struct window *window)
|
|
{
|
|
struct buffer *buffer;
|
|
int ret = 0;
|
|
|
|
if (!window->buffers[0].busy)
|
|
buffer = &window->buffers[0];
|
|
else if (!window->buffers[1].busy)
|
|
buffer = &window->buffers[1];
|
|
else
|
|
return NULL;
|
|
|
|
if (!buffer->buffer) {
|
|
ret = create_shm_buffer(window->display, buffer,
|
|
window->width, window->height,
|
|
WL_SHM_FORMAT_XRGB8888);
|
|
|
|
if (ret < 0)
|
|
return NULL;
|
|
|
|
/* paint the padding */
|
|
memset(buffer->shm_data, 0xff,
|
|
window->width * window->height * 4);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static void
|
|
paint_pixels(void *image, int padding, int width, int height, uint32_t time)
|
|
{
|
|
const int halfh = padding + (height - padding * 2) / 2;
|
|
const int halfw = padding + (width - padding * 2) / 2;
|
|
int ir, or;
|
|
uint32_t *pixel = image;
|
|
int y;
|
|
|
|
/* squared radii thresholds */
|
|
or = (halfw < halfh ? halfw : halfh) - 8;
|
|
ir = or - 32;
|
|
or *= or;
|
|
ir *= ir;
|
|
|
|
pixel += padding * width;
|
|
for (y = padding; y < height - padding; y++) {
|
|
int x;
|
|
int y2 = (y - halfh) * (y - halfh);
|
|
|
|
pixel += padding;
|
|
for (x = padding; x < width - padding; x++) {
|
|
uint32_t v;
|
|
|
|
/* squared distance from center */
|
|
int r2 = (x - halfw) * (x - halfw) + y2;
|
|
|
|
if (r2 < ir)
|
|
v = (r2 / 32 + time / 64) * 0x0080401;
|
|
else if (r2 < or)
|
|
v = (y + time / 32) * 0x0080401;
|
|
else
|
|
v = (x + time / 16) * 0x0080401;
|
|
v &= 0x00ffffff;
|
|
|
|
/* cross if compositor uses X from XRGB as alpha */
|
|
if (abs(x - y) > 6 && abs(x + y - height) > 6)
|
|
v |= 0xff000000;
|
|
|
|
*pixel++ = v;
|
|
}
|
|
|
|
pixel += padding;
|
|
}
|
|
}
|
|
|
|
static const struct wl_callback_listener frame_listener;
|
|
|
|
static void
|
|
redraw(void *data, struct wl_callback *callback, uint32_t time)
|
|
{
|
|
struct window *window = data;
|
|
struct buffer *buffer;
|
|
|
|
buffer = window_next_buffer(window);
|
|
if (!buffer) {
|
|
fprintf(stderr,
|
|
!callback ? "Failed to create the first buffer.\n" :
|
|
"Both buffers busy at redraw(). Server bug?\n");
|
|
abort();
|
|
}
|
|
|
|
paint_pixels(buffer->shm_data, 20, window->width, window->height, time);
|
|
|
|
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
|
|
wl_surface_damage(window->surface,
|
|
20, 20, window->width - 40, window->height - 40);
|
|
|
|
if (callback)
|
|
wl_callback_destroy(callback);
|
|
|
|
window->callback = wl_surface_frame(window->surface);
|
|
wl_callback_add_listener(window->callback, &frame_listener, window);
|
|
wl_surface_commit(window->surface);
|
|
buffer->busy = 1;
|
|
}
|
|
|
|
static const struct wl_callback_listener frame_listener = {
|
|
redraw
|
|
};
|
|
|
|
static void
|
|
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
|
{
|
|
struct display *d = data;
|
|
|
|
d->formats |= (1 << format);
|
|
}
|
|
|
|
struct wl_shm_listener shm_listener = {
|
|
shm_format
|
|
};
|
|
|
|
static void
|
|
xdg_shell_ping(void *data, struct xdg_shell *shell, uint32_t serial)
|
|
{
|
|
xdg_shell_pong(shell, serial);
|
|
}
|
|
|
|
static const struct xdg_shell_listener xdg_shell_listener = {
|
|
xdg_shell_ping,
|
|
};
|
|
|
|
#define XDG_VERSION 4 /* The version of xdg-shell that we implement */
|
|
#ifdef static_assert
|
|
static_assert(XDG_VERSION == XDG_SHELL_VERSION_CURRENT,
|
|
"Interface version doesn't match implementation version");
|
|
#endif
|
|
|
|
static void
|
|
registry_handle_global(void *data, struct wl_registry *registry,
|
|
uint32_t id, const char *interface, uint32_t version)
|
|
{
|
|
struct display *d = data;
|
|
|
|
if (strcmp(interface, "wl_compositor") == 0) {
|
|
d->compositor =
|
|
wl_registry_bind(registry,
|
|
id, &wl_compositor_interface, 1);
|
|
} else if (strcmp(interface, "xdg_shell") == 0) {
|
|
d->shell = wl_registry_bind(registry,
|
|
id, &xdg_shell_interface, 1);
|
|
xdg_shell_use_unstable_version(d->shell, XDG_VERSION);
|
|
xdg_shell_add_listener(d->shell, &xdg_shell_listener, d);
|
|
} else if (strcmp(interface, "_wl_fullscreen_shell") == 0) {
|
|
d->fshell = wl_registry_bind(registry,
|
|
id, &_wl_fullscreen_shell_interface, 1);
|
|
} else if (strcmp(interface, "wl_shm") == 0) {
|
|
d->shm = wl_registry_bind(registry,
|
|
id, &wl_shm_interface, 1);
|
|
wl_shm_add_listener(d->shm, &shm_listener, d);
|
|
}
|
|
}
|
|
|
|
static void
|
|
registry_handle_global_remove(void *data, struct wl_registry *registry,
|
|
uint32_t name)
|
|
{
|
|
}
|
|
|
|
static const struct wl_registry_listener registry_listener = {
|
|
registry_handle_global,
|
|
registry_handle_global_remove
|
|
};
|
|
|
|
static struct display *
|
|
create_display(void)
|
|
{
|
|
struct display *display;
|
|
|
|
display = malloc(sizeof *display);
|
|
if (display == NULL) {
|
|
fprintf(stderr, "out of memory\n");
|
|
exit(1);
|
|
}
|
|
display->display = wl_display_connect(NULL);
|
|
assert(display->display);
|
|
|
|
display->formats = 0;
|
|
display->registry = wl_display_get_registry(display->display);
|
|
wl_registry_add_listener(display->registry,
|
|
®istry_listener, display);
|
|
wl_display_roundtrip(display->display);
|
|
if (display->shm == NULL) {
|
|
fprintf(stderr, "No wl_shm global\n");
|
|
exit(1);
|
|
}
|
|
|
|
wl_display_roundtrip(display->display);
|
|
|
|
if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
|
|
fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
|
|
exit(1);
|
|
}
|
|
|
|
wl_display_get_fd(display->display);
|
|
|
|
return display;
|
|
}
|
|
|
|
static void
|
|
destroy_display(struct display *display)
|
|
{
|
|
if (display->shm)
|
|
wl_shm_destroy(display->shm);
|
|
|
|
if (display->shell)
|
|
xdg_shell_destroy(display->shell);
|
|
|
|
if (display->fshell)
|
|
_wl_fullscreen_shell_release(display->fshell);
|
|
|
|
if (display->compositor)
|
|
wl_compositor_destroy(display->compositor);
|
|
|
|
wl_registry_destroy(display->registry);
|
|
wl_display_flush(display->display);
|
|
wl_display_disconnect(display->display);
|
|
free(display);
|
|
}
|
|
|
|
static void
|
|
signal_int(int signum)
|
|
{
|
|
running = 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct sigaction sigint;
|
|
struct display *display;
|
|
struct window *window;
|
|
int ret = 0;
|
|
|
|
display = create_display();
|
|
window = create_window(display, 250, 250);
|
|
if (!window)
|
|
return 1;
|
|
|
|
sigint.sa_handler = signal_int;
|
|
sigemptyset(&sigint.sa_mask);
|
|
sigint.sa_flags = SA_RESETHAND;
|
|
sigaction(SIGINT, &sigint, NULL);
|
|
|
|
/* Initialise damage to full surface, so the padding gets painted */
|
|
wl_surface_damage(window->surface, 0, 0,
|
|
window->width, window->height);
|
|
|
|
redraw(window, NULL, 0);
|
|
|
|
while (running && ret != -1)
|
|
ret = wl_display_dispatch(display->display);
|
|
|
|
fprintf(stderr, "simple-shm exiting\n");
|
|
destroy_window(window);
|
|
destroy_display(display);
|
|
|
|
return 0;
|
|
}
|