egl_compositor_objs = egl-compositor.o evdev.o cairo-util.o
egl-compositor.so : CFLAGS += $(EAGLE_CFLAGS) $(shell pkg-config --cflags libpng cairo gdk-pixbuf-2.0)
-egl-compositor.so : LDLIBS += $(EAGLE_LDLIBS) $(shell pkg-config --libs libpng cairo gdk-pixbuf-2.0) -rdynamic
+egl-compositor.so : LDLIBS += $(EAGLE_LDLIBS) $(shell pkg-config --libs libpng cairo gdk-pixbuf-2.0) -rdynamic -lrt
egl-compositor.so : $(egl_compositor_objs)
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <glib.h>
+#include <sys/poll.h>
#include <png.h>
#include <math.h>
#include <linux/input.h>
#include <xf86drmMode.h>
+#include <sys/timerfd.h>
+#include <time.h>
#include "wayland.h"
#include "cairo-util.h"
struct egl_surface *background;
struct egl_surface *overlay;
double overlay_y, overlay_target, overlay_previous;
+
+ /* Repaint state. */
+ struct wl_event_source *timer_source;
+ int repaint_needed;
+ int repaint_on_timeout;
+ int timer_fd;
+ struct timespec previous_swap;
+ uint32_t current_frame;
};
struct egl_surface {
}
static void
-repaint(void *data)
+repaint(int fd, uint32_t mask, void *data)
{
struct egl_compositor *ec = data;
+ struct itimerspec its;
struct wl_surface_iterator *iterator;
struct wl_surface *surface;
struct egl_surface *es;
+ struct timespec ts;
+ uint64_t expires;
+ uint32_t msecs;
+
+ if (ec->repaint_on_timeout)
+ read(fd, &expires, sizeof expires);
+
+ if (!ec->repaint_needed) {
+ ec->repaint_on_timeout = 0;
+ return;
+ }
draw_surface(ec->background);
draw_surface(ec->pointer);
eglSwapBuffers(ec->display, ec->surface);
-
- wl_display_post_acknowledge(ec->wl_display);
+ ec->repaint_needed = 0;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ msecs = ts.tv_sec * 1000 + ts.tv_nsec / (1000 * 1000);
+ wl_display_post_frame(ec->wl_display, ec->current_frame, msecs);
+ ec->current_frame++;
+
+ its.it_interval.tv_sec = 0;
+ its.it_interval.tv_nsec = 0;
+ its.it_value.tv_sec = 0;
+ its.it_value.tv_nsec = 10 * 1000 * 1000;
+ if (timerfd_settime(ec->timer_fd, 0, &its, NULL) < 0) {
+ fprintf(stderr, "could not set timerfd\n: %m");
+ return;
+ }
+ ec->repaint_on_timeout = 1;
animate_overlay(ec);
}
static void
+idle_repaint(void *data)
+{
+ repaint(0, 0, data);
+}
+
+static void
schedule_repaint(struct egl_compositor *ec)
{
struct wl_event_loop *loop;
- loop = wl_display_get_event_loop(ec->wl_display);
- wl_event_loop_add_idle(loop, repaint, ec);
+ ec->repaint_needed = 1;
+ if (!ec->repaint_on_timeout) {
+ loop = wl_display_get_event_loop(ec->wl_display);
+ wl_event_loop_add_idle(loop, idle_repaint, ec);
+ }
}
static void
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
eglBindTexImage(ec->display, es->surface, GL_TEXTURE_2D);
-
- schedule_repaint(ec);
}
static void
notify_surface_map(struct wl_compositor *compositor,
struct wl_surface *surface, struct wl_map *map)
{
- struct egl_compositor *ec = (struct egl_compositor *) compositor;
struct egl_surface *es;
es = wl_surface_get_data(surface);
return;
es->map = *map;
-
- schedule_repaint(ec);
}
static void
eglCopyNativeBuffers(ec->display, es->surface, GL_FRONT_LEFT, dst_x, dst_y,
src, GL_FRONT_LEFT, x, y, width, height);
- schedule_repaint(ec);
+ eglDestroySurface(ec->display, src);
}
static void
struct wl_surface *surface,
int32_t x, int32_t y, int32_t width, int32_t height)
{
+ /* FIXME: This need to take a damage region, of course. */
+}
+
+static uint32_t
+notify_commit(struct wl_compositor *compositor)
+{
struct egl_compositor *ec = (struct egl_compositor *) compositor;
- /* FIXME: This need to take a damage region, of course. */
schedule_repaint(ec);
+
+ return ec->current_frame;
}
static void
notify_surface_map,
notify_surface_copy,
notify_surface_damage,
+ notify_commit,
notify_pointer_motion,
notify_key
};
struct screenshooter *shooter;
uint32_t fb_name;
int stride;
+ struct wl_event_loop *loop;
const static EGLint attribs[] =
{ EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE };
wl_display_add_object(display, &shooter->base);
wl_display_add_global(display, &shooter->base);
+ ec->timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
+ if (ec->timer_fd < 0) {
+ fprintf(stderr, "could not create timerfd\n: %m");
+ return NULL;
+ }
+
+ loop = wl_display_get_event_loop(ec->wl_display);
+ ec->timer_source = wl_event_loop_add_fd(loop, ec->timer_fd,
+ WL_EVENT_READABLE,
+ repaint, ec);
+ ec->repaint_needed = 0;
+ ec->repaint_on_timeout = 0;
+
schedule_repaint(ec);
return &ec->base;
uint32_t object, uint32_t opcode,
uint32_t size, uint32_t *p, void *data)
{
- if (object == 1)
+ if (object == 1 && opcode == 4)
move_flower(data);
}
struct wl_display *display;
struct wl_list object_list;
struct wl_list link;
- uint32_t pending_acknowledge;
- uint32_t acknowledge_key;
+ uint32_t pending_frame;
};
struct wl_display {
#define WL_DISPLAY_INVALID_METHOD 1
#define WL_DISPLAY_NO_MEMORY 2
#define WL_DISPLAY_ACKNOWLEDGE 3
+#define WL_DISPLAY_FRAME 4
static void
wl_client_connection_data(int fd, uint32_t mask, void *data)
wl_display_commit(struct wl_client *client,
struct wl_display *display, uint32_t key)
{
- client->pending_acknowledge = 1;
- client->acknowledge_key = key;
+ const struct wl_compositor_interface *interface;
+ uint32_t frame, event[4];
+
+ client->pending_frame = 1;
+
+ interface = display->compositor->interface;
+ frame = interface->notify_commit(display->compositor);
+
+ event[0] = display->base.id;
+ event[1] = WL_DISPLAY_ACKNOWLEDGE | ((sizeof event) << 16);
+ event[2] = key;
+ event[3] = frame;
+
+ wl_connection_write(client->connection, event, sizeof event);
return 0;
}
}
WL_EXPORT void
-wl_display_post_acknowledge(struct wl_display *display)
+wl_display_post_frame(struct wl_display *display,
+ uint32_t frame, uint32_t msecs)
{
struct wl_client *client;
- uint32_t event[3];
+ uint32_t event[4];
event[0] = display->base.id;
- event[1] = WL_DISPLAY_ACKNOWLEDGE | ((sizeof event) << 16);
+ event[1] = WL_DISPLAY_FRAME | ((sizeof event) << 16);
+ event[2] = frame;
+ event[3] = msecs;
client = container_of(display->client_list.next,
struct wl_client, link);
while (&client->link != &display->client_list) {
- if (client->pending_acknowledge) {
- event[2] = client->acknowledge_key;
+ if (client->pending_frame) {
wl_connection_write(client->connection,
event, sizeof event);
- client->pending_acknowledge = 0;
+ client->pending_frame = 0;
}
client = container_of(client->link.next,
struct wl_client, link);
wl_display_post_key_event(struct wl_display *display,
struct wl_object *source, int key, int state);
void
-wl_display_post_acknowledge(struct wl_display *display);
+wl_display_post_frame(struct wl_display *display,
+ uint32_t frame, uint32_t msecs);
struct wl_compositor {
const struct wl_compositor_interface *interface;
struct wl_surface *surface,
int32_t x, int32_t y,
int32_t width, int32_t height);
+ uint32_t (*notify_commit)(struct wl_compositor *compositor);
void (*notify_pointer_motion)(struct wl_compositor *compositor,
struct wl_object *source,
int32_t x, int32_t y);
window->buffer->height,
window->buffer->stride);
+ wl_surface_map(window->surface,
+ window->x - window->margin,
+ window->y - window->margin,
+ window->width + 2 * window->margin,
+ window->height + 2 * window->margin);
+
width = window->width - 20;
height = window->height - 60;
buffer = buffer_create(window->fd, width, height, (width * 4 + 15) & ~15);
die("failed to make context current\n");
glViewport(0, 0, width, height);
-
- if (window->gears == NULL)
- window->gears = gears_create(0, 0, 0, 0.92);
-
window->resized = 0;
return FALSE;
}
-
-static gboolean
-animate_gears(gpointer data)
-{
- struct window *window = data;
- struct buffer *buffer;
- static uint32_t key;
-
- /* Right now, resizing the window from the animation is fine,
- * since the window drawing code is so slow, but once we
- * implement faster resizing, this will show lag between
- * pointer motion and window size even if resizing is fast.
- * We need to keep processing motion events and posting new
- * frames as fast as possible so when the server composites
- * the next frame it will have the most recent size possible.
- * In that case, we need the two ack protocol, where the first
- * ack signals that the server got the request so we can free
- * the buffer, to prevent us from allocating a ton of buffer
- * that will never be displayed. */
- if (window->resized)
- draw_window(window);
-
- gears_draw(window->gears, window->gears_angle);
-
- buffer = window->egl_buffer;
- wl_surface_copy(window->surface,
- 10 + window->margin, 50 + window->margin,
- buffer->name, buffer->stride,
- 0, 0, buffer->width, buffer->height);
-
- /* Shouldn't need to do this here, but without proper commit
- * support in the server, doing this before rendering the
- * gears show the window briefly before it's fully
- * rendered. */
-
- wl_surface_map(window->surface,
- window->x - window->margin,
- window->y - window->margin,
- window->width + 2 * window->margin,
- window->height + 2 * window->margin);
-
- wl_display_commit(window->display, key++);
- window->gears_angle += 1;
-
- return FALSE;
-}
-
enum window_state {
WINDOW_STABLE,
WINDOW_MOVING,
LOCATION_OUTSIDE
};
+static int
+update_gears(void *data)
+{
+ struct window *window = data;
+
+ if (window->resized)
+ draw_window(window);
+ gears_draw(window->gears, window->gears_angle);
+
+ return FALSE;
+}
+
static void
event_handler(struct wl_display *display,
uint32_t object, uint32_t opcode,
uint32_t size, uint32_t *p, void *data)
{
struct window *window = data;
+ struct buffer *buffer;
int location;
int grip_size = 16;
/* FIXME: Object ID 1 is the display, for anything else we
* assume it's an input device. */
if (object == 1 && opcode == 3) {
+ uint32_t key = p[0];
+
+ /* Ignore acknowledge events for window move requests. */
+ if (key != 0)
+ return;
+
/* The acknowledge event means that the server
* processed our last commit request and we can now
- * safely free the buffer. */
+ * safely free the old window buffer if we resized and
+ * render the next frame into our back buffer.. */
+
if (window->buffer != NULL) {
buffer_destroy(window->buffer, window->fd);
window->buffer = NULL;
}
-
- g_idle_add(animate_gears, window);
+ g_idle_add(update_gears, window);
+ } else if (object == 1 && opcode == 4) {
+ /* The frame event means that the previous frame was
+ * composited, and we can now send the request to copy
+ * the frame we've rendered in the mean time into the
+ * servers surface buffer. */
+ buffer = window->egl_buffer;
+ wl_surface_copy(window->surface,
+ 10 + window->margin, 50 + window->margin,
+ buffer->name, buffer->stride,
+ 0, 0, buffer->width, buffer->height);
+ wl_display_commit(window->display, 0);
+ window->gears_angle += 1;
} else if (object == 1) {
fprintf(stderr, "unexpected event from display: %d\n",
window->y - window->margin,
window->width + 2 * window->margin,
window->height + 2 * window->margin);
- /* FIXME: We should do this here:
- *
- * wl_display_commit(window->display, 1);
- *
- * to make sure the server processes the move,
- * but that'll mess with the other commit from
- * animate_gears with the current server
- * implementation. Since the current server
- * doesn't rely on commit anyway yet, we can
- * just forget about it for now. */
+ wl_display_commit(window->display, 1);
break;
case WINDOW_RESIZING_LOWER_RIGHT:
window->width = window->drag_x + x;
window->width = 400;
if (window->height < 400)
window->height = 400;
+
+ /* Right now, resizing the window from the
+ * per-frame callback is fine, since the
+ * window drawing code is so slow that we
+ * can't draw more than one window per frame
+ * anyway. However, once we implement faster
+ * resizing, this will show lag between
+ * pointer motion and window size even if
+ * resizing is fast. We need to keep
+ * processing motion events and posting new
+ * frames as fast as possible so when the
+ * server composites the next frame it will
+ * have the most recent size possible, like
+ * what we do for window moves. */
+
window->resized = 1;
break;
}
window->state = WINDOW_STABLE;
window->fd = fd;
window->background = cairo_pattern_create_rgba (red, green, blue, alpha);
- window->resized = 1;
window->egl_display = eglCreateDisplayNative("/dev/dri/card0", "i965");
if (window->egl_display == NULL)
if (window->context == NULL)
die("failed to create context\n");
- animate_gears(window);
+ draw_window(window);
+ window->gears = gears_create(0, 0, 0, 0.92);
+ gears_draw(window->gears, window->gears_angle);
+ window->gears_angle += 1;
+ wl_display_commit(window->display, 0);
return window;
}