Implement output filtering
parent
d8be27d86e
commit
37692a873a
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,718 @@
|
||||
// Package xinerama is the X client API for the XINERAMA extension.
|
||||
package xinerama
|
||||
|
||||
// This file is automatically generated from xinerama.xml. Edit at your peril!
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
)
|
||||
|
||||
// Init must be called before using the XINERAMA extension.
|
||||
func Init(c *xgb.Conn) error {
|
||||
reply, err := xproto.QueryExtension(c, 8, "XINERAMA").Reply()
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case !reply.Present:
|
||||
return xgb.Errorf("No extension named XINERAMA could be found on on the server.")
|
||||
}
|
||||
|
||||
c.ExtLock.Lock()
|
||||
c.Extensions["XINERAMA"] = reply.MajorOpcode
|
||||
c.ExtLock.Unlock()
|
||||
for evNum, fun := range xgb.NewExtEventFuncs["XINERAMA"] {
|
||||
xgb.NewEventFuncs[int(reply.FirstEvent)+evNum] = fun
|
||||
}
|
||||
for errNum, fun := range xgb.NewExtErrorFuncs["XINERAMA"] {
|
||||
xgb.NewErrorFuncs[int(reply.FirstError)+errNum] = fun
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
xgb.NewExtEventFuncs["XINERAMA"] = make(map[int]xgb.NewEventFun)
|
||||
xgb.NewExtErrorFuncs["XINERAMA"] = make(map[int]xgb.NewErrorFun)
|
||||
}
|
||||
|
||||
type ScreenInfo struct {
|
||||
XOrg int16
|
||||
YOrg int16
|
||||
Width uint16
|
||||
Height uint16
|
||||
}
|
||||
|
||||
// ScreenInfoRead reads a byte slice into a ScreenInfo value.
|
||||
func ScreenInfoRead(buf []byte, v *ScreenInfo) int {
|
||||
b := 0
|
||||
|
||||
v.XOrg = int16(xgb.Get16(buf[b:]))
|
||||
b += 2
|
||||
|
||||
v.YOrg = int16(xgb.Get16(buf[b:]))
|
||||
b += 2
|
||||
|
||||
v.Width = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Height = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// ScreenInfoReadList reads a byte slice into a list of ScreenInfo values.
|
||||
func ScreenInfoReadList(buf []byte, dest []ScreenInfo) int {
|
||||
b := 0
|
||||
for i := 0; i < len(dest); i++ {
|
||||
dest[i] = ScreenInfo{}
|
||||
b += ScreenInfoRead(buf[b:], &dest[i])
|
||||
}
|
||||
return xgb.Pad(b)
|
||||
}
|
||||
|
||||
// Bytes writes a ScreenInfo value to a byte slice.
|
||||
func (v ScreenInfo) Bytes() []byte {
|
||||
buf := make([]byte, 8)
|
||||
b := 0
|
||||
|
||||
xgb.Put16(buf[b:], uint16(v.XOrg))
|
||||
b += 2
|
||||
|
||||
xgb.Put16(buf[b:], uint16(v.YOrg))
|
||||
b += 2
|
||||
|
||||
xgb.Put16(buf[b:], v.Width)
|
||||
b += 2
|
||||
|
||||
xgb.Put16(buf[b:], v.Height)
|
||||
b += 2
|
||||
|
||||
return buf[:b]
|
||||
}
|
||||
|
||||
// ScreenInfoListBytes writes a list of ScreenInfo values to a byte slice.
|
||||
func ScreenInfoListBytes(buf []byte, list []ScreenInfo) int {
|
||||
b := 0
|
||||
var structBytes []byte
|
||||
for _, item := range list {
|
||||
structBytes = item.Bytes()
|
||||
copy(buf[b:], structBytes)
|
||||
b += len(structBytes)
|
||||
}
|
||||
return xgb.Pad(b)
|
||||
}
|
||||
|
||||
// Skipping definition for base type 'Bool'
|
||||
|
||||
// Skipping definition for base type 'Byte'
|
||||
|
||||
// Skipping definition for base type 'Card8'
|
||||
|
||||
// Skipping definition for base type 'Char'
|
||||
|
||||
// Skipping definition for base type 'Void'
|
||||
|
||||
// Skipping definition for base type 'Double'
|
||||
|
||||
// Skipping definition for base type 'Float'
|
||||
|
||||
// Skipping definition for base type 'Int16'
|
||||
|
||||
// Skipping definition for base type 'Int32'
|
||||
|
||||
// Skipping definition for base type 'Int8'
|
||||
|
||||
// Skipping definition for base type 'Card16'
|
||||
|
||||
// Skipping definition for base type 'Card32'
|
||||
|
||||
// GetScreenCountCookie is a cookie used only for GetScreenCount requests.
|
||||
type GetScreenCountCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// GetScreenCount sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling GetScreenCountCookie.Reply()
|
||||
func GetScreenCount(c *xgb.Conn, Window xproto.Window) GetScreenCountCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetScreenCount' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(getScreenCountRequest(c, Window), cookie)
|
||||
return GetScreenCountCookie{cookie}
|
||||
}
|
||||
|
||||
// GetScreenCountUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func GetScreenCountUnchecked(c *xgb.Conn, Window xproto.Window) GetScreenCountCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetScreenCount' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(getScreenCountRequest(c, Window), cookie)
|
||||
return GetScreenCountCookie{cookie}
|
||||
}
|
||||
|
||||
// GetScreenCountReply represents the data returned from a GetScreenCount request.
|
||||
type GetScreenCountReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
ScreenCount byte
|
||||
Window xproto.Window
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a GetScreenCount request.
|
||||
func (cook GetScreenCountCookie) Reply() (*GetScreenCountReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return getScreenCountReply(buf), nil
|
||||
}
|
||||
|
||||
// getScreenCountReply reads a byte slice into a GetScreenCountReply value.
|
||||
func getScreenCountReply(buf []byte) *GetScreenCountReply {
|
||||
v := new(GetScreenCountReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
v.ScreenCount = buf[b]
|
||||
b += 1
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.Window = xproto.Window(xgb.Get32(buf[b:]))
|
||||
b += 4
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for GetScreenCount
|
||||
// getScreenCountRequest writes a GetScreenCount request to a byte slice.
|
||||
func getScreenCountRequest(c *xgb.Conn, Window xproto.Window) []byte {
|
||||
size := 8
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 2 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
xgb.Put32(buf[b:], uint32(Window))
|
||||
b += 4
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// GetScreenSizeCookie is a cookie used only for GetScreenSize requests.
|
||||
type GetScreenSizeCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// GetScreenSize sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling GetScreenSizeCookie.Reply()
|
||||
func GetScreenSize(c *xgb.Conn, Window xproto.Window, Screen uint32) GetScreenSizeCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetScreenSize' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(getScreenSizeRequest(c, Window, Screen), cookie)
|
||||
return GetScreenSizeCookie{cookie}
|
||||
}
|
||||
|
||||
// GetScreenSizeUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func GetScreenSizeUnchecked(c *xgb.Conn, Window xproto.Window, Screen uint32) GetScreenSizeCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetScreenSize' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(getScreenSizeRequest(c, Window, Screen), cookie)
|
||||
return GetScreenSizeCookie{cookie}
|
||||
}
|
||||
|
||||
// GetScreenSizeReply represents the data returned from a GetScreenSize request.
|
||||
type GetScreenSizeReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
// padding: 1 bytes
|
||||
Width uint32
|
||||
Height uint32
|
||||
Window xproto.Window
|
||||
Screen uint32
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a GetScreenSize request.
|
||||
func (cook GetScreenSizeCookie) Reply() (*GetScreenSizeReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return getScreenSizeReply(buf), nil
|
||||
}
|
||||
|
||||
// getScreenSizeReply reads a byte slice into a GetScreenSizeReply value.
|
||||
func getScreenSizeReply(buf []byte) *GetScreenSizeReply {
|
||||
v := new(GetScreenSizeReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
b += 1 // padding
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.Width = xgb.Get32(buf[b:])
|
||||
b += 4
|
||||
|
||||
v.Height = xgb.Get32(buf[b:])
|
||||
b += 4
|
||||
|
||||
v.Window = xproto.Window(xgb.Get32(buf[b:]))
|
||||
b += 4
|
||||
|
||||
v.Screen = xgb.Get32(buf[b:])
|
||||
b += 4
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for GetScreenSize
|
||||
// getScreenSizeRequest writes a GetScreenSize request to a byte slice.
|
||||
func getScreenSizeRequest(c *xgb.Conn, Window xproto.Window, Screen uint32) []byte {
|
||||
size := 12
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 3 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
xgb.Put32(buf[b:], uint32(Window))
|
||||
b += 4
|
||||
|
||||
xgb.Put32(buf[b:], Screen)
|
||||
b += 4
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// GetStateCookie is a cookie used only for GetState requests.
|
||||
type GetStateCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// GetState sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling GetStateCookie.Reply()
|
||||
func GetState(c *xgb.Conn, Window xproto.Window) GetStateCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetState' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(getStateRequest(c, Window), cookie)
|
||||
return GetStateCookie{cookie}
|
||||
}
|
||||
|
||||
// GetStateUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func GetStateUnchecked(c *xgb.Conn, Window xproto.Window) GetStateCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'GetState' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(getStateRequest(c, Window), cookie)
|
||||
return GetStateCookie{cookie}
|
||||
}
|
||||
|
||||
// GetStateReply represents the data returned from a GetState request.
|
||||
type GetStateReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
State byte
|
||||
Window xproto.Window
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a GetState request.
|
||||
func (cook GetStateCookie) Reply() (*GetStateReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return getStateReply(buf), nil
|
||||
}
|
||||
|
||||
// getStateReply reads a byte slice into a GetStateReply value.
|
||||
func getStateReply(buf []byte) *GetStateReply {
|
||||
v := new(GetStateReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
v.State = buf[b]
|
||||
b += 1
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.Window = xproto.Window(xgb.Get32(buf[b:]))
|
||||
b += 4
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for GetState
|
||||
// getStateRequest writes a GetState request to a byte slice.
|
||||
func getStateRequest(c *xgb.Conn, Window xproto.Window) []byte {
|
||||
size := 8
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 1 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
xgb.Put32(buf[b:], uint32(Window))
|
||||
b += 4
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// IsActiveCookie is a cookie used only for IsActive requests.
|
||||
type IsActiveCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// IsActive sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling IsActiveCookie.Reply()
|
||||
func IsActive(c *xgb.Conn) IsActiveCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'IsActive' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(isActiveRequest(c), cookie)
|
||||
return IsActiveCookie{cookie}
|
||||
}
|
||||
|
||||
// IsActiveUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func IsActiveUnchecked(c *xgb.Conn) IsActiveCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'IsActive' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(isActiveRequest(c), cookie)
|
||||
return IsActiveCookie{cookie}
|
||||
}
|
||||
|
||||
// IsActiveReply represents the data returned from a IsActive request.
|
||||
type IsActiveReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
// padding: 1 bytes
|
||||
State uint32
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a IsActive request.
|
||||
func (cook IsActiveCookie) Reply() (*IsActiveReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return isActiveReply(buf), nil
|
||||
}
|
||||
|
||||
// isActiveReply reads a byte slice into a IsActiveReply value.
|
||||
func isActiveReply(buf []byte) *IsActiveReply {
|
||||
v := new(IsActiveReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
b += 1 // padding
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.State = xgb.Get32(buf[b:])
|
||||
b += 4
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for IsActive
|
||||
// isActiveRequest writes a IsActive request to a byte slice.
|
||||
func isActiveRequest(c *xgb.Conn) []byte {
|
||||
size := 4
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 4 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// QueryScreensCookie is a cookie used only for QueryScreens requests.
|
||||
type QueryScreensCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// QueryScreens sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling QueryScreensCookie.Reply()
|
||||
func QueryScreens(c *xgb.Conn) QueryScreensCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'QueryScreens' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(queryScreensRequest(c), cookie)
|
||||
return QueryScreensCookie{cookie}
|
||||
}
|
||||
|
||||
// QueryScreensUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func QueryScreensUnchecked(c *xgb.Conn) QueryScreensCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'QueryScreens' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(queryScreensRequest(c), cookie)
|
||||
return QueryScreensCookie{cookie}
|
||||
}
|
||||
|
||||
// QueryScreensReply represents the data returned from a QueryScreens request.
|
||||
type QueryScreensReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
// padding: 1 bytes
|
||||
Number uint32
|
||||
// padding: 20 bytes
|
||||
ScreenInfo []ScreenInfo // size: xgb.Pad((int(Number) * 8))
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a QueryScreens request.
|
||||
func (cook QueryScreensCookie) Reply() (*QueryScreensReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return queryScreensReply(buf), nil
|
||||
}
|
||||
|
||||
// queryScreensReply reads a byte slice into a QueryScreensReply value.
|
||||
func queryScreensReply(buf []byte) *QueryScreensReply {
|
||||
v := new(QueryScreensReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
b += 1 // padding
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.Number = xgb.Get32(buf[b:])
|
||||
b += 4
|
||||
|
||||
b += 20 // padding
|
||||
|
||||
v.ScreenInfo = make([]ScreenInfo, v.Number)
|
||||
b += ScreenInfoReadList(buf[b:], v.ScreenInfo)
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for QueryScreens
|
||||
// queryScreensRequest writes a QueryScreens request to a byte slice.
|
||||
func queryScreensRequest(c *xgb.Conn) []byte {
|
||||
size := 4
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 5 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// QueryVersionCookie is a cookie used only for QueryVersion requests.
|
||||
type QueryVersionCookie struct {
|
||||
*xgb.Cookie
|
||||
}
|
||||
|
||||
// QueryVersion sends a checked request.
|
||||
// If an error occurs, it will be returned with the reply by calling QueryVersionCookie.Reply()
|
||||
func QueryVersion(c *xgb.Conn, Major byte, Minor byte) QueryVersionCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(true, true)
|
||||
c.NewRequest(queryVersionRequest(c, Major, Minor), cookie)
|
||||
return QueryVersionCookie{cookie}
|
||||
}
|
||||
|
||||
// QueryVersionUnchecked sends an unchecked request.
|
||||
// If an error occurs, it can only be retrieved using xgb.WaitForEvent or xgb.PollForEvent.
|
||||
func QueryVersionUnchecked(c *xgb.Conn, Major byte, Minor byte) QueryVersionCookie {
|
||||
c.ExtLock.RLock()
|
||||
defer c.ExtLock.RUnlock()
|
||||
if _, ok := c.Extensions["XINERAMA"]; !ok {
|
||||
panic("Cannot issue request 'QueryVersion' using the uninitialized extension 'XINERAMA'. xinerama.Init(connObj) must be called first.")
|
||||
}
|
||||
cookie := c.NewCookie(false, true)
|
||||
c.NewRequest(queryVersionRequest(c, Major, Minor), cookie)
|
||||
return QueryVersionCookie{cookie}
|
||||
}
|
||||
|
||||
// QueryVersionReply represents the data returned from a QueryVersion request.
|
||||
type QueryVersionReply struct {
|
||||
Sequence uint16 // sequence number of the request for this reply
|
||||
Length uint32 // number of bytes in this reply
|
||||
// padding: 1 bytes
|
||||
Major uint16
|
||||
Minor uint16
|
||||
}
|
||||
|
||||
// Reply blocks and returns the reply data for a QueryVersion request.
|
||||
func (cook QueryVersionCookie) Reply() (*QueryVersionReply, error) {
|
||||
buf, err := cook.Cookie.Reply()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return queryVersionReply(buf), nil
|
||||
}
|
||||
|
||||
// queryVersionReply reads a byte slice into a QueryVersionReply value.
|
||||
func queryVersionReply(buf []byte) *QueryVersionReply {
|
||||
v := new(QueryVersionReply)
|
||||
b := 1 // skip reply determinant
|
||||
|
||||
b += 1 // padding
|
||||
|
||||
v.Sequence = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Length = xgb.Get32(buf[b:]) // 4-byte units
|
||||
b += 4
|
||||
|
||||
v.Major = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
v.Minor = xgb.Get16(buf[b:])
|
||||
b += 2
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Write request to wire for QueryVersion
|
||||
// queryVersionRequest writes a QueryVersion request to a byte slice.
|
||||
func queryVersionRequest(c *xgb.Conn, Major byte, Minor byte) []byte {
|
||||
size := 8
|
||||
b := 0
|
||||
buf := make([]byte, size)
|
||||
|
||||
c.ExtLock.RLock()
|
||||
buf[b] = c.Extensions["XINERAMA"]
|
||||
c.ExtLock.RUnlock()
|
||||
b += 1
|
||||
|
||||
buf[b] = 0 // request opcode
|
||||
b += 1
|
||||
|
||||
xgb.Put16(buf[b:], uint16(size/4)) // write request size in 4-byte units
|
||||
b += 2
|
||||
|
||||
buf[b] = Major
|
||||
b += 1
|
||||
|
||||
buf[b] = Minor
|
||||
b += 1
|
||||
|
||||
return buf
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
*.swp
|
||||
*.png
|
||||
tst_first
|
||||
tst_graphics
|
||||
TAGS
|
||||
|
@ -0,0 +1,13 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
@ -0,0 +1,36 @@
|
||||
all: callback.go types_auto.go gofmt
|
||||
|
||||
install:
|
||||
go install -p 6 . ./ewmh ./gopher ./icccm ./keybind ./motif ./mousebind \
|
||||
./xcursor ./xevent ./xgraphics ./xinerama ./xprop ./xrect ./xwindow
|
||||
|
||||
push:
|
||||
git push origin master
|
||||
git push github master
|
||||
|
||||
build-ex:
|
||||
find ./_examples/ -type d -wholename './_examples/[a-z]*' -print0 \
|
||||
| xargs -0 go build -p 6
|
||||
|
||||
gofmt:
|
||||
gofmt -w *.go */*.go _examples/*/*.go
|
||||
colcheck *.go */*.go _examples/*/*.go
|
||||
|
||||
callback.go:
|
||||
scripts/write-events callbacks > xevent/callback.go
|
||||
|
||||
types_auto.go:
|
||||
scripts/write-events evtypes > xevent/types_auto.go
|
||||
|
||||
tags:
|
||||
find ./ \( -name '*.go' -and -not -wholename './tests/*' -and -not -wholename './_examples/*' \) -print0 | xargs -0 gotags > TAGS
|
||||
|
||||
loc:
|
||||
find ./ -name '*.go' -and -not -wholename './tests*' -and -not -name '*keysymdef.go' -and -not -name '*gopher.go' -print | sort | xargs wc -l
|
||||
|
||||
ex-%:
|
||||
go run _examples/$*/main.go
|
||||
|
||||
gopherimg:
|
||||
go-bindata -f GopherPng -p gopher -i gopher/gophercolor-small.png -o gopher/gopher.go
|
||||
|
@ -0,0 +1,55 @@
|
||||
xgbutil is a utility library designed to work with the X Go Binding. This
|
||||
project's main goal is to make various X related tasks easier. For example,
|
||||
binding keys, using the EWMH or ICCCM specs with the window manager,
|
||||
moving/resizing windows, assigning function callbacks to particular events,
|
||||
drawing images to a window, etc.
|
||||
|
||||
xgbutil attempts to be thread safe, but it has not been completely tested in
|
||||
this regard. In general, the X event loop implemented in the xevent package is
|
||||
sequential. The idea is to be sequential by default, and let the user spawn
|
||||
concurrent code at their discretion. (i.e., the complexity of making the main
|
||||
event loop generally concurrent is vast.)
|
||||
|
||||
You may sleep safely at night by assuming that XGB is thread safe, though.
|
||||
|
||||
To start using xgbutil, you should have at least a passing familiarity with X.
|
||||
Your first stop should be the examples directory.
|
||||
|
||||
Installation
|
||||
============
|
||||
go get github.com/BurntSushi/xgbutil
|
||||
|
||||
Dependencies
|
||||
============
|
||||
XGB is the main dependency. Use of the xgraphics packages requires graphics-go
|
||||
and freetype-go.
|
||||
|
||||
XGB project URL: https://github.com/BurntSushi/xgb
|
||||
|
||||
Quick Example
|
||||
=============
|
||||
go get github.com/BurntSushi/xgbutil/_examples/window-name-sizes
|
||||
"$GOPATH"/bin/window-name-sizes
|
||||
|
||||
The output will be a list of names of all top-level windows and their geometry
|
||||
including window manager decorations. (Assuming your window manager supports
|
||||
some basic EWMH properties.)
|
||||
|
||||
Documentation
|
||||
=============
|
||||
https://godoc.org/github.com/BurntSushi/xgbutil
|
||||
|
||||
Examples
|
||||
========
|
||||
There are several examples in the examples directory covering common use cases.
|
||||
They are heavily documented and should run out of the box.
|
||||
|
||||
Python
|
||||
======
|
||||
An older project of mine, xpybutil, served as inspiration for xgbutil. If you
|
||||
want to use Python, xpybutil should help quite a bit. Please note though, that
|
||||
at this point, xgbutil provides a lot more functionality and is much better
|
||||
documented.
|
||||
|
||||
xpybutil project URL: https://github.com/BurntSushi/xpybutil
|
||||
|
@ -0,0 +1,29 @@
|
||||
I like to keep all my code to 80 columns or less. I have plenty of screen real
|
||||
estate, but enjoy 80 columns so that I can have multiple code windows open side
|
||||
to side and not be plagued by the ugly auto-wrapping of a text editor.
|
||||
|
||||
If you don't oblige me, I will fix any patch you submit to abide 80 columns.
|
||||
|
||||
Note that this style restriction does not preclude gofmt, but introduces a few
|
||||
peculiarities. The first is that gofmt will occasionally add spacing (typically
|
||||
to comments) that ends up going over 80 columns. Either shorten the comment or
|
||||
put it on its own line.
|
||||
|
||||
The second and more common hiccup is when a function definition extends beyond
|
||||
80 columns. If one adds line breaks to keep it below 80 columns, gofmt will
|
||||
indent all subsequent lines in a function definition to the same indentation
|
||||
level of the function body. This results in a less-than-ideal separation
|
||||
between function definition and function body. To remedy this, simply add a
|
||||
line break like so:
|
||||
|
||||
func RestackWindowExtra(xu *xgbutil.XUtil, win xproto.Window, stackMode int,
|
||||
sibling xproto.Window, source int) error {
|
||||
|
||||
return ClientEvent(xu, win, "_NET_RESTACK_WINDOW", source, int(sibling),
|
||||
stackMode)
|
||||
}
|
||||
|
||||
Something similar should also be applied to long 'if' or 'for' conditionals,
|
||||
although it would probably be preferrable to break up the conditional to
|
||||
smaller chunks with a few helper variables.
|
||||
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
Package xgbutil is a utility library designed to make common tasks with the X
|
||||
server easier. The central design choice that has driven development is to hide
|
||||
the complexity of X wherever possible but expose it when necessary.
|
||||
|
||||
For example, the xevent package provides an implementation of an X event loop
|
||||
that acts as a dispatcher to event handlers set up with the xevent, keybind and
|
||||
mousebind packages. At the same time, the event queue is exposed and can be
|
||||
modified using xevent.Peek and xevent.DequeueAt.
|
||||
|
||||
Sub-packages
|
||||
|
||||
The xgbutil package is considerably small, and only contains some type
|
||||
definitions and the initial setup for an X connection. Much of the
|
||||
functionality of xgbutil comes from its sub-packages. Each sub-package is
|
||||
appropriately documented.
|
||||
|
||||
Installation
|
||||
|
||||
xgbutil is go-gettable:
|
||||
|
||||
go get github.com/BurntSushi/xgbutil
|
||||
|
||||
Dependencies
|
||||
|
||||
XGB is the main dependency, and is required for all packages inside xgbutil.
|
||||
|
||||
graphics-go and freetype-go are also required if using the xgraphics package.
|
||||
|
||||
Quick Example
|
||||
|
||||
A quick example to demonstrate that xgbutil is working correctly:
|
||||
|
||||
go get github.com/BurntSushi/xgbutil/examples/window-name-sizes
|
||||
GO/PATH/bin/window-name-sizes
|
||||
|
||||
The output will be a list of names of all top-level windows and their geometry
|
||||
including window manager decorations. (Assuming your window manager supports
|
||||
some basic EWMH properties.)
|
||||
|
||||
Examples
|
||||
|
||||
The examples directory contains a sizable number of examples demonstrating
|
||||
common tasks with X. They are intended to demonstrate a single thing each,
|
||||
although a few that require setup are necessarily long. Each example is
|
||||
heavily documented.
|
||||
|
||||
The examples directory should be your first stop when learning how to use
|
||||
xgbutil.
|
||||
|
||||
xgbutil is also used heavily throughout my window manager, Wingo. It may be
|
||||
useful reference material.
|
||||
|
||||
Wingo project page: https://github.com/BurntSushi/wingo
|
||||
|
||||
Thread Safety
|
||||
|
||||
While I am fairly confident that XGB is thread safe, I am only somewhat
|
||||
confident that xgbutil is thread safe. It simply has not been tested enough for
|
||||
my confidence to be higher.
|
||||
|
||||
Note that the xevent package's X event loop is not concurrent. Namely,
|
||||
designing a generally concurrent X event loop is extremely complex. Instead,
|
||||
the onus is on you, the user, to design concurrent callback functions if
|
||||
concurrency is desired.
|
||||
*/
|
||||
package xgbutil
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Package ewmh provides a comprehensive API to get and set properties specified
|
||||
by the EWMH spec, as well as perform actions specified by the EWMH spec.
|
||||
|
||||
Since there are so many functions and they adhere to an existing spec,
|
||||
this package file does not contain much documentation. Indeed, each
|
||||
method has only a single comment associated with it: the EWMH property name.
|
||||
|
||||
The idea is to
|
||||
provide a consistent interface to use all facilities described in the EWMH
|
||||
spec: http://standards.freedesktop.org/wm-spec/wm-spec-latest.html.
|
||||
|
||||
Naming scheme
|
||||
|
||||
Using "_NET_ACTIVE_WINDOW" as an example,
|
||||
functions "ActiveWindowGet" and "ActiveWindowSet" get and set the
|
||||
property, respectively. Both of these functions exist for most EWMH
|
||||
properties. Additionally, some EWMH properties support sending a client
|
||||
message event to request the window manager to perform some action. In the
|
||||
case of "_NET_ACTIVE_WINDOW", this request is used to set the active
|
||||
window.
|
||||
|
||||
These sorts of functions end in "Req". So for "_NET_ACTIVE_WINDOW",
|
||||
the method name is "ActiveWindowReq". Moreover, most requests include
|
||||
various parameters that don't need to be changed often (like the source
|
||||
indication). Thus, by default, functions ending in "Req" force these to
|
||||
sensible defaults. If you need access to all of the parameters, use the
|
||||
corresponding "ReqExtra" method. So for "_NET_ACTIVE_WINDOW", that would
|
||||
be "ActiveWindowReqExtra". (If no "ReqExtra" method exists, then the
|
||||
"Req" method covers all available parameters.)
|
||||
|
||||
This naming scheme has one exception: if a property's only use is through
|
||||
sending an event (like "_NET_CLOSE_WINDOW"), then the name will be
|
||||
"CloseWindow" for the short-hand version and "CloseWindowExtra"
|
||||
for access to all of the parameters. (Since there is no "_NET_CLOSE_WINDOW"
|
||||
property, there is no need for "CloseWindowGet" and "CloseWindowSet"
|
||||
functions.)
|
||||
|
||||
For properties that store more than just a simple integer, name or list
|
||||
of integers, structs have been created and exposed to organize the
|
||||
information returned in a sensible manner. For example, the
|
||||
"_NET_DESKTOP_GEOMETRY" property would typically return a slice of integers
|
||||
of length 2, where the first integer is the width and the second is the
|
||||
height. Xgbutil will wrap this in a struct with the obvious members. These
|
||||
structs are documented.
|
||||
|
||||
Finally, functions ending in "*Set" are typically only used when setting
|
||||
properties on clients *you've* created or when the window manager sets
|
||||
properties. Thus, it's unlikely that you should use them unless you're
|
||||
creating a top-level client or building a window manager.
|
||||
|
||||
Functions ending in "Get" or "Req[Extra]" are commonly used.
|
||||
|
||||
N.B. Not all properties have "*Req" functions.
|
||||
*/
|
||||
package ewmh
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,31 @@
|
||||
package ewmh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
// GetEwmhWM uses the EWMH spec to find if a conforming window manager
|
||||
// is currently running or not. If it is, then its name will be returned.
|
||||
// Otherwise, an error will be returned explaining why one couldn't be found.
|
||||
func GetEwmhWM(xu *xgbutil.XUtil) (string, error) {
|
||||
childCheck, err := SupportingWmCheckGet(xu, xu.RootWin())
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("GetEwmhWM: Failed because: %s", err)
|
||||
}
|
||||
|
||||
childCheck2, err := SupportingWmCheckGet(xu, childCheck)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("GetEwmhWM: Failed because: %s", err)
|
||||
}
|
||||
|
||||
if childCheck != childCheck2 {
|
||||
return "", fmt.Errorf(
|
||||
"GetEwmhWM: _NET_SUPPORTING_WM_CHECK value on the root window "+
|
||||
"(%x) does not match _NET_SUPPORTING_WM_CHECK value "+
|
||||
"on the child window (%x).", childCheck, childCheck2)
|
||||
}
|
||||
|
||||
return WmNameGet(xu, childCheck)
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
Package icccm provides an API for a portion of the ICCCM, namely, getters
|
||||
and setters for many of the properties specified in the ICCCM. There is also a
|
||||
smattering of support for other protocols specified by ICCCM. For example, to
|
||||
satisfy the WM_DELETE_WINDOW protocol, package icccm provides 'IsDeleteProtocol'
|
||||
which returns whether a ClientMessage event satisfies the WM_DELETE_WINDOW
|
||||
protocol.
|
||||
|
||||
If a property has values that aren't simple strings or integers, struct types
|
||||
are provided to organize the data. In particular, WM_NORMAL_HINTS and WM_HINTS.
|
||||
|
||||
Also note that properties like WM_NORMAL_HINTS and WM_HINTS contain a 'Flags'
|
||||
field (a bit mask) that specifies which values are "active". This is of
|
||||
importance when setting and reading WM_NORMAL_HINTS and WM_HINTS; one must make
|
||||
sure the appropriate bit is set in Flags.
|
||||
|
||||
For example, you might want to check if a window has specified a resize
|
||||
increment in the WM_NORMAL_HINTS property. The values in the corresponding
|
||||
NormalHints struct are WidthInc and HeightInc. So to check if such values exist
|
||||
*and* should be used:
|
||||
|
||||
normalHints, err := icccm.WmNormalHintsGet(XUtilValue, window-id)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
if normalHints.Flags&icccm.SizeHintPResizeInc > 0 {
|
||||
// Use normalHints.WidthInc and normalHints.HeightInc
|
||||
}
|
||||
|
||||
When you should use icccm
|
||||
|
||||
Although the ICCCM is extremely old, a lot of it is still used. In fact, the
|
||||
EWMH spec itself specifically states that the ICCCM should still be used unless
|
||||
otherwise noted by the EWMH. For example, WM_HINTS and WM_NORMAL_HINTS are
|
||||
still used, but _NET_WM_NAME replaces WM_NAME.
|
||||
|
||||
With that said, many applications (like xterm or LibreOffice) have not been
|
||||
updated to be fully EWMH compliant. Therefore, code that finds a window's name
|
||||
often looks like this:
|
||||
|
||||
winName, err := ewmh.WmNameGet(XUtilValue, window-id)
|
||||
if err != nil || winName == "" {
|
||||
winName, err = icccm.WmNameGet(XUtilValue, window-id)
|
||||
if err != nill || winName == "" {
|
||||
winName = "N/A"
|
||||
}
|
||||
}
|
||||
|
||||
Something similar can be said for the _NET_WM_ICON and the IconPixmap field
|
||||
in WM_HINTS.
|
||||
|
||||
Naming scheme
|
||||
|
||||
The naming scheme is precisely the same as the one found in the ewmh package.
|
||||
The documentation for the ewmh package describes the naming scheme in more
|
||||
detail. The only difference (currently) is that the icccm package only contains
|
||||
functions ending in "Get" and "Set". It is planned to add "Req" functions. (An
|
||||
example of a Req function would be to send a ClientMessage implementing the
|
||||
WM_DELETE_WINDOW protocol to a client window.)
|
||||
*/
|
||||
package icccm
|
@ -0,0 +1,358 @@
|
||||
package icccm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
"github.com/BurntSushi/xgbutil/xprop"
|
||||
)
|
||||
|
||||
const (
|
||||
HintInput = (1 << iota)
|
||||
HintState
|
||||
HintIconPixmap
|
||||
HintIconWindow
|
||||
HintIconPosition
|
||||
HintIconMask
|
||||
HintWindowGroup
|
||||
HintMessage
|
||||
HintUrgency
|
||||
)
|
||||
|
||||
const (
|
||||
SizeHintUSPosition = (1 << iota)
|
||||
SizeHintUSSize
|
||||
SizeHintPPosition
|
||||
SizeHintPSize
|
||||
SizeHintPMinSize
|
||||
SizeHintPMaxSize
|
||||
SizeHintPResizeInc
|
||||
SizeHintPAspect
|
||||
SizeHintPBaseSize
|
||||
SizeHintPWinGravity
|
||||
)
|
||||
|
||||
const (
|
||||
StateWithdrawn = iota
|
||||
StateNormal
|
||||
StateZoomed
|
||||
StateIconic
|
||||
StateInactive
|
||||
)
|
||||
|
||||
// WM_NAME get
|
||||
func WmNameGet(xu *xgbutil.XUtil, win xproto.Window) (string, error) {
|
||||
return xprop.PropValStr(xprop.GetProperty(xu, win, "WM_NAME"))
|
||||
}
|
||||
|
||||
// WM_NAME set
|
||||
func WmNameSet(xu *xgbutil.XUtil, win xproto.Window, name string) error {
|
||||
return xprop.ChangeProp(xu, win, 8, "WM_NAME", "STRING", ([]byte)(name))
|
||||
}
|
||||
|
||||
// WM_ICON_NAME get
|
||||
func WmIconNameGet(xu *xgbutil.XUtil, win xproto.Window) (string, error) {
|
||||
return xprop.PropValStr(xprop.GetProperty(xu, win, "WM_ICON_NAME"))
|
||||
}
|
||||
|
||||
// WM_ICON_NAME set
|
||||
func WmIconNameSet(xu *xgbutil.XUtil, win xproto.Window, name string) error {
|
||||
return xprop.ChangeProp(xu, win, 8, "WM_ICON_NAME", "STRING",
|
||||
([]byte)(name))
|
||||
}
|
||||
|
||||
// NormalHints is a struct that organizes the information related to the
|
||||
// WM_NORMAL_HINTS property. Please see the ICCCM spec for more details.
|
||||
type NormalHints struct {
|
||||
Flags uint
|
||||
X, Y int
|
||||
Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight uint
|
||||
WidthInc, HeightInc uint
|
||||
MinAspectNum, MinAspectDen, MaxAspectNum, MaxAspectDen uint
|
||||
BaseWidth, BaseHeight, WinGravity uint
|
||||
}
|
||||
|
||||
// WM_NORMAL_HINTS get
|
||||
func WmNormalHintsGet(xu *xgbutil.XUtil,
|
||||
win xproto.Window) (nh *NormalHints, err error) {
|
||||
|
||||
lenExpect := 18
|
||||
hints, err := xprop.PropValNums(xprop.GetProperty(xu, win,
|
||||
"WM_NORMAL_HINTS"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(hints) != lenExpect {
|
||||
return nil,
|
||||
fmt.Errorf("WmNormalHint: There are %d fields in WM_NORMAL_HINTS, "+
|
||||
"but xgbutil expects %d.", len(hints), lenExpect)
|
||||
}
|
||||
|
||||
nh = &NormalHints{}
|
||||
nh.Flags = hints[0]
|
||||
nh.X = int(hints[1])
|
||||
nh.Y = int(hints[2])
|
||||
nh.Width = hints[3]
|
||||
nh.Height = hints[4]
|
||||
nh.MinWidth = hints[5]
|
||||
nh.MinHeight = hints[6]
|
||||
nh.MaxWidth = hints[7]
|
||||
nh.MaxHeight = hints[8]
|
||||
nh.WidthInc = hints[9]
|
||||
nh.HeightInc = hints[10]
|
||||
nh.MinAspectNum = hints[11]
|
||||
nh.MinAspectDen = hints[12]
|
||||
nh.MaxAspectNum = hints[13]
|
||||
nh.MaxAspectDen = hints[14]
|
||||
nh.BaseWidth = hints[15]
|
||||
nh.BaseHeight = hints[16]
|
||||
nh.WinGravity = hints[17]
|
||||
|
||||
if nh.WinGravity <= 0 {
|
||||
nh.WinGravity = xproto.GravityNorthWest
|
||||
}
|
||||
|
||||
return nh, nil
|
||||
}
|
||||
|
||||
// WM_NORMAL_HINTS set
|
||||
// Make sure to set the flags in the NormalHints struct correctly!
|
||||
func WmNormalHintsSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
nh *NormalHints) error {
|
||||
|
||||
raw := []uint{
|
||||
nh.Flags,
|
||||
uint(nh.X), uint(nh.Y), nh.Width, nh.Height,
|
||||
nh.MinWidth, nh.MinHeight,
|
||||
nh.MaxWidth, nh.MaxHeight,
|
||||
nh.WidthInc, nh.HeightInc,
|
||||
nh.MinAspectNum, nh.MinAspectDen,
|
||||
nh.MaxAspectNum, nh.MaxAspectDen,
|
||||
nh.BaseWidth, nh.BaseHeight,
|
||||
nh.WinGravity,
|
||||
}
|
||||
return xprop.ChangeProp32(xu, win, "WM_NORMAL_HINTS", "WM_SIZE_HINTS",
|
||||
raw...)
|
||||
}
|
||||
|
||||
// Hints is a struct that organizes information related to the WM_HINTS
|
||||
// property. Once again, I refer you to the ICCCM spec for documentation.
|
||||
type Hints struct {
|
||||
Flags uint
|
||||
Input, InitialState uint
|
||||
IconX, IconY int
|
||||
IconPixmap, IconMask xproto.Pixmap
|
||||
WindowGroup, IconWindow xproto.Window
|
||||
}
|
||||
|
||||
// WM_HINTS get
|
||||
func WmHintsGet(xu *xgbutil.XUtil,
|
||||
win xproto.Window) (hints *Hints, err error) {
|
||||
|
||||
lenExpect := 9
|
||||
raw, err := xprop.PropValNums(xprop.GetProperty(xu, win, "WM_HINTS"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(raw) != lenExpect {
|
||||
return nil,
|
||||
fmt.Errorf("WmHints: There are %d fields in "+
|
||||
"WM_HINTS, but xgbutil expects %d.", len(raw), lenExpect)
|
||||
}
|
||||
|
||||
hints = &Hints{}
|
||||
hints.Flags = raw[0]
|
||||
hints.Input = raw[1]
|
||||
hints.InitialState = raw[2]
|
||||
hints.IconPixmap = xproto.Pixmap(raw[3])
|
||||
hints.IconWindow = xproto.Window(raw[4])
|
||||
hints.IconX = int(raw[5])
|
||||
hints.IconY = int(raw[6])
|
||||
hints.IconMask = xproto.Pixmap(raw[7])
|
||||
hints.WindowGroup = xproto.Window(raw[8])
|
||||
|
||||
return hints, nil
|
||||
}
|
||||
|
||||
// WM_HINTS set
|
||||
// Make sure to set the flags in the Hints struct correctly!
|
||||
func WmHintsSet(xu *xgbutil.XUtil, win xproto.Window, hints *Hints) error {
|
||||
raw := []uint{
|
||||
hints.Flags, hints.Input, hints.InitialState,
|
||||
uint(hints.IconPixmap), uint(hints.IconWindow),
|
||||
uint(hints.IconX), uint(hints.IconY),
|
||||
uint(hints.IconMask),
|
||||
uint(hints.WindowGroup),
|
||||
}
|
||||
return xprop.ChangeProp32(xu, win, "WM_HINTS", "WM_HINTS", raw...)
|
||||
}
|
||||
|
||||
// WmClass struct contains two data points:
|
||||
// the instance and a class of a window.
|
||||
type WmClass struct {
|
||||
Instance, Class string
|
||||
}
|
||||
|
||||
// WM_CLASS get
|
||||
func WmClassGet(xu *xgbutil.XUtil, win xproto.Window) (*WmClass, error) {
|
||||
raw, err := xprop.PropValStrs(xprop.GetProperty(xu, win, "WM_CLASS"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(raw) != 2 {
|
||||
return nil,
|
||||
fmt.Errorf("WmClass: Two string make up WM_CLASS, but "+
|
||||
"xgbutil found %d in '%v'.", len(raw), raw)
|
||||
}
|
||||
|
||||
return &WmClass{
|
||||
Instance: raw[0],
|
||||
Class: raw[1],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WM_CLASS set
|
||||
func WmClassSet(xu *xgbutil.XUtil, win xproto.Window, class *WmClass) error {
|
||||
raw := make([]byte, len(class.Instance)+len(class.Class)+2)
|
||||
copy(raw, class.Instance)
|
||||
copy(raw[(len(class.Instance)+1):], class.Class)
|
||||
|
||||
return xprop.ChangeProp(xu, win, 8, "WM_CLASS", "STRING", raw)
|
||||
}
|
||||
|
||||
// WM_TRANSIENT_FOR get
|
||||
func WmTransientForGet(xu *xgbutil.XUtil,
|
||||
win xproto.Window) (xproto.Window, error) {
|
||||
|
||||
return xprop.PropValWindow(xprop.GetProperty(xu, win, "WM_TRANSIENT_FOR"))
|
||||
}
|
||||
|
||||
// WM_TRANSIENT_FOR set
|
||||
func WmTransientForSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
transient xproto.Window) error {
|
||||
|
||||
return xprop.ChangeProp32(xu, win, "WM_TRANSIENT_FOR", "WINDOW",
|
||||
uint(transient))
|
||||
}
|
||||
|
||||
// WM_PROTOCOLS get
|
||||
func WmProtocolsGet(xu *xgbutil.XUtil, win xproto.Window) ([]string, error) {
|
||||
raw, err := xprop.GetProperty(xu, win, "WM_PROTOCOLS")
|
||||
return xprop.PropValAtoms(xu, raw, err)
|
||||
}
|
||||
|
||||
// WM_PROTOCOLS set
|
||||
func WmProtocolsSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
atomNames []string) error {
|
||||
|
||||
atoms, err := xprop.StrToAtoms(xu, atomNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return xprop.ChangeProp32(xu, win, "WM_PROTOCOLS", "ATOM", atoms...)
|
||||
}
|
||||
|
||||
// WM_COLORMAP_WINDOWS get
|
||||
func WmColormapWindowsGet(xu *xgbutil.XUtil,
|
||||
win xproto.Window) ([]xproto.Window, error) {
|
||||
|
||||
return xprop.PropValWindows(xprop.GetProperty(xu, win,
|
||||
"WM_COLORMAP_WINDOWS"))
|
||||
}
|
||||
|
||||
// WM_COLORMAP_WINDOWS set
|
||||
func WmColormapWindowsSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
windows []xproto.Window) error {
|
||||
|
||||
return xprop.ChangeProp32(xu, win, "WM_COLORMAP_WINDOWS", "WINDOW",
|
||||
xprop.WindowToInt(windows)...)
|
||||
}
|
||||
|
||||
// WM_CLIENT_MACHINE get
|
||||
func WmClientMachineGet(xu *xgbutil.XUtil, win xproto.Window) (string, error) {
|
||||
return xprop.PropValStr(xprop.GetProperty(xu, win, "WM_CLIENT_MACHINE"))
|
||||
}
|
||||
|
||||
// WM_CLIENT_MACHINE set
|
||||
func WmClientMachineSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
client string) error {
|
||||
|
||||
return xprop.ChangeProp(xu, win, 8, "WM_CLIENT_MACHINE", "STRING",
|
||||
([]byte)(client))
|
||||
}
|
||||
|
||||
// WmState is a struct that organizes information related to the WM_STATE
|
||||
// property. Namely, the state (corresponding to a State* constant in this file)
|
||||
// and the icon window (probably not used).
|
||||
type WmState struct {
|
||||
State uint
|
||||
Icon xproto.Window
|
||||
}
|
||||
|
||||
// WM_STATE get
|
||||
func WmStateGet(xu *xgbutil.XUtil, win xproto.Window) (*WmState, error) {
|
||||
raw, err := xprop.PropValNums(xprop.GetProperty(xu, win, "WM_STATE"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(raw) != 2 {
|
||||
return nil,
|
||||
fmt.Errorf("WmState: Expected two integers in WM_STATE property "+
|
||||
"but xgbutil found %d in '%v'.", len(raw), raw)
|
||||
}
|
||||
|
||||
return &WmState{
|
||||
State: raw[0],
|
||||
Icon: xproto.Window(raw[1]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WM_STATE set
|
||||
func WmStateSet(xu *xgbutil.XUtil, win xproto.Window, state *WmState) error {
|
||||
raw := []uint{
|
||||
state.State,
|
||||
uint(state.Icon),
|
||||
}
|
||||
|
||||
return xprop.ChangeProp32(xu, win, "WM_STATE", "WM_STATE", raw...)
|
||||
}
|
||||
|
||||
// IconSize is a struct the organizes information related to the WM_ICON_SIZE
|
||||
// property. Mostly info about its dimensions.
|
||||
type IconSize struct {
|
||||
MinWidth, MinHeight, MaxWidth, MaxHeight, WidthInc, HeightInc uint
|
||||
}
|
||||
|
||||
// WM_ICON_SIZE get
|
||||
func WmIconSizeGet(xu *xgbutil.XUtil, win xproto.Window) (*IconSize, error) {
|
||||
raw, err := xprop.PropValNums(xprop.GetProperty(xu, win, "WM_ICON_SIZE"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(raw) != 6 {
|
||||
return nil,
|
||||
fmt.Errorf("WmIconSize: Expected six integers in WM_ICON_SIZE "+
|
||||
"property, but xgbutil found "+"%d in '%v'.", len(raw), raw)
|
||||
}
|
||||
|
||||
return &IconSize{
|
||||
MinWidth: raw[0], MinHeight: raw[1],
|
||||
MaxWidth: raw[2], MaxHeight: raw[3],
|
||||
WidthInc: raw[4], HeightInc: raw[5],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WM_ICON_SIZE set
|
||||
func WmIconSizeSet(xu *xgbutil.XUtil, win xproto.Window,
|
||||
icondim *IconSize) error {
|
||||
|
||||
raw := []uint{
|
||||
icondim.MinWidth, icondim.MinHeight,
|
||||
icondim.MaxWidth, icondim.MaxHeight,
|
||||
icondim.WidthInc, icondim.HeightInc,
|
||||
}
|
||||
|
||||
return xprop.ChangeProp32(xu, win, "WM_ICON_SIZE", "WM_ICON_SIZE", raw...)
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package icccm
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
"github.com/BurntSushi/xgbutil/xevent"
|
||||
"github.com/BurntSushi/xgbutil/xprop"
|
||||
)
|
||||
|
||||
// IsDeleteProtocol checks whether a ClientMessage event satisfies the
|
||||
// WM_DELETE_WINDOW protocol. Namely, the format must be 32, the type must
|
||||
// be the WM_PROTOCOLS atom, and the first data item must be the atom
|
||||
// WM_DELETE_WINDOW.
|
||||
//
|
||||
// Note that if you're using the xwindow package, you should use the
|
||||
// WMGracefulClose method instead of directly using IsDeleteProtocol.
|
||||
func IsDeleteProtocol(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) bool {
|
||||
// Make sure the Format is 32. (Meaning that each data item is
|
||||
// 32 bits.)
|
||||
if ev.Format != 32 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check to make sure the Type atom is WM_PROTOCOLS.
|
||||
typeName, err := xprop.AtomName(X, ev.Type)
|
||||
if err != nil || typeName != "WM_PROTOCOLS" { // not what we want
|
||||
return false
|
||||
}
|
||||
|
||||
// Check to make sure the first data item is WM_DELETE_WINDOW.
|
||||
protocolType, err := xprop.AtomName(X,
|
||||
xproto.Atom(ev.Data.Data32[0]))
|
||||
if err != nil || protocolType != "WM_DELETE_WINDOW" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsFocusProtocol checks whether a ClientMessage event satisfies the
|
||||
// WM_TAKE_FOCUS protocol.
|
||||
func IsFocusProtocol(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) bool {
|
||||
// Make sure the Format is 32. (Meaning that each data item is
|
||||
// 32 bits.)
|
||||
if ev.Format != 32 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check to make sure the Type atom is WM_PROTOCOLS.
|
||||
typeName, err := xprop.AtomName(X, ev.Type)
|
||||
if err != nil || typeName != "WM_PROTOCOLS" { // not what we want
|
||||
return false
|
||||
}
|
||||
|
||||
// Check to make sure the first data item is WM_TAKE_FOCUS.
|
||||
protocolType, err := xprop.AtomName(X,
|
||||
xproto.Atom(ev.Data.Data32[0]))
|
||||
if err != nil || protocolType != "WM_TAKE_FOCUS" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
@ -0,0 +1 @@
|
||||
au BufWritePost *.go silent!make tags > /dev/null 2>&1
|
@ -0,0 +1,167 @@
|
||||
package xgbutil
|
||||
|
||||
/*
|
||||
types.go contains several types used in the XUtil structure. In an ideal world,
|
||||
they would be defined in their appropriate packages, but must be defined here
|
||||
(and exported) for use in some sub-packages. (Namely, xevent, keybind and
|
||||
mousebind.)
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
)
|
||||
|
||||
// Callback is an interface that should be implemented by event callback
|
||||
// functions. Namely, to assign a function to a particular event/window
|
||||
// combination, simply define a function with type 'SomeEventFun' (pre-defined
|
||||
// in xevent/callback.go), and call the 'Connect' method.
|
||||
// The 'Run' method is used inside the Main event loop, and shouldn't be used
|
||||
// by the user.
|
||||
// Also, it is perfectly legitimate to connect to events that don't specify
|
||||
// a window (like MappingNotify and KeymapNotify). In this case, simply
|
||||
// use 'xgbutil.NoWindow' as the window id.
|
||||
//
|
||||
// Example to respond to ConfigureNotify events on window 0x1
|
||||
//
|
||||
// xevent.ConfigureNotifyFun(
|
||||
// func(X *xgbutil.XUtil, e xevent.ConfigureNotifyEvent) {
|
||||
// fmt.Printf("(%d, %d) %dx%d\n", e.X, e.Y, e.Width, e.Height)
|
||||
// }).Connect(X, 0x1)
|
||||
type Callback interface {
|
||||
// Connect modifies XUtil's state to attach an event handler to a
|
||||
// particular event.
|
||||
Connect(xu *XUtil, win xproto.Window)
|
||||
|
||||
// Run is exported for use in the xevent package but should not be
|
||||
// used by the user. (It is used to run the callback function in the
|
||||
// main event loop.)
|
||||
Run(xu *XUtil, ev interface{})
|
||||
}
|
||||
|
||||
// CallbackHook works similarly to the more general Callback, but it is
|
||||
// for hooks into the main xevent loop. As such it does not get attached
|
||||
// to a window.
|
||||
type CallbackHook interface {
|
||||
// Connect connects this hook to the main loop of the passed XUtil
|
||||
// instance.
|
||||
Connect(xu *XUtil)
|
||||
|
||||
// Run is exported for use in the xevent package, but should not be
|
||||
// used by the user. It should return true if it's ok to process
|
||||
// the event as usual, or false if it should be suppressed.
|
||||
Run(xu *XUtil, ev interface{}) bool
|
||||
}
|
||||
|
||||
// CallbackKey works similarly to the more general Callback, but it adds
|
||||
// parameters specific to key bindings.
|
||||
type CallbackKey interface {
|
||||
// Connect modifies XUtil's state to attach an event handler to a
|
||||
// particular key press. If grab is true, connect will request a passive
|
||||
// grab.
|
||||
Connect(xu *XUtil, win xproto.Window, keyStr string, grab bool) error
|
||||
|
||||
// Run is exported for use in the keybind package but should not be
|
||||
// used by the user. (It is used to run the callback function in the
|
||||
// main event loop.
|
||||
Run(xu *XUtil, ev interface{})
|
||||
}
|
||||
|
||||
// CallbackMouse works similarly to the more general Callback, but it adds
|
||||
// parameters specific to mouse bindings.
|
||||
type CallbackMouse interface {
|
||||
// Connect modifies XUtil's state to attach an event handler to a
|
||||
// particular button press.
|
||||
// If sync is true, the grab will be synchronous. (This will require a
|
||||
// call to xproto.AllowEvents in response, otherwise no further events
|
||||
// will be processed and your program will lock.)
|
||||
// If grab is true, connect will request a passive grab.
|
||||
Connect(xu *XUtil, win xproto.Window, buttonStr string,
|
||||
sync bool, grab bool) error
|
||||
|
||||
// Run is exported for use in the mousebind package but should not be
|
||||
// used by the user. (It is used to run the callback function in the
|
||||
// main event loop.)
|
||||
Run(xu *XUtil, ev interface{})
|
||||
}
|
||||
|
||||
// KeyKey is the type of the key in the map of keybindings.
|
||||
// It essentially represents the tuple
|
||||
// (event type, window id, modifier, keycode).
|
||||
// It is exported for use in the keybind package. It should not be used.
|
||||
type KeyKey struct {
|
||||
Evtype int
|
||||
Win xproto.Window
|
||||
Mod uint16
|
||||
Code xproto.Keycode
|
||||
}
|
||||
|
||||
// KeyString is the type of a key binding string used to connect to particular
|
||||
// key combinations. A list of all such key strings is maintained in order to
|
||||
// rebind keys when the keyboard mapping has been changed.
|
||||
type KeyString struct {
|
||||
Str string
|
||||
Callback CallbackKey
|
||||
Evtype int
|
||||
Win xproto.Window
|
||||
Grab bool
|
||||
}
|
||||
|
||||
// MouseKey is the type of the key in the map of mouse bindings.
|
||||
// It essentially represents the tuple
|
||||
// (event type, window id, modifier, button).
|
||||
// It is exported for use in the mousebind package. It should not be used.
|
||||
type MouseKey struct {
|
||||
Evtype int
|
||||
Win xproto.Window
|
||||
Mod uint16
|
||||
Button xproto.Button
|
||||
}
|
||||
|
||||
// KeyboardMapping embeds a keyboard mapping reply from XGB.
|
||||
// It should be retrieved using keybind.KeyMapGet, if necessary.
|
||||
// xgbutil tries quite hard to absolve you from ever having to use this.
|
||||
// A keyboard mapping is a table that maps keycodes to one or more keysyms.
|
||||
type KeyboardMapping struct {
|
||||
*xproto.GetKeyboardMappingReply
|
||||
}
|
||||
|
||||
// ModifierMapping embeds a modifier mapping reply from XGB.
|
||||
// It should be retrieved using keybind.ModMapGet, if necessary.
|
||||
// xgbutil tries quite hard to absolve you from ever having to use this.
|
||||
// A modifier mapping is a table that maps modifiers to one or more keycodes.
|
||||
type ModifierMapping struct {
|
||||
*xproto.GetModifierMappingReply
|
||||
}
|
||||
|
||||
// ErrorHandlerFun is the type of function required to handle errors that
|
||||
// come in through the main event loop.
|
||||
// For example, to set a new error handler, use:
|
||||
//
|
||||
// xevent.ErrorHandlerSet(xgbutil.ErrorHandlerFun(
|
||||
// func(err xgb.Error) {
|
||||
// // do something with err
|
||||
// }))
|
||||
type ErrorHandlerFun func(err xgb.Error)
|
||||
|
||||
// EventOrError is a struct that contains either an event value or an error
|
||||
// value. It is an error to contain both. Containing neither indicates an
|
||||
// error too.
|
||||
// This is exported for use in the xevent package. You shouldn't have any
|
||||
// direct contact with values of this type, unless you need to inspect the
|
||||
// queue directly with xevent.Peek.
|
||||
type EventOrError struct {
|
||||
Event xgb.Event
|
||||
Err xgb.Error
|
||||
}
|
||||
|
||||
// MouseDragFun is the kind of function used on each dragging step
|
||||
// and at the end of a drag.
|
||||
type MouseDragFun func(xu *XUtil, rootX, rootY, eventX, eventY int)
|
||||
|
||||
// MouseDragBeginFun is the kind of function used to initialize a drag.
|
||||
// The difference between this and MouseDragFun is that the begin function
|
||||
// returns a bool (of whether or not to cancel the drag) and an X resource
|
||||
// identifier corresponding to a cursor.
|
||||
type MouseDragBeginFun func(xu *XUtil, rootX, rootY,
|
||||
eventX, eventY int) (bool, xproto.Cursor)
|
@ -0,0 +1,389 @@
|
||||
package xevent
|
||||
|
||||
/*
|
||||
Does all the plumbing to allow a simple callback interface for users.
|
||||
|
||||
This file is automatically generated using `scripts/write-events callbacks`.
|
||||
|
||||
Edit it at your peril.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
type KeyPressFun func(xu *xgbutil.XUtil, event KeyPressEvent)
|
||||
|
||||
func (callback KeyPressFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, KeyPress, win, callback)
|
||||
}
|
||||
|
||||
func (callback KeyPressFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(KeyPressEvent))
|
||||
}
|
||||
|
||||
type KeyReleaseFun func(xu *xgbutil.XUtil, event KeyReleaseEvent)
|
||||
|
||||
func (callback KeyReleaseFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, KeyRelease, win, callback)
|
||||
}
|
||||
|
||||
func (callback KeyReleaseFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(KeyReleaseEvent))
|
||||
}
|
||||
|
||||
type ButtonPressFun func(xu *xgbutil.XUtil, event ButtonPressEvent)
|
||||
|
||||
func (callback ButtonPressFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ButtonPress, win, callback)
|
||||
}
|
||||
|
||||
func (callback ButtonPressFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ButtonPressEvent))
|
||||
}
|
||||
|
||||
type ButtonReleaseFun func(xu *xgbutil.XUtil, event ButtonReleaseEvent)
|
||||
|
||||
func (callback ButtonReleaseFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ButtonRelease, win, callback)
|
||||
}
|
||||
|
||||
func (callback ButtonReleaseFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ButtonReleaseEvent))
|
||||
}
|
||||
|
||||
type MotionNotifyFun func(xu *xgbutil.XUtil, event MotionNotifyEvent)
|
||||
|
||||
func (callback MotionNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, MotionNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback MotionNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(MotionNotifyEvent))
|
||||
}
|
||||
|
||||
type EnterNotifyFun func(xu *xgbutil.XUtil, event EnterNotifyEvent)
|
||||
|
||||
func (callback EnterNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, EnterNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback EnterNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(EnterNotifyEvent))
|
||||
}
|
||||
|
||||
type LeaveNotifyFun func(xu *xgbutil.XUtil, event LeaveNotifyEvent)
|
||||
|
||||
func (callback LeaveNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, LeaveNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback LeaveNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(LeaveNotifyEvent))
|
||||
}
|
||||
|
||||
type FocusInFun func(xu *xgbutil.XUtil, event FocusInEvent)
|
||||
|
||||
func (callback FocusInFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, FocusIn, win, callback)
|
||||
}
|
||||
|
||||
func (callback FocusInFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(FocusInEvent))
|
||||
}
|
||||
|
||||
type FocusOutFun func(xu *xgbutil.XUtil, event FocusOutEvent)
|
||||
|
||||
func (callback FocusOutFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, FocusOut, win, callback)
|
||||
}
|
||||
|
||||
func (callback FocusOutFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(FocusOutEvent))
|
||||
}
|
||||
|
||||
type KeymapNotifyFun func(xu *xgbutil.XUtil, event KeymapNotifyEvent)
|
||||
|
||||
func (callback KeymapNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, KeymapNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback KeymapNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(KeymapNotifyEvent))
|
||||
}
|
||||
|
||||
type ExposeFun func(xu *xgbutil.XUtil, event ExposeEvent)
|
||||
|
||||
func (callback ExposeFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, Expose, win, callback)
|
||||
}
|
||||
|
||||
func (callback ExposeFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ExposeEvent))
|
||||
}
|
||||
|
||||
type GraphicsExposureFun func(xu *xgbutil.XUtil, event GraphicsExposureEvent)
|
||||
|
||||
func (callback GraphicsExposureFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, GraphicsExposure, win, callback)
|
||||
}
|
||||
|
||||
func (callback GraphicsExposureFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(GraphicsExposureEvent))
|
||||
}
|
||||
|
||||
type NoExposureFun func(xu *xgbutil.XUtil, event NoExposureEvent)
|
||||
|
||||
func (callback NoExposureFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, NoExposure, win, callback)
|
||||
}
|
||||
|
||||
func (callback NoExposureFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(NoExposureEvent))
|
||||
}
|
||||
|
||||
type VisibilityNotifyFun func(xu *xgbutil.XUtil, event VisibilityNotifyEvent)
|
||||
|
||||
func (callback VisibilityNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, VisibilityNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback VisibilityNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(VisibilityNotifyEvent))
|
||||
}
|
||||
|
||||
type CreateNotifyFun func(xu *xgbutil.XUtil, event CreateNotifyEvent)
|
||||
|
||||
func (callback CreateNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, CreateNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback CreateNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(CreateNotifyEvent))
|
||||
}
|
||||
|
||||
type DestroyNotifyFun func(xu *xgbutil.XUtil, event DestroyNotifyEvent)
|
||||
|
||||
func (callback DestroyNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, DestroyNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback DestroyNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(DestroyNotifyEvent))
|
||||
}
|
||||
|
||||
type UnmapNotifyFun func(xu *xgbutil.XUtil, event UnmapNotifyEvent)
|
||||
|
||||
func (callback UnmapNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, UnmapNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback UnmapNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(UnmapNotifyEvent))
|
||||
}
|
||||
|
||||
type MapNotifyFun func(xu *xgbutil.XUtil, event MapNotifyEvent)
|
||||
|
||||
func (callback MapNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, MapNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback MapNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(MapNotifyEvent))
|
||||
}
|
||||
|
||||
type MapRequestFun func(xu *xgbutil.XUtil, event MapRequestEvent)
|
||||
|
||||
func (callback MapRequestFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, MapRequest, win, callback)
|
||||
}
|
||||
|
||||
func (callback MapRequestFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(MapRequestEvent))
|
||||
}
|
||||
|
||||
type ReparentNotifyFun func(xu *xgbutil.XUtil, event ReparentNotifyEvent)
|
||||
|
||||
func (callback ReparentNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ReparentNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback ReparentNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ReparentNotifyEvent))
|
||||
}
|
||||
|
||||
type ConfigureNotifyFun func(xu *xgbutil.XUtil, event ConfigureNotifyEvent)
|
||||
|
||||
func (callback ConfigureNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ConfigureNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback ConfigureNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ConfigureNotifyEvent))
|
||||
}
|
||||
|
||||
type ConfigureRequestFun func(xu *xgbutil.XUtil, event ConfigureRequestEvent)
|
||||
|
||||
func (callback ConfigureRequestFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ConfigureRequest, win, callback)
|
||||
}
|
||||
|
||||
func (callback ConfigureRequestFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ConfigureRequestEvent))
|
||||
}
|
||||
|
||||
type GravityNotifyFun func(xu *xgbutil.XUtil, event GravityNotifyEvent)
|
||||
|
||||
func (callback GravityNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, GravityNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback GravityNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(GravityNotifyEvent))
|
||||
}
|
||||
|
||||
type ResizeRequestFun func(xu *xgbutil.XUtil, event ResizeRequestEvent)
|
||||
|
||||
func (callback ResizeRequestFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ResizeRequest, win, callback)
|
||||
}
|
||||
|
||||
func (callback ResizeRequestFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ResizeRequestEvent))
|
||||
}
|
||||
|
||||
type CirculateNotifyFun func(xu *xgbutil.XUtil, event CirculateNotifyEvent)
|
||||
|
||||
func (callback CirculateNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, CirculateNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback CirculateNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(CirculateNotifyEvent))
|
||||
}
|
||||
|
||||
type CirculateRequestFun func(xu *xgbutil.XUtil, event CirculateRequestEvent)
|
||||
|
||||
func (callback CirculateRequestFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, CirculateRequest, win, callback)
|
||||
}
|
||||
|
||||
func (callback CirculateRequestFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(CirculateRequestEvent))
|
||||
}
|
||||
|
||||
type PropertyNotifyFun func(xu *xgbutil.XUtil, event PropertyNotifyEvent)
|
||||
|
||||
func (callback PropertyNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, PropertyNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback PropertyNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(PropertyNotifyEvent))
|
||||
}
|
||||
|
||||
type SelectionClearFun func(xu *xgbutil.XUtil, event SelectionClearEvent)
|
||||
|
||||
func (callback SelectionClearFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, SelectionClear, win, callback)
|
||||
}
|
||||
|
||||
func (callback SelectionClearFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(SelectionClearEvent))
|
||||
}
|
||||
|
||||
type SelectionRequestFun func(xu *xgbutil.XUtil, event SelectionRequestEvent)
|
||||
|
||||
func (callback SelectionRequestFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, SelectionRequest, win, callback)
|
||||
}
|
||||
|
||||
func (callback SelectionRequestFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(SelectionRequestEvent))
|
||||
}
|
||||
|
||||
type SelectionNotifyFun func(xu *xgbutil.XUtil, event SelectionNotifyEvent)
|
||||
|
||||
func (callback SelectionNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, SelectionNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback SelectionNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(SelectionNotifyEvent))
|
||||
}
|
||||
|
||||
type ColormapNotifyFun func(xu *xgbutil.XUtil, event ColormapNotifyEvent)
|
||||
|
||||
func (callback ColormapNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ColormapNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback ColormapNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ColormapNotifyEvent))
|
||||
}
|
||||
|
||||
type ClientMessageFun func(xu *xgbutil.XUtil, event ClientMessageEvent)
|
||||
|
||||
func (callback ClientMessageFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ClientMessage, win, callback)
|
||||
}
|
||||
|
||||
func (callback ClientMessageFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ClientMessageEvent))
|
||||
}
|
||||
|
||||
type MappingNotifyFun func(xu *xgbutil.XUtil, event MappingNotifyEvent)
|
||||
|
||||
func (callback MappingNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, MappingNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback MappingNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(MappingNotifyEvent))
|
||||
}
|
||||
|
||||
type ShapeNotifyFun func(xu *xgbutil.XUtil, event ShapeNotifyEvent)
|
||||
|
||||
func (callback ShapeNotifyFun) Connect(xu *xgbutil.XUtil,
|
||||
win xproto.Window) {
|
||||
attachCallback(xu, ShapeNotify, win, callback)
|
||||
}
|
||||
|
||||
func (callback ShapeNotifyFun) Run(xu *xgbutil.XUtil, event interface{}) {
|
||||
callback(xu, event.(ShapeNotifyEvent))
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
Package xevent provides an event handler interface for attaching callback
|
||||
functions to X events, and an implementation of an X event loop.
|
||||
|
||||
The X event loop
|
||||
|
||||
One of the biggest conveniences offered by xgbutil is its event handler system.
|
||||
That is, the ability to attach an arbitrary callback function to any X event.
|
||||
In order for such things to work, xgbutil needs to control the main X event
|
||||
loop and act as a dispatcher for all event handlers created by you.
|
||||
|
||||
To run the X event loop, use xevent.Main or xevent.MainPing. The former
|
||||
runs a normal event loop in the current goroutine and processes events. The
|
||||
latter runs the event loop in a new goroutine and returns a pingBefore and
|
||||
a pingAfter channel. The pingBefore channel is sent a benign value right before
|
||||
an event is dequeued, and the pingAfter channel is sent a benign value right
|
||||
after after all callbacks for that event have finished execution. These
|
||||
synchronization points in the main event loop can be combined with a 'select'
|
||||
statement to process data from other input sources. An example of this is given
|
||||
in the documentation for the MainPing function. A complete example called
|
||||
multiple-source-event-loop can also be found in the examples directory of the
|
||||
xgbutil package.
|
||||
|
||||
To quit the main event loop, you may use xevent.Quit, but there is nothing
|
||||
inherently wrong with stopping dead using os.Exit. xevent.Quit is provided for
|
||||
your convenience should you need to run any clean-up code after the main event
|
||||
loop returns.
|
||||
|
||||
The X event queue
|
||||
|
||||
xgbutil's event queue contains values that are either events or errors. (Never
|
||||
both and never neither.) Namely, errors are received in the event loop from
|
||||
unchecked requests. (Errors generated by checked requests are guaranteed to be
|
||||
returned to the caller and are never received in the event loop.) Also, a
|
||||
default error handler function can be set with xevent.ErrorHandlerSet.
|
||||
|
||||
To this end, xgbutil's event queue can be inspected. This is advantageous when
|
||||
information about what events will be processed in the future could be helpful
|
||||
(i.e., if there is an UnmapNotify event waiting to be processed.) The event
|
||||
queue can also be manipulated to facilitate event compression. (Two events that
|
||||
are common candidates for compression are ConfigureNotify and MotionNotify.)
|
||||
|
||||
Detach events
|
||||
|
||||
Whenever a window can no longer receive events (i.e., when it is destroyed),
|
||||
all event handlers related to that window should be detached. (If this is
|
||||
omitted, then Go's garbage collector will not be able to reuse memory occupied
|
||||
by the now-unused event handlers for that window.) Moreover, its possible that
|
||||
a window id can be reused after it has been discarded, which could result in
|
||||
odd behavior in your application.
|
||||
|
||||
To detach a window from all event handlers in the xevent package, use
|
||||
xevent.Detach. If you're also using the keybind and mousebind packages, you'll
|
||||
need to call keybind.Detach and mousebind.Detach too. So to detach your window
|
||||
from all possible event handlers in xgbutil, use something like:
|
||||
|
||||
xevent.Detach(XUtilValue, your-window-id)
|
||||
keybind.Detach(XUtilValue, your-window-id)
|
||||
mousebind.Detach(XUtilValue, your-window-id)
|
||||
|
||||
Quick example
|
||||
|
||||
A small example that shows how to respond to ConfigureNotify events sent to
|
||||
your-window-id.
|
||||
|
||||
xevent.ConfigureNotifyFun(
|
||||
func(X *xgbutil.XUtil, e xevent.ConfigureNotifyEvent) {
|
||||
fmt.Printf("(%d, %d) %dx%d\n", e.X, e.Y, e.Width, e.Height)
|
||||
}).Connect(XUtilValue, your-window-id)
|
||||
|
||||
More examples
|
||||
|
||||
The xevent package is used in several of the examples in the examples directory
|
||||
in the xgbutil package.
|
||||
*/
|
||||
package xevent
|
@ -0,0 +1,291 @@
|
||||
package xevent
|
||||
|
||||
/*
|
||||
xevent/eventloop.go contains code that implements a main X event loop.
|
||||
|
||||
Namely, it provides facilities to read new events into xevent's event queue,
|
||||
run a normal main event loop and run a main event loop that pings a channel
|
||||
each time an event is about to be dequeued. The latter facility allows one to
|
||||
easily include other input sources for processing in a program's main event
|
||||
loop.
|
||||
*/
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb/shape"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
// Read reads one or more events and queues them in XUtil.
|
||||
// If 'block' is True, then call 'WaitForEvent' before sucking up
|
||||
// all events that have been queued by XGB.
|
||||
func Read(xu *xgbutil.XUtil, block bool) {
|
||||
if block {
|
||||
ev, err := xu.Conn().WaitForEvent()
|
||||
if ev == nil && err == nil {
|
||||
xgbutil.Logger.Fatal("BUG: Could not read an event or an error.")
|
||||
}
|
||||
Enqueue(xu, ev, err)
|
||||
}
|
||||
|
||||
// Clean up anything that's in the queue
|
||||
for {
|
||||
ev, err := xu.Conn().PollForEvent()
|
||||
|
||||
// No events left...
|
||||
if ev == nil && err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// We're good, queue it up
|
||||
Enqueue(xu, ev, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Main starts the main X event loop. It will read events and call appropriate
|
||||
// callback functions.
|
||||
// N.B. If you have multiple X connections in the same program, you should be
|
||||
// able to run this in different goroutines concurrently. However, only
|
||||
// *one* of these should run for *each* connection.
|
||||
func Main(xu *xgbutil.XUtil) {
|
||||
mainEventLoop(xu, nil, nil, nil)
|
||||
}
|
||||
|
||||
// MainPing starts the main X event loop, and returns three "ping" channels:
|
||||
// the first is pinged before an event is dequeued, the second is pinged
|
||||
// after all callbacks for a particular event have been called and the last
|
||||
// is pinged when the event loop stops (e.g., after a call to xevent.Quit).
|
||||
// pingAfter channel.
|
||||
//
|
||||
// This is useful if your event loop needs to draw from other sources. e.g.,
|
||||
//
|
||||
// pingBefore, pingAfter, pingQuit := xevent.MainPing()
|
||||
// for {
|
||||
// select {
|
||||
// case <-pingBefore:
|
||||
// // Wait for event processing to finish.
|
||||
// <-pingAfter
|
||||
// case val <- someOtherChannel:
|
||||
// // do some work with val
|
||||
// case <-pingQuit:
|
||||
// fmt.Printf("xevent loop has quit")
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Note that an unbuffered channel is returned, which implies that any work
|
||||
// done with 'val' will delay further X event processing.
|
||||
//
|
||||
// A complete example using MainPing can be found in the examples directory in
|
||||
// the xgbutil package under the name multiple-source-event-loop.
|
||||
func MainPing(xu *xgbutil.XUtil) (chan struct{}, chan struct{}, chan struct{}) {
|
||||
pingBefore := make(chan struct{}, 0)
|
||||
pingAfter := make(chan struct{}, 0)
|
||||
pingQuit := make(chan struct{}, 0)
|
||||
go func() {
|
||||
mainEventLoop(xu, pingBefore, pingAfter, pingQuit)
|
||||
}()
|
||||
return pingBefore, pingAfter, pingQuit
|
||||
}
|
||||
|
||||
// mainEventLoop runs the main event loop with an optional ping channel.
|
||||
func mainEventLoop(xu *xgbutil.XUtil,
|
||||
pingBefore, pingAfter, pingQuit chan struct{}) {
|
||||
for {
|
||||
if Quitting(xu) {
|
||||
if pingQuit != nil {
|
||||
pingQuit <- struct{}{}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Gobble up as many events as possible (into the queue).
|
||||
// If there are no events, we block.
|
||||
Read(xu, true)
|
||||
|
||||
// Now process every event/error in the queue.
|
||||
processEventQueue(xu, pingBefore, pingAfter)
|
||||
}
|
||||
}
|
||||
|
||||
// processEventQueue processes every item in the event/error queue.
|
||||
func processEventQueue(xu *xgbutil.XUtil, pingBefore, pingAfter chan struct{}) {
|
||||
for !Empty(xu) {
|
||||
if Quitting(xu) {
|
||||
return
|
||||
}
|
||||
|
||||
// We send the ping *before* the next event is dequeued.
|
||||
// This is so the queue doesn't present a misrepresentation of which
|
||||
// events haven't been processed yet.
|
||||
if pingBefore != nil && pingAfter != nil {
|
||||
pingBefore <- struct{}{}
|
||||
}
|
||||
ev, err := Dequeue(xu)
|
||||
|
||||
// If we gobbled up an error, send it to the error event handler
|
||||
// and move on the next event/error.
|
||||
if err != nil {
|
||||
ErrorHandlerGet(xu)(err)
|
||||
if pingBefore != nil && pingAfter != nil {
|
||||
pingAfter <- struct{}{}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// We know there isn't an error. If there isn't an event either,
|
||||
// then there's a bug somewhere.
|
||||
if ev == nil {
|
||||
xgbutil.Logger.Fatal("BUG: Expected an event but got nil.")
|
||||
}
|
||||
|
||||
hooks := getHooks(xu)
|
||||
for _, hook := range hooks {
|
||||
if !hook.Run(xu, ev) {
|
||||
goto END
|
||||
}
|
||||
}
|
||||
|
||||
switch event := ev.(type) {
|
||||
case xproto.KeyPressEvent:
|
||||
e := KeyPressEvent{&event}
|
||||
|
||||
// If we're redirecting key events, this is the place to do it!
|
||||
if wid := RedirectKeyGet(xu); wid > 0 {
|
||||
e.Event = wid
|
||||
}
|
||||
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, KeyPress, e.Event)
|
||||
case xproto.KeyReleaseEvent:
|
||||
e := KeyReleaseEvent{&event}
|
||||
|
||||
// If we're redirecting key events, this is the place to do it!
|
||||
if wid := RedirectKeyGet(xu); wid > 0 {
|
||||
e.Event = wid
|
||||
}
|
||||
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, KeyRelease, e.Event)
|
||||
case xproto.ButtonPressEvent:
|
||||
e := ButtonPressEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, ButtonPress, e.Event)
|
||||
case xproto.ButtonReleaseEvent:
|
||||
e := ButtonReleaseEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, ButtonRelease, e.Event)
|
||||
case xproto.MotionNotifyEvent:
|
||||
e := MotionNotifyEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, MotionNotify, e.Event)
|
||||
case xproto.EnterNotifyEvent:
|
||||
e := EnterNotifyEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, EnterNotify, e.Event)
|
||||
case xproto.LeaveNotifyEvent:
|
||||
e := LeaveNotifyEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, LeaveNotify, e.Event)
|
||||
case xproto.FocusInEvent:
|
||||
e := FocusInEvent{&event}
|
||||
runCallbacks(xu, e, FocusIn, e.Event)
|
||||
case xproto.FocusOutEvent:
|
||||
e := FocusOutEvent{&event}
|
||||
runCallbacks(xu, e, FocusOut, e.Event)
|
||||
case xproto.KeymapNotifyEvent:
|
||||
e := KeymapNotifyEvent{&event}
|
||||
runCallbacks(xu, e, KeymapNotify, NoWindow)
|
||||
case xproto.ExposeEvent:
|
||||
e := ExposeEvent{&event}
|
||||
runCallbacks(xu, e, Expose, e.Window)
|
||||
case xproto.GraphicsExposureEvent:
|
||||
e := GraphicsExposureEvent{&event}
|
||||
runCallbacks(xu, e, GraphicsExposure, xproto.Window(e.Drawable))
|
||||
case xproto.NoExposureEvent:
|
||||
e := NoExposureEvent{&event}
|
||||
runCallbacks(xu, e, NoExposure, xproto.Window(e.Drawable))
|
||||
case xproto.VisibilityNotifyEvent:
|
||||
e := VisibilityNotifyEvent{&event}
|
||||
runCallbacks(xu, e, VisibilityNotify, e.Window)
|
||||
case xproto.CreateNotifyEvent:
|
||||
e := CreateNotifyEvent{&event}
|
||||
runCallbacks(xu, e, CreateNotify, e.Parent)
|
||||
case xproto.DestroyNotifyEvent:
|
||||
e := DestroyNotifyEvent{&event}
|
||||
runCallbacks(xu, e, DestroyNotify, e.Window)
|
||||
case xproto.UnmapNotifyEvent:
|
||||
e := UnmapNotifyEvent{&event}
|
||||
runCallbacks(xu, e, UnmapNotify, e.Window)
|
||||
case xproto.MapNotifyEvent:
|
||||
e := MapNotifyEvent{&event}
|
||||
runCallbacks(xu, e, MapNotify, e.Event)
|
||||
case xproto.MapRequestEvent:
|
||||
e := MapRequestEvent{&event}
|
||||
runCallbacks(xu, e, MapRequest, e.Window)
|
||||
runCallbacks(xu, e, MapRequest, e.Parent)
|
||||
case xproto.ReparentNotifyEvent:
|
||||
e := ReparentNotifyEvent{&event}
|
||||
runCallbacks(xu, e, ReparentNotify, e.Window)
|
||||
case xproto.ConfigureNotifyEvent:
|
||||
e := ConfigureNotifyEvent{&event}
|
||||
runCallbacks(xu, e, ConfigureNotify, e.Window)
|
||||
case xproto.ConfigureRequestEvent:
|
||||
e := ConfigureRequestEvent{&event}
|
||||
runCallbacks(xu, e, ConfigureRequest, e.Window)
|
||||
runCallbacks(xu, e, ConfigureRequest, e.Parent)
|
||||
case xproto.GravityNotifyEvent:
|
||||
e := GravityNotifyEvent{&event}
|
||||
runCallbacks(xu, e, GravityNotify, e.Window)
|
||||
case xproto.ResizeRequestEvent:
|
||||
e := ResizeRequestEvent{&event}
|
||||
runCallbacks(xu, e, ResizeRequest, e.Window)
|
||||
case xproto.CirculateNotifyEvent:
|
||||
e := CirculateNotifyEvent{&event}
|
||||
runCallbacks(xu, e, CirculateNotify, e.Window)
|
||||
case xproto.CirculateRequestEvent:
|
||||
e := CirculateRequestEvent{&event}
|
||||
runCallbacks(xu, e, CirculateRequest, e.Window)
|
||||
case xproto.PropertyNotifyEvent:
|
||||
e := PropertyNotifyEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, PropertyNotify, e.Window)
|
||||
case xproto.SelectionClearEvent:
|
||||
e := SelectionClearEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, SelectionClear, e.Owner)
|
||||
case xproto.SelectionRequestEvent:
|
||||
e := SelectionRequestEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, SelectionRequest, e.Requestor)
|
||||
case xproto.SelectionNotifyEvent:
|
||||
e := SelectionNotifyEvent{&event}
|
||||
xu.TimeSet(e.Time)
|
||||
runCallbacks(xu, e, SelectionNotify, e.Requestor)
|
||||
case xproto.ColormapNotifyEvent:
|
||||
e := ColormapNotifyEvent{&event}
|
||||
runCallbacks(xu, e, ColormapNotify, e.Window)
|
||||
case xproto.ClientMessageEvent:
|
||||
e := ClientMessageEvent{&event}
|
||||
runCallbacks(xu, e, ClientMessage, e.Window)
|
||||
case xproto.MappingNotifyEvent:
|
||||
e := MappingNotifyEvent{&event}
|
||||
runCallbacks(xu, e, MappingNotify, NoWindow)
|
||||
case shape.NotifyEvent:
|
||||
e := ShapeNotifyEvent{&event}
|
||||
runCallbacks(xu, e, ShapeNotify, e.AffectedWindow)
|
||||
default:
|
||||
if event != nil {
|
||||
xgbutil.Logger.Printf("ERROR: UNSUPPORTED EVENT TYPE: %T",
|
||||
event)
|
||||
}
|
||||
}
|
||||
|
||||
END:
|
||||
|
||||
if pingBefore != nil && pingAfter != nil {
|
||||
pingAfter <- struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,336 @@
|
||||
package xevent
|
||||
|
||||
/*
|
||||
Defines event types and their associated methods automatically.
|
||||
|
||||
This file is automatically generated using `scripts/write-events evtypes`.
|
||||
|
||||
Edit it at your peril.
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgb/shape"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
)
|
||||
|
||||
type KeyPressEvent struct {
|
||||
*xproto.KeyPressEvent
|
||||
}
|
||||
|
||||
const KeyPress = xproto.KeyPress
|
||||
|
||||
func (ev KeyPressEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.KeyPressEvent)
|
||||
}
|
||||
|
||||
type KeyReleaseEvent struct {
|
||||
*xproto.KeyReleaseEvent
|
||||
}
|
||||
|
||||
const KeyRelease = xproto.KeyRelease
|
||||
|
||||
func (ev KeyReleaseEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.KeyReleaseEvent)
|
||||
}
|
||||
|
||||
type ButtonPressEvent struct {
|
||||
*xproto.ButtonPressEvent
|
||||
}
|
||||
|
||||
const ButtonPress = xproto.ButtonPress
|
||||
|
||||
func (ev ButtonPressEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ButtonPressEvent)
|
||||
}
|
||||
|
||||
type ButtonReleaseEvent struct {
|
||||
*xproto.ButtonReleaseEvent
|
||||
}
|
||||
|
||||
const ButtonRelease = xproto.ButtonRelease
|
||||
|
||||
func (ev ButtonReleaseEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ButtonReleaseEvent)
|
||||
}
|
||||
|
||||
type MotionNotifyEvent struct {
|
||||
*xproto.MotionNotifyEvent
|
||||
}
|
||||
|
||||
const MotionNotify = xproto.MotionNotify
|
||||
|
||||
func (ev MotionNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.MotionNotifyEvent)
|
||||
}
|
||||
|
||||
type EnterNotifyEvent struct {
|
||||
*xproto.EnterNotifyEvent
|
||||
}
|
||||
|
||||
const EnterNotify = xproto.EnterNotify
|
||||
|
||||
func (ev EnterNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.EnterNotifyEvent)
|
||||
}
|
||||
|
||||
type LeaveNotifyEvent struct {
|
||||
*xproto.LeaveNotifyEvent
|
||||
}
|
||||
|
||||
const LeaveNotify = xproto.LeaveNotify
|
||||
|
||||
func (ev LeaveNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.LeaveNotifyEvent)
|
||||
}
|
||||
|
||||
type FocusInEvent struct {
|
||||
*xproto.FocusInEvent
|
||||
}
|
||||
|
||||
const FocusIn = xproto.FocusIn
|
||||
|
||||
func (ev FocusInEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.FocusInEvent)
|
||||
}
|
||||
|
||||
type FocusOutEvent struct {
|
||||
*xproto.FocusOutEvent
|
||||
}
|
||||
|
||||
const FocusOut = xproto.FocusOut
|
||||
|
||||
func (ev FocusOutEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.FocusOutEvent)
|
||||
}
|
||||
|
||||
type KeymapNotifyEvent struct {
|
||||
*xproto.KeymapNotifyEvent
|
||||
}
|
||||
|
||||
const KeymapNotify = xproto.KeymapNotify
|
||||
|
||||
func (ev KeymapNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.KeymapNotifyEvent)
|
||||
}
|
||||
|
||||
type ExposeEvent struct {
|
||||
*xproto.ExposeEvent
|
||||
}
|
||||
|
||||
const Expose = xproto.Expose
|
||||
|
||||
func (ev ExposeEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ExposeEvent)
|
||||
}
|
||||
|
||||
type GraphicsExposureEvent struct {
|
||||
*xproto.GraphicsExposureEvent
|
||||
}
|
||||
|
||||
const GraphicsExposure = xproto.GraphicsExposure
|
||||
|
||||
func (ev GraphicsExposureEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.GraphicsExposureEvent)
|
||||
}
|
||||
|
||||
type NoExposureEvent struct {
|
||||
*xproto.NoExposureEvent
|
||||
}
|
||||
|
||||
const NoExposure = xproto.NoExposure
|
||||
|
||||
func (ev NoExposureEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.NoExposureEvent)
|
||||
}
|
||||
|
||||
type VisibilityNotifyEvent struct {
|
||||
*xproto.VisibilityNotifyEvent
|
||||
}
|
||||
|
||||
const VisibilityNotify = xproto.VisibilityNotify
|
||||
|
||||
func (ev VisibilityNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.VisibilityNotifyEvent)
|
||||
}
|
||||
|
||||
type CreateNotifyEvent struct {
|
||||
*xproto.CreateNotifyEvent
|
||||
}
|
||||
|
||||
const CreateNotify = xproto.CreateNotify
|
||||
|
||||
func (ev CreateNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.CreateNotifyEvent)
|
||||
}
|
||||
|
||||
type DestroyNotifyEvent struct {
|
||||
*xproto.DestroyNotifyEvent
|
||||
}
|
||||
|
||||
const DestroyNotify = xproto.DestroyNotify
|
||||
|
||||
func (ev DestroyNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.DestroyNotifyEvent)
|
||||
}
|
||||
|
||||
type UnmapNotifyEvent struct {
|
||||
*xproto.UnmapNotifyEvent
|
||||
}
|
||||
|
||||
const UnmapNotify = xproto.UnmapNotify
|
||||
|
||||
func (ev UnmapNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.UnmapNotifyEvent)
|
||||
}
|
||||
|
||||
type MapNotifyEvent struct {
|
||||
*xproto.MapNotifyEvent
|
||||
}
|
||||
|
||||
const MapNotify = xproto.MapNotify
|
||||
|
||||
func (ev MapNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.MapNotifyEvent)
|
||||
}
|
||||
|
||||
type MapRequestEvent struct {
|
||||
*xproto.MapRequestEvent
|
||||
}
|
||||
|
||||
const MapRequest = xproto.MapRequest
|
||||
|
||||
func (ev MapRequestEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.MapRequestEvent)
|
||||
}
|
||||
|
||||
type ReparentNotifyEvent struct {
|
||||
*xproto.ReparentNotifyEvent
|
||||
}
|
||||
|
||||
const ReparentNotify = xproto.ReparentNotify
|
||||
|
||||
func (ev ReparentNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ReparentNotifyEvent)
|
||||
}
|
||||
|
||||
type ConfigureRequestEvent struct {
|
||||
*xproto.ConfigureRequestEvent
|
||||
}
|
||||
|
||||
const ConfigureRequest = xproto.ConfigureRequest
|
||||
|
||||
func (ev ConfigureRequestEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ConfigureRequestEvent)
|
||||
}
|
||||
|
||||
type GravityNotifyEvent struct {
|
||||
*xproto.GravityNotifyEvent
|
||||
}
|
||||
|
||||
const GravityNotify = xproto.GravityNotify
|
||||
|
||||
func (ev GravityNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.GravityNotifyEvent)
|
||||
}
|
||||
|
||||
type ResizeRequestEvent struct {
|
||||
*xproto.ResizeRequestEvent
|
||||
}
|
||||
|
||||
const ResizeRequest = xproto.ResizeRequest
|
||||
|
||||
func (ev ResizeRequestEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ResizeRequestEvent)
|
||||
}
|
||||
|
||||
type CirculateNotifyEvent struct {
|
||||
*xproto.CirculateNotifyEvent
|
||||
}
|
||||
|
||||
const CirculateNotify = xproto.CirculateNotify
|
||||
|
||||
func (ev CirculateNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.CirculateNotifyEvent)
|
||||
}
|
||||
|
||||
type CirculateRequestEvent struct {
|
||||
*xproto.CirculateRequestEvent
|
||||
}
|
||||
|
||||
const CirculateRequest = xproto.CirculateRequest
|
||||
|
||||
func (ev CirculateRequestEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.CirculateRequestEvent)
|
||||
}
|
||||
|
||||
type PropertyNotifyEvent struct {
|
||||
*xproto.PropertyNotifyEvent
|
||||
}
|
||||
|
||||
const PropertyNotify = xproto.PropertyNotify
|
||||
|
||||
func (ev PropertyNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.PropertyNotifyEvent)
|
||||
}
|
||||
|
||||
type SelectionClearEvent struct {
|
||||
*xproto.SelectionClearEvent
|
||||
}
|
||||
|
||||
const SelectionClear = xproto.SelectionClear
|
||||
|
||||
func (ev SelectionClearEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.SelectionClearEvent)
|
||||
}
|
||||
|
||||
type SelectionRequestEvent struct {
|
||||
*xproto.SelectionRequestEvent
|
||||
}
|
||||
|
||||
const SelectionRequest = xproto.SelectionRequest
|
||||
|
||||
func (ev SelectionRequestEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.SelectionRequestEvent)
|
||||
}
|
||||
|
||||
type SelectionNotifyEvent struct {
|
||||
*xproto.SelectionNotifyEvent
|
||||
}
|
||||
|
||||
const SelectionNotify = xproto.SelectionNotify
|
||||
|
||||
func (ev SelectionNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.SelectionNotifyEvent)
|
||||
}
|
||||
|
||||
type ColormapNotifyEvent struct {
|
||||
*xproto.ColormapNotifyEvent
|
||||
}
|
||||
|
||||
const ColormapNotify = xproto.ColormapNotify
|
||||
|
||||
func (ev ColormapNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.ColormapNotifyEvent)
|
||||
}
|
||||
|
||||
type MappingNotifyEvent struct {
|
||||
*xproto.MappingNotifyEvent
|
||||
}
|
||||
|
||||
const MappingNotify = xproto.MappingNotify
|
||||
|
||||
func (ev MappingNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.MappingNotifyEvent)
|
||||
}
|
||||
|
||||
type ShapeNotifyEvent struct {
|
||||
*shape.NotifyEvent
|
||||
}
|
||||
|
||||
const ShapeNotify = shape.Notify
|
||||
|
||||
func (ev ShapeNotifyEvent) String() string {
|
||||
return fmt.Sprintf("%v", ev.NotifyEvent)
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package xevent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
)
|
||||
|
||||
// ClientMessageEvent embeds the struct by the same name from the xgb library.
|
||||
type ClientMessageEvent struct {
|
||||
*xproto.ClientMessageEvent
|
||||
}
|
||||
|
||||
const ClientMessage = xproto.ClientMessage
|
||||
|
||||
// NewClientMessage takes all arguments required to build a ClientMessageEvent
|
||||
// struct and hides the messy details.
|
||||
// The variadic parameters coincide with the "data" part of a client message.
|
||||
// The type of the variadic parameters depends upon the value of Format.
|
||||
// If Format is 8, 'data' should have type byte.
|
||||
// If Format is 16, 'data' should have type int16.
|
||||
// If Format is 32, 'data' should have type int.
|
||||
// Any other value of Format returns an error.
|
||||
func NewClientMessage(Format byte, Window xproto.Window, Type xproto.Atom,
|
||||
data ...interface{}) (*ClientMessageEvent, error) {
|
||||
|
||||
// Create the client data list first
|
||||
var clientData xproto.ClientMessageDataUnion
|
||||
|
||||
// Don't support formats 8 or 16 yet. They aren't used in EWMH anyway.
|
||||
switch Format {
|
||||
case 8:
|
||||
buf := make([]byte, 20)
|
||||
for i := 0; i < 20; i++ {
|
||||
if i >= len(data) {
|
||||
break
|
||||
}
|
||||
buf[i] = data[i].(byte)
|
||||
}
|
||||
clientData = xproto.ClientMessageDataUnionData8New(buf)
|
||||
case 16:
|
||||
buf := make([]uint16, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
if i >= len(data) {
|
||||
break
|
||||
}
|
||||
buf[i] = uint16(data[i].(int16))
|
||||
}
|
||||
clientData = xproto.ClientMessageDataUnionData16New(buf)
|
||||
case 32:
|
||||
buf := make([]uint32, 5)
|
||||
for i := 0; i < 5; i++ {
|
||||
if i >= len(data) {
|
||||
break
|
||||
}
|
||||
buf[i] = uint32(data[i].(int))
|
||||
}
|
||||
clientData = xproto.ClientMessageDataUnionData32New(buf)
|
||||
default:
|
||||
return nil, fmt.Errorf("NewClientMessage: Unsupported format '%d'.",
|
||||
Format)
|
||||
}
|
||||
|
||||
return &ClientMessageEvent{&xproto.ClientMessageEvent{
|
||||
Format: Format,
|
||||
Window: Window,
|
||||
Type: Type,
|
||||
Data: clientData,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
// ConfigureNotifyEvent embeds the struct by the same name in XGB.
|
||||
type ConfigureNotifyEvent struct {
|
||||
*xproto.ConfigureNotifyEvent
|
||||
}
|
||||
|
||||
const ConfigureNotify = xproto.ConfigureNotify
|
||||
|
||||
// NewConfigureNotify takes all arguments required to build a
|
||||
// ConfigureNotifyEvent struct and returns a ConfigureNotifyEvent value.
|
||||
func NewConfigureNotify(Event, Window, AboveSibling xproto.Window,
|
||||
X, Y, Width, Height int, BorderWidth uint16,
|
||||
OverrideRedirect bool) *ConfigureNotifyEvent {
|
||||
|
||||
return &ConfigureNotifyEvent{&xproto.ConfigureNotifyEvent{
|
||||
Event: Event, Window: Window, AboveSibling: AboveSibling,
|
||||
X: int16(X), Y: int16(Y), Width: uint16(Width), Height: uint16(Height),
|
||||
BorderWidth: BorderWidth, OverrideRedirect: OverrideRedirect,
|
||||
}}
|
||||
}
|
@ -0,0 +1,237 @@
|
||||
package xevent
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
// Sometimes we need to specify NO WINDOW when a window is typically
|
||||
// expected. (Like connecting to MappingNotify or KeymapNotify events.)
|
||||
// Use this value to do that.
|
||||
var NoWindow xproto.Window = 0
|
||||
|
||||
// IgnoreMods is a list of X modifiers that we don't want interfering
|
||||
// with our mouse or key bindings. In particular, for each mouse or key binding
|
||||
// issued, there is a seperate mouse or key binding made for each of the
|
||||
// modifiers specified.
|
||||
//
|
||||
// You may modify this slice to add (or remove) modifiers, but it should be
|
||||
// done before *any* key or mouse bindings are attached with the keybind and
|
||||
// mousebind packages. It should not be modified afterwards.
|
||||
//
|
||||
// TODO: We're assuming numlock is in the 'mod2' modifier, which is a pretty
|
||||
// common setup, but by no means guaranteed. This should be modified to actually
|
||||
// inspect the modifiers table and look for the special Num_Lock keysym.
|
||||
var IgnoreMods []uint16 = []uint16{
|
||||
0,
|
||||
xproto.ModMaskLock, // Caps lock
|
||||
xproto.ModMask2, // Num lock
|
||||
xproto.ModMaskLock | xproto.ModMask2, // Caps and Num lock
|
||||
}
|
||||
|
||||
// Enqueue queues up an event read from X.
|
||||
// Note that an event read may return an error, in which case, this queue
|
||||
// entry will be an error and not an event.
|
||||
//
|
||||
// ev, err := XUtilValue.Conn().WaitForEvent()
|
||||
// xevent.Enqueue(XUtilValue, ev, err)
|
||||
//
|
||||
// You probably shouldn't have to enqueue events yourself. This is done
|
||||
// automatically if you're using xevent.Main{Ping} and/or xevent.Read.
|
||||
func Enqueue(xu *xgbutil.XUtil, ev xgb.Event, err xgb.Error) {
|
||||
xu.EvqueueLck.Lock()
|
||||
defer xu.EvqueueLck.Unlock()
|
||||
|
||||
xu.Evqueue = append(xu.Evqueue, xgbutil.EventOrError{
|
||||
Event: ev,
|
||||
Err: err,
|
||||
})
|
||||
}
|
||||
|
||||
// Dequeue pops an event/error from the queue and returns it.
|
||||
// The queue item is unwrapped and returned as multiple return values.
|
||||
// Only one of the return values can be nil.
|
||||
func Dequeue(xu *xgbutil.XUtil) (xgb.Event, xgb.Error) {
|
||||
xu.EvqueueLck.Lock()
|
||||
defer xu.EvqueueLck.Unlock()
|
||||
|
||||
everr := xu.Evqueue[0]
|
||||
xu.Evqueue = xu.Evqueue[1:]
|
||||
return everr.Event, everr.Err
|
||||
}
|
||||
|
||||
// DequeueAt removes a particular item from the queue.
|
||||
// This is primarily useful when attempting to compress events.
|
||||
func DequeueAt(xu *xgbutil.XUtil, i int) {
|
||||
xu.EvqueueLck.Lock()
|
||||
defer xu.EvqueueLck.Unlock()
|
||||
|
||||
xu.Evqueue = append(xu.Evqueue[:i], xu.Evqueue[i+1:]...)
|
||||
}
|
||||
|
||||
// Empty returns whether the event queue is empty or not.
|
||||
func Empty(xu *xgbutil.XUtil) bool {
|
||||
xu.EvqueueLck.RLock()
|
||||
defer xu.EvqueueLck.RUnlock()
|
||||
|
||||
return len(xu.Evqueue) == 0
|
||||
}
|
||||
|
||||
// Peek returns a *copy* of the current queue so we can examine it.
|
||||
// This can be useful when trying to determine if a particular kind of
|
||||
// event will be processed in the future.
|
||||
func Peek(xu *xgbutil.XUtil) []xgbutil.EventOrError {
|
||||
xu.EvqueueLck.RLock()
|
||||
defer xu.EvqueueLck.RUnlock()
|
||||
|
||||
cpy := make([]xgbutil.EventOrError, len(xu.Evqueue))
|
||||
copy(cpy, xu.Evqueue)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// ErrorHandlerSet sets the default error handler for errors that come
|
||||
// into the main event loop. (This may be removed in the future in favor
|
||||
// of a particular callback interface like events, but these sorts of errors
|
||||
// aren't handled often in practice, so maybe not.)
|
||||
// This is only called for errors returned from unchecked (asynchronous error
|
||||
// handling) requests.
|
||||
// The default error handler just emits them to stderr.
|
||||
func ErrorHandlerSet(xu *xgbutil.XUtil, fun xgbutil.ErrorHandlerFun) {
|
||||
xu.ErrorHandler = fun
|
||||
}
|
||||
|
||||
// ErrorHandlerGet retrieves the default error handler.
|
||||
func ErrorHandlerGet(xu *xgbutil.XUtil) xgbutil.ErrorHandlerFun {
|
||||
return xu.ErrorHandler
|
||||
}
|
||||
|
||||
type HookFun func(xu *xgbutil.XUtil, event interface{}) bool
|
||||
|
||||
func (callback HookFun) Connect(xu *xgbutil.XUtil) {
|
||||
xu.HooksLck.Lock()
|
||||
defer xu.HooksLck.Unlock()
|
||||
|
||||
// COW
|
||||
newHooks := make([]xgbutil.CallbackHook, len(xu.Hooks))
|
||||
copy(newHooks, xu.Hooks)
|
||||
newHooks = append(newHooks, callback)
|
||||
|
||||
xu.Hooks = newHooks
|
||||
}
|
||||
|
||||
func (callback HookFun) Run(xu *xgbutil.XUtil, event interface{}) bool {
|
||||
return callback(xu, event)
|
||||
}
|
||||
|
||||
func getHooks(xu *xgbutil.XUtil) []xgbutil.CallbackHook {
|
||||
xu.HooksLck.RLock()
|
||||
defer xu.HooksLck.RUnlock()
|
||||
|
||||
return xu.Hooks
|
||||
}
|
||||
|
||||
// RedirectKeyEvents, when set to a window id (greater than 0), will force
|
||||
// *all* Key{Press,Release} to callbacks attached to the specified window.
|
||||
// This is close to emulating a Keyboard grab without the racing.
|
||||
// To stop redirecting key events, use window identifier '0'.
|
||||
func RedirectKeyEvents(xu *xgbutil.XUtil, wid xproto.Window) {
|
||||
xu.KeyRedirect = wid
|
||||
}
|
||||
|
||||
// RedirectKeyGet gets the window that key events are being redirected to.
|
||||
// If 0, then no redirection occurs.
|
||||
func RedirectKeyGet(xu *xgbutil.XUtil) xproto.Window {
|
||||
return xu.KeyRedirect
|
||||
}
|
||||
|
||||
// Quit elegantly exits out of the main event loop.
|
||||
// "Elegantly" in this case means that it finishes processing the current
|
||||
// event, and breaks out of the loop afterwards.
|
||||
// There is no particular reason to use this instead of something like os.Exit
|
||||
// other than you might have code to run after the main event loop exits to
|
||||
// "clean up."
|
||||
func Quit(xu *xgbutil.XUtil) {
|
||||
xu.Quit = true
|
||||
}
|
||||
|
||||
// Quitting returns whether it's time to quit.
|
||||
// This is only used in the main event loop in xevent.
|
||||
func Quitting(xu *xgbutil.XUtil) bool {
|
||||
return xu.Quit
|
||||
}
|
||||
|
||||
// attachCallback associates a (event, window) tuple with an event.
|
||||
// Use copy on write since we run callbacks *a lot* more than attaching them.
|
||||
// (The copy on write only applies to the slice of callbacks rather than
|
||||
// the map itself, since the initial allocation is guaranteed to come before
|
||||
// any use of it.)
|
||||
func attachCallback(xu *xgbutil.XUtil, evtype int, win xproto.Window,
|
||||
fun xgbutil.Callback) {
|
||||
|
||||
xu.CallbacksLck.Lock()
|
||||
defer xu.CallbacksLck.Unlock()
|
||||
|
||||
if _, ok := xu.Callbacks[evtype]; !ok {
|
||||
xu.Callbacks[evtype] = make(map[xproto.Window][]xgbutil.Callback, 20)
|
||||
}
|
||||
if _, ok := xu.Callbacks[evtype][win]; !ok {
|
||||
xu.Callbacks[evtype][win] = make([]xgbutil.Callback, 0)
|
||||
}
|
||||
|
||||
// COW
|
||||
newCallbacks := make([]xgbutil.Callback, len(xu.Callbacks[evtype][win]))
|
||||
copy(newCallbacks, xu.Callbacks[evtype][win])
|
||||
newCallbacks = append(newCallbacks, fun)
|
||||
xu.Callbacks[evtype][win] = newCallbacks
|
||||
}
|
||||
|
||||
// runCallbacks executes every callback corresponding to a
|
||||
// particular event/window tuple.
|
||||
func runCallbacks(xu *xgbutil.XUtil, event interface{}, evtype int,
|
||||
win xproto.Window) {
|
||||
|
||||
// The callback slice for a particular (event type, window) tuple uses
|
||||
// copy on write. So just take a pointer to whatever is there and use that.
|
||||
// We can be sure that the slice won't change from underneathe us.
|
||||
xu.CallbacksLck.RLock()
|
||||
cbs := xu.Callbacks[evtype][win]
|
||||
xu.CallbacksLck.RUnlock()
|
||||
|
||||
for _, cb := range cbs {
|
||||
cb.Run(xu, event)
|
||||
}
|
||||
}
|
||||
|
||||
// Detach removes all callbacks associated with a particular window.
|
||||
// Note that if you're also using the keybind and mousebind packages, a complete
|
||||
// detachment should look like:
|
||||
//
|
||||
// keybind.Detach(XUtilValue, window-id)
|
||||
// mousebind.Detach(XUtilValue, window-id)
|
||||
// xevent.Detach(XUtilValue, window-id)
|
||||
//
|
||||
// If a window is no longer receiving events, these methods should be called.
|
||||
// Otherwise, the memory used to store the handler info for that window will
|
||||
// never be released.
|
||||
func Detach(xu *xgbutil.XUtil, win xproto.Window) {
|
||||
xu.CallbacksLck.Lock()
|
||||
defer xu.CallbacksLck.Unlock()
|
||||
|
||||
for evtype, _ := range xu.Callbacks {
|
||||
delete(xu.Callbacks[evtype], win)
|
||||
}
|
||||
}
|
||||
|
||||
// SendRootEvent takes a type implementing the xgb.Event interface, converts it
|
||||
// to raw X bytes, and sends it to the root window using the SendEvent request.
|
||||
func SendRootEvent(xu *xgbutil.XUtil, ev xgb.Event, evMask uint32) error {
|
||||
return xproto.SendEventChecked(xu.Conn(), false, xu.RootWin(), evMask,
|
||||
string(ev.Bytes())).Check()
|
||||
}
|
||||
|
||||
// ReplayPointer is a quick alias to AllowEvents with 'ReplayPointer' mode.
|
||||
func ReplayPointer(xu *xgbutil.XUtil) {
|
||||
xproto.AllowEvents(xu.Conn(), xproto.AllowReplayPointer, 0)
|
||||
}
|
@ -0,0 +1,348 @@
|
||||
package xgbutil
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xinerama"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
)
|
||||
|
||||
// Logger is used through xgbutil when messages need to be emitted to stderr.
|
||||
var Logger = log.New(os.Stderr, "[xgbutil] ", log.Lshortfile)
|
||||
|
||||
// The current maximum request size. I think we can expand this with
|
||||
// BigReq, but it probably isn't worth it at the moment.
|
||||
const MaxReqSize = (1 << 16) * 4
|
||||
|
||||
// An XUtil represents the state of xgbutil. It keeps track of the current
|
||||
// X connection, the root window, event callbacks, key/mouse bindings, etc.
|
||||
// Regrettably, many of the members are exported, even though they should not
|
||||
// be used directly by the user. They are exported for use in sub-packages.
|
||||
// (Namely, xevent, keybind and mousebind.) In fact, there should *never*
|
||||
// be a reason to access any members of an XUtil value directly. Any
|
||||
// interaction with an XUtil value should be through its methods.
|
||||
type XUtil struct {
|
||||
// conn is the XGB connection object used to issue protocol requests.
|
||||
conn *xgb.Conn
|
||||
|
||||
// Quit can be set to true, and the main event loop will finish processing
|
||||
// the current event, and gracefully quit afterwards.
|
||||
// This is exported for use in the xevent package. Please us xevent.Quit
|
||||
// to set this value.
|
||||
Quit bool // when true, the main event loop will stop gracefully
|
||||
|
||||
// setup contains all the setup information retrieved at connection time.
|
||||
setup *xproto.SetupInfo
|
||||
|
||||
// screen is a simple alias to the default screen info.
|
||||
screen *xproto.ScreenInfo
|
||||
|
||||
// root is an alias to the default root window.
|
||||
root xproto.Window
|
||||
|
||||
// Atoms is a cache of atom names to resource identifiers. This minimizes
|
||||
// round trips to the X server, since atom identifiers never change.
|
||||
// It is exported for use in the xprop package. It should not be used.
|
||||
Atoms map[string]xproto.Atom
|
||||
AtomsLck *sync.RWMutex
|
||||
|
||||
// AtomNames is a cache just like 'atoms', but in the reverse direction.
|
||||
// It is exported for use in the xprop package. It should not be used.
|
||||
AtomNames map[xproto.Atom]string
|
||||
AtomNamesLck *sync.RWMutex
|
||||
|
||||
// Evqueue is the queue that stores the results of xgb.WaitForEvent.
|
||||
// Namely, each value is either an Event *or* an Error.
|
||||
// It is exported for use in the xevent package. Do not use it.
|
||||
// If you need to interact with the event queue, please use the functions
|
||||
// available in the xevent package: Dequeue, DequeueAt, QueueEmpty
|
||||
// and QueuePeek.
|
||||
Evqueue []EventOrError
|
||||
EvqueueLck *sync.RWMutex
|
||||
|
||||
// Callbacks is a map of event numbers to a map of window identifiers
|
||||
// to callback functions.
|
||||
// This is the data structure that stores all callback functions, where
|
||||
// a callback function is always attached to a (event, window) tuple.
|
||||
// It is exported for use in the xevent package. Do not use it.
|
||||
Callbacks map[int]map[xproto.Window][]Callback
|
||||
CallbacksLck *sync.RWMutex
|
||||
|
||||
// Hooks are called by the XEvent main loop before processing the event
|
||||
// itself. These are meant for instances when it's not possible / easy
|
||||
// to use the normal Hook system. You should not modify this yourself.
|
||||
Hooks []CallbackHook
|
||||
HooksLck *sync.RWMutex
|
||||
|
||||
// eventTime is the last time recorded by an event. It is automatically
|
||||
// updated if xgbutil's main event loop is used.
|
||||
eventTime xproto.Timestamp
|
||||
|
||||
// Keymap corresponds to xgbutil's current conception of the keyboard
|
||||
// mapping. It is automatically kept up-to-date if xgbutil's event loop
|
||||
// is used.
|
||||
// It is exported for use in the keybind package. It should not be
|
||||
// accessed directly. Instead, use keybind.KeyMapGet.
|
||||
Keymap *KeyboardMapping
|
||||
|
||||
// Modmap corresponds to xgbutil's current conception of the modifier key
|
||||
// mapping. It is automatically kept up-to-date if xgbutil's event loop
|
||||
// is used.
|
||||
// It is exported for use in the keybind package. It should not be
|
||||
// accessed directly. Instead, use keybind.ModMapGet.
|
||||
Modmap *ModifierMapping
|
||||
|
||||
// KeyRedirect corresponds to a window identifier that, when set,
|
||||
// automatically receives *all* keyboard events. This is a sort-of
|
||||
// synthetic grab and is helpful in avoiding race conditions.
|
||||
// It is exported for use in the xevent and keybind packages. Do not use
|
||||
// it directly. To redirect key events, please use xevent.RedirectKeyEvents.
|
||||
KeyRedirect xproto.Window
|
||||
|
||||
// Keybinds is the data structure storing all callbacks for key bindings.
|
||||
// This is extremely similar to the general notion of event callbacks,
|
||||
// but adds extra support to make handling key bindings easier. (Like
|
||||
// specifying human readable key sequences to bind to.)
|
||||
// KeyBindKey is a struct representing the 4-tuple
|
||||
// (event-type, window-id, modifiers, keycode).
|
||||
// It is exported for use in the keybind package. Do not access it directly.
|
||||
Keybinds map[KeyKey][]CallbackKey
|
||||
KeybindsLck *sync.RWMutex
|
||||
|
||||
// Keygrabs is a frequency count of the number of callbacks associated
|
||||
// with a particular KeyBindKey. This is necessary because we can only
|
||||
// grab a particular key *once*, but we may want to attach several callbacks
|
||||
// to a single keypress.
|
||||
// It is exported for use in the keybind package. Do not access it directly.
|
||||
Keygrabs map[KeyKey]int
|
||||
|
||||
// Keystrings is a list of all key strings used to connect keybindings.
|
||||
// They are used to rebuild key grabs when the keyboard mapping is updated.
|
||||
// It is exported for use in the keybind package. Do not access it directly.
|
||||
Keystrings []KeyString
|
||||
|
||||
// Mousebinds is the data structure storing all callbacks for mouse
|
||||
// bindings.This is extremely similar to the general notion of event
|
||||
// callbacks,but adds extra support to make handling mouse bindings easier.
|
||||
// (Like specifying human readable mouse sequences to bind to.)
|
||||
// MouseBindKey is a struct representing the 4-tuple
|
||||
// (event-type, window-id, modifiers, button).
|
||||
// It is exported for use in the mousebind package. Do not use it.
|
||||
Mousebinds map[MouseKey][]CallbackMouse
|
||||
MousebindsLck *sync.RWMutex
|
||||
|
||||
// Mousegrabs is a frequency count of the number of callbacks associated
|
||||
// with a particular MouseBindKey. This is necessary because we can only
|
||||
// grab a particular mouse button *once*, but we may want to attach
|
||||
// several callbacks to a single button press.
|
||||
// It is exported for use in the mousebind package. Do not use it.
|
||||
Mousegrabs map[MouseKey]int
|
||||
|
||||
// InMouseDrag is true if a drag is currently in progress.
|
||||
// It is exported for use in the mousebind package. Do not use it.
|
||||
InMouseDrag bool
|
||||
|
||||
// MouseDragStep is the function executed for each step (i.e., pointer
|
||||
// movement) in the current mouse drag. Note that this is nil when a drag
|
||||
// is not in progress.
|
||||
// It is exported for use in the mousebind package. Do not use it.
|
||||
MouseDragStepFun MouseDragFun
|
||||
|
||||
// MouseDragEnd is the function executed at the end of the current
|
||||
// mouse drag. This is nil when a drag is not in progress.
|
||||
// It is exported for use in the mousebind package. Do not use it.
|
||||
MouseDragEndFun MouseDragFun
|
||||
|
||||
// gc is a general purpose graphics context; used to paint images.
|
||||
// Since we don't do any real X drawing, we don't really care about the
|
||||
// particulars of our graphics context.
|
||||
gc xproto.Gcontext
|
||||
|
||||
// dummy is a dummy window used for mouse/key GRABs.
|
||||
// Basically, whenever a grab is instituted, mouse and key events are
|
||||
// redirected to the dummy the window.
|
||||
dummy xproto.Window
|
||||
|
||||
// ErrorHandler is the function that handles errors *in the event loop*.
|
||||
// By default, it simply emits them to stderr.
|
||||
// It is exported for use in the xevent package. To set the default error
|
||||
// handler, please use xevent.ErrorHandlerSet.
|
||||
ErrorHandler ErrorHandlerFun
|
||||
}
|
||||
|
||||
// NewConn connects to the X server using the DISPLAY environment variable
|
||||
// and creates a new XUtil. Most environments have the DISPLAY environment
|
||||
// variable set, so this is probably what you want to use to connect to X.
|
||||
func NewConn() (*XUtil, error) {
|
||||
return NewConnDisplay("")
|
||||
}
|
||||
|
||||
// NewConnDisplay connects to the X server and creates a new XUtil.
|
||||
// If 'display' is empty, the DISPLAY environment variable is used. Otherwise
|
||||
// there are several different display formats supported:
|
||||
//
|
||||
// NewConn(":1") -> net.Dial("unix", "", "/tmp/.X11-unix/X1")
|
||||
// NewConn("/tmp/launch-12/:0") -> net.Dial("unix", "", "/tmp/launch-12/:0")
|
||||
// NewConn("hostname:2.1") -> net.Dial("tcp", "", "hostname:6002")
|
||||
// NewConn("tcp/hostname:1.0") -> net.Dial("tcp", "", "hostname:6001")
|
||||
func NewConnDisplay(display string) (*XUtil, error) {
|
||||
c, err := xgb.NewConnDisplay(display)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewConnXgb(c)
|
||||
|
||||
}
|
||||
|
||||
// NewConnXgb use the specific xgb.Conn to create a new XUtil.
|
||||
//
|
||||
// NewConn, NewConnDisplay are wrapper of this function.
|
||||
func NewConnXgb(c *xgb.Conn) (*XUtil, error) {
|
||||
setup := xproto.Setup(c)
|
||||
screen := setup.DefaultScreen(c)
|
||||
|
||||
// Initialize our central struct that stores everything.
|
||||
xu := &XUtil{
|
||||
conn: c,
|
||||
Quit: false,
|
||||
Evqueue: make([]EventOrError, 0, 1000),
|
||||
EvqueueLck: &sync.RWMutex{},
|
||||
setup: setup,
|
||||
screen: screen,
|
||||
root: screen.Root,
|
||||
eventTime: xproto.Timestamp(0), // last event time
|
||||
Atoms: make(map[string]xproto.Atom, 50),
|
||||
AtomsLck: &sync.RWMutex{},
|
||||
AtomNames: make(map[xproto.Atom]string, 50),
|
||||
AtomNamesLck: &sync.RWMutex{},
|
||||
Callbacks: make(map[int]map[xproto.Window][]Callback, 33),
|
||||
CallbacksLck: &sync.RWMutex{},
|
||||
Hooks: make([]CallbackHook, 0),
|
||||
HooksLck: &sync.RWMutex{},
|
||||
Keymap: nil, // we don't have anything yet
|
||||
Modmap: nil,
|
||||
KeyRedirect: 0,
|
||||
Keybinds: make(map[KeyKey][]CallbackKey, 10),
|
||||
KeybindsLck: &sync.RWMutex{},
|
||||
Keygrabs: make(map[KeyKey]int, 10),
|
||||
Keystrings: make([]KeyString, 0, 10),
|
||||
Mousebinds: make(map[MouseKey][]CallbackMouse, 10),
|
||||
MousebindsLck: &sync.RWMutex{},
|
||||
Mousegrabs: make(map[MouseKey]int, 10),
|
||||
InMouseDrag: false,
|
||||
MouseDragStepFun: nil,
|
||||
MouseDragEndFun: nil,
|
||||
ErrorHandler: func(err xgb.Error) { Logger.Println(err) },
|
||||
}
|
||||
|
||||
var err error = nil
|
||||
// Create a general purpose graphics context
|
||||
xu.gc, err = xproto.NewGcontextId(xu.conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xproto.CreateGC(xu.conn, xu.gc, xproto.Drawable(xu.root),
|
||||
xproto.GcForeground, []uint32{xu.screen.WhitePixel})
|
||||
|
||||
// Create a dummy window
|
||||
xu.dummy, err = xproto.NewWindowId(xu.conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xproto.CreateWindow(xu.conn, xu.Screen().RootDepth, xu.dummy, xu.RootWin(),
|
||||
-1000, -1000, 1, 1, 0,
|
||||
xproto.WindowClassInputOutput, xu.Screen().RootVisual,
|
||||
xproto.CwEventMask|xproto.CwOverrideRedirect,
|
||||
[]uint32{1, xproto.EventMaskPropertyChange})
|
||||
xproto.MapWindow(xu.conn, xu.dummy)
|
||||
|
||||
// Register the Xinerama extension... because it doesn't cost much.
|
||||
err = xinerama.Init(xu.conn)
|
||||
|
||||
// If we can't register Xinerama, that's okay. Output something
|
||||
// and move on.
|
||||
if err != nil {
|
||||
Logger.Printf("WARNING: %s\n", err)
|
||||
Logger.Printf("MESSAGE: The 'xinerama' package cannot be used " +
|
||||
"because the XINERAMA extension could not be loaded.")
|
||||
}
|
||||
|
||||
return xu, nil
|
||||
}
|
||||
|
||||
// Conn returns the xgb connection object.
|
||||
func (xu *XUtil) Conn() *xgb.Conn {
|
||||
return xu.conn
|
||||
}
|
||||
|
||||
// ExtInitialized returns true if an extension has been initialized.
|
||||
// This is useful for determining whether an extension is available or not.
|
||||
func (xu *XUtil) ExtInitialized(extName string) bool {
|
||||
_, ok := xu.Conn().Extensions[extName]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Sync forces XGB to catch up with all events/requests and synchronize.
|
||||
// This is done by issuing a benign round trip request to X.
|
||||
func (xu *XUtil) Sync() {
|
||||
xproto.GetInputFocus(xu.Conn()).Reply()
|
||||
}
|
||||
|
||||
// Setup returns the setup information retrieved during connection time.
|
||||
func (xu *XUtil) Setup() *xproto.SetupInfo {
|
||||
return xu.setup
|
||||
}
|
||||
|
||||
// Screen returns the default screen
|
||||
func (xu *XUtil) Screen() *xproto.ScreenInfo {
|
||||
return xu.screen
|
||||
}
|
||||
|
||||
// RootWin returns the current root window.
|
||||
func (xu *XUtil) RootWin() xproto.Window {
|
||||
return xu.root
|
||||
}
|
||||
|
||||
// RootWinSet will change the current root window to the one provided.
|
||||
// N.B. This probably shouldn't be used unless you're desperately trying
|
||||
// to support multiple X screens. (This is *not* the same as Xinerama/RandR or
|
||||
// TwinView. All of those have a single root window.)
|
||||
func (xu *XUtil) RootWinSet(root xproto.Window) {
|
||||
xu.root = root
|
||||
}
|
||||
|
||||
// TimeGet gets the most recent time seen by an event.
|
||||
func (xu *XUtil) TimeGet() xproto.Timestamp {
|
||||
return xu.eventTime
|
||||
}
|
||||
|
||||
// TimeSet sets the most recent time seen by an event.
|
||||
func (xu *XUtil) TimeSet(t xproto.Timestamp) {
|
||||
xu.eventTime = t
|
||||
}
|
||||
|
||||
// GC gets a general purpose graphics context that is typically used to simply
|
||||
// paint images.
|
||||
func (xu *XUtil) GC() xproto.Gcontext {
|
||||
return xu.gc
|
||||
}
|
||||
|
||||
// Dummy gets the id of the dummy window.
|
||||
func (xu *XUtil) Dummy() xproto.Window {
|
||||
return xu.dummy
|
||||
}
|
||||
|
||||
// Grabs the server. Everything becomes synchronous.
|
||||
func (xu *XUtil) Grab() {
|
||||
xproto.GrabServer(xu.Conn())
|
||||
}
|
||||
|
||||
// Ungrabs the server.
|
||||
func (xu *XUtil) Ungrab() {
|
||||
xproto.UngrabServer(xu.Conn())
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package xprop
|
||||
|
||||
/*
|
||||
xprop/atom.go contains functions related to interning atoms and retrieving
|
||||
atom names from an atom identifier.
|
||||
|
||||
It also manages an atom cache so that once an atom is interned from the X
|
||||
server, all future atom interns use that value. (So that one and only one
|
||||
request is sent for interning each atom.)
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
// Atm is a short alias for Atom in the common case of interning an atom.
|
||||
// Namely, interning the atom always succeeds. (If the atom does not already
|
||||
// exist, a new one is created.)
|
||||
func Atm(xu *xgbutil.XUtil, name string) (xproto.Atom, error) {
|
||||
aid, err := Atom(xu, name, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if aid == 0 {
|
||||
return 0, fmt.Errorf("Atm: '%s' returned an identifier of 0.", name)
|
||||
}
|
||||
|
||||
return aid, err
|
||||
}
|
||||
|
||||
// Atom interns an atom and panics if there is any error.
|
||||
func Atom(xu *xgbutil.XUtil, name string,
|
||||
onlyIfExists bool) (xproto.Atom, error) {
|
||||
|
||||
// Check the cache first
|
||||
if aid, ok := atomGet(xu, name); ok {
|
||||
return aid, nil
|
||||
}
|
||||
|
||||
reply, err := xproto.InternAtom(xu.Conn(), onlyIfExists,
|
||||
uint16(len(name)), name).Reply()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Atom: Error interning atom '%s': %s", name, err)
|
||||
}
|
||||
|
||||
// If we're here, it means we didn't have this atom cached. So cache it!
|
||||
cacheAtom(xu, name, reply.Atom)
|
||||
|
||||
return reply.Atom, nil
|
||||
}
|
||||
|
||||
// AtomName fetches a string representation of an ATOM given its integer id.
|
||||
func AtomName(xu *xgbutil.XUtil, aid xproto.Atom) (string, error) {
|
||||
// Check the cache first
|
||||
if atomName, ok := atomNameGet(xu, aid); ok {
|
||||
return string(atomName), nil
|
||||
}
|
||||
|
||||
reply, err := xproto.GetAtomName(xu.Conn(), aid).Reply()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("AtomName: Error fetching name for ATOM "+
|
||||
"id '%d': %s", aid, err)
|
||||
}
|
||||
|
||||
// If we're here, it means we didn't have ths ATOM id cached. So cache it.
|
||||
atomName := string(reply.Name)
|
||||
cacheAtom(xu, atomName, aid)
|
||||
|
||||
return atomName, nil
|
||||
}
|
||||
|
||||
// atomGet retrieves an atom identifier from a cache if it exists.
|
||||
func atomGet(xu *xgbutil.XUtil, name string) (xproto.Atom, bool) {
|
||||
xu.AtomsLck.RLock()
|
||||
defer xu.AtomsLck.RUnlock()
|
||||
|
||||
aid, ok := xu.Atoms[name]
|
||||
return aid, ok
|
||||
}
|
||||
|
||||
// atomNameGet retrieves an atom name from a cache if it exists.
|
||||
func atomNameGet(xu *xgbutil.XUtil, aid xproto.Atom) (string, bool) {
|
||||
xu.AtomNamesLck.RLock()
|
||||
defer xu.AtomNamesLck.RUnlock()
|
||||
|
||||
name, ok := xu.AtomNames[aid]
|
||||
return name, ok
|
||||
}
|
||||
|
||||
// cacheAtom puts an atom into the cache.
|
||||
func cacheAtom(xu *xgbutil.XUtil, name string, aid xproto.Atom) {
|
||||
xu.AtomsLck.Lock()
|
||||
xu.AtomNamesLck.Lock()
|
||||
defer xu.AtomsLck.Unlock()
|
||||
defer xu.AtomNamesLck.Unlock()
|
||||
|
||||
xu.Atoms[name] = aid
|
||||
xu.AtomNames[aid] = name
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Package xprop provides a cache for interning atoms and helper functions for
|
||||
dealing with GetProperty and ChangeProperty X requests.
|
||||
|
||||
Atoms
|
||||
|
||||
Atoms in X are interned, meaning that strings are assigned unique integer
|
||||
identifiers. This minimizes the amount of data transmitted over an X connection.
|
||||
|
||||
Once atoms have been interned, they are never changed while the X server is
|
||||
running. xgbutil takes advantage of this invariant and will only issue an
|
||||
intern atom request once and cache the result.
|
||||
|
||||
To use the xprop package to intern an atom, use Atom:
|
||||
|
||||
atom, err := xprop.Atom(XUtilValue, "THE_ATOM_NAME", false)
|
||||
if err == nil {
|
||||
println("The atom number: ", atom.Atom)
|
||||
}
|
||||
|
||||
The 'false' parameter corresponds to the 'only_if_exists' parameter of the
|
||||
X InternAtom request. When it's false, the atom being interned always returns
|
||||
a non-zero atom number---even if the string being interned hasn't been interned
|
||||
before. If 'only_if_exists' is true, the atom being interned will return a 0
|
||||
atom number if it hasn't already been interned.
|
||||
|
||||
The typical case is to set 'only_if_exists' to false. To this end, xprop.Atm is
|
||||
an alias that always sets this value to false.
|
||||
|
||||
The reverse can also be done: getting an atom string if you have an atom
|
||||
number. This can be done with the xprop.AtomName function.
|
||||
|
||||
Properties
|
||||
|
||||
The other facility of xprop is to help with the use of GetProperty and
|
||||
ChangeProperty. Please see the source code of the ewmh package for plenty of
|
||||
examples.
|
||||
|
||||
*/
|
||||
package xprop
|
@ -0,0 +1,270 @@
|
||||
package xprop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/BurntSushi/xgb"
|
||||
"github.com/BurntSushi/xgb/xproto"
|
||||
|
||||
"github.com/BurntSushi/xgbutil"
|
||||
)
|
||||
|
||||
// GetProperty abstracts the messiness of calling xgb.GetProperty.
|
||||
func GetProperty(xu *xgbutil.XUtil, win xproto.Window, atom string) (
|
||||
*xproto.GetPropertyReply, error) {
|
||||
|
||||
atomId, err := Atm(xu, atom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply, err := xproto.GetProperty(xu.Conn(), false, win, atomId,
|
||||
xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetProperty: Error retrieving property '%s' "+
|
||||
"on window %x: %s", atom, win, err)
|
||||
}
|
||||
|
||||
if reply.Format == 0 {
|
||||
return nil, fmt.Errorf("GetProperty: No such property '%s' on "+
|
||||
"window %x.", atom, win)
|
||||
}
|
||||
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
// ChangeProperty abstracts the semi-nastiness of xgb.ChangeProperty.
|
||||
func ChangeProp(xu *xgbutil.XUtil, win xproto.Window, format byte, prop string,
|
||||
typ string, data []byte) error {
|
||||
|
||||
propAtom, err := Atm(xu, prop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
typAtom, err := Atm(xu, typ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return xproto.ChangePropertyChecked(xu.Conn(), xproto.PropModeReplace, win,
|
||||
propAtom, typAtom, format,
|
||||
uint32(len(data)/(int(format)/8)), data).Check()
|
||||
}
|
||||
|
||||
// ChangeProperty32 makes changing 32 bit formatted properties easier
|
||||
// by constructing the raw X data for you.
|
||||
func ChangeProp32(xu *xgbutil.XUtil, win xproto.Window, prop string, typ string,
|
||||
data ...uint) error {
|
||||
|
||||
buf := make([]byte, len(data)*4)
|
||||
for i, datum := range data {
|
||||
xgb.Put32(buf[(i*4):], uint32(datum))
|
||||
}
|
||||
|
||||
return ChangeProp(xu, win, 32, prop, typ, buf)
|
||||
}
|
||||
|
||||
// WindowToUint is a covenience function for converting []xproto.Window
|
||||
// to []uint.
|
||||
func WindowToInt(ids []xproto.Window) []uint {
|
||||
ids32 := make([]uint, len(ids))
|
||||
for i, v := range ids {
|
||||
ids32[i] = uint(v)
|
||||
}
|
||||
return ids32
|
||||
}
|
||||
|
||||
// AtomToInt is a covenience function for converting []xproto.Atom
|
||||
// to []uint.
|
||||
func AtomToUint(ids []xproto.Atom) []uint {
|
||||
ids32 := make([]uint, len(ids))
|
||||
for i, v := range ids {
|
||||
ids32[i] = uint(v)
|
||||
}
|
||||
return ids32
|
||||
}
|
||||
|
||||
// StrToAtoms is a convenience function for converting
|
||||
// []string to []uint32 atoms.
|
||||
// NOTE: If an atom name in the list doesn't exist, it will be created.
|
||||
func StrToAtoms(xu *xgbutil.XUtil, atomNames []string) ([]uint, error) {
|
||||
var err error
|
||||
atoms := make([]uint, len(atomNames))
|
||||
for i, atomName := range atomNames {
|
||||
a, err := Atom(xu, atomName, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
atoms[i] = uint(a)
|
||||
}
|
||||
return atoms, err
|
||||
}
|
||||
|
||||
// PropValAtom transforms a GetPropertyReply struct into an ATOM name.
|
||||
// The property reply must be in 32 bit format.
|
||||
func PropValAtom(xu *xgbutil.XUtil, reply *xproto.GetPropertyReply,
|
||||
err error) (string, error) {
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return "", fmt.Errorf("PropValAtom: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
|
||||
return AtomName(xu, xproto.Atom(xgb.Get32(reply.Value)))
|
||||
}
|
||||
|
||||
// PropValAtoms is the same as PropValAtom, except that it returns a slice
|
||||
// of atom names. Also must be 32 bit format.
|
||||
// This is a method of an XUtil struct, unlike the other 'PropVal...' functions.
|
||||
func PropValAtoms(xu *xgbutil.XUtil, reply *xproto.GetPropertyReply,
|
||||
err error) ([]string, error) {
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return nil, fmt.Errorf("PropValAtoms: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
|
||||
ids := make([]string, reply.ValueLen)
|
||||
vals := reply.Value
|
||||
for i := 0; len(vals) >= 4; i++ {
|
||||
ids[i], err = AtomName(xu, xproto.Atom(xgb.Get32(vals)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vals = vals[4:]
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// PropValWindow transforms a GetPropertyReply struct into an X resource
|
||||
// window identifier.
|
||||
// The property reply must be in 32 bit format.
|
||||
func PropValWindow(reply *xproto.GetPropertyReply,
|
||||
err error) (xproto.Window, error) {
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return 0, fmt.Errorf("PropValId: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
return xproto.Window(xgb.Get32(reply.Value)), nil
|
||||
}
|
||||
|
||||
// PropValWindows is the same as PropValWindow, except that it returns a slice
|
||||
// of identifiers. Also must be 32 bit format.
|
||||
func PropValWindows(reply *xproto.GetPropertyReply,
|
||||
err error) ([]xproto.Window, error) {
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return nil, fmt.Errorf("PropValIds: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
|
||||
ids := make([]xproto.Window, reply.ValueLen)
|
||||
vals := reply.Value
|
||||
for i := 0; len(vals) >= 4; i++ {
|
||||
ids[i] = xproto.Window(xgb.Get32(vals))
|
||||
vals = vals[4:]
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// PropValNum transforms a GetPropertyReply struct into an unsigned
|
||||
// integer. Useful when the property value is a single integer.
|
||||
func PropValNum(reply *xproto.GetPropertyReply, err error) (uint, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return 0, fmt.Errorf("PropValNum: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
return uint(xgb.Get32(reply.Value)), nil
|
||||
}
|
||||
|
||||
// PropValNums is the same as PropValNum, except that it returns a slice
|
||||
// of integers. Also must be 32 bit format.
|
||||
func PropValNums(reply *xproto.GetPropertyReply, err error) ([]uint, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return nil, fmt.Errorf("PropValIds: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
|
||||
nums := make([]uint, reply.ValueLen)
|
||||
vals := reply.Value
|
||||
for i := 0; len(vals) >= 4; i++ {
|
||||
nums[i] = uint(xgb.Get32(vals))
|
||||
vals = vals[4:]
|
||||
}
|
||||
return nums, nil
|
||||
}
|
||||
|
||||
// PropValNum64 transforms a GetPropertyReply struct into a 64 bit
|
||||
// integer. Useful when the property value is a single integer.
|
||||
func PropValNum64(reply *xproto.GetPropertyReply, err error) (int64, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if reply.Format != 32 {
|
||||
return 0, fmt.Errorf("PropValNum: Expected format 32 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
return int64(xgb.Get32(reply.Value)), nil
|
||||
}
|
||||
|
||||
// PropValStr transforms a GetPropertyReply struct into a string.
|
||||
// Useful when the property value is a null terminated string represented
|
||||
// by integers. Also must be 8 bit format.
|
||||
func PropValStr(reply *xproto.GetPropertyReply, err error) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if reply.Format != 8 {
|
||||
return "", fmt.Errorf("PropValStr: Expected format 8 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
return string(reply.Value), nil
|
||||
}
|
||||
|
||||
// PropValStrs is the same as PropValStr, except that it returns a slice
|
||||
// of strings. The raw byte string is a sequence of null terminated strings,
|
||||
// which is translated into a slice of strings.
|
||||
func PropValStrs(reply *xproto.GetPropertyReply, err error) ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply.Format != 8 {
|
||||
return nil, fmt.Errorf("PropValStrs: Expected format 8 but got %d",
|
||||
reply.Format)
|
||||
}
|
||||
|
||||
var strs []string
|
||||
sstart := 0
|
||||
for i, c := range reply.Value {
|
||||
if c == 0 {
|
||||
strs = append(strs, string(reply.Value[sstart:i]))
|
||||
sstart = i + 1
|
||||
}
|
||||
}
|
||||
if sstart < int(reply.ValueLen) {
|
||||
strs = append(strs, string(reply.Value[sstart:]))
|
||||
}
|
||||
return strs, nil
|
||||
}
|
Loading…
Reference in New Issue