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