Package mousebind

import "github.com/BurntSushi/xgbutil/mousebind"
Overview
Index

Overview ▾

Package mousebind provides an easy to use interface to assign callback functions to human readable button sequences.

Namely, the mousebind package exports two function types: ButtonPressFun and ButtonReleaseFun. Values of these types are functions, and have a method called 'Connect' that attaches an event handler to be run when a particular button press is issued.

This is virtually identical to the way calbacks are attached using the xevent package, but the Connect method in the mousebind package has a couple extra parameters that are specific to mouse bindings. Namely, the button sequence to respond to (which is a combination of zero or more modifiers and exactly one button), whether to establish a passive grab and whether to make the grab synchronous or not. One can still attach callbacks to Button{Press,Release} events using xevent, but it will be run for *all* Button{Press,Release} events. (This is typically what one might do when setting up an active grab.)

Initialization

Before using the mousebind package, you should *always* make a single call to mousebind.Initialize for each X connection you're working with.

Button sequence format

Button sequences are human readable strings made up of zero or more modifiers and exactly one button. Namely:

[Mod[-Mod[...]]-]BUTTONNUMBER

Where 'Mod' can be one of: shift, lock, control, mod1, mod2, mod3, mod4, mod5, button1, button2, button3, button4, button5 or any. You can view which keys activate each modifier using the 'xmodmap' program. (If you don't have 'xmodmap', you could also run the 'xmodmap' example in the examples directory.) The 'button[1-5]' modifiers correspond to the button number in the name. (This implies that buttons 1 through 5 can act as both a button number and a modifier.)

BUTTONNUMER must correspond to a valid button number on your mouse. The best way to determine the numbers of each button on your mouse is to launch the xev program in a terminal, click the corresponding button in the new window that opens, and read the event output in the terminal that launched xev. Usually a left click is button 1, a right click is button 3 and a middle click is button 2.

An example button sequence might look like 'Mod4-Control-Shift-1'. The mouse binding for that button sequence is activated when all three modifiers---mod4, control and shift---are pressed along with the '1' button on your mouse.

When to issue a passive grab

One of the parameters of the 'Connect' method is whether to issue a passive grab or not. A passive grab is useful when you need to respond to a button press on some parent window (like the root window) without actually focusing that window. Not using a passive grab is useful when you only need to read button presses when the window is focused.

For more information on the semantics of passive grabs, please see http://tronche.com/gui/x/xlib/input/XGrabButton.html.

Also, by default, when issuing a grab on a particular (modifiers, button) tuple, several grabs are actually made. In particular, for each grab requested, another grab is made with the "num lock" mask, another grab is made with the "caps lock" mask, and another grab is made with both the "num lock" and "caps locks" masks. This allows button events to be reported regardless of whether caps lock or num lock is enabled.

The extra masks added can be modified by changing the xevent.IgnoreMods slice. If you modify xevent.IgnoreMods, it should be modified once on program startup (i.e., before any key or mouse bindings are established) and never modified again.

When to use a synchronous binding

In the vast majority of cases, 'sync' in the 'Connect' method should be set to false, which indicates that a passive grab should be asynchronous. (The value of sync is irrelevant if 'grab' is false.) This implies that any events generated by the grab are sent to the grabbing window (the second parameter of the 'Connect' method) and only the grabbing window.

Sometimes, however, you might want button events to cascade down the window tree. That is, a button press on a parent window is grabbed, but then that button press should be sent to any children windows. With an asynchronous grab, this is impossible. With a synchronous grab, however, the button event can be 'replayed' to all child windows. For example:

mousebind.Initialize(XUtilValue) // call once before using mousebind package
mousebind.ButtonPressFun(
	func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) {
		// do something when button is pressed
		// And now replay the pointer event that fired this handler to all
		// child windows. All event processing is stopped on the
		// X server until this is called.
		xproto.AllowEvents(X.Conn(), xproto.AllowReplayPointer, 0)
	}).Connect(XUtilValue, some-window-id,
		"Mod4-Control-Shift-1", true, true)

This sort of example is precisely how reparenting window managers allow one to click on a window and have it be activated or "raised" *and* have the button press sent to the client window as well.

Note that with a synchronous grab, all event processing will be halted by the X server until *some* call to xproto.AllowEvents is made.

Mouse bindings on the root window example

To run a particular function whenever the 'Mod4-Control-Shift-1' button combination is pressed (mod4 is typically the 'super' or 'windows' key, but can vary based on your system), use something like:

mousebind.Initialize(XUtilValue) // call once before using mousebind package
mousebind.ButtonPressFun(
	func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) {
		// do something when button is pressed
	}).Connect(XUtilValue, XUtilValue.RootWin(),
		"Mod4-Control-Shift-1", false, true)

Note that we issue a passive grab because Button{Press,Release} events on the root window will only be reported when the root window has focus if no grab exists.

Mouse bindings on a window you create example

This code snippet attaches an event handler to some window you've created without using a grab. Thus, the function will only be activated when the button sequence is pressed and your window has focus.

mousebind.Initialize(XUtilValue) // call once before using mousebind package
mousebind.ButtonPressFun(
	func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) {
		// do something when button is pressed
	}).Connect(XUtilValue, your-window-id, "Mod4-t", false, false)

Run a function on all button press events example

This code snippet actually does *not* use the mousebind package, but illustrates how the Button{Press,Release} event handlers in the xevent package can still be useful. Namely, the mousebind package discriminates among events depending upon the button sequences pressed, whereas the xevent package is more general: it can only discriminate at the event level.

xevent.ButtonPressFun(
	func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) {
		// do something when any button is pressed
	}).Connect(XUtilValue, your-window-id)

This is the kind of handler you might use to capture all button press events.

More examples

A complete working example using the mousebind package can be found in 'simple-mousebinding' in the examples directory of the xgbutil package.

Index ▾

func DeduceButtonInfo(state uint16, detail xproto.Button) (uint16, xproto.Button)
func Detach(xu *xgbutil.XUtil, win xproto.Window)
func DetachPress(xu *xgbutil.XUtil, win xproto.Window)
func DetachRelease(xu *xgbutil.XUtil, win xproto.Window)
func Drag(xu *xgbutil.XUtil, grabwin xproto.Window, win xproto.Window, buttonStr string, grab bool, begin xgbutil.MouseDragBeginFun, step xgbutil.MouseDragFun, end xgbutil.MouseDragFun)
func DragBegin(xu *xgbutil.XUtil, ev xevent.ButtonPressEvent, grabwin xproto.Window, win xproto.Window, begin xgbutil.MouseDragBeginFun, step xgbutil.MouseDragFun, end xgbutil.MouseDragFun)
func DragEnd(xu *xgbutil.XUtil, ev xevent.ButtonReleaseEvent)
func Grab(xu *xgbutil.XUtil, win xproto.Window, mods uint16, button xproto.Button, sync bool)
func GrabChecked(xu *xgbutil.XUtil, win xproto.Window, mods uint16, button xproto.Button, sync bool) error
func GrabPointer(xu *xgbutil.XUtil, win xproto.Window, confine xproto.Window, cursor xproto.Cursor) (bool, error)
func Initialize(xu *xgbutil.XUtil)
func ParseString(xu *xgbutil.XUtil, str string) (uint16, xproto.Button, error)
func Ungrab(xu *xgbutil.XUtil, win xproto.Window, mods uint16, button xproto.Button)
func UngrabPointer(xu *xgbutil.XUtil)
type ButtonPressFun
    func (callback ButtonPressFun) Connect(xu *xgbutil.XUtil, win xproto.Window, buttonStr string, sync bool, grab bool) error
    func (callback ButtonPressFun) Run(xu *xgbutil.XUtil, event interface{})
type ButtonReleaseFun
    func (callback ButtonReleaseFun) Connect(xu *xgbutil.XUtil, win xproto.Window, buttonStr string, sync bool, grab bool) error
    func (callback ButtonReleaseFun) Run(xu *xgbutil.XUtil, event interface{})

Package files

callback.go doc.go drag.go mousebind.go xutil.go

func DeduceButtonInfo

func DeduceButtonInfo(state uint16,
    detail xproto.Button) (uint16, xproto.Button)

DeduceButtonInfo takes a (modifiers, button) tuple and returns the relevant modifiers that were activated. This accounts for modifiers in xevent.IgnoreMods and the the button mask of the button that is pressed.

func Detach

func Detach(xu *xgbutil.XUtil, win xproto.Window)

Detach removes all handlers for all mouse events for the provided window id. This should be called whenever a window is no longer receiving events to make sure the garbage collector can release memory used to store the handler info.

func DetachPress

func DetachPress(xu *xgbutil.XUtil, win xproto.Window)

DetachPress is the same as Detach, except it only removes handlers for button *press* events.

func DetachRelease

func DetachRelease(xu *xgbutil.XUtil, win xproto.Window)

DetachRelease is the same as Detach, except it only removes handlers for mouse *release* events.

func Drag

func Drag(xu *xgbutil.XUtil, grabwin xproto.Window, win xproto.Window,
    buttonStr string, grab bool,
    begin xgbutil.MouseDragBeginFun, step xgbutil.MouseDragFun,
    end xgbutil.MouseDragFun)

Drag is the public interface that will make the appropriate connections to register a drag event for three functions: the begin function, the step function and the end function. The 'grabwin' is the window that the grab is placed on (and therefore the window where all button events are redirected to after the drag has started), and the 'win' is the window that the initial 'begin' callback is set on. In typical use cases, these windows should be the same. If 'grab' is false, then no pointer grab is issued.

func DragBegin

func DragBegin(xu *xgbutil.XUtil, ev xevent.ButtonPressEvent,
    grabwin xproto.Window, win xproto.Window,
    begin xgbutil.MouseDragBeginFun, step xgbutil.MouseDragFun,
    end xgbutil.MouseDragFun)

DragBegin executes the "begin" function registered for the current drag. It also initiates the grab with the cursor id return by the begin callback.

N.B. This function is automatically called in the Drag convenience function. This should be used when the drag can be started from a source other than a button press handled by the WM. If you use this function, then there should also be a call to DragEnd when the drag is done. (This is automatically done for you if you use Drag.)

func DragEnd

func DragEnd(xu *xgbutil.XUtil, ev xevent.ButtonReleaseEvent)

DragEnd executes the "end" function registered for the current drag. This must be called at some point if DragStart has been called.

func Grab

func Grab(xu *xgbutil.XUtil, win xproto.Window, mods uint16,
    button xproto.Button, sync bool)

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 GrabChecked

func GrabChecked(xu *xgbutil.XUtil, win xproto.Window, mods uint16,
    button xproto.Button, sync bool) error

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 GrabPointer

func GrabPointer(xu *xgbutil.XUtil, win xproto.Window, confine xproto.Window,
    cursor xproto.Cursor) (bool, error)

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 Initialize

func Initialize(xu *xgbutil.XUtil)

Initialize attaches the appropriate callbacks to make mouse bindings easier. i.e., prep the dummy window to handle mouse dragging events

func ParseString

func ParseString(xu *xgbutil.XUtil, str string) (uint16, xproto.Button, error)

ParseString takes a string of the format '[Mod[-Mod[...]]]-BUTTONNUMBER', i.e., 'Mod4-1', and returns a modifiers/button combination. "Mod" could also be one of {button1, button2, button3, button4, button5}. An error is returned if the string is malformed or if no BUTTONNUMBER could be found.

func Ungrab

func Ungrab(xu *xgbutil.XUtil, win xproto.Window, mods uint16,
    button xproto.Button)

Ungrab undoes Grab. It will handle all combinations of modifiers found in xevent.IgnoreMods.

func UngrabPointer

func UngrabPointer(xu *xgbutil.XUtil)

UngrabPointer undoes GrabPointer.

type ButtonPressFun

type ButtonPressFun xevent.ButtonPressFun

ButtonPressFun represents a function that is called when a particular mouse binding is fired.

func (ButtonPressFun) Connect

func (callback ButtonPressFun) Connect(xu *xgbutil.XUtil, win xproto.Window,
    buttonStr string, sync bool, grab bool) error

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 (ButtonPressFun) Run

func (callback ButtonPressFun) Run(xu *xgbutil.XUtil, event interface{})

type ButtonReleaseFun

type ButtonReleaseFun xevent.ButtonReleaseFun

ButtonReleaseFun represents a function that is called when a particular mouse binding is fired.

func (ButtonReleaseFun) Connect

func (callback ButtonReleaseFun) Connect(xu *xgbutil.XUtil, win xproto.Window,
    buttonStr string, sync bool, grab bool) error

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 (ButtonReleaseFun) Run

func (callback ButtonReleaseFun) Run(xu *xgbutil.XUtil, event interface{})