detached cdeaf22

merge: client PID

commit
cdeaf221795b38f2abc6e8ee8d6ee559fa9d59e6
author
brice · · 2026-09-24 17:52 UTC
merge of
78bc258 + 29a3b98 · diff against the first parent
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +43 -0
M internal/e2e/feasibility_test.go +29 -0
@@ -2,6 +2,7 @@ package e2e_test
22
33 import (
44 "context"
5+ "os"
56 "os/exec"
67 "path/filepath"
78 "strings"
@@ -292,3 +293,31 @@ func (*surfaceHandler) SetBufferScale(*wayland.Surface, int32)
292293 func (*surfaceHandler) DamageBuffer(*wayland.Surface, int32, int32, int32, int32) {}
293294 func (*surfaceHandler) Offset(*wayland.Surface, int32, int32) {}
294295 func (*surfaceHandler) GetRelease(*wayland.Surface, uint32) {}
296+
297+// The client PID comes from the socket credentials; in-process clients share ours.
298+func TestClientPID(t *testing.T) {
299+ captured := make(chan *server.Resource, 1)
300+ socket, d, _ := startServer(t, captured)
301+ c, err := wlturbo.Connect(socket)
302+ if err != nil {
303+ t.Fatal(err)
304+ }
305+ defer c.Close()
306+ must(t, c.Roundtrip())
307+ g, ok := c.Registry().FindGlobal("wl_compositor")
308+ if !ok {
309+ t.Fatal("wl_compositor not advertised")
310+ }
311+ comp, err := c.Registry().BindID(g.Name, g.Interface, 1)
312+ must(t, err)
313+ must(t, c.SendRequest(comp, uint16(wayland.CompositorRequestCreateSurface), c.AllocateID()))
314+ must(t, c.Roundtrip())
315+ r := receive(t, captured)
316+ pid := make(chan int, 1)
317+ if !d.Do(func() { pid <- r.Client().PID() }) {
318+ t.Fatal("display stopped")
319+ }
320+ if got := <-pid; got != os.Getpid() {
321+ t.Fatalf("pid %d, want %d", got, os.Getpid())
322+ }
323+}
@@ -2,6 +2,7 @@ package e2e_test
2 2
3 import (3 import (
4 "context"4 "context"
5+ "os"
5 "os/exec"6 "os/exec"
6 "path/filepath"7 "path/filepath"
7 "strings"8 "strings"
@@ -292,3 +293,31 @@ func (*surfaceHandler) SetBufferScale(*wayland.Surface, int32)
292 func (*surfaceHandler) DamageBuffer(*wayland.Surface, int32, int32, int32, int32) {}293 func (*surfaceHandler) DamageBuffer(*wayland.Surface, int32, int32, int32, int32) {}
293 func (*surfaceHandler) Offset(*wayland.Surface, int32, int32) {}294 func (*surfaceHandler) Offset(*wayland.Surface, int32, int32) {}
294 func (*surfaceHandler) GetRelease(*wayland.Surface, uint32) {}295 func (*surfaceHandler) GetRelease(*wayland.Surface, uint32) {}
296+
297+// The client PID comes from the socket credentials; in-process clients share ours.
298+func TestClientPID(t *testing.T) {
299+ captured := make(chan *server.Resource, 1)
300+ socket, d, _ := startServer(t, captured)
301+ c, err := wlturbo.Connect(socket)
302+ if err != nil {
303+ t.Fatal(err)
304+ }
305+ defer c.Close()
306+ must(t, c.Roundtrip())
307+ g, ok := c.Registry().FindGlobal("wl_compositor")
308+ if !ok {
309+ t.Fatal("wl_compositor not advertised")
310+ }
311+ comp, err := c.Registry().BindID(g.Name, g.Interface, 1)
312+ must(t, err)
313+ must(t, c.SendRequest(comp, uint16(wayland.CompositorRequestCreateSurface), c.AllocateID()))
314+ must(t, c.Roundtrip())
315+ r := receive(t, captured)
316+ pid := make(chan int, 1)
317+ if !d.Do(func() { pid <- r.Client().PID() }) {
318+ t.Fatal("display stopped")
319+ }
320+ if got := <-pid; got != os.Getpid() {
321+ t.Fatalf("pid %d, want %d", got, os.Getpid())
322+ }
323+}
M server/display.go +12 -0
@@ -181,6 +181,18 @@ func (d *Display) Stopped() <-chan struct{} { return d.stopped }
181181 // LiveResources reports resources not yet destroyed. Display goroutine only.
182182 func LiveResources() int { return len(live.resources) }
183183
184+// PID is the process ID of the client, read from the socket credentials
185+// (SO_PEERCRED) when it connected. Zero for an invalid client.
186+// Display goroutine only.
187+func (c Client) PID() int {
188+ if c.c == 0 {
189+ return 0
190+ }
191+ var pid, uid, gid int32
192+ wlClientGetCredentials(c.c, unsafe.Pointer(&pid), unsafe.Pointer(&uid), unsafe.Pointer(&gid))
193+ return int(pid)
194+}
195+
184196 func (c Client) CreateResource(iface *Interface, version int32, id uint32, h Handler) (*Resource, error) {
185197 rc := wlResourceCreate(c.c, iface.c, version, id)
186198 if rc == 0 {
@@ -181,6 +181,18 @@ func (d *Display) Stopped() <-chan struct{} { return d.stopped }
181 // LiveResources reports resources not yet destroyed. Display goroutine only.181 // LiveResources reports resources not yet destroyed. Display goroutine only.
182 func LiveResources() int { return len(live.resources) }182 func LiveResources() int { return len(live.resources) }
183 183
184+// PID is the process ID of the client, read from the socket credentials
185+// (SO_PEERCRED) when it connected. Zero for an invalid client.
186+// Display goroutine only.
187+func (c Client) PID() int {
188+ if c.c == 0 {
189+ return 0
190+ }
191+ var pid, uid, gid int32
192+ wlClientGetCredentials(c.c, unsafe.Pointer(&pid), unsafe.Pointer(&uid), unsafe.Pointer(&gid))
193+ return int(pid)
194+}
195+
184 func (c Client) CreateResource(iface *Interface, version int32, id uint32, h Handler) (*Resource, error) {196 func (c Client) CreateResource(iface *Interface, version int32, id uint32, h Handler) (*Resource, error) {
185 rc := wlResourceCreate(c.c, iface.c, version, id)197 rc := wlResourceCreate(c.c, iface.c, version, id)
186 if rc == 0 {198 if rc == 0 {
M server/lib.go +2 -0
@@ -29,6 +29,7 @@ var (
2929 wlResourceGetID func(resource uintptr) uint32
3030 wlResourceGetClient func(resource uintptr) uintptr
3131 wlResourceGetVersion func(resource uintptr) int32
32+ wlClientGetCredentials func(client uintptr, pid, uid, gid unsafe.Pointer)
3233 )
3334
3435 var (
@@ -75,6 +76,7 @@ func load() error {
7576 reg(&wlResourceGetID, "wl_resource_get_id")
7677 reg(&wlResourceGetClient, "wl_resource_get_client")
7778 reg(&wlResourceGetVersion, "wl_resource_get_version")
79+ reg(&wlClientGetCredentials, "wl_client_get_credentials")
7880
7981 cbDispatcher = purego.NewCallback(dispatch)
8082 cbDestroy = purego.NewCallback(destroyed)
@@ -29,6 +29,7 @@ var (
29 wlResourceGetID func(resource uintptr) uint3229 wlResourceGetID func(resource uintptr) uint32
30 wlResourceGetClient func(resource uintptr) uintptr30 wlResourceGetClient func(resource uintptr) uintptr
31 wlResourceGetVersion func(resource uintptr) int3231 wlResourceGetVersion func(resource uintptr) int32
32+ wlClientGetCredentials func(client uintptr, pid, uid, gid unsafe.Pointer)
32 )33 )
33 34
34 var (35 var (
@@ -75,6 +76,7 @@ func load() error {
75 reg(&wlResourceGetID, "wl_resource_get_id")76 reg(&wlResourceGetID, "wl_resource_get_id")
76 reg(&wlResourceGetClient, "wl_resource_get_client")77 reg(&wlResourceGetClient, "wl_resource_get_client")
77 reg(&wlResourceGetVersion, "wl_resource_get_version")78 reg(&wlResourceGetVersion, "wl_resource_get_version")
79+ reg(&wlClientGetCredentials, "wl_client_get_credentials")
78 80
79 cbDispatcher = purego.NewCallback(dispatch)81 cbDispatcher = purego.NewCallback(dispatch)
80 cbDestroy = purego.NewCallback(destroyed)82 cbDestroy = purego.NewCallback(destroyed)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 14

↑↓ moveenter openesc close