mirror of
https://github.com/golang/go
synced 2024-11-21 23:24:41 -07:00
Make draw/x11 treat $DISPLAY the same way x-go-bindings does.
This ought to make draw/x11 work on a Mac. R=rsc CC=golang-dev https://golang.org/cl/1265042
This commit is contained in:
parent
eadebba36f
commit
2d8aee45b8
@ -10,21 +10,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// getDisplay reads the DISPLAY environment variable, and returns the "12" in ":12.0".
|
|
||||||
func getDisplay() string {
|
|
||||||
d := os.Getenv("DISPLAY")
|
|
||||||
if len(d) < 1 || d[0] != ':' {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
i := 1
|
|
||||||
for ; i < len(d); i++ {
|
|
||||||
if d[i] < '0' || d[i] > '9' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return d[1:i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// readU16BE reads a big-endian uint16 from r, using b as a scratch buffer.
|
// readU16BE reads a big-endian uint16 from r, using b as a scratch buffer.
|
||||||
func readU16BE(r io.Reader, b []byte) (uint16, os.Error) {
|
func readU16BE(r io.Reader, b []byte) (uint16, os.Error) {
|
||||||
_, err := io.ReadFull(r, b[0:2])
|
_, err := io.ReadFull(r, b[0:2])
|
||||||
@ -50,9 +35,12 @@ func readStr(r io.Reader, b []byte) (string, os.Error) {
|
|||||||
return string(b[0:n]), nil
|
return string(b[0:n]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readAuth reads the X authority file and returns the name/data pair for the DISPLAY.
|
// readAuth reads the X authority file and returns the name/data pair for the display.
|
||||||
// b is a scratch buffer to use, and should be at least 256 bytes long (i.e. it should be able to hold a hostname).
|
// displayStr is the "12" out of a $DISPLAY like ":12.0".
|
||||||
func readAuth(b []byte) (name, data string, err os.Error) {
|
func readAuth(displayStr string) (name, data string, err os.Error) {
|
||||||
|
// b is a scratch buffer to use and should be at least 256 bytes long
|
||||||
|
// (i.e. it should be able to hold a hostname).
|
||||||
|
var b [256]byte
|
||||||
// As per /usr/include/X11/Xauth.h.
|
// As per /usr/include/X11/Xauth.h.
|
||||||
const familyLocal = 256
|
const familyLocal = 256
|
||||||
|
|
||||||
@ -76,7 +64,6 @@ func readAuth(b []byte) (name, data string, err os.Error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
display := getDisplay()
|
|
||||||
for {
|
for {
|
||||||
family, err := readU16BE(br, b[0:2])
|
family, err := readU16BE(br, b[0:2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -98,7 +85,7 @@ func readAuth(b []byte) (name, data string, err os.Error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if family == familyLocal && addr == hostname && disp == display {
|
if family == familyLocal && addr == hostname && disp == displayStr {
|
||||||
return name0, data0, nil
|
return name0, data0, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type resID uint32 // X resource IDs.
|
type resID uint32 // X resource IDs.
|
||||||
@ -197,9 +199,65 @@ func (c *conn) pumper() {
|
|||||||
// TODO(nigeltao): Should we explicitly close our kbd/mouse/resize/quit chans?
|
// TODO(nigeltao): Should we explicitly close our kbd/mouse/resize/quit chans?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate ourselves with the X server.
|
// connect connects to the X server given by the full X11 display name (e.g.
|
||||||
func (c *conn) authenticate() os.Error {
|
// ":12.0") and returns the connection as well as the portion of the full name
|
||||||
key, value, err := readAuth(c.buf[0:])
|
// that is the display number (e.g. "12").
|
||||||
|
// Examples:
|
||||||
|
// connect(":1") // calls net.Dial("unix", "", "/tmp/.X11-unix/X1"), displayStr="1"
|
||||||
|
// connect("/tmp/launch-123/:0") // calls net.Dial("unix", "", "/tmp/launch-123/:0"), displayStr="0"
|
||||||
|
// connect("hostname:2.1") // calls net.Dial("tcp", "", "hostname:6002"), displayStr="2"
|
||||||
|
// connect("tcp/hostname:1.0") // calls net.Dial("tcp", "", "hostname:6001"), displayStr="1"
|
||||||
|
func connect(display string) (conn net.Conn, displayStr string, err os.Error) {
|
||||||
|
colonIdx := strings.LastIndex(display, ":")
|
||||||
|
if colonIdx < 0 {
|
||||||
|
return nil, "", os.NewError("bad display: " + display)
|
||||||
|
}
|
||||||
|
// Parse the section before the colon.
|
||||||
|
var protocol, host, socket string
|
||||||
|
if display[0] == '/' {
|
||||||
|
socket = display[0:colonIdx]
|
||||||
|
} else {
|
||||||
|
if i := strings.LastIndex(display, "/"); i < 0 {
|
||||||
|
// The default protocol is TCP.
|
||||||
|
protocol = "tcp"
|
||||||
|
host = display[0:colonIdx]
|
||||||
|
} else {
|
||||||
|
protocol = display[0:i]
|
||||||
|
host = display[i+1 : colonIdx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Parse the section after the colon.
|
||||||
|
after := display[colonIdx+1:]
|
||||||
|
if after == "" {
|
||||||
|
return nil, "", os.NewError("bad display: " + display)
|
||||||
|
}
|
||||||
|
if i := strings.LastIndex(after, "."); i < 0 {
|
||||||
|
displayStr = after
|
||||||
|
} else {
|
||||||
|
displayStr = after[0:i]
|
||||||
|
}
|
||||||
|
displayInt, err := strconv.Atoi(displayStr)
|
||||||
|
if err != nil || displayInt < 0 {
|
||||||
|
return nil, "", os.NewError("bad display: " + display)
|
||||||
|
}
|
||||||
|
// Make the connection.
|
||||||
|
if socket != "" {
|
||||||
|
conn, err = net.Dial("unix", "", socket+":"+displayStr)
|
||||||
|
} else if host != "" {
|
||||||
|
conn, err = net.Dial(protocol, "", host+":"+strconv.Itoa(6000+displayInt))
|
||||||
|
} else {
|
||||||
|
conn, err = net.Dial("unix", "", "/tmp/.X11-unix/X"+displayStr)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", os.NewError("cannot connect to " + display + ": " + err.String())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// authenticate authenticates ourselves with the X server.
|
||||||
|
// displayStr is the "12" out of ":12.0".
|
||||||
|
func authenticate(w *bufio.Writer, displayStr string) os.Error {
|
||||||
|
key, value, err := readAuth(displayStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -210,31 +268,31 @@ func (c *conn) authenticate() os.Error {
|
|||||||
// 0x006c means little-endian. 0x000b, 0x0000 means X major version 11, minor version 0.
|
// 0x006c means little-endian. 0x000b, 0x0000 means X major version 11, minor version 0.
|
||||||
// 0x0012 and 0x0010 means the auth key and value have lenths 18 and 16.
|
// 0x0012 and 0x0010 means the auth key and value have lenths 18 and 16.
|
||||||
// The final 0x0000 is padding, so that the string length is a multiple of 4.
|
// The final 0x0000 is padding, so that the string length is a multiple of 4.
|
||||||
_, err = io.WriteString(c.w, "\x6c\x00\x0b\x00\x00\x00\x12\x00\x10\x00\x00\x00")
|
_, err = io.WriteString(w, "\x6c\x00\x0b\x00\x00\x00\x12\x00\x10\x00\x00\x00")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = io.WriteString(c.w, key)
|
_, err = io.WriteString(w, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Again, the 0x0000 is padding.
|
// Again, the 0x0000 is padding.
|
||||||
_, err = io.WriteString(c.w, "\x00\x00")
|
_, err = io.WriteString(w, "\x00\x00")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = io.WriteString(c.w, value)
|
_, err = io.WriteString(w, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = c.w.Flush()
|
err = w.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads a uint8 from r, using b as a scratch buffer.
|
// readU8 reads a uint8 from r, using b as a scratch buffer.
|
||||||
func readU8(r io.Reader, b []byte) (uint8, os.Error) {
|
func readU8(r io.Reader, b []byte) (uint8, os.Error) {
|
||||||
_, err := io.ReadFull(r, b[0:1])
|
_, err := io.ReadFull(r, b[0:1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -243,7 +301,7 @@ func readU8(r io.Reader, b []byte) (uint8, os.Error) {
|
|||||||
return uint8(b[0]), nil
|
return uint8(b[0]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads a little-endian uint16 from r, using b as a scratch buffer.
|
// readU16LE reads a little-endian uint16 from r, using b as a scratch buffer.
|
||||||
func readU16LE(r io.Reader, b []byte) (uint16, os.Error) {
|
func readU16LE(r io.Reader, b []byte) (uint16, os.Error) {
|
||||||
_, err := io.ReadFull(r, b[0:2])
|
_, err := io.ReadFull(r, b[0:2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -252,7 +310,7 @@ func readU16LE(r io.Reader, b []byte) (uint16, os.Error) {
|
|||||||
return uint16(b[0]) | uint16(b[1])<<8, nil
|
return uint16(b[0]) | uint16(b[1])<<8, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads a little-endian uint32 from r, using b as a scratch buffer.
|
// readU32LE reads a little-endian uint32 from r, using b as a scratch buffer.
|
||||||
func readU32LE(r io.Reader, b []byte) (uint32, os.Error) {
|
func readU32LE(r io.Reader, b []byte) (uint32, os.Error) {
|
||||||
_, err := io.ReadFull(r, b[0:4])
|
_, err := io.ReadFull(r, b[0:4])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -261,7 +319,7 @@ func readU32LE(r io.Reader, b []byte) (uint32, os.Error) {
|
|||||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, nil
|
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets b[0:4] to be the big-endian representation of u.
|
// setU32LE sets b[0:4] to be the big-endian representation of u.
|
||||||
func setU32LE(b []byte, u uint32) {
|
func setU32LE(b []byte, u uint32) {
|
||||||
b[0] = byte((u >> 0) & 0xff)
|
b[0] = byte((u >> 0) & 0xff)
|
||||||
b[1] = byte((u >> 8) & 0xff)
|
b[1] = byte((u >> 8) & 0xff)
|
||||||
@ -269,7 +327,7 @@ func setU32LE(b []byte, u uint32) {
|
|||||||
b[3] = byte((u >> 24) & 0xff)
|
b[3] = byte((u >> 24) & 0xff)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that we have an agreeable X pixmap Format.
|
// checkPixmapFormats checks that we have an agreeable X pixmap Format.
|
||||||
func checkPixmapFormats(r io.Reader, b []byte, n int) (agree bool, err os.Error) {
|
func checkPixmapFormats(r io.Reader, b []byte, n int) (agree bool, err os.Error) {
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
_, err = io.ReadFull(r, b[0:8])
|
_, err = io.ReadFull(r, b[0:8])
|
||||||
@ -284,7 +342,7 @@ func checkPixmapFormats(r io.Reader, b []byte, n int) (agree bool, err os.Error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that we have an agreeable X Depth (i.e. one that has an agreeable X VisualType).
|
// checkDepths checks that we have an agreeable X Depth (i.e. one that has an agreeable X VisualType).
|
||||||
func checkDepths(r io.Reader, b []byte, n int, visual uint32) (agree bool, err os.Error) {
|
func checkDepths(r io.Reader, b []byte, n int, visual uint32) (agree bool, err os.Error) {
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
depth, err := readU16LE(r, b)
|
depth, err := readU16LE(r, b)
|
||||||
@ -321,7 +379,7 @@ func checkDepths(r io.Reader, b []byte, n int, visual uint32) (agree bool, err o
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that we have an agreeable X Screen.
|
// checkScreens checks that we have an agreeable X Screen.
|
||||||
func checkScreens(r io.Reader, b []byte, n int) (root, visual uint32, err os.Error) {
|
func checkScreens(r io.Reader, b []byte, n int) (root, visual uint32, err os.Error) {
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
root0, err := readU32LE(r, b)
|
root0, err := readU32LE(r, b)
|
||||||
@ -356,7 +414,8 @@ func checkScreens(r io.Reader, b []byte, n int) (root, visual uint32, err os.Err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the protocol handshake with the X server, and ensure that the server provides a compatible Screen, Depth, etcetera.
|
// handshake performs the protocol handshake with the X server, and ensures
|
||||||
|
// that the server provides a compatible Screen, Depth, etc.
|
||||||
func (c *conn) handshake() os.Error {
|
func (c *conn) handshake() os.Error {
|
||||||
_, err := io.ReadFull(c.r, c.buf[0:8])
|
_, err := io.ReadFull(c.r, c.buf[0:8])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -447,21 +506,28 @@ func (c *conn) handshake() os.Error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a new draw.Context, backed by a newly created and mapped X11 window.
|
// NewWindow calls NewWindowDisplay with $DISPLAY.
|
||||||
func NewWindow() (draw.Context, os.Error) {
|
func NewWindow() (draw.Context, os.Error) {
|
||||||
display := getDisplay()
|
display := os.Getenv("DISPLAY")
|
||||||
if len(display) == 0 {
|
if len(display) == 0 {
|
||||||
return nil, os.NewError("unsupported DISPLAY")
|
return nil, os.NewError("$DISPLAY not set")
|
||||||
}
|
}
|
||||||
s, err := net.Dial("unix", "", "/tmp/.X11-unix/X"+display)
|
return NewWindowDisplay(display)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWindowDisplay returns a new draw.Context, backed by a newly created and
|
||||||
|
// mapped X11 window. The X server to connect to is specified by the display
|
||||||
|
// string, such as ":1".
|
||||||
|
func NewWindowDisplay(display string) (draw.Context, os.Error) {
|
||||||
|
socket, displayStr, err := connect(display)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c := new(conn)
|
c := new(conn)
|
||||||
c.c = s
|
c.c = socket
|
||||||
c.r = bufio.NewReader(s)
|
c.r = bufio.NewReader(socket)
|
||||||
c.w = bufio.NewWriter(s)
|
c.w = bufio.NewWriter(socket)
|
||||||
err = c.authenticate()
|
err = authenticate(c.w, displayStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user