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