diff --git a/Makefile b/Makefile index 22b19308..8905dbe0 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ $(compositors) $(clients) : CFLAGS += $(shell pkg-config --cflags libdrm) 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) diff --git a/egl-compositor.c b/egl-compositor.c index 132fcbd3..7d6cd0e2 100644 --- a/egl-compositor.c +++ b/egl-compositor.c @@ -12,10 +12,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include "wayland.h" #include "cairo-util.h" @@ -38,6 +41,14 @@ struct egl_compositor { 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 { @@ -516,12 +527,24 @@ animate_overlay(struct egl_compositor *ec) } 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); @@ -540,19 +563,42 @@ repaint(void *data) draw_surface(ec->pointer); eglSwapBuffers(ec->display, ec->surface); + ec->repaint_needed = 0; - wl_display_post_acknowledge(ec->wl_display); + 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 @@ -611,15 +657,12 @@ notify_surface_attach(struct wl_compositor *compositor, 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); @@ -627,8 +670,6 @@ notify_surface_map(struct wl_compositor *compositor, return; es->map = *map; - - schedule_repaint(ec); } static void @@ -654,18 +695,25 @@ notify_surface_copy(struct wl_compositor *compositor, 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 notify_surface_damage(struct wl_compositor *compositor, 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 @@ -702,6 +750,7 @@ static const struct wl_compositor_interface interface = { notify_surface_map, notify_surface_copy, notify_surface_damage, + notify_commit, notify_pointer_motion, notify_key }; @@ -889,6 +938,7 @@ wl_compositor_create(struct wl_display *display) 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 }; @@ -960,6 +1010,19 @@ wl_compositor_create(struct wl_display *display) 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; diff --git a/flower.c b/flower.c index 3356fc4a..adc5aecb 100644 --- a/flower.c +++ b/flower.c @@ -104,7 +104,7 @@ event_handler(struct wl_display *display, 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); } diff --git a/wayland.c b/wayland.c index c33d135a..7803f3d0 100644 --- a/wayland.c +++ b/wayland.c @@ -19,8 +19,7 @@ struct wl_client { 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 { @@ -318,6 +317,7 @@ wl_client_event(struct wl_client *client, struct wl_object *object, uint32_t eve #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) @@ -502,8 +502,20 @@ static int 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; } @@ -686,23 +698,25 @@ wl_display_post_key_event(struct wl_display *display, } 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); diff --git a/wayland.h b/wayland.h index ecc9de00..730de2ae 100644 --- a/wayland.h +++ b/wayland.h @@ -110,7 +110,8 @@ void 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; @@ -139,6 +140,7 @@ struct wl_compositor_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); diff --git a/window.c b/window.c index fb9e28a5..4753f636 100644 --- a/window.c +++ b/window.c @@ -154,6 +154,12 @@ draw_window(void *data) 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); @@ -167,62 +173,11 @@ draw_window(void *data) 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, @@ -241,27 +196,59 @@ enum location { 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", @@ -281,16 +268,7 @@ event_handler(struct wl_display *display, 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; @@ -299,6 +277,21 @@ event_handler(struct wl_display *display, 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; } @@ -364,7 +357,6 @@ window_create(struct wl_display *display, int fd) 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) @@ -381,7 +373,11 @@ window_create(struct wl_display *display, int fd) 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; }