mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 13:32:50 -05:00
core/wayland: thread-safety meta fixes + cleanups + hypr workaround
- fork go-wayland/client and modify to make it thread-safe internally - use sync.Map and atomic values in many places to cut down on mutex boilerplate - do not create extworkspace client unless explicitly requested
This commit is contained in:
3
core/pkg/go-wayland/AUTHORS
Normal file
3
core/pkg/go-wayland/AUTHORS
Normal file
@@ -0,0 +1,3 @@
|
||||
// Keep this sorted
|
||||
|
||||
rajveermalviya
|
||||
24
core/pkg/go-wayland/LICENSE
Normal file
24
core/pkg/go-wayland/LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
Copyright 2021 go-wayland authors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
25
core/pkg/go-wayland/README.md
Normal file
25
core/pkg/go-wayland/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Wayland implementation in Go
|
||||
|
||||
[](https://pkg.go.dev/github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland)
|
||||
|
||||
This module contains pure Go implementation of the Wayland protocol.
|
||||
Currently only wayland-client functionality is supported.
|
||||
|
||||
Go code is generated from protocol XML files using
|
||||
[`go-wayland-scanner`](cmd/go-wayland-scanner/scanner.go).
|
||||
|
||||
To load cursor, minimal port of `wayland-cursor` & `xcursor` in pure Go
|
||||
is located at [`wayland/cursor`](wayland/cursor) & [`wayland/cursor/xcursor`](wayland/cursor/xcursor)
|
||||
respectively.
|
||||
|
||||
To demonstrate the functionality of this module
|
||||
[`examples/imageviewer`](examples/imageviewer) contains a simple image
|
||||
viewer. It demos displaying a top-level window, resizing of window,
|
||||
cursor themes, pointer and keyboard. Because it's in pure Go, it can be
|
||||
compiled without CGO. You can try it using the following commands:
|
||||
|
||||
```sh
|
||||
CGO_ENABLED=0 go install github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/examples/imageviewer@latest
|
||||
|
||||
imageviewer file.jpg
|
||||
```
|
||||
4
core/pkg/go-wayland/generate
Executable file
4
core/pkg/go-wayland/generate
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd ./wayland
|
||||
go generate -x ./...
|
||||
9
core/pkg/go-wayland/generatep
Executable file
9
core/pkg/go-wayland/generatep
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Runs go generate for each directory, but in parallel. Any arguments are appended to the
|
||||
# go generate command.
|
||||
# Usage: $ ./generatep [go generate arguments]
|
||||
# Print all generate commands: $ ./generatep -x
|
||||
|
||||
cd ./wayland
|
||||
find . -type f -name '*.go' -exec dirname {} \; | sort -u | parallel -j 0 go generate $1 {}/.
|
||||
7544
core/pkg/go-wayland/wayland/client/client.go
Normal file
7544
core/pkg/go-wayland/wayland/client/client.go
Normal file
File diff suppressed because it is too large
Load Diff
33
core/pkg/go-wayland/wayland/client/common.go
Normal file
33
core/pkg/go-wayland/wayland/client/common.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package client
|
||||
|
||||
type Dispatcher interface {
|
||||
Dispatch(opcode uint32, fd int, data []byte)
|
||||
}
|
||||
|
||||
type Proxy interface {
|
||||
Context() *Context
|
||||
SetContext(ctx *Context)
|
||||
ID() uint32
|
||||
SetID(id uint32)
|
||||
}
|
||||
|
||||
type BaseProxy struct {
|
||||
ctx *Context
|
||||
id uint32
|
||||
}
|
||||
|
||||
func (p *BaseProxy) ID() uint32 {
|
||||
return p.id
|
||||
}
|
||||
|
||||
func (p *BaseProxy) SetID(id uint32) {
|
||||
p.id = id
|
||||
}
|
||||
|
||||
func (p *BaseProxy) Context() *Context {
|
||||
return p.ctx
|
||||
}
|
||||
|
||||
func (p *BaseProxy) SetContext(ctx *Context) {
|
||||
p.ctx = ctx
|
||||
}
|
||||
110
core/pkg/go-wayland/wayland/client/context.go
Normal file
110
core/pkg/go-wayland/wayland/client/context.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
conn *net.UnixConn
|
||||
objects sync.Map // map[uint32]Proxy - thread-safe concurrent map
|
||||
currentID uint32
|
||||
idMu sync.Mutex // protects currentID increment
|
||||
}
|
||||
|
||||
func (ctx *Context) Register(p Proxy) {
|
||||
ctx.idMu.Lock()
|
||||
ctx.currentID++
|
||||
id := ctx.currentID
|
||||
ctx.idMu.Unlock()
|
||||
|
||||
p.SetID(id)
|
||||
p.SetContext(ctx)
|
||||
ctx.objects.Store(id, p)
|
||||
}
|
||||
|
||||
func (ctx *Context) Unregister(p Proxy) {
|
||||
ctx.objects.Delete(p.ID())
|
||||
}
|
||||
|
||||
func (ctx *Context) GetProxy(id uint32) Proxy {
|
||||
if val, ok := ctx.objects.Load(id); ok {
|
||||
return val.(Proxy)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *Context) Close() error {
|
||||
return ctx.conn.Close()
|
||||
}
|
||||
|
||||
// Dispatch reads and processes incoming messages and calls [client.Dispatcher.Dispatch] on the
|
||||
// respective wayland protocol.
|
||||
// Dispatch must be called on the same goroutine as other interactions with the Context.
|
||||
// If a multi goroutine approach is desired, use [Context.GetDispatch] instead.
|
||||
// Dispatch blocks if there are no incoming messages.
|
||||
// A Dispatch loop is usually used to handle incoming messages.
|
||||
func (ctx *Context) Dispatch() error {
|
||||
return ctx.GetDispatch()()
|
||||
}
|
||||
|
||||
var ErrDispatchSenderNotFound = errors.New("dispatch: unable to find sender")
|
||||
var ErrDispatchSenderUnsupported = errors.New("dispatch: sender does not implement Dispatch method")
|
||||
var ErrDispatchUnableToReadMsg = errors.New("dispatch: unable to read msg")
|
||||
|
||||
// GetDispatch reads incoming messages and returns the dispatch function which calls
|
||||
// [client.Dispatcher.Dispatch] on the respective wayland protocol.
|
||||
// This function is now thread-safe and can be called from multiple goroutines.
|
||||
// GetDispatch blocks if there are no incoming messages.
|
||||
func (ctx *Context) GetDispatch() func() error {
|
||||
senderID, opcode, fd, data, err := ctx.ReadMsg() // Blocks if there are no incoming messages
|
||||
if err != nil {
|
||||
return func() error {
|
||||
return fmt.Errorf("%w: %w", ErrDispatchUnableToReadMsg, err)
|
||||
}
|
||||
}
|
||||
|
||||
return func() error {
|
||||
val, ok := ctx.objects.Load(senderID)
|
||||
if !ok {
|
||||
return fmt.Errorf("%w (senderID=%d)", ErrDispatchSenderNotFound, senderID)
|
||||
}
|
||||
|
||||
sender, ok := val.(Dispatcher)
|
||||
if !ok {
|
||||
return fmt.Errorf("%w (senderID=%d)", ErrDispatchSenderUnsupported, senderID)
|
||||
}
|
||||
|
||||
sender.Dispatch(opcode, fd, data)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func Connect(addr string) (*Display, error) {
|
||||
if addr == "" {
|
||||
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
|
||||
if runtimeDir == "" {
|
||||
return nil, errors.New("env XDG_RUNTIME_DIR not set")
|
||||
}
|
||||
if addr == "" {
|
||||
addr = os.Getenv("WAYLAND_DISPLAY")
|
||||
}
|
||||
if addr == "" {
|
||||
addr = "wayland-0"
|
||||
}
|
||||
addr = runtimeDir + "/" + addr
|
||||
}
|
||||
|
||||
ctx := &Context{}
|
||||
|
||||
conn, err := net.DialUnix("unix", nil, &net.UnixAddr{Name: addr, Net: "unix"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx.conn = conn
|
||||
|
||||
return NewDisplay(ctx), nil
|
||||
}
|
||||
111
core/pkg/go-wayland/wayland/client/context_test.go
Normal file
111
core/pkg/go-wayland/wayland/client/context_test.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package client_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
|
||||
)
|
||||
|
||||
// Shows a dispatch loop that will block the goroutine.
|
||||
// This approach has no risk of data races but the loop blocks the goroutine when no messages are
|
||||
// received. This can be a valid approach if there are no more changes that need to be made after
|
||||
// setting up and starting the loop.
|
||||
// For a multi goroutine approach, use [client.Context.GetDispatch].
|
||||
func ExampleContext_Dispatch() {
|
||||
display, err := client.Connect("")
|
||||
if err != nil {
|
||||
log.Fatalf("Error connecting to Wayland server: %v", err)
|
||||
}
|
||||
|
||||
registry, err := display.GetRegistry()
|
||||
if err != nil {
|
||||
log.Fatalf("Error getting Wayland registry: %v", err)
|
||||
}
|
||||
|
||||
var seat *client.Seat
|
||||
registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
|
||||
switch e.Interface {
|
||||
case client.SeatInterfaceName:
|
||||
seat = client.NewSeat(display.Context())
|
||||
err := registry.Bind(e.Name, e.Interface, e.Version, seat)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to bind %s interface: %v", client.SeatInterfaceName, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
display.Roundtrip()
|
||||
display.Roundtrip()
|
||||
|
||||
keyboard, err := seat.GetKeyboard()
|
||||
if err != nil {
|
||||
log.Printf("Error getting keyboard: %v", err)
|
||||
}
|
||||
log.Printf("Got keyboard: %v\n", keyboard)
|
||||
|
||||
for {
|
||||
err := display.Context().Dispatch()
|
||||
if err != nil {
|
||||
log.Printf("Dispatch error: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shows how the dispatch loop can be done in another goroutine.
|
||||
// This prevents the goroutine from being blocked and allows making changes to wayland objects while
|
||||
// the dispatch loop is blocking another goroutine.
|
||||
func ExampleContext_GetDispatch() {
|
||||
display, err := client.Connect("")
|
||||
if err != nil {
|
||||
log.Fatalf("Error connecting to Wayland server: %v", err)
|
||||
}
|
||||
|
||||
registry, err := display.GetRegistry()
|
||||
if err != nil {
|
||||
log.Fatalf("Error getting Wayland registry: %v", err)
|
||||
}
|
||||
|
||||
var seat *client.Seat
|
||||
registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
|
||||
switch e.Interface {
|
||||
case client.SeatInterfaceName:
|
||||
seat = client.NewSeat(display.Context())
|
||||
err := registry.Bind(e.Name, e.Interface, e.Version, seat)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to bind %s interface: %v", client.SeatInterfaceName, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
display.Roundtrip()
|
||||
display.Roundtrip()
|
||||
dispatchQueue := make(chan func() error)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
dispatchQueue <- display.Context().GetDispatch()
|
||||
}
|
||||
}()
|
||||
|
||||
keyboard, err := seat.GetKeyboard()
|
||||
if err != nil {
|
||||
log.Printf("Error getting keyboard: %v", err)
|
||||
}
|
||||
log.Printf("Got keyboard: %v\n", keyboard)
|
||||
|
||||
err = errors.Join(keyboard.Release(), seat.Release(), display.Context().Close())
|
||||
if err != nil {
|
||||
fmt.Printf("Error cleaning up: %v\n", err)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
// Add other cases here to do other things
|
||||
case dispatchFunc := <-dispatchQueue:
|
||||
err := dispatchFunc()
|
||||
if err != nil {
|
||||
log.Printf("Dispatch error: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
core/pkg/go-wayland/wayland/client/display.go
Normal file
37
core/pkg/go-wayland/wayland/client/display.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Roundtrip blocks until all pending request are processed by the server.
|
||||
// It is the implementation of [wl_display_roundtrip].
|
||||
//
|
||||
// [wl_display_roundtrip]: https://wayland.freedesktop.org/docs/html/apb.html#Client-classwl__display_1ab60f38c2f80980ac84f347e932793390
|
||||
func (i *Display) Roundtrip() error {
|
||||
callback, err := i.Sync()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get sync callback: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err2 := callback.Destroy(); err2 != nil {
|
||||
log.Printf("unable to destroy callback: %v\n", err2)
|
||||
}
|
||||
}()
|
||||
|
||||
done := false
|
||||
callback.SetDoneHandler(func(_ CallbackDoneEvent) {
|
||||
done = true
|
||||
})
|
||||
|
||||
// Wait for callback to return
|
||||
for !done {
|
||||
err := i.Context().GetDispatch()()
|
||||
if err != nil {
|
||||
return fmt.Errorf("roundtrip: failed to dispatch: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
6
core/pkg/go-wayland/wayland/client/doc.go
Normal file
6
core/pkg/go-wayland/wayland/client/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Package client is Go port of wayland-client library
|
||||
// for writing pure Go GUI software for wayland supported
|
||||
// platforms.
|
||||
package client
|
||||
|
||||
//go:generate go run github.com/yaslama/go-wayland/cmd/go-wayland-scanner -pkg client -prefix wl -o client.go -i https://gitlab.freedesktop.org/wayland/wayland/-/raw/1.23.0/protocol/wayland.xml?ref_type=tags
|
||||
120
core/pkg/go-wayland/wayland/client/event.go
Normal file
120
core/pkg/go-wayland/wayland/client/event.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
var oobSpace = unix.CmsgSpace(4)
|
||||
|
||||
func (ctx *Context) ReadMsg() (senderID uint32, opcode uint32, fd int, msg []byte, err error) {
|
||||
fd = -1
|
||||
|
||||
oob := make([]byte, oobSpace)
|
||||
header := make([]byte, 8)
|
||||
|
||||
n, oobn, _, _, err := ctx.conn.ReadMsgUnix(header, oob)
|
||||
if err != nil {
|
||||
return senderID, opcode, fd, msg, err
|
||||
}
|
||||
if n != 8 {
|
||||
return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: incorrect number of bytes read for header (n=%d)", n)
|
||||
}
|
||||
|
||||
if oobn > 0 {
|
||||
fds, err := getFdsFromOob(oob, oobn, "header")
|
||||
if err != nil {
|
||||
return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: %w", err)
|
||||
}
|
||||
|
||||
if len(fds) > 0 {
|
||||
fd = fds[0]
|
||||
}
|
||||
}
|
||||
|
||||
senderID = Uint32(header[:4])
|
||||
opcodeAndSize := Uint32(header[4:8])
|
||||
opcode = opcodeAndSize & 0xffff
|
||||
size := opcodeAndSize >> 16
|
||||
|
||||
msgSize := int(size) - 8
|
||||
if msgSize == 0 {
|
||||
return senderID, opcode, fd, nil, nil
|
||||
}
|
||||
|
||||
msg = make([]byte, msgSize)
|
||||
|
||||
if fd == -1 {
|
||||
// if something was read before, then zero it out
|
||||
if oobn > 0 {
|
||||
oob = make([]byte, oobSpace)
|
||||
}
|
||||
|
||||
n, oobn, _, _, err = ctx.conn.ReadMsgUnix(msg, oob)
|
||||
} else {
|
||||
n, err = ctx.conn.Read(msg)
|
||||
}
|
||||
if err != nil {
|
||||
return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: %w", err)
|
||||
}
|
||||
if n != msgSize {
|
||||
return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: incorrect number of bytes read for msg (n=%d, msgSize=%d)", n, msgSize)
|
||||
}
|
||||
|
||||
if fd == -1 && oobn > 0 {
|
||||
fds, err := getFdsFromOob(oob, oobn, "msg")
|
||||
if err != nil {
|
||||
return senderID, opcode, fd, msg, fmt.Errorf("ctx.ReadMsg: %w", err)
|
||||
}
|
||||
|
||||
if len(fds) > 0 {
|
||||
fd = fds[0]
|
||||
}
|
||||
}
|
||||
|
||||
return senderID, opcode, fd, msg, nil
|
||||
}
|
||||
|
||||
func getFdsFromOob(oob []byte, oobn int, source string) ([]int, error) {
|
||||
if oobn > len(oob) {
|
||||
return nil, fmt.Errorf("getFdsFromOob: incorrect number of bytes read from %s for oob (oobn=%d)", source, oobn)
|
||||
}
|
||||
scms, err := unix.ParseSocketControlMessage(oob)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getFdsFromOob: unable to parse control message from %s: %w", source, err)
|
||||
}
|
||||
|
||||
var fdsRet []int
|
||||
for _, scm := range scms {
|
||||
fds, err := unix.ParseUnixRights(&scm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getFdsFromOob: unable to parse unix rights from %s: %w", source, err)
|
||||
}
|
||||
|
||||
fdsRet = append(fdsRet, fds...)
|
||||
}
|
||||
|
||||
return fdsRet, nil
|
||||
}
|
||||
|
||||
func Uint32(src []byte) uint32 {
|
||||
_ = src[3]
|
||||
return *(*uint32)(unsafe.Pointer(&src[0]))
|
||||
}
|
||||
|
||||
func String(src []byte) string {
|
||||
idx := bytes.IndexByte(src, 0)
|
||||
src = src[:idx:idx]
|
||||
return *(*string)(unsafe.Pointer(&src))
|
||||
}
|
||||
|
||||
func Fixed(src []byte) float64 {
|
||||
_ = src[3]
|
||||
fx := *(*int32)(unsafe.Pointer(&src[0]))
|
||||
return fixedToFloat64(fx)
|
||||
}
|
||||
44
core/pkg/go-wayland/wayland/client/request.go
Normal file
44
core/pkg/go-wayland/wayland/client/request.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (ctx *Context) WriteMsg(b []byte, oob []byte) error {
|
||||
n, oobn, err := ctx.conn.WriteMsgUnix(b, oob, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(b) || oobn != len(oob) {
|
||||
return fmt.Errorf("ctx.WriteMsg: incorrect number of bytes written (n=%d oobn=%d)", n, oobn)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func PutUint32(dst []byte, v uint32) {
|
||||
_ = dst[3]
|
||||
*(*uint32)(unsafe.Pointer(&dst[0])) = v
|
||||
}
|
||||
|
||||
func PutFixed(dst []byte, f float64) {
|
||||
fx := fixedFromfloat64(f)
|
||||
_ = dst[3]
|
||||
*(*int32)(unsafe.Pointer(&dst[0])) = fx
|
||||
}
|
||||
|
||||
// PutString places a string in Wayland's wire format on the destination buffer.
|
||||
// It first places the length of the string (plus one for the null terminator) and then the string
|
||||
// followed by a null byte.
|
||||
// The length of dst must be equal to, or greater than, len(v) + 5.
|
||||
func PutString(dst []byte, v string) {
|
||||
PutUint32(dst[:4], uint32(len(v)+1))
|
||||
copy(dst[4:], v)
|
||||
dst[4+len(v)] = '\x00' // To cause panic if dst is not large enough
|
||||
}
|
||||
|
||||
func PutArray(dst []byte, a []byte) {
|
||||
PutUint32(dst[:4], uint32(len(a)))
|
||||
copy(dst[4:], a)
|
||||
}
|
||||
24
core/pkg/go-wayland/wayland/client/util.go
Normal file
24
core/pkg/go-wayland/wayland/client/util.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package client
|
||||
|
||||
import "math"
|
||||
|
||||
// From wayland/wayland-util.h
|
||||
|
||||
func fixedToFloat64(f int32) float64 {
|
||||
u_i := (1023+44)<<52 + (1 << 51) + int64(f)
|
||||
u_d := math.Float64frombits(uint64(u_i))
|
||||
return u_d - (3 << 43)
|
||||
}
|
||||
|
||||
func fixedFromfloat64(d float64) int32 {
|
||||
u_d := d + (3 << (51 - 8))
|
||||
u_i := int64(math.Float64bits(u_d))
|
||||
return int32(u_i)
|
||||
}
|
||||
|
||||
func PaddedLen(l int) int {
|
||||
if (l & 0x3) != 0 {
|
||||
return l + (4 - (l & 0x3))
|
||||
}
|
||||
return l
|
||||
}
|
||||
Reference in New Issue
Block a user