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 wlClientGetCredentials func(client uintptr, pid, uid, gid unsafe.Pointer)
26)
27
28var (
29 loadOnce sync.Once
30 loadErr error
31
32 symDisplayFlushClients, symEventLoopDispatch, symResourceCreate uintptr
33 symResourceSetDispatcher, symResourcePostEventArr, symResourceDestroy, symResourceGetID uintptr
34 symErrnoLocation uintptr
35
36 // Shared C callbacks. purego callbacks are never freed, so there is one
37 // of each for the whole process.
38 cbDispatcher uintptr
39 cbDestroy uintptr
40 cbBind uintptr
41 cbWake uintptr
42)
43
44func load() error {
45 loadOnce.Do(func() {
46 lib, err := purego.Dlopen("libwayland-server.so.0", purego.RTLD_NOW|purego.RTLD_GLOBAL)
47 if err != nil {
48 loadErr = fmt.Errorf("purego-libwayland: open libwayland-server.so.0: %w", err)
49 return
50 }
51 defer func() {
52 if r := recover(); r != nil {
53 loadErr = fmt.Errorf("purego-libwayland: %v", r)
54 }
55 }()
56 reg := func(fptr any, name string) { purego.RegisterLibFunc(fptr, lib, name) }
57 reg(&wlDisplayCreate, "wl_display_create")
58 reg(&wlDisplayDestroy, "wl_display_destroy")
59 reg(&wlDisplayDestroyClients, "wl_display_destroy_clients")
60 reg(&wlDisplayAddSocketFD, "wl_display_add_socket_fd")
61 reg(&wlDisplayGetEventLoop, "wl_display_get_event_loop")
62 reg(&wlEventLoopGetFD, "wl_event_loop_get_fd")
63 reg(&wlEventLoopAddFD, "wl_event_loop_add_fd")
64 reg(&wlEventSourceRemove, "wl_event_source_remove")
65 reg(&wlGlobalCreate, "wl_global_create")
66 reg(&wlGlobalRemove, "wl_global_remove")
67 reg(&wlResourcePostError, "wl_resource_post_error")
68 reg(&wlClientGetCredentials, "wl_client_get_credentials")
69 for _, entry := range []struct {
70 name string
71 sym *uintptr
72 }{
73 {"wl_display_flush_clients", &symDisplayFlushClients},
74 {"wl_event_loop_dispatch", &symEventLoopDispatch},
75 {"wl_resource_create", &symResourceCreate},
76 {"wl_resource_get_id", &symResourceGetID},
77 {"wl_resource_set_dispatcher", &symResourceSetDispatcher},
78 {"wl_resource_post_event_array", &symResourcePostEventArr},
79 {"wl_resource_destroy", &symResourceDestroy},
80 } {
81 *entry.sym, err = purego.Dlsym(lib, entry.name)
82 if err != nil {
83 loadErr = fmt.Errorf("purego-libwayland: resolve %s: %w", entry.name, err)
84 return
85 }
86 }
87 symErrnoLocation, err = purego.Dlsym(lib, "__errno_location")
88 if err != nil {
89 libc, openErr := purego.Dlopen("libc.so.6", purego.RTLD_NOW|purego.RTLD_GLOBAL)
90 if openErr != nil {
91 loadErr = fmt.Errorf("purego-libwayland: open libc.so.6: %w", openErr)
92 return
93 }
94 symErrnoLocation, err = purego.Dlsym(libc, "__errno_location")
95 if err != nil {
96 loadErr = fmt.Errorf("purego-libwayland: resolve __errno_location: %w", err)
97 return
98 }
99 }
100
101 cbDispatcher = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
102 // C memory owned by libwayland; reinterpret pointer bits without uintptr-to-pointer conversion.
103 msg, args := a.Int(3), a.Int(4)
104 return uintptr(uint32(dispatch(a.Int(0), a.Int(1), uint32(a.Int(2)), *(*unsafe.Pointer)(unsafe.Pointer(&msg)), *(*unsafe.Pointer)(unsafe.Pointer(&args)))))
105 })
106 cbDestroy = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
107 destroyed(a.Int(0))
108 return 0
109 })
110 cbBind = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
111 bind(a.Int(0), a.Int(1), uint32(a.Int(2)), uint32(a.Int(3)))
112 return 0
113 })
114 cbWake = purego.NewCallbackInts(func(a *purego.CallbackArgs) uintptr {
115 return uintptr(uint32(wake(int32(a.Int(0)), uint32(a.Int(1)), a.Int(2))))
116 })
117 })
118 return loadErr
119}
120
121// Integer and pointer-only entry points avoid RegisterLibFunc's reflective call path.
122func wlDisplayFlushClients(display uintptr) {
123 purego.Syscall6(symDisplayFlushClients, display, 0, 0, 0, 0, 0)
124}
125func wlEventLoopDispatch(loop uintptr, timeout int32) int32 {
126 r, _, _ := purego.Syscall6(symEventLoopDispatch, loop, uintptr(timeout), 0, 0, 0, 0)
127 return int32(r)
128}
129func lastErrno() unix.Errno {
130 p, _, _ := purego.Syscall6(symErrnoLocation, 0, 0, 0, 0, 0, 0)
131 // C memory owned by libc; reinterpret pointer bits without uintptr-to-pointer conversion.
132 return unix.Errno(**(**int32)(unsafe.Pointer(&p)))
133}
134func wlResourceCreate(client, iface uintptr, version int32, id uint32) uintptr {
135 r, _, _ := purego.Syscall6(symResourceCreate, client, iface, uintptr(version), uintptr(id), 0, 0)
136 return r
137}
138func wlResourceGetID(resource uintptr) uint32 {
139 r, _, _ := purego.Syscall6(symResourceGetID, resource, 0, 0, 0, 0, 0)
140 return uint32(r)
141}
142func wlResourceSetDispatcher(resource, dispatcher, impl, data, destroy uintptr) {
143 purego.Syscall6(symResourceSetDispatcher, resource, dispatcher, impl, data, destroy, 0)
144}
145func wlResourcePostEventArr(resource uintptr, opcode uint32, args unsafe.Pointer) {
146 purego.Syscall6(symResourcePostEventArr, resource, uintptr(opcode), uintptr(args), 0, 0, 0)
147}
148func wlResourceDestroy(resource uintptr) {
149 purego.Syscall6(symResourceDestroy, resource, 0, 0, 0, 0, 0)
150}