Linux can now read from a shared memory map and the sdk can create it

This commit is contained in:
2026-08-23 23:44:36 +01:00
parent 9adc0a9456
commit a449ced75f
4 changed files with 43 additions and 29 deletions
+47
View File
@@ -0,0 +1,47 @@
// I should rename winutils to something else but what this package does
// is interface some windows stuff that we need for the:
// - Broadcast Channel
// - Valid Data Event windows thing
package mmaputils
import (
"io"
"time"
)
type Reader interface {
io.Reader
io.ReaderAt
io.ReadCloser
}
type IRacingWinUtils struct {
Utils *utils
}
func Init() (*IRacingWinUtils, error) {
u, err := newUtils()
if err != nil {
return nil, err
}
return &IRacingWinUtils{u}, nil
}
func (u *IRacingWinUtils) Close() {
u.Utils.Close()
}
// OpenWinEvent will open the named windows event
func (u *IRacingWinUtils) OpenWinEvent(name string) error {
return u.Utils.OpenEvent(name)
}
// OpenBroadcastChannel will open the broadcast channel
func (u *IRacingWinUtils) OpenBroadcastChannel(name string) error {
return u.Utils.OpenBroadcastChannel(name)
}
// CheckValidDataEvent checks if our windows even is telling us we are good to go
func (u *IRacingWinUtils) CheckValidDataEvent(timeout time.Duration) bool {
return u.Utils.CheckValidDataEvent(timeout)
}
+129
View File
@@ -0,0 +1,129 @@
//go:build (linux && cgo) || (darwin && cgo)
package mmaputils
import (
"fmt"
"net"
"os"
"time"
"github.com/ESilva15/goirsdk/sharedMem"
)
type utils struct {
socketPath string
listener *net.UnixConn
}
func newUtils() (*utils, error) {
return &utils{}, nil
}
func (u *utils) Close() {
if u.listener != nil {
u.listener.Close()
os.Remove(u.socketPath)
}
}
// OpenMemMap returns a Reader interface that can be used to read the data
// No need to encapsulate it
func OpenMemMap(name string, size uint32) (Reader, error) {
file, err := sharedMem.Open(name, size)
if err != nil {
return nil, fmt.Errorf("failed to open `%s` with err: %+v", name, err)
}
return file, nil
}
// OpenEvent creates or connects to a Unix socket for event signaling on Linux
func (u *utils) OpenEvent(eventName string) error {
u.socketPath = fmt.Sprintf("/tmp/iracing_%s.sock", eventName)
_ = os.Remove(u.socketPath)
addr, err := net.ResolveUnixAddr("unixgram", u.socketPath)
if err != nil {
return err
}
l, err := net.ListenUnixgram("unixgram", addr)
if err != nil {
return err
}
u.listener = l
return nil
}
func (u *utils) OpenBroadcastChannel(name string) error {
// No-op or log stub on Linux
return nil
}
// CheckValidDataEvent waits for a pulse byte sent over the socket
func (u *utils) CheckValidDataEvent(timeout time.Duration) bool {
if u.listener == nil {
return false
}
_ = u.listener.SetReadDeadline(time.Now().Add(timeout))
buf := make([]byte, 1)
_, err := u.listener.Read(buf)
return err == nil
}
func (u *utils) SendBroadcastMessage(id, p1, p2 uintptr) error {
return nil
}
// package winutils
//
// import (
// "errors"
// "sync"
// "time"
// )
//
// const (
// WAIT_OBJECT_0 = 0
// WAIT_TIMEOUT = 258
// )
//
// var (
// once sync.Once
// ErrUnsupportedOS = errors.New("not found")
// )
//
// type utils struct {
// }
//
// // INITIALIZATION
// func newUtils() (*utils, error) {
// return nil, ErrUnsupportedOS
// }
//
// func (u *utils) Close() {
// }
//
// // openEvent opens a windows.Handle for a given event
// func (u *utils) OpenEvent(eventName string) error {
// return ErrUnsupportedOS
// }
//
// // OpenBroadcastChannel opens up a broadcast channel to send commands to iracing
// func (u *utils) OpenBroadcastChannel(name string) error {
// return ErrUnsupportedOS
// }
//
// // INITIALIZATION
//
// // openEvent waits for a good response for some given time
// func (u *utils) CheckValidDataEvent(timeout time.Duration) bool {
// return false
// }
//
// // SendBroadcastMessage sends a message trough the broadcast channel
// func (u *utils) SendBroadcastMessage(id, p1, p2 uintptr) error {
// return ErrUnsupportedOS
// }
+131
View File
@@ -0,0 +1,131 @@
// go:build windows
package mmaputils
import (
"sync"
"time"
"unsafe"
"github.com/ESilva15/goirsdk/sharedMem"
"golang.org/x/sys/windows"
)
const (
WAIT_OBJECT_0 = 0
WAIT_TIMEOUT = 258
)
var once sync.Once
type utils struct {
user32DLL *windows.LazyDLL
wEvent windows.Handle
wBroadcastChn uintptr
}
// INITIALIZATION
func newUtils() (*utils, error) {
return &utils{
user32DLL: openUser32DLL(),
}, nil
}
func (u *utils) Close() {
closeEvent(&u.wEvent)
// Do we need to unload the user32DLL ???
// Do we need to close the broadcast channel ???
}
// OpenMemMap returns a Reader interface that can be used to read the data
// No need to encapsulate it
func OpenMemMap(name string, size uint32) (Reader, error) {
file, err := sharedMem.Open("Local\\"+name, size)
if err != nil {
return nil, err
}
return file, nil
}
// openEvent opens a windows.Handle for a given event
func (u *utils) OpenEvent(eventName string) error {
name, err := windows.UTF16PtrFromString(eventName)
if err != nil {
return err
}
event, err := windows.OpenEvent(windows.SYNCHRONIZE, false, name)
if err != nil {
return err
}
u.wEvent = event
return nil
}
// loadUser32DLL loads the user32.dll which is used to create some processes
func openUser32DLL() *windows.LazyDLL {
return windows.NewLazyDLL("user32.dll")
}
// OpenBroadcastChannel opens up a broadcast channel to send commands to iracing
func (u *utils) OpenBroadcastChannel(name string) error {
registerWindowsMessageW := u.user32DLL.NewProc("RegisterWindowMessageW")
msgPtr, err := windows.UTF16PtrFromString(name)
if err != nil {
return err
}
ret, _, err := registerWindowsMessageW.Call(uintptr(unsafe.Pointer(msgPtr)))
if ret == 0 {
return err
}
u.wBroadcastChn = ret
return nil
}
// INITIALIZATION
// closeEvent closes a given windows.Handle
func closeEvent(h *windows.Handle) {
windows.CloseHandle(*h)
}
// openEvent waits for a good response for some given time
func (u *utils) CheckValidDataEvent(timeout time.Duration) bool {
t0 := time.Now().UnixNano()
timeoutInt := uint32(timeout / time.Millisecond)
result, err := windows.WaitForSingleObject(u.wEvent, timeoutInt)
if err != nil {
remaining := timeoutInt - uint32((time.Now().UnixNano()-t0)/1000000)
if remaining > 0 {
time.Sleep(time.Duration(remaining) * time.Millisecond)
}
return false
}
// Check the result of the wait
if result == WAIT_OBJECT_0 {
return true
} else if result == WAIT_TIMEOUT {
return false
}
return false
}
// SendBroadcastMessage sends a message trough the broadcast channel
func (u *utils) SendBroadcastMessage(id, p1, p2 uintptr) error {
sendMsg := u.user32DLL.NewProc("SendNotifyMessageW")
ret, _, err := sendMsg.Call(0xffff, id, p1, p2)
if ret == 1 {
return nil
} else {
return err
}
}