1package server
2
3import "sync"
4
5// Message describes one request or event. Signature uses the libwayland
6// format ("n", "?oii", "2u", ...). Types holds one entry per argument, and may
7// be shorter than the argument list; entries are non-nil only for typed
8// new_id and object arguments.
9type Message struct {
10 Name string
11 Signature string
12 Types []*Interface
13}
14
15// Interface describes a protocol interface. Build its C table once with
16// NewInterfaces; the table lives for the process lifetime.
17type Interface struct {
18 Name string
19 Version int32
20 Requests []Message
21 Events []Message
22
23 c uintptr // struct wl_interface*
24}
25
26// C layout (LP64):
27//
28// struct wl_message { const char *name; const char *signature; const struct wl_interface **types; } // 24 bytes
29// struct wl_interface { const char *name; int version; int method_count; const struct wl_message *methods;
30// int event_count; const struct wl_message *events; } // 40 bytes
31const (
32 sizeMessage = 24
33 sizeInterface = 40
34)
35
36var (
37 tablesMu sync.Mutex
38 tables arena
39)
40
41// NewInterfaces writes C tables for a set of interfaces that may reference
42// each other. Interfaces from other sets must already be built. It does not
43// load libwayland, so generated packages call it from init.
44func NewInterfaces(ifaces ...*Interface) error {
45 tablesMu.Lock()
46 defer tablesMu.Unlock()
47 if err := tables.reserve(tableSize(ifaces)); err != nil {
48 return err
49 }
50 a := &tables
51 // Pass 1: reserve every wl_interface so cross references resolve.
52 offs := make([]int, len(ifaces))
53 for i, it := range ifaces {
54 offs[i], it.c = a.alloc(sizeInterface)
55 }
56 // Pass 2: fill messages and headers.
57 for i, it := range ifaces {
58 o := offs[i]
59 a.putPtr(o, a.cstring(it.Name))
60 a.putI32(o+8, it.Version)
61 a.putI32(o+12, int32(len(it.Requests)))
62 a.putPtr(o+16, writeMessages(a, it.Requests))
63 a.putI32(o+24, int32(len(it.Events)))
64 a.putPtr(o+32, writeMessages(a, it.Events))
65 }
66 return nil
67}
68
69// tableSize is an upper bound of the arena bytes NewInterfaces uses,
70// counting 7 bytes of alignment padding per allocation.
71func tableSize(ifaces []*Interface) int {
72 n := 0
73 for _, it := range ifaces {
74 n += sizeInterface + 7 + len(it.Name) + 1 + 7
75 for _, msgs := range [][]Message{it.Requests, it.Events} {
76 n += sizeMessage*len(msgs) + 7
77 for _, m := range msgs {
78 n += len(m.Name) + 1 + 7 + len(m.Signature) + 1 + 7 + 8*argCount(m.Signature) + 7
79 }
80 }
81 }
82 return n
83}
84
85// writeMessages writes a wl_message array. Every message with arguments gets
86// a types array, because libwayland reads it for each object and new_id
87// argument even when the interface is unknown.
88func writeMessages(a *arena, msgs []Message) uintptr {
89 if len(msgs) == 0 {
90 return 0
91 }
92 off, addr := a.alloc(sizeMessage * len(msgs))
93 for i, m := range msgs {
94 var types uintptr
95 if n := argCount(m.Signature); n > 0 {
96 if len(m.Types) > n {
97 panic("purego-libwayland: " + m.Name + " has more types than arguments")
98 }
99 toff, taddr := a.alloc(8 * n)
100 for j, t := range m.Types {
101 if t != nil {
102 if t.c == 0 {
103 panic("purego-libwayland: interface " + t.Name + " referenced before NewInterfaces")
104 }
105 a.putPtr(toff+8*j, t.c)
106 }
107 }
108 types = taddr
109 }
110 mo := off + i*sizeMessage
111 a.putPtr(mo, a.cstring(m.Name))
112 a.putPtr(mo+8, a.cstring(m.Signature))
113 a.putPtr(mo+16, types)
114 }
115 return addr
116}