1package server
2
3import (
4 "fmt"
5 "sync"
6 "unsafe"
7
8 "github.com/bnema/purego"
9 "golang.org/x/sys/unix"
10)
11
12// libwayland-server entry points. Only non-varargs functions are bound.
13var (
14 wlDisplayCreate func() uintptr
15 wlDisplayDestroy func(display uintptr)
16 wlDisplayDestroyClients func(display uintptr)
17 wlDisplayAddSocketFD func(display uintptr, fd int32) int32
18 wlDisplayGetEventLoop func(display uintptr) uintptr
19 wlEventLoopGetFD func(loop uintptr) int32
20 wlEventLoopAddFD func(loop uintptr, fd int32, mask uint32, fn, data uintptr) uintptr
21 wlEventSourceRemove func(source uintptr) int32
22 wlGlobalCreate func(display, iface uintptr, version int32, data uintptr, bind uintptr) uintptr
23 wlGlobalRemove func(global uintptr)
24 wlResourcePostError func(resource uintptr, code uint32, msg uintptr)
25)
26
27var (
28 loadOnce sync.Once
29 loadErr error
30
31 symDisplayFlushClients, symEventLoopDispatch, symResourceCreate uintptr
32 symResourceSetDispatcher, symResourcePostEventArr, symResourceDestroy, symResourceGetID uintptr
33 symClientGetCredentials, symClientGetFD, symClientCreate uintptr
34 symClientAddDestroyListener, symListRemove uintptr
35 symErrnoLocation uintptr
36 symDisplaySetGlobalFilter, symEventSourceFDUpdate uintptr
37 // wl_fixes support (libwayland 1.26+); zero on older libraries.
38 symFixesAckGlobalRemove uintptr
39
40 // Shared C callbacks. purego callbacks are never freed, so there is one
41 // of each for the whole process.
42 cbDispatcher uintptr
43 cbDestroy uintptr
44 cbClientGone uintptr
45 cbBind uintptr
46 cbWake uintptr
47 cbFD uintptr
48 cbGlobalFilter uintptr
49)
50
51func load() error {
52 loadOnce.Do(func() {
53 lib, err := purego.Dlopen("libwayland-server.so.0", purego.RTLD_NOW|purego.RTLD_GLOBAL)
54 if err != nil {
55 loadErr = fmt.Errorf("purego-libwayland: open libwayland-server.so.0: %w", err)
56 return
57 }
58 defer func() {
59 if r := recover(); r != nil {
60 loadErr = fmt.Errorf("purego-libwayland: %v", r)
61 }
62 }()
63 reg := func(fptr any, name string) { purego.RegisterLibFunc(fptr, lib, name) }
64 reg(&wlDisplayCreate, "wl_display_create")
65 reg(&wlDisplayDestroy, "wl_display_destroy")
66 reg(&wlDisplayDestroyClients, "wl_display_destroy_clients")
67 reg(&wlDisplayAddSocketFD, "wl_display_add_socket_fd")
68 reg(&wlDisplayGetEventLoop, "wl_display_get_event_loop")
69 reg(&wlEventLoopGetFD, "wl_event_loop_get_fd")
70 reg(&wlEventLoopAddFD, "wl_event_loop_add_fd")
71 reg(&wlEventSourceRemove, "wl_event_source_remove")
72 reg(&wlGlobalCreate, "wl_global_create")
73 reg(&wlGlobalRemove, "wl_global_remove")
74 reg(&wlResourcePostError, "wl_resource_post_error")
75 for _, entry := range []struct {
76 name string
77 sym *uintptr
78 }{
79 {"wl_display_set_global_filter", &symDisplaySetGlobalFilter},
80 {"wl_event_source_fd_update", &symEventSourceFDUpdate},
81 {"wl_display_flush_clients", &symDisplayFlushClients},
82 {"wl_event_loop_dispatch", &symEventLoopDispatch},
83 {"wl_resource_create", &symResourceCreate},
84 {"wl_resource_get_id", &symResourceGetID},
85 {"wl_client_get_credentials", &symClientGetCredentials},
86 {"wl_client_get_fd", &symClientGetFD},
87 {"wl_client_create", &symClientCreate},
88 {"wl_client_add_destroy_listener", &symClientAddDestroyListener},
89 {"wl_list_remove", &symListRemove},
90 {"wl_resource_set_dispatcher", &symResourceSetDispatcher},
91 {"wl_resource_post_event_array", &symResourcePostEventArr},
92 {"wl_resource_destroy", &symResourceDestroy},
93 } {
94 *entry.sym, err = purego.Dlsym(lib, entry.name)
95 if err != nil {
96 loadErr = fmt.Errorf("purego-libwayland: resolve %s: %w", entry.name, err)
97 return
98 }
99 }
100 symFixesAckGlobalRemove, _ = purego.Dlsym(lib, "wl_fixes_handle_ack_global_remove")
101 symErrnoLocation, err = purego.Dlsym(lib, "__errno_location")
102 if err != nil {
103 libc, openErr := purego.Dlopen("libc.so.6", purego.RTLD_NOW|purego.RTLD_GLOBAL)
104 if openErr != nil {
105 loadErr = fmt.Errorf("purego-libwayland: open libc.so.6: %w", openErr)
106 return
107 }
108 symErrnoLocation, err = purego.Dlsym(libc, "__errno_location")
109 if err != nil {
110 loadErr = fmt.Errorf("purego-libwayland: resolve __errno_location: %w", err)
111 return
112 }
113 }
114
115 cbDispatcher = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
116 // C memory owned by libwayland; reinterpret pointer bits without uintptr-to-pointer conversion.
117 msg, args := a.Int(3), a.Int(4)
118 return uintptr(uint32(dispatch(a.Int(0), a.Int(1), uint32(a.Int(2)), *(*unsafe.Pointer)(unsafe.Pointer(&msg)), *(*unsafe.Pointer)(unsafe.Pointer(&args)))))
119 })
120 cbDestroy = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
121 destroyed(a.Int(0))
122 return 0
123 })
124 cbClientGone = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
125 clientGone(a.Int(0))
126 return 0
127 })
128 cbBind = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
129 bind(a.Int(0), a.Int(1), uint32(a.Int(2)), uint32(a.Int(3)))
130 return 0
131 })
132 cbWake = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
133 return uintptr(uint32(wake(int32(a.Int(0)), uint32(a.Int(1)), a.Int(2))))
134 })
135 cbGlobalFilter = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr { return uintptr(globalFilter(a.Int(0), a.Int(1), a.Int(2))) })
136 cbFD = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
137 return uintptr(uint32(fdReadable(int32(a.Int(0)), uint32(a.Int(1)), a.Int(2))))
138 })
139 })
140 return loadErr
141}
142
143// Integer and pointer-only entry points avoid RegisterLibFunc's reflective call path.
144func wlDisplayFlushClients(display uintptr) {
145 purego.Syscall6(symDisplayFlushClients, display, 0, 0, 0, 0, 0)
146}
147func wlEventLoopDispatch(loop uintptr, timeout int32) int32 {
148 r, _, _ := purego.Syscall6(symEventLoopDispatch, loop, uintptr(timeout), 0, 0, 0, 0)
149 return int32(r)
150}
151func lastErrno() unix.Errno {
152 p, _, _ := purego.Syscall6(symErrnoLocation, 0, 0, 0, 0, 0, 0)
153 // C memory owned by libc; reinterpret pointer bits without uintptr-to-pointer conversion.
154 return unix.Errno(**(**int32)(unsafe.Pointer(&p)))
155}
156func wlResourceCreate(client, iface uintptr, version int32, id uint32) uintptr {
157 r, _, _ := purego.Syscall6(symResourceCreate, client, iface, uintptr(version), uintptr(id), 0, 0)
158 return r
159}
160func wlResourceGetID(resource uintptr) uint32 {
161 r, _, _ := purego.Syscall6(symResourceGetID, resource, 0, 0, 0, 0, 0)
162 return uint32(r)
163}
164func wlResourceSetDispatcher(resource, dispatcher, impl, data, destroy uintptr) {
165 purego.Syscall6(symResourceSetDispatcher, resource, dispatcher, impl, data, destroy, 0)
166}
167func wlResourcePostEventArr(resource uintptr, opcode uint32, args unsafe.Pointer) {
168 purego.Syscall6(symResourcePostEventArr, resource, uintptr(opcode), uintptr(args), 0, 0, 0)
169}
170func wlResourceDestroy(resource uintptr) {
171 purego.Syscall6(symResourceDestroy, resource, 0, 0, 0, 0, 0)
172}
173func wlFixesHandleAckGlobalRemove(fixes, registry uintptr, name uint32) {
174 purego.Syscall6(symFixesAckGlobalRemove, fixes, registry, uintptr(name), 0, 0, 0)
175}
176
177func wlDisplaySetGlobalFilter(display, callback, data uintptr) {
178 purego.Syscall6(symDisplaySetGlobalFilter, display, callback, data, 0, 0, 0)
179}
180func wlEventSourceFDUpdate(source uintptr, mask uint32) int32 {
181 r, _, _ := purego.Syscall6(symEventSourceFDUpdate, source, uintptr(mask), 0, 0, 0, 0)
182 return int32(r)
183}