All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 2m18s
Test / Hakurei (push) Successful in 3m17s
Test / Sandbox (race detector) (push) Successful in 4m7s
Test / Hpkg (push) Successful in 4m13s
Test / Hakurei (race detector) (push) Successful in 5m3s
Test / Flake checks (push) Successful in 1m40s
These packages are highly specific to hakurei and are difficult to use safely from other pieces of code. Their exported symbols are made available until v0.4.0 where they will be removed for #24. Signed-off-by: Ophestra <cat@gensokyo.uk>
123 lines
2.5 KiB
Go
123 lines
2.5 KiB
Go
package wayland
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"os"
|
|
"runtime"
|
|
"sync"
|
|
"syscall"
|
|
)
|
|
|
|
// Conn represents a connection to the wayland display server.
|
|
type Conn struct {
|
|
conn *net.UnixConn
|
|
|
|
done chan struct{}
|
|
doneOnce sync.Once
|
|
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// Attach connects Conn to a wayland socket.
|
|
func (c *Conn) Attach(p string) (err error) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
if c.conn != nil {
|
|
return errors.New("socket already attached")
|
|
}
|
|
|
|
c.conn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: p, Net: "unix"})
|
|
return
|
|
}
|
|
|
|
// Close releases resources and closes the connection to the wayland compositor.
|
|
func (c *Conn) Close() error {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
if c.done == nil {
|
|
return errors.New("no socket bound")
|
|
}
|
|
|
|
c.doneOnce.Do(func() {
|
|
c.done <- struct{}{}
|
|
<-c.done
|
|
})
|
|
|
|
// closed by wayland
|
|
runtime.SetFinalizer(c.conn, nil)
|
|
return nil
|
|
}
|
|
|
|
// Bind binds the new socket to pathname.
|
|
func (c *Conn) Bind(pathname, appID, instanceID string) (*os.File, error) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
if c.conn == nil {
|
|
return nil, errors.New("socket not attached")
|
|
}
|
|
if c.done != nil {
|
|
return nil, errors.New("socket already bound")
|
|
}
|
|
|
|
if rc, err := c.conn.SyscallConn(); err != nil {
|
|
// unreachable
|
|
return nil, err
|
|
} else {
|
|
c.done = make(chan struct{})
|
|
return bindRawConn(c.done, rc, pathname, appID, instanceID)
|
|
}
|
|
}
|
|
|
|
func bindRawConn(done chan struct{}, rc syscall.RawConn, p, appID, instanceID string) (*os.File, error) {
|
|
var syncPipe [2]*os.File
|
|
|
|
if r, w, err := os.Pipe(); err != nil {
|
|
return nil, err
|
|
} else {
|
|
syncPipe[0] = r
|
|
syncPipe[1] = w
|
|
}
|
|
|
|
setupDone := make(chan error, 1) // does not block with c.done
|
|
|
|
go func() {
|
|
if err := rc.Control(func(fd uintptr) {
|
|
// prevent runtime from closing the read end of sync fd
|
|
runtime.SetFinalizer(syncPipe[0], nil)
|
|
|
|
// allow the Bind method to return after setup
|
|
setupDone <- bind(fd, p, appID, instanceID, syncPipe[0].Fd())
|
|
close(setupDone)
|
|
|
|
// keep socket alive until done is requested
|
|
<-done
|
|
runtime.KeepAlive(syncPipe[1])
|
|
}); err != nil {
|
|
setupDone <- err
|
|
}
|
|
|
|
// notify Close that rc.Control has returned
|
|
close(done)
|
|
}()
|
|
|
|
// return write end of the pipe
|
|
return syncPipe[1], <-setupDone
|
|
}
|
|
|
|
func bind(fd uintptr, p, appID, instanceID string, syncFd uintptr) error {
|
|
// ensure p is available
|
|
if f, err := os.Create(p); err != nil {
|
|
return err
|
|
} else if err = f.Close(); err != nil {
|
|
return err
|
|
} else if err = os.Remove(p); err != nil {
|
|
return err
|
|
}
|
|
|
|
return bindWaylandFd(p, fd, appID, instanceID, syncFd)
|
|
}
|