From: Kristian Høgsberg Date: Tue, 7 Oct 2008 14:10:36 +0000 (-0400) Subject: Bunch of new stuff: simple compositor, cairo+gem use in client, events. X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a67a71a7cf2f6cf17cb10eeeb4e9bb867bb9eb40;p=platform%2Fupstream%2Fweston.git Bunch of new stuff: simple compositor, cairo+gem use in client, events. --- diff --git a/Makefile b/Makefile index 661cd6f..44a0b04 100644 --- 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 --- 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 diff --git a/client.c b/client.c index 8312be8..d01ed2c 100644 --- a/client.c +++ b/client.c @@ -1,109 +1,135 @@ -#include #include -#include #include -#include +#include #include +#include +#include +#include #include -#include -#include -#include +#include +#include + +#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 /:\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 index 0000000..711c42f --- /dev/null +++ b/compositor.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/event-loop.c b/event-loop.c index 2946a64..96b5680 100644 --- a/event-loop.c +++ b/event-loop.c @@ -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 index 0000000..2e5bd18 --- /dev/null +++ b/wayland-client.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..ae62bca --- /dev/null +++ b/wayland-client.h @@ -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 diff --git a/wayland.c b/wayland.c index 496f866..a9eaf49 100644 --- a/wayland.c +++ b/wayland.c @@ -15,8 +15,13 @@ 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); diff --git a/wayland.h b/wayland.h index 409e3a3..c95fc57 100644 --- 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