// A convenience function to grab the KeyboardMapping and ModifierMapping // from X. We need to do this on startup (see Initialize) and whenever we // get a MappingNotify event. func MapsGet(xu *xgbutil.XUtil) (*xproto.GetKeyboardMappingReply, *xproto.GetModifierMappingReply) { min, max := minMaxKeycodeGet(xu) queryKeymap, _ := xproto.GetKeyboardMapping(xu.Conn(), nil, min, byte(max-min+1)) newKeymap, keyErr := queryKeymap.Reply() queryModmap, _ := xproto.GetModifierMapping(xu.Conn(), nil) newModmap, modErr := queryModmap.Reply() // If there are errors, we really need to panic. We just can't do // any key binding without a mapping from the server. if keyErr != nil { panic(fmt.Sprintf("COULD NOT GET KEYBOARD MAPPING: %v\n"+ "THIS IS AN UNRECOVERABLE ERROR.\n", keyErr)) } if modErr != nil { panic(fmt.Sprintf("COULD NOT GET MODIFIER MAPPING: %v\n"+ "THIS IS AN UNRECOVERABLE ERROR.\n", keyErr)) } return newKeymap, newModmap }
// GrabKeyboard grabs the entire keyboard. // Returns whether GrabStatus is successful and an error if one is reported by // XGB. It is possible to not get an error and the grab to be unsuccessful. // The purpose of 'win' is that after a grab is successful, ALL Key*Events will // be sent to that window. Make sure you have a callback attached :-) func GrabKeyboard(xu *xgbutil.XUtil, win xproto.Window) error { query, _ := xproto.GrabKeyboard(xu.Conn(), nil, false, win, 0, xproto.GrabModeAsync, xproto.GrabModeAsync) reply, err := query.Reply() if err != nil { return fmt.Errorf("GrabKeyboard: Error grabbing keyboard on "+ "window '%x': %s", win, err) } switch reply.Status { case xproto.GrabStatusSuccess: // all is well case xproto.GrabStatusAlreadyGrabbed: return fmt.Errorf("GrabKeyboard: Could not grab keyboard. " + "Status: AlreadyGrabbed.") case xproto.GrabStatusInvalidTime: return fmt.Errorf("GrabKeyboard: Could not grab keyboard. " + "Status: InvalidTime.") case xproto.GrabStatusNotViewable: return fmt.Errorf("GrabKeyboard: Could not grab keyboard. " + "Status: NotViewable.") case xproto.GrabStatusFrozen: return fmt.Errorf("GrabKeyboard: Could not grab keyboard. " + "Status: Frozen.") } return nil }
// PhyiscalHeads returns the list of heads in a physical ordering. // Namely, left to right then top to bottom. (Defined by (X, Y).) // Xinerama must have been initialized, otherwise the xinerama.QueryScreens // request will panic. // PhysicalHeads also checks to make sure each rectangle has a unique (x, y) // tuple, so as not to return the geometry of cloned displays. // (At present moment, xgbutil initializes Xinerama automatically during // initial connection.) func PhysicalHeads(xu *xgbutil.XUtil) (Heads, error) { query, _ := xinerama.QueryScreens(xu.Conn(), nil) xinfo, err := query.Reply() if err != nil { return nil, err } hds := make(Heads, 0) for _, info := range xinfo.ScreenInfo { head := xrect.New(int(info.XOrg), int(info.YOrg), int(info.Width), int(info.Height)) // Maybe Xinerama is enabled, but we have cloned displays... unique := true for _, h := range hds { if h.X() == head.X() && h.Y() == head.Y() { unique = false break } } if unique { hds = append(hds, head) } } sort.Sort(hds) return hds, nil }
// Ungrab undoes Grab. It will handle all combinations od modifiers found // in xevent.IgnoreMods. func Ungrab(xu *xgbutil.XUtil, win xproto.Window, mods uint16, key xproto.Keycode) { for _, m := range xevent.IgnoreMods { query, _ := xproto.UngrabKeyChecked(xu.Conn(), nil, key, win, mods|m) query.Check() } }
// Ungrab undoes Grab. It will handle all combinations of modifiers found // in xevent.IgnoreMods. func Ungrab(xu *xgbutil.XUtil, win xproto.Window, mods uint16, button xproto.Button) { for _, m := range xevent.IgnoreMods { query, _ := xproto.UngrabButtonChecked(xu.Conn(), nil, byte(button), win, mods|m) query.Check() } }
// Grab grabs a key with mods on a particular window. // This will also grab all combinations of modifiers found in xevent.IgnoreMods. func Grab(xu *xgbutil.XUtil, win xproto.Window, mods uint16, key xproto.Keycode) { for _, m := range xevent.IgnoreMods { xproto.GrabKey(xu.Conn(), nil, true, win, mods|m, key, xproto.GrabModeAsync, xproto.GrabModeAsync) } }
// Grab grabs a button with mods on a particular window. // Will also grab all combinations of modifiers found in xevent.IgnoreMods // If 'sync' is True, then no further events can be processed until the // grabbing client allows them to be. (Which is done via AllowEvents. Thus, // if sync is True, you *must* make some call to AllowEvents at some // point, or else your client will lock.) func Grab(xu *xgbutil.XUtil, win xproto.Window, mods uint16, button xproto.Button, sync bool) { var pSync byte = xproto.GrabModeAsync if sync { pSync = xproto.GrabModeSync } for _, m := range xevent.IgnoreMods { xproto.GrabButton(xu.Conn(), nil, true, win, pointerMasks, pSync, xproto.GrabModeAsync, 0, 0, byte(button), mods|m) } }
// GrabChecked Grabs a key with mods on a particular window. // This is the same as Grab, except that it issue a checked request. // Which means that an error could be returned and handled on the spot. // (Checked requests are slower than unchecked requests.) // This will also grab all combinations of modifiers found in xevent.IgnoreMods. func GrabChecked(xu *xgbutil.XUtil, win xproto.Window, mods uint16, key xproto.Keycode) error { var err error for _, m := range xevent.IgnoreMods { query, _ := xproto.GrabKeyChecked(xu.Conn(), nil, true, win, mods|m, key, xproto.GrabModeAsync, xproto.GrabModeAsync) err = query.Check() if err != nil { return err } } return nil }
// GrabPointer grabs the entire pointer. // Returns whether GrabStatus is successful and an error if one is reported by // XGB. It is possible to not get an error and the grab to be unsuccessful. // The purpose of 'win' is that after a grab is successful, ALL Button*Events // will be sent to that window. Make sure you have a callback attached :-) func GrabPointer(xu *xgbutil.XUtil, win xproto.Window, confine xproto.Window, cursor xproto.Cursor) (bool, error) { query, _ := xproto.GrabPointer(xu.Conn(), nil, false, win, pointerMasks, xproto.GrabModeAsync, xproto.GrabModeAsync, confine, cursor, 0) reply, err := query.Reply() if err != nil { return false, fmt.Errorf("GrabPointer: Error grabbing pointer on "+ "window '%x': %s", win, err) } return reply.Status == xproto.GrabStatusSuccess, 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 } query, _ := xproto.ChangePropertyChecked(xu.Conn(), nil, xproto.PropModeReplace, win, propAtom, typAtom, format, uint32(len(data)/(int(format)/8)), data) return query.Check() }
// 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 } query, _ := xproto.GetAtomName(xu.Conn(), nil, aid) reply, err := query.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 }
// GrabChecked grabs a button with mods on a particular window. It does the // same thing as Grab, but issues a checked request and returns an error // on failure. // Will also grab all combinations of modifiers found in xevent.IgnoreMods // If 'sync' is True, then no further events can be processed until the // grabbing client allows them to be. (Which is done via AllowEvents. Thus, // if sync is True, you *must* make some call to AllowEvents at some // point, or else your client will lock.) func GrabChecked(xu *xgbutil.XUtil, win xproto.Window, mods uint16, button xproto.Button, sync bool) error { var pSync byte = xproto.GrabModeAsync if sync { pSync = xproto.GrabModeSync } var err error for _, m := range xevent.IgnoreMods { query, _ := xproto.GrabButtonChecked(xu.Conn(), nil, true, win, pointerMasks, pSync, xproto.GrabModeAsync, 0, 0, byte(button), mods|m) err = query.Check() if err != nil { return err } } return nil }
// 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 } query, _ := xproto.InternAtom(xu.Conn(), nil, onlyIfExists, uint16(len(name)), name) reply, err := query.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 }
// 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) } }
// 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 } query, _ := xproto.GetProperty(xu.Conn(), nil, false, win, atomId, xproto.GetPropertyTypeAny, 0, (1<<32)-1) reply, err := query.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 }
// UngrabKeyboard undoes GrabKeyboard. func UngrabKeyboard(xu *xgbutil.XUtil) { xproto.UngrabKeyboard(xu.Conn(), nil, 0) }
// 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 { query, _ := xproto.SendEventChecked(xu.Conn(), nil, false, xu.RootWin(), evMask, string(ev.Bytes())) return query.Check() }
// UngrabPointer undoes GrabPointer. func UngrabPointer(xu *xgbutil.XUtil) { xproto.UngrabPointer(xu.Conn(), nil, 0) }
// ReplayPointer is a quick alias to AllowEvents with 'ReplayPointer' mode. func ReplayPointer(xu *xgbutil.XUtil) { xproto.AllowEvents(xu.Conn(), nil, xproto.AllowReplayPointer, 0) }