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