Bunch of new stuff: simple compositor, cairo+gem use in client, events.
authorKristian Høgsberg <krh@redhat.com>
Tue, 7 Oct 2008 14:10:36 +0000 (10:10 -0400)
committerKristian Høgsberg <krh@redhat.com>
Tue, 7 Oct 2008 14:10:36 +0000 (10:10 -0400)
Makefile
NOTES
client.c
compositor.c [new file with mode: 0644]
event-loop.c
wayland-client.c [new file with mode: 0644]
wayland-client.h [new file with mode: 0644]
wayland.c
wayland.h

index 661cd6f..44a0b04 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,17 +1,24 @@
-CFLAGS = -Wall -g $(shell pkg-config --cflags libffi)
-LDLIBS = $(shell pkg-config --libs libffi)
+CFLAGS +=  -Wall -g $(shell pkg-config --cflags libffi libdrm)
+LDLIBS += $(shell pkg-config --libs libffi libdrm)
 
 all : wayland client
 
-wayland_objs = wayland.o event-loop.o hash.o
+wayland_objs = wayland.o event-loop.o hash.o compositor.o
 
 wayland : $(wayland_objs)
        gcc -o $@ $(wayland_objs) $(LDLIBS)
 
+libwayland_objs = wayland-client.o
+
+libwayland.so : $(libwayland_objs)
+       gcc -o $@ $(libwayland_objs) -shared
+
 client_objs = client.o
+client : CFLAGS += $(shell pkg-config --cflags cairo)
+client : LDLIBS += $(shell pkg-config --libs cairo)
 
-client : $(client_objs)
-       gcc -o $@ $(client_objs)
+client : $(client_objs) libwayland.so
+       gcc -o $@ -L. -lwayland $(LDLIBS) $(client_objs)
 
 clean :
-       rm client wayland *.o
\ No newline at end of file
+       rm -f client wayland *.o libwayland.so
\ No newline at end of file
diff --git a/NOTES b/NOTES
index 429de30..b3e7211 100644 (file)
--- a/NOTES
+++ b/NOTES
@@ -33,6 +33,10 @@ Could be a "shell" for launching gdm X server, user session servers,
 safe mode xservers, graphics text console.  From gdm, we could also
 launch a rdp session, solid ice sessions.  
 
+All surface commands (copy, attach, map=set quads) are buffered until
+the client sends a commit command, which executes everything
+atomically...
+
 
 ISSUES:
 
@@ -56,7 +60,21 @@ synaptics, 3-button emulation, xkb, scim
 
 changing screen resolution, adding monitors.
 
+
 RMI
 
+The wayland protocol is a async object oriented protocol.  All
+requests are method invocations on some object.  The request include
+an object id that uniquely identifies an object on the server.  Each
+object implements an interface and the requests include an opcode that
+identifies which method in the interface to invoke.
+
+The server sends back events to the client, each event is emitted from
+an object.  Events can be error conditions.  The event includes the
+object id and the event opcode, from which the client can determine
+the type of event.  Events are generated both in repsonse to a request
+(in which case the requet and the event constitutes a round trip) or
+spontanously when the server state changes.
+
 the get_interface method is called on an object to get an object
 handle that implements the specified interface.
\ No newline at end of file
index 8312be8..d01ed2c 100644 (file)
--- a/client.c
+++ b/client.c
-#include <stdlib.h>
 #include <stdint.h>
-#include <stddef.h>
 #include <stdio.h>
-#include <errno.h>
+#include <stdlib.h>
 #include <string.h>
+#include <i915_drm.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
 #include <unistd.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <ctype.h>
+#include <math.h>
+#include <cairo.h>
+
+#include "wayland-client.h"
 
+static const char gem_device[] = "/dev/dri/card0";
 static const char socket_name[] = "\0wayland";
 
-struct method {
-       const char *name;
-       uint32_t opcode;
-};
-
-static const struct method display_methods[] = {
-       { "get_interface", 0 },
-       { "create_surface", 1 },
-       { NULL }
-};
-
-static const struct method surface_methods[] = {
-       { "get_interface", 0 },
-       { "post", 1 },
-       { NULL }
-};
-
-struct interface {
-       const char *name;
-       const struct method *methods;
-};
-
-static const struct interface interfaces[] = {
-       { "display", display_methods },
-       { "surface", surface_methods },
-       { NULL },
-};
-
-int send_request(int sock, const char *buffer, int buffer_len)
+static uint32_t name_cairo_surface(int fd, cairo_surface_t *surface)
 {
-       const struct method *methods;
-       char interface[32], method[32];
-       uint32_t request[3], id, new_id;
-       int i;
-
-       if (sscanf(buffer, "%d/%32[^:]:%32s %d\n",
-                  &id, interface, method, &new_id) != 4) {
-               printf("invalid input, expected <id>/<interface>:<method>\n");
-               return -1;
-       }
+       struct drm_i915_gem_create create;
+       struct drm_gem_flink flink;
+       struct drm_i915_gem_pwrite pwrite;
+       int32_t width, height, stride;
+
+       width = cairo_image_surface_get_width(surface);
+       height = cairo_image_surface_get_height(surface);
+       stride = cairo_image_surface_get_stride(surface);
 
-       printf("got object id %d, interface \"%s\", name \"%s\"\n",
-              id, interface, method);
+       memset(&create, 0, sizeof(create));
+       create.size = height * stride;
 
-       for (i = 0; interfaces[i].name != NULL; i++) {
-               if (strcmp(interfaces[i].name, interface) == 0)
-                       break;
+       if (ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) {
+               fprintf(stderr, "gem create failed: %m\n");
+               return 0;
        }
-       if (interfaces[i].name == NULL) {
-               printf("unknown interface \"%s\"\n", interface);
-               return -1;
+
+       pwrite.handle = create.handle;
+       pwrite.offset = 0;
+       pwrite.size = height * stride;
+       pwrite.data_ptr = (uint64_t) (uintptr_t)
+               cairo_image_surface_get_data(surface);
+       if (ioctl(fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite) < 0) {
+               fprintf(stderr, "gem pwrite failed: %m\n");
+               return 0;
        }
 
-       methods = interfaces[i].methods;
-       for (i = 0; methods[i].name != NULL; i++) {
-               if (strcmp(methods[i].name, method) == 0)
-                       break;
+       flink.handle = create.handle;
+       if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) {
+               fprintf(stderr, "gem flink failed: %m\n");
+               return 0;
        }
 
-       if (methods[i].name == NULL) {
-               printf("unknown request \"%s\"\n", method);
-               return -1;
+#if 0
+       /* We need to hold on to the handle until the server has received
+        * the attach request... we probably need a confirmation event.
+        * I guess the breadcrumb idea will suffice. */
+       struct drm_gem_close close;
+       close.handle = create.handle;
+       if (ioctl(fd, DRM_IOCTL_GEM_CLOSE, &close) < 0) {
+               fprintf(stderr, "gem close failed: %m\n");
+               return 0;
        }
+#endif
 
-       request[0] = id;
-       request[1] = methods[i].opcode;
-       request[2] = new_id;
+       return flink.name;
+}
+
+static void *
+draw_stuff(int width, int height)
+{
+       cairo_surface_t *surface;
+       cairo_t *cr;
 
-       return write(sock, request, sizeof request);
+       surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
+                                            width, height);
+
+       cr = cairo_create(surface);
+
+       cairo_arc(cr, width / 2, height / 2, width / 2 - 10, 0, 2 * M_PI);
+       cairo_set_source_rgb(cr, 1, 0, 0);
+       cairo_fill_preserve(cr);
+       cairo_set_source_rgb(cr, 1, 1, 0);
+       cairo_stroke(cr);
+
+       cairo_arc(cr, width / 2, height / 2, width / 4 - 10, 0, 2 * M_PI);
+       cairo_set_source_rgb(cr, 0, 0, 1);
+       cairo_fill_preserve(cr);
+       cairo_set_source_rgb(cr, 1, 1, 0);
+       cairo_stroke(cr);
+
+       cairo_destroy(cr);
+
+       return surface;
 }
 
 int main(int argc, char *argv[])
 {
-       struct sockaddr_un name;
-       socklen_t size;
-       int sock, len;
-       char buffer[256];
+       struct wl_connection *connection;
+       struct wl_display *display;
+       struct wl_surface *surface;
+       int width = 300, height = 300, fd;
+       uint32_t name;
+       cairo_surface_t *s;
+
+       fd = open(gem_device, O_RDWR);
+       if (fd < 0) {
+               fprintf(stderr, "drm open failed: %m\n");
+               return -1;
+       }
 
-       sock = socket(PF_LOCAL, SOCK_STREAM, 0);
-       if (sock < 0)
+       connection = wl_connection_create(socket_name);
+       if (connection == NULL) {
+               fprintf(stderr, "failed to create connection: %m\n");
                return -1;
+       }
+
+       display = wl_connection_get_display(connection);
 
-       name.sun_family = AF_LOCAL;
-       memcpy(name.sun_path, socket_name, sizeof socket_name);
+       surface = wl_display_create_surface(display);
 
-       size = offsetof (struct sockaddr_un, sun_path) + sizeof socket_name;
+       s = draw_stuff(width, height);
+       name = name_cairo_surface(fd, s);
 
-       if (connect (sock, (struct sockaddr *) &name, size) < 0)
+       wl_surface_attach(surface, name, width, height,
+                         cairo_image_surface_get_stride(s));
+
+       if (wl_connection_flush(connection) < 0) {
+               fprintf(stderr, "flush error: %m\n");
                return -1;
+       }
 
-       while (len = read(STDIN_FILENO, buffer, sizeof buffer), len > 0)
-               if (send_request(sock, buffer, len) < 0)
-                       break;
+       while (1)
+               wl_connection_iterate(connection);
 
        return 0;
 }
diff --git a/compositor.c b/compositor.c
new file mode 100644 (file)
index 0000000..711c42f
--- /dev/null
@@ -0,0 +1,131 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <i915_drm.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/fb.h>
+
+#include "wayland.h"
+
+struct lame_compositor {
+       struct wl_compositor base;
+       void *fb;
+       int32_t width, height, stride;
+       int gem_fd;
+};
+
+void notify_surface_create(struct wl_compositor *compositor,
+                          struct wl_surface *surface)
+{
+}
+                                  
+void notify_surface_attach(struct wl_compositor *compositor,
+                          struct wl_surface *surface, uint32_t name, 
+                          uint32_t width, uint32_t height, uint32_t stride)
+{
+       struct lame_compositor *lc = (struct lame_compositor *) compositor;
+       struct drm_i915_gem_pread pread;
+       struct drm_gem_open open_arg;
+       struct drm_gem_close close_arg;
+       char *data, *dst;
+       int i, ret, x = 600, y = 200;
+
+       open_arg.name = name;
+       ret = ioctl(lc->gem_fd, DRM_IOCTL_GEM_OPEN, &open_arg);
+       if (ret != 0) {
+               fprintf(stderr, "failed to gem_open name %d, fd=%d: %m\n", name, lc->gem_fd);
+               return;
+       }
+
+       data = malloc(open_arg.size);
+       if (data == NULL) {
+               fprintf(stderr, "swap buffers malloc failed\n");
+               return;
+       }
+
+       pread.size = open_arg.size;
+       pread.handle = open_arg.handle;
+       pread.pad = 0;
+       pread.offset = 0;
+       pread.size = stride * height;
+       pread.data_ptr = (long) data;
+
+       if (ioctl(lc->gem_fd, DRM_IOCTL_I915_GEM_PREAD, &pread)) {
+               fprintf(stderr, "gem pread failed");
+               return;
+       }
+
+       dst = lc->fb + lc->stride * y + x * 4;
+       for (i = 0; i < height; i++)
+               memcpy(dst + lc->stride * i, data + stride * i, width * 4);
+
+       close_arg.handle = open_arg.handle;
+       ret = ioctl(lc->gem_fd, DRM_IOCTL_GEM_CLOSE, &close_arg);
+       if (ret != 0) {
+               fprintf(stderr, "failed to gem_close name %d: %m\n", name);
+       }
+
+       free(data);
+}
+
+struct wl_compositor_interface interface = {
+       notify_surface_create,
+       notify_surface_attach
+};
+
+static const char fb_device[] = "/dev/fb";
+static const char gem_device[] = "/dev/dri/card0";
+
+struct wl_compositor *
+wl_compositor_create(void)
+{
+       struct lame_compositor *lc;
+       struct fb_fix_screeninfo fix;
+       struct fb_var_screeninfo var;
+       int fd;
+
+       lc = malloc(sizeof *lc);
+       if (lc == NULL)
+               return NULL;
+
+       lc->base.interface = &interface;
+
+       fd = open(fb_device, O_RDWR);
+       if (fd < 0) {
+               fprintf(stderr, "open %s failed: %m\n", fb_device);
+               return NULL;
+       }
+
+       if (ioctl(fd, FBIOGET_FSCREENINFO, &fix) < 0) {
+               fprintf(stderr, "fb get fixed failed\n");
+               return NULL;
+       }
+
+       if (ioctl(fd, FBIOGET_VSCREENINFO, &var) < 0) {
+               fprintf(stderr, "fb get fixed failed\n");
+               return NULL;
+       }
+
+       lc->stride = fix.line_length;
+       lc->width = var.xres;
+       lc->height = var.yres;
+       lc->fb = mmap(NULL, lc->stride * lc->height,
+                     PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+       close(fd);
+       if (lc->fb == MAP_FAILED) {
+               fprintf(stderr, "fb map failed\n");
+               return NULL;
+       }
+
+       lc->gem_fd = open(gem_device, O_RDWR);
+       if (lc->gem_fd < 0) {
+               fprintf(stderr, "failed to open drm device\n");
+               return NULL;
+       }
+
+       return &lc->base;
+}
index 2946a64..96b5680 100644 (file)
@@ -60,6 +60,24 @@ wl_event_loop_remove_source(struct wl_event_loop *loop,
        return epoll_ctl(loop->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
 }
 
+int
+wl_event_loop_update_source(struct wl_event_loop *loop,
+                           struct wl_event_source *source,
+                           uint32_t mask)
+{
+       struct epoll_event ep;
+
+       ep.events = 0;
+       if (mask & WL_EVENT_READABLE)
+               ep.events |= EPOLLIN;
+       if (mask & WL_EVENT_WRITEABLE)
+               ep.events |= EPOLLOUT;
+       ep.data.ptr = source;
+
+       return epoll_ctl(loop->epoll_fd,
+                        EPOLL_CTL_MOD, source->fd, &ep);
+}
+
 struct wl_event_loop *
 wl_event_loop_create(void)
 {
@@ -93,6 +111,7 @@ wl_event_loop_wait(struct wl_event_loop *loop)
        struct epoll_event ep[32];
        struct wl_event_source *source;
        int i, count;
+       uint32_t mask;
 
        count = epoll_wait(loop->epoll_fd, ep, ARRAY_LENGTH(ep), -1);
        if (count < 0)
@@ -100,7 +119,13 @@ wl_event_loop_wait(struct wl_event_loop *loop)
 
        for (i = 0; i < count; i++) {
                source = ep[i].data.ptr;
-               source->func(source->fd, ep[i].events, source->data);
+               mask = 0;
+               if (ep[i].events & EPOLLIN)
+                       mask |= WL_EVENT_READABLE;
+               if (ep[i].events & EPOLLOUT)
+                       mask |= WL_EVENT_WRITEABLE;
+
+               source->func(source->fd, mask, source->data);
        }
 
        return 0;
diff --git a/wayland-client.c b/wayland-client.c
new file mode 100644 (file)
index 0000000..2e5bd18
--- /dev/null
@@ -0,0 +1,214 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <ctype.h>
+#include <sys/poll.h>
+
+static const char socket_name[] = "\0wayland";
+
+struct wl_buffer {
+       char data[4096];
+       int head, tail;
+};
+
+struct wl_connection {
+       int fd;
+       struct wl_buffer in, out;
+       struct wl_display *display;
+       uint32_t id;
+};
+
+struct wl_proxy {
+       struct wl_connection *connection;
+       uint32_t id;
+};
+
+struct wl_display {
+       struct wl_proxy proxy;
+};
+
+struct wl_surface {
+       struct wl_proxy proxy;
+};
+
+struct wl_connection *
+wl_connection_create(const char *address)
+{
+       struct wl_connection *connection;
+       struct wl_display *display;
+       struct sockaddr_un name;
+       socklen_t size;
+       char buffer[256];
+       uint32_t id, length;
+
+       connection = malloc(sizeof *connection);
+       if (connection == NULL)
+               return NULL;
+
+       memset(connection, 0, sizeof *connection);
+       connection->id = 256; /* Need to get our id-range. */
+       connection->fd = socket(PF_LOCAL, SOCK_STREAM, 0);
+       if (connection->fd < 0) {
+               free(connection);
+               return NULL;
+       }
+
+       name.sun_family = AF_LOCAL;
+       memcpy(name.sun_path, address, strlen(address + 1) + 2);
+
+       size = offsetof (struct sockaddr_un, sun_path) + sizeof socket_name;
+
+       if (connect (connection->fd, (struct sockaddr *) &name, size) < 0) {
+               close(connection->fd);
+               free(connection);
+               return NULL;
+       }
+
+       /* FIXME: actually discover advertised objects here. */
+       read(connection->fd, &id, sizeof id);
+       read(connection->fd, &length, sizeof length);
+       read(connection->fd, buffer, (length + 3) & ~3);
+
+       display = malloc(sizeof *display);
+       display->proxy.connection = connection;
+       display->proxy.id = id;
+       connection->display = display;
+
+       return connection;
+}
+
+void
+wl_connection_destroy(struct wl_connection *connection)
+{
+       close(connection->fd);
+       free(connection->display);
+       free(connection);
+}
+
+int
+wl_connection_get_fd(struct wl_connection *connection)
+{
+       return connection->fd;
+}
+
+static void
+handle_event(struct wl_connection *connection)
+{
+       struct wl_buffer *b;
+       uint32_t *p, opcode, size;
+
+       b = &connection->in;
+       p = (uint32_t *) (b->data + b->tail);
+       opcode = p[1] & 0xffff;
+       size = p[1] >> 16;
+       printf("signal from object %d, opcode %d, size %d\n",
+              p[0], opcode, size);
+       b->tail += size;
+}
+
+void
+wl_connection_iterate(struct wl_connection *connection)
+{
+       struct wl_buffer *b;
+       uint32_t *p, opcode, size;
+       int len;
+
+       b = &connection->in;
+       len = read(connection->fd, b->data + b->head, sizeof b->data);
+       b->head += len;
+       while (len > 0) {
+
+               if (b->head - b->tail < 8)
+                       break;
+               
+               p = (uint32_t *) (b->data + b->tail);
+               opcode = p[1] & 0xffff;
+               size = p[1] >> 16;
+               if (b->head - b->tail < size)
+                       break;
+
+               handle_event(connection);
+       }
+
+       if (len < 0) {
+               fprintf(stderr, "read error: %m\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+int
+wl_connection_flush(struct wl_connection *connection)
+{
+       struct wl_buffer *b;
+       int len;
+
+       b = &connection->out;
+       if (b->head == b->tail)
+               return 0;
+
+       len = write(connection->fd, b->data + b->tail, b->head - b->tail);
+       b->tail += len;
+
+       return len;
+};
+
+struct wl_display *
+wl_connection_get_display(struct wl_connection *connection)
+{
+       return connection->display;
+}
+
+#define WL_DISPLAY_CREATE_SURFACE 0
+
+struct wl_surface *
+wl_display_create_surface(struct wl_display *display)
+{
+       struct wl_surface *surface;
+       struct wl_connection *connection;
+       uint32_t request[3];
+
+       surface = malloc(sizeof *surface);
+       if (surface == NULL)
+               return NULL;
+
+       connection = display->proxy.connection;
+       surface->proxy.id = connection->id++;
+       surface->proxy.connection = connection;
+
+       request[0] = display->proxy.id;
+       request[1] = WL_DISPLAY_CREATE_SURFACE | ((sizeof request) << 16);
+       request[2] = surface->proxy.id;
+
+       memcpy(connection->out.data + connection->out.head,
+              request, sizeof request);
+       connection->out.head += sizeof request;
+
+       return surface;
+}
+
+#define WL_SURFACE_ATTACH 0
+
+void wl_surface_attach(struct wl_surface *surface,
+                      uint32_t name, int width, int height, int stride)
+{
+       uint32_t request[6];
+       struct wl_connection *connection;
+
+       request[0] = surface->proxy.id;
+       request[1] = WL_SURFACE_ATTACH | ((sizeof request) << 16);
+       request[2] = name;
+       request[3] = width;
+       request[4] = height;
+       request[5] = stride;
+
+       connection = surface->proxy.connection;
+       memcpy(connection->out.data + connection->out.head,
+              request, sizeof request);
+       connection->out.head += sizeof request;
+}
diff --git a/wayland-client.h b/wayland-client.h
new file mode 100644 (file)
index 0000000..ae62bca
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef _WAYLAND_CLIENT_H
+#define _WAYLAND_CLIENT_H
+
+struct wl_connection;
+struct wl_display;
+struct wl_surface;
+
+struct wl_connection *
+wl_connection_create(const char *address);
+void
+wl_connection_destroy(struct wl_connection *connection);
+int
+wl_connection_get_fd(struct wl_connection *connection);
+void
+wl_connection_iterate(struct wl_connection *connection);
+int
+wl_connection_flush(struct wl_connection *connection);
+
+struct wl_display *
+wl_connection_get_display(struct wl_connection *connection);
+struct wl_surface *
+wl_display_create_surface(struct wl_display *display);
+
+void
+wl_surface_attach(struct wl_surface *surface,
+                 uint32_t name, int width, int height, int stride);
+
+#endif
index 496f866..a9eaf49 100644 (file)
--- a/wayland.c
+++ b/wayland.c
 struct wl_region {
 };
 
+struct wl_buffer {
+       char data[4096];
+       int head, tail;
+};
+
 struct wl_client {
-       char protocol_buffer[4096];
+       struct wl_buffer input, output;
        struct wl_event_source *source;
        struct wl_display *display;
 };
@@ -25,6 +30,9 @@ struct wl_display {
        struct wl_object base;
        struct wl_event_loop *loop;
        struct wl_hash objects;
+
+       struct wl_compositor *compositor;
+       struct wl_compositor_interface *compositor_interface;
 };
 
 struct wl_surface {
@@ -46,29 +54,18 @@ struct wl_surface {
 };
 
 static void
-wl_surface_get_interface(struct wl_client *client,
-                        struct wl_surface *surface,
-                        const char *interface, uint32_t id)
+wl_surface_attach(struct wl_client *client,
+                 struct wl_surface *surface, uint32_t name, 
+                 uint32_t width, uint32_t height, uint32_t stride)
 {
-       /* client sends a new object id, and an interface identifier
-        * to name the object that implements the interface */
-       printf("surface::get_interface\n");
-}
+       struct wl_compositor_interface *interface;
 
-static const struct wl_argument get_interface_arguments[] = {
-       { WL_ARGUMENT_STRING },
-       { WL_ARGUMENT_NEW_ID }
-};
-
-static void
-wl_surface_post(struct wl_client *client,
-               struct wl_surface *surface, uint32_t name, 
-               uint32_t width, uint32_t height, uint32_t stride)
-{
-       printf("surface::post\n");
+       interface = client->display->compositor->interface;
+       interface->notify_surface_attach(client->display->compositor,
+                                        surface, name, width, height, stride);
 }
 
-static const struct wl_argument post_arguments[] = {
+static const struct wl_argument attach_arguments[] = {
        { WL_ARGUMENT_UINT32 },
        { WL_ARGUMENT_UINT32 },
        { WL_ARGUMENT_UINT32 },
@@ -89,10 +86,8 @@ static const struct wl_argument update_arguments[] = {
 };
 
 static const struct wl_method surface_methods[] = {
-       { "get_interface", wl_surface_get_interface,
-         ARRAY_LENGTH(get_interface_arguments), get_interface_arguments },
-       { "post", wl_surface_post,
-         ARRAY_LENGTH(post_arguments), post_arguments }
+       { "attach", wl_surface_attach,
+         ARRAY_LENGTH(attach_arguments), attach_arguments }
 };
 
 static const struct wl_interface surface_interface = {
@@ -105,6 +100,7 @@ struct wl_surface *
 wl_surface_create(struct wl_display *display, uint32_t id)
 {
        struct wl_surface *surface;
+       struct wl_compositor_interface *interface;
 
        surface = malloc(sizeof *surface);
        if (surface == NULL)
@@ -112,12 +108,9 @@ wl_surface_create(struct wl_display *display, uint32_t id)
 
        surface->base.id = id;
        surface->base.interface = &surface_interface;
-       /* doesn't create any pixel buffers, just the wl_surface
-        * object.  the client allocates and attaches pixel buffers
-        * itself and renders to them, then posts them using
-        * wl_window_post. */
 
-       /* add to display list */
+       interface = display->compositor->interface;
+       interface->notify_surface_create(display->compositor, surface);
 
        return surface;
 }
@@ -125,6 +118,28 @@ wl_surface_create(struct wl_display *display, uint32_t id)
 static void
 wl_client_data(int fd, uint32_t mask, void *data);
 
+static int
+advertise_object(struct wl_client *client, struct wl_object *object)
+{
+       const struct wl_interface *interface;
+       uint32_t length, *p;
+
+       interface = object->interface;
+       p = (uint32_t *) (client->output.data + client->output.head);
+       length = strlen(interface->name);
+       *p++ = object->id;
+       *p++ = length;
+       memcpy(p, interface->name, length);
+       memset((char *) p + length, 0, -length & 3);
+       client->output.head += 8 + ((length + 3) & ~3);
+
+       wl_event_loop_update_source(client->display->loop, client->source,
+                                   WL_EVENT_READABLE | WL_EVENT_WRITEABLE);
+
+
+       return 0;
+}
+
 struct wl_client *
 wl_client_create(struct wl_display *display, int fd)
 {
@@ -134,22 +149,13 @@ wl_client_create(struct wl_display *display, int fd)
        if (client == NULL)
                return NULL;
 
+       memset(client, 0, sizeof *client);
        client->display = display;
-
        client->source = wl_event_loop_add_fd(display->loop, fd,
-                                             WL_EVENT_READABLE |
                                              WL_EVENT_READABLE,
                                              wl_client_data, client);
 
-       printf("new client: %p\n", client);
-
-       /* Send global objects to client in hand shake response, eg:
-        *
-        *   display: 0,
-        *   glyph_cache: 1
-        *   some other extension global object: 2
-        *
-        * etc */
+       advertise_object(client, &display->base);
 
        return client;
 }
@@ -164,7 +170,7 @@ wl_client_destroy(struct wl_client *client)
 
 static void
 demarshal(struct wl_client *client, struct wl_object *target,
-         const struct wl_method *method, uint32_t *data, int len)
+         const struct wl_method *method, uint32_t *data)
 {
        ffi_type *types[10];
        ffi_cif cif;
@@ -198,11 +204,9 @@ demarshal(struct wl_client *client, struct wl_object *target,
                case WL_ARGUMENT_UINT32:
                        types[i + 2] = &ffi_type_uint32;
                        values[i + 2].uint32 = *p;
-                       printf("got uint32 (%d)\n", *p);
                        p++;
                        break;
                case WL_ARGUMENT_STRING:
-                       printf("got string\n");
                        types[i + 2] = &ffi_type_pointer;
                        /* FIXME */
                        values[i + 2].uint32 = *p++;
@@ -215,7 +219,6 @@ demarshal(struct wl_client *client, struct wl_object *target,
                        if (object->interface != method->arguments[i].data)
                                printf("wrong object type\n");
                        values[i + 2].object = object;
-                       printf("got object (%d)\n", *p);
                        p++;
                        break;
                case WL_ARGUMENT_NEW_ID:
@@ -224,7 +227,6 @@ demarshal(struct wl_client *client, struct wl_object *target,
                        object = wl_hash_lookup(&client->display->objects, *p);
                        if (object != NULL)
                                printf("object already exists (%d)\n", *p);
-                       printf("got new_id (%d)\n", *p);
                        p++;
                        break;
                default:
@@ -240,74 +242,106 @@ demarshal(struct wl_client *client, struct wl_object *target,
 }
 
 static void
+wl_client_event(struct wl_client *client, struct wl_object *object, uint32_t event)
+{
+       const struct wl_interface *interface;
+       uint32_t *p;
+
+       interface = object->interface;
+
+       p = (void *) client->output.data + client->output.head;
+       p[0] = object->id;
+       p[1] = event | (8 << 16);
+       client->output.head += 8;
+       wl_event_loop_update_source(client->display->loop, client->source,
+                                   WL_EVENT_READABLE | WL_EVENT_WRITEABLE);
+}
+
+#define WL_DISPLAY_INVALID_OBJECT 0
+#define WL_DISPLAY_INVALID_METHOD 1
+
+static void
+wl_client_process_input(struct wl_client *client)
+{
+       const struct wl_method *method;
+       struct wl_object *object;
+       uint32_t *p, opcode, size;
+
+       while (1) {
+               if (client->input.head - client->input.tail < 2 * sizeof *p)
+                       break;
+
+               p = (uint32_t *) (client->input.data + client->input.tail);
+               opcode = p[1] & 0xffff;
+               size = p[1] >> 16;
+               if (client->input.head - client->input.tail < size)
+                       break;
+
+               object = wl_hash_lookup(&client->display->objects,
+                                       p[0]);
+               if (object == NULL) {
+                       wl_client_event(client, &client->display->base,
+                                       WL_DISPLAY_INVALID_OBJECT);
+                       client->input.tail += size;
+                       continue;
+               }
+                               
+               if (opcode >= object->interface->method_count) {
+                       wl_client_event(client, &client->display->base,
+                                       WL_DISPLAY_INVALID_METHOD);
+                       client->input.tail += size;
+                       continue;
+               }
+                               
+               method = &object->interface->methods[opcode];
+               demarshal(client, object, method, p + 2);
+               client->input.tail += size;
+       }
+}
+
+static void
 wl_client_data(int fd, uint32_t mask, void *data)
 {
        struct wl_client *client = data;
-       struct wl_object *object;
-       const struct wl_method *method;
-       char buffer[256];
-       uint32_t *p;
        int len;
 
        if (mask & WL_EVENT_READABLE) {
-               len = read(fd, buffer, sizeof buffer);
-               if (len == 0) {
-                       wl_client_destroy(client);
+               len = read(fd, client->input.data, sizeof client->input.data);
+               if (len > 0) {
+                       client->input.head += len;
+                       wl_client_process_input(client);
+               } else if (len == 0 && errno == ECONNRESET) {
+                       fprintf(stderr,
+                               "read from client %p: %m (%d)\n",
+                               client, errno);
                } else {
-                       printf("got %d bytes from client %p\n",
-                              len, client);
-                       if (len < 2 * sizeof *p)
-                               /* read more... */
-                               return;
-
-                       p = (uint32_t *) buffer;
-                       object = wl_hash_lookup(&client->display->objects,
-                                               p[0]);
-                       if (object == NULL) {
-                               /* send error */
-                               printf("invalid object\n");
-                               return;
-                       }
-
-                       if (p[1] >= object->interface->method_count) {
-                               /* send error */
-                               printf("invalid method\n");
-                               return;
-                       }
-
-                       method = &object->interface->methods[p[1]];
-                       printf("calling method %s on interface %s\n",
-                              method->name, object->interface->name);
-                       demarshal(client, object, method, p + 2, len - 8);
+                               wl_client_destroy(client);
                }
        }
 
        if (mask & WL_EVENT_WRITEABLE) {
+               len = write(fd, client->output.data + client->output.tail,
+                           client->output.head - client->output.tail);
+               client->output.tail += len;
+
+               if (client->output.tail == client->output.head)
+                       wl_event_loop_update_source(client->display->loop,
+                                                   client->source,
+                                                   WL_EVENT_READABLE);
        }
 }
 
-static void
-wl_display_get_interface(struct wl_client *client,
-                        struct wl_display *display,
-                        const char *interface, uint32_t id)
-{
-       /* client sends a new object id, and an interface identifier
-        * to name the object that implements the interface */
-       printf("display::get_interface\n");
-}
-
 static int
 wl_display_create_surface(struct wl_client *client,
                          struct wl_display *display, uint32_t id)
 {
        struct wl_surface *surface;
 
-       printf("display::create_surface, client %p, display %p, new_id=%d\n",
-              client, display, id);
-
        surface = wl_surface_create(display, id);
        wl_hash_insert(&display->objects, &surface->base);
 
+       /* FIXME: garbage collect client resources when client exits. */
+
        return 0;
 }
 
@@ -316,14 +350,19 @@ static const struct wl_argument create_surface_arguments[] = {
 };
 
 static const struct wl_method display_methods[] = {
-       { "get_interface", wl_display_get_interface,
-         ARRAY_LENGTH(get_interface_arguments), get_interface_arguments },
        { "create_surface", wl_display_create_surface,
          ARRAY_LENGTH(create_surface_arguments), create_surface_arguments },
 };
 
+static const struct wl_event display_events[] = {
+       { "invalid_object" },
+       { "invalid_method" },
+};
+
 static const struct wl_interface display_interface = {
-       "display", 1, ARRAY_LENGTH(display_methods), display_methods,
+       "display", 1,
+       ARRAY_LENGTH(display_methods), display_methods,
+       ARRAY_LENGTH(display_events), display_events,
 };
 
 struct wl_display *
@@ -349,6 +388,13 @@ wl_display_create(void)
 }
 
 void
+wl_display_set_compositor(struct wl_display *display,
+                         struct wl_compositor *compositor)
+{
+       display->compositor = compositor;
+}
+
+void
 wl_display_run(struct wl_display *display)
 {
        while (1)
@@ -397,7 +443,8 @@ wl_display_add_socket(struct wl_display *display)
        if (listen(sock, 1) < 0)
                return -1;
 
-       wl_event_loop_add_fd(display->loop, sock, WL_EVENT_READABLE,
+       wl_event_loop_add_fd(display->loop, sock,
+                            WL_EVENT_READABLE,
                             socket_data, display);
 
        return 0;
@@ -407,6 +454,7 @@ wl_display_add_socket(struct wl_display *display)
 int main(int argc, char *argv[])
 {
        struct wl_display *display;
+       struct wl_compositor *compositor;
 
        display = wl_display_create();
 
@@ -415,6 +463,9 @@ int main(int argc, char *argv[])
                exit(EXIT_FAILURE);
        }
 
+       compositor = wl_compositor_create();
+       wl_display_set_compositor(display, compositor);
+
        printf("wayland online, display is %p\n", display);
 
        wl_display_run(display);
index 409e3a3..c95fc57 100644 (file)
--- a/wayland.h
+++ b/wayland.h
@@ -18,6 +18,10 @@ struct wl_event_source *wl_event_loop_add_fd(struct wl_event_loop *loop,
                                             int fd, uint32_t mask,
                                             wl_event_loop_func_t func,
                                             void *data);
+int wl_event_loop_update_source(struct wl_event_loop *loop,
+                               struct wl_event_source *source,
+                               uint32_t mask);
+
 int wl_event_loop_remove_source(struct wl_event_loop *loop,
                                struct wl_event_source *source);
 int wl_event_loop_wait(struct wl_event_loop *loop);
@@ -52,11 +56,17 @@ struct wl_method {
        const struct wl_argument *arguments;
 };
 
+struct wl_event {
+       const char *name;
+};
+
 struct wl_interface {
        const char *name;
        int version;
        int method_count;
        const struct wl_method *methods;
+       int event_count;
+       const struct wl_event *events;
 };
 
 struct wl_object {
@@ -64,4 +74,26 @@ struct wl_object {
        uint32_t id;
 };
 
+struct wl_surface;
+struct wl_display;
+
+struct wl_compositor {
+       struct wl_compositor_interface *interface;
+};
+
+struct wl_compositor_interface {
+       void (*notify_surface_create)(struct wl_compositor *compositor,
+                                     struct wl_surface *surface);
+                                  
+       void (*notify_surface_attach)(struct wl_compositor *compositor,
+                                     struct wl_surface *surface, uint32_t name, 
+                                     uint32_t width, uint32_t height, uint32_t stride);
+};
+
+struct wl_compositor *wl_compositor_create(void);
+
+void wl_display_set_compositor(struct wl_display *display,
+                              struct wl_compositor *compositor);
+
+
 #endif