64 lines
2.1 KiB
Go
64 lines
2.1 KiB
Go
|
package i3
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"io"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// detectByteOrder sends messages to i3 to determine the byte order it uses.
|
||
|
// For details on this technique, see:
|
||
|
// https://build.i3wm.org/docs/ipc.html#_appendix_a_detecting_byte_order_in_memory_safe_languages
|
||
|
func detectByteOrder(conn io.ReadWriter) (binary.ByteOrder, error) {
|
||
|
const (
|
||
|
// targetLen is 0x00 01 01 00 in both, big and little endian
|
||
|
targetLen = 65536 + 256
|
||
|
|
||
|
// SUBSCRIBE was introduced in 3.e (2010-03-30)
|
||
|
prefixSubscribe = "[]"
|
||
|
|
||
|
// RUN_COMMAND was always present
|
||
|
prefixCmd = "nop byte-order detection. padding: "
|
||
|
)
|
||
|
|
||
|
// 2. Send a big endian encoded message of type SUBSCRIBE:
|
||
|
payload := []byte(prefixSubscribe + strings.Repeat(" ", targetLen-len(prefixSubscribe)))
|
||
|
if err := binary.Write(conn, binary.BigEndian, &header{magic, uint32(len(payload)), messageTypeSubscribe}); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if _, err := conn.Write(payload); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// 3. Send a byte order independent RUN_COMMAND message:
|
||
|
payload = []byte(prefixCmd + strings.Repeat("a", targetLen-len(prefixCmd)))
|
||
|
if err := binary.Write(conn, binary.BigEndian, &header{magic, uint32(len(payload)), messageTypeRunCommand}); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if _, err := conn.Write(payload); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// 4. Receive a message header, decode the message type as big endian:
|
||
|
var header [14]byte
|
||
|
if _, err := io.ReadFull(conn, header[:]); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if messageType(binary.BigEndian.Uint32(header[10:14])) == messageReplyTypeCommand {
|
||
|
order := binary.LittleEndian // our big endian message was not answered
|
||
|
// Read remaining payload
|
||
|
_, err := io.ReadFull(conn, make([]byte, order.Uint32(header[6:10])))
|
||
|
return order, err
|
||
|
}
|
||
|
order := binary.BigEndian // our big endian message was answered
|
||
|
// Read remaining payload
|
||
|
if _, err := io.ReadFull(conn, make([]byte, order.Uint32(header[6:10]))); err != nil {
|
||
|
return order, err
|
||
|
}
|
||
|
|
||
|
// Slurp the pending RUN_COMMAND reply.
|
||
|
sock := &socket{conn: conn, order: order}
|
||
|
_, err := sock.recvMsg()
|
||
|
return binary.BigEndian, err
|
||
|
}
|