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