diff --git a/protocol/wayland.dtd b/protocol/wayland.dtd index ee062eea..6a9c55b5 100644 --- a/protocol/wayland.dtd +++ b/protocol/wayland.dtd @@ -28,5 +28,7 @@ + + diff --git a/protocol/wayland.xml b/protocol/wayland.xml index 31daa748..26d3dea3 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -2455,7 +2455,8 @@ - + diff --git a/src/connection.c b/src/connection.c index b89166fb..56922fd9 100644 --- a/src/connection.c +++ b/src/connection.c @@ -435,10 +435,28 @@ wl_connection_put_fd(struct wl_connection *connection, int32_t fd) return ring_buffer_put(&connection->fds_out, &fd, sizeof fd); } +static const char * +parse_array_signature(const char *signature, struct argument_details *details) +{ + details->type = *signature; + if (signature[1] == '[') { + details->element_type = signature[2]; + signature += 3; + while (*signature != ']') { + details->element_bits *= 10; + details->element_bits += (*signature) - '0'; + ++signature; + } + } + return signature + 1; +} + const char * get_next_argument(const char *signature, struct argument_details *details) { details->nullable = 0; + details->element_type = '\0'; + details->element_bits = 0; for(; *signature; ++signature) { switch(*signature) { case 'i': @@ -447,10 +465,11 @@ get_next_argument(const char *signature, struct argument_details *details) case 's': case 'o': case 'n': - case 'a': case 'h': details->type = *signature; return signature + 1; + case 'a': + return parse_array_signature(signature, details); case '?': details->nullable = 1; } @@ -471,9 +490,14 @@ arg_count_for_signature(const char *signature) case 's': case 'o': case 'n': - case 'a': case 'h': ++count; + break; + case 'a': + ++count; + if (signature[1] == '[') + while (*signature != ']') ++signature; + break; } } return count; @@ -1281,6 +1305,86 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection) return result; } +static void +wl_closure_print_array(FILE *f, struct argument_details *arg, + struct wl_array *array) +{ + int8_t *i8; + int16_t *i16; + int32_t *i32; + uint8_t *u8; + uint16_t *u16; + uint32_t *u32; + + fprintf(f, "array["); + switch (arg->element_type) { + case 'i': + switch (arg->element_bits) { + case 8: + wl_array_for_each(i8, array) { + if (i8 == array->data) + fprintf(f, "%"PRId8, *i8); + else + fprintf(f, ",%"PRId8, *i8); + } + break; + case 16: + wl_array_for_each(i16, array) { + if (i16 == array->data) + fprintf(f, "%"PRId16, *i16); + else + fprintf(f, ",%"PRId16, *i16); + } + break; + case 32: + wl_array_for_each(i32, array) { + if (i32 == array->data) + fprintf(f, "%"PRId32, *i32); + else + fprintf(f, ",%"PRId32, *i32); + } + break; + default: + abort(); + } + break; + case 'u': + switch (arg->element_bits) { + case 8: + wl_array_for_each(u8, array) { + if (u8 == array->data) + fprintf(f, "%"PRIu8, *u8); + else + fprintf(f, ",%"PRIu8, *u8); + } + break; + case 16: + wl_array_for_each(u16, array) { + if (u16 == array->data) + fprintf(f, "%"PRIu16, *u16); + else + fprintf(f, ",%"PRIu16, *u16); + } + break; + case 32: + wl_array_for_each(u32, array) { + if (u32 == array->data) + fprintf(f, "%"PRIu32, *u32); + else + fprintf(f, ",%"PRIu32, *u32); + } + break; + default: + abort(); + } + break; + case '\0': + fprintf(f, "size: %zu", array->size); + break; + } + fprintf(f, "]"); +} + void wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg), @@ -1369,7 +1473,7 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, fprintf(f, "nil"); break; case 'a': - fprintf(f, "array[%zu]", closure->args[i].a->size); + wl_closure_print_array(f, &arg, closure->args[i].a); break; case 'h': fprintf(f, "fd %d", closure->args[i].h); diff --git a/src/scanner.c b/src/scanner.c index c512d231..6617beb0 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -209,6 +209,12 @@ enum arg_type { FD }; +enum element_type { + ELEMENT_TYPE_NONE, + ELEMENT_TYPE_INT, + ELEMENT_TYPE_UNSIGNED, +}; + struct arg { char *name; enum arg_type type; @@ -217,6 +223,8 @@ struct arg { struct wl_list link; char *summary; char *enumeration_name; + enum element_type element_type; + int element_bits; }; struct enumeration { @@ -716,7 +724,9 @@ start_element(void *data, const char *element_name, const char **atts) const char *allow_null = NULL; const char *enumeration_name = NULL; const char *bitfield = NULL; + const char *element_type = NULL; int i, version = 0; + int element_bits = 0; ctx->loc.line_number = XML_GetCurrentLineNumber(ctx->parser); for (i = 0; atts[i]; i += 2) { @@ -743,6 +753,23 @@ start_element(void *data, const char *element_name, const char **atts) enumeration_name = atts[i + 1]; if (strcmp(atts[i], "bitfield") == 0) bitfield = atts[i + 1]; + if (strcmp(atts[i], "element-type") == 0) + element_type = atts[i + 1]; + if (strcmp(atts[i], "element-bits") == 0) { + element_bits = strtouint(atts[i + 1]); + switch (element_bits) { + case 8: + case 16: + case 32: + /* Note: the wire protocol only guarantees 4 byte alignment + * of array data. Therefore we cannot allow 64 bit elements + * as they require 8 byte alignment on common targets. */ + break; + default: + fail(&ctx->loc, + "invalid element-bits value (%d)", atts[i + 1]); + } + } } ctx->character_data_length = 0; @@ -841,6 +868,21 @@ start_element(void *data, const char *element_name, const char **atts) else arg->enumeration_name = xstrdup(enumeration_name); + if (element_type == NULL) { + arg->element_type = ELEMENT_TYPE_NONE; + } else { + if (strcmp(element_type, "int") == 0) { + arg->element_type = ELEMENT_TYPE_INT; + } else if (strcmp(element_type, "uint") == 0) { + arg->element_type = ELEMENT_TYPE_UNSIGNED; + } else { + fail(&ctx->loc, + "invalid element type '%s', must be int or uint", + element_type); + } + } + arg->element_bits = element_bits; + if (summary) arg->summary = xstrdup(summary); @@ -942,6 +984,68 @@ find_enumeration(struct protocol *protocol, return NULL; } +static void +verify_argument_attributes(struct parse_context *ctx, + struct interface *interface, + struct arg *a) +{ + struct enumeration *e; + enum arg_type t; + + if (a->type == ARRAY) { + if ((a->element_type != ELEMENT_TYPE_NONE) != (a->element_bits != 0)) { + fail(&ctx->loc, + "if one of element-type or element-bits is specified " + "the other must be as well"); + } + } else { + if (a->element_type != ELEMENT_TYPE_NONE) { + fail(&ctx->loc, + "only args of type array may specify an element-type"); + } + if (a->element_bits != 0) { + fail(&ctx->loc, + "only args of type array may specify element-bits"); + } + } + + switch (a->element_type) { + case ELEMENT_TYPE_NONE: + t = a->type; + break; + case ELEMENT_TYPE_INT: + t = INT; + break; + case ELEMENT_TYPE_UNSIGNED: + t = UNSIGNED; + break; + default: + abort(); + } + + if (!a->enumeration_name) + return; + + e = find_enumeration(ctx->protocol, interface, + a->enumeration_name); + + switch (t) { + case INT: + if (e && e->bitfield) + fail(&ctx->loc, + "bitfield-style enum must only be referenced by uint"); + break; + case UNSIGNED: + break; + case ARRAY: + fail(&ctx->loc, + "array arg must specify element-type if enum is specified"); + default: + fail(&ctx->loc, + "enumeration-style argument has wrong type"); + } +} + static void verify_arguments(struct parse_context *ctx, struct interface *interface, @@ -952,27 +1056,7 @@ verify_arguments(struct parse_context *ctx, wl_list_for_each(m, messages, link) { struct arg *a; wl_list_for_each(a, &m->arg_list, link) { - struct enumeration *e; - - if (!a->enumeration_name) - continue; - - - e = find_enumeration(ctx->protocol, interface, - a->enumeration_name); - - switch (a->type) { - case INT: - if (e && e->bitfield) - fail(&ctx->loc, - "bitfield-style enum must only be referenced by uint"); - break; - case UNSIGNED: - break; - default: - fail(&ctx->loc, - "enumeration-style argument has wrong type"); - } + verify_argument_attributes(ctx, interface, a); } } @@ -1793,7 +1877,17 @@ emit_messages(const char *name, struct wl_list *message_list, printf("o"); break; case ARRAY: - printf("a"); + switch (a->element_type) { + case ELEMENT_TYPE_NONE: + printf("a"); + break; + case ELEMENT_TYPE_INT: + printf("a[i%d]", a->element_bits); + break; + case ELEMENT_TYPE_UNSIGNED: + printf("a[u%d]", a->element_bits); + break; + } break; case FD: printf("h"); diff --git a/src/wayland-private.h b/src/wayland-private.h index 6b506584..8b55cb03 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -151,6 +151,8 @@ struct wl_closure { struct argument_details { char type; int nullable; + char element_type; + int element_bits; }; const char * diff --git a/src/wayland-util.h b/src/wayland-util.h index c99069cc..eb638fdf 100644 --- a/src/wayland-util.h +++ b/src/wayland-util.h @@ -127,6 +127,8 @@ struct wl_object; * * `a`: array * * `h`: fd * * `?`: following argument (`o` or `s`) is nullable + * * `[i32]`: the preceding array argument has 32 bit signed integer elements + * * `[u8]`: the preceding array argument has 8 bit unsigned integer elements * * While demarshaling primitive arguments is straightforward, when demarshaling * messages containing `object` or `new_id` arguments, the protocol