Exemple #1
0
func (self *Window) bindKeys() {
	// navigate

	self.freqLimitedFunc(func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
		file := self.set.Next()
		if file != nil {
			self.currentFile = file
			file.Draw()
		}
	}).Connect(X, self.Id, "Space", false)

	self.freqLimitedFunc(func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
		file := self.set.Prev()
		if file != nil {
			self.currentFile = file
			file.Draw()
		}
	}).Connect(X, self.Id, "c", false)

	self.freqLimitedFunc(func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
		file := self.set.Rand()
		if file != nil {
			self.currentFile = file
			file.Draw()
		}
	}).Connect(X, self.Id, "f", false)

	// image operations

	self.freqLimitedFunc(func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
		self.currentFile.ZoomIn()
		self.currentFile.Draw()
	}).Connect(X, self.Id, "z", false)

	self.freqLimitedFunc(func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
		self.currentFile.ZoomOut()
		self.currentFile.Draw()
	}).Connect(X, self.Id, "x", false)

	keybind.KeyPressFun(func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
		self.currentFile.Move(0, MOVE_STEP)
		self.currentFile.Draw()
	}).Connect(X, self.Id, "w", false)

	keybind.KeyPressFun(func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
		self.currentFile.Move(0, -MOVE_STEP)
		self.currentFile.Draw()
	}).Connect(X, self.Id, "s", false)

	keybind.KeyPressFun(func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
		self.currentFile.Move(MOVE_STEP, 0)
		self.currentFile.Draw()
	}).Connect(X, self.Id, "a", false)

	keybind.KeyPressFun(func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
		self.currentFile.Move(-MOVE_STEP, 0)
		self.currentFile.Draw()
	}).Connect(X, self.Id, "d", false)

}
Exemple #2
0
func keyHandlers(X *xgbutil.XUtil,
	cycle *prompt.Cycle, items []*prompt.CycleItem) {

	geom := headGeom(X)

	keybind.KeyPressFun(
		func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
			shown := cycle.Show(geom, cycleNext, items)
			if !shown {
				log.Fatal("Did not show cycle prompt.")
			}
			cycle.Next()
		}).Connect(X, X.RootWin(), cycleNext, true)
	keybind.KeyPressFun(
		func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
			cycle.Next()
		}).Connect(X, cycle.GrabId(), cycleNext, true)

	keybind.KeyPressFun(
		func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
			shown := cycle.Show(geom, cyclePrev, items)
			if !shown {
				log.Fatal("Did not show cycle prompt.")
			}
			cycle.Prev()
		}).Connect(X, X.RootWin(), cyclePrev, true)
	keybind.KeyPressFun(
		func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
			cycle.Prev()
		}).Connect(X, cycle.GrabId(), cyclePrev, true)
}
func main() {
	X, err := xgbutil.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	keybind.Initialize(X) // call once before using keybind package

	// Read an example gopher image into a regular png image.
	img, _, err := image.Decode(bytes.NewBuffer(gopher.GopherPng()))
	if err != nil {
		log.Fatal(err)
	}

	// Now convert it into an X image.
	ximg := xgraphics.NewConvert(X, img)

	// Now show it in a new window.
	// We set the window title and tell the program to quit gracefully when
	// the window is closed.
	// There is also a convenience method, XShow, that requires no parameters.
	win := showImage(ximg, "The Go Gopher!", true)

	// Listen for key press events.
	win.Listen(xproto.EventMaskKeyPress)

	err = keybind.KeyPressFun(
		func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
			println("fullscreen!")
			err := ewmh.WmStateReq(X, win.Id, ewmh.StateToggle,
				"_NET_WM_STATE_FULLSCREEN")
			if err != nil {
				log.Fatal(err)
			}
		}).Connect(X, win.Id, "f", false)
	if err != nil {
		log.Fatal(err)
	}

	err = keybind.KeyPressFun(
		func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
			println("quit fullscreen!")
			err := ewmh.WmStateReq(X, win.Id, ewmh.StateToggle,
				"_NET_WM_STATE_FULLSCREEN")
			if err != nil {
				log.Fatal(err)
			}
		}).Connect(X, win.Id, "Escape", false)
	if err != nil {
		log.Fatal(err)
	}

	// If we don't block, the program will end and the window will disappear.
	// We could use a 'select{}' here, but xevent.Main will emit errors if
	// something went wrong, so use that instead.
	xevent.Main(X)
}
Exemple #4
0
func (hotkey Hotkey) attach(X *xgbutil.XUtil) {
	log.Println(hotkey.Key)
	err := keybind.KeyPressFun(
		func(X *xgbutil.XUtil, e xevent.KeyPressEvent) {
			go exec.Command("/bin/sh", "-c", hotkey.Cmd).Run()
		}).Connect(X, X.RootWin(), hotkey.Key, true)
	if err != nil {
		log.Fatalf("Could not bind %s: %s", hotkey.Key, err.Error())
	}
}
Exemple #5
0
// API
func (ct *CommandTray) Bind(key string) {
	if ct.mod != "" {
		return
	}
	ct.mod = key

	utils.FailMeMaybe(keybind.KeyPressFun(func(_ *xgbutil.XUtil, _ xevent.KeyPressEvent) {
		ct.Focus()
	}).Connect(ct.X, ct.X.RootWin(), ct.mod, true))
}
Exemple #6
0
func (self *Window) freqLimitedFunc(fun func(*xgbutil.XUtil, xevent.KeyPressEvent)) keybind.KeyPressFun {
	return keybind.KeyPressFun(func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
		if self.forbidCommand {
			return
		}
		fun(X, ev)
		self.forbidCommand = true
		go func() {
			<-time.After(CMD_FREQ)
			self.forbidCommand = false
		}()
	})
}
Exemple #7
0
func (kcmd keyCommand) attach(run func()) {
	if run == nil {
		return
	}

	if kcmd.cmd == "PromptCyclePrev" || kcmd.cmd == "PromptCycleNext" {
		// We've got to parse the key string first and make sure
		// there are some modifiers; otherwise this utterly fails!
		mods, _, _ := keybind.ParseString(X, kcmd.keyStr)
		if mods == 0 {
			logger.Warning.Printf("Sorry but the key binding '%s' for the %s "+
				"command is invalid. It must have a modifier "+
				"to work properly. i.e., Mod1-tab where 'Mod1' "+
				"is the modifier.", kcmd.keyStr, kcmd.cmd)
			return
		}

		keybind.KeyPressFun(
			func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
				run()
			}).Connect(X, wingo.root.Id, kcmd.keyStr, true)
		keybind.KeyPressFun(
			func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
				run()
			}).Connect(X, X.Dummy(), kcmd.keyStr, true)
	} else {
		if kcmd.down {
			keybind.KeyPressFun(
				func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
					run()
				}).Connect(X, wingo.root.Id, kcmd.keyStr, true)
		} else {
			keybind.KeyReleaseFun(
				func(X *xgbutil.XUtil, ev xevent.KeyReleaseEvent) {
					run()
				}).Connect(X, wingo.root.Id, kcmd.keyStr, true)
		}
	}
}
Exemple #8
0
func main() {
	X, err := xgbutil.NewConn()
	if err != nil {
		log.Fatalf("Could not connect to X: %v", err)
	}

	keybind.Initialize(X)
	keybind.KeyPressFun(
		func(X *xgbutil.XUtil, e xevent.KeyPressEvent) {
			fmt.Println("Key press!")
		}).Connect(X, X.RootWin(), "Mod4-j")

	xevent.Main(X)
}
Exemple #9
0
func main() {
	X, err := xgbutil.NewConn()
	fatal(err)

	keybind.Initialize(X)

	slct := prompt.NewSelect(X,
		prompt.DefaultSelectTheme, prompt.DefaultSelectConfig)

	// Create some artifical groups to use.
	artGroups := []prompt.SelectGroup{
		slct.NewStaticGroup("Group 1"),
		slct.NewStaticGroup("Group 2"),
		slct.NewStaticGroup("Group 3"),
		slct.NewStaticGroup("Group 4"),
		slct.NewStaticGroup("Group 5"),
	}

	// And now create some artificial items.
	items := []*item{
		newItem("andrew", 1), newItem("bruce", 2),
		newItem("kaitlyn", 3),
		newItem("cauchy", 4), newItem("plato", 1),
		newItem("platonic", 2),
		newItem("andrew gallant", 3),
		newItem("Andrew Gallant", 4), newItem("Andrew", 1),
		newItem("jim", 1), newItem("jimmy", 2),
		newItem("jimbo", 3),
	}

	groups := make([]*prompt.SelectGroupItem, len(artGroups))
	for i, artGroup := range artGroups {
		groups[i] = slct.AddGroup(artGroup)
	}
	for _, item := range items {
		item.promptItem = slct.AddChoice(item)
	}

	geom := headGeom(X)
	keybind.KeyPressFun(
		func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
			showGroups := newGroups(groups, items)
			slct.Show(geom, prompt.TabCompletePrefix, showGroups)
		}).Connect(X, X.RootWin(), selectActivate, true)

	println("Loaded...")
	xevent.Main(X)
}
Exemple #10
0
// setupEventHandlers attaches the canvas' channels to the window and
// sets the appropriate callbacks to some events:
// ConfigureNotify events will cause the window to update its state of geometry.
// Expose events will cause the window to repaint the current image.
// Button events to allow panning.
// Key events to perform various tasks when certain keys are pressed.
func (w *Window) setupEventHandlers(chans chans) {
	w.Listen(xproto.EventMaskStructureNotify | xproto.EventMaskExposure |
		xproto.EventMaskButtonPress | xproto.EventMaskButtonRelease | xproto.EventMaskKeyPress)

	// Get the current geometry in case we don't get a ConfigureNotify event
	// (or have already missed it).
	if _, err := w.Geometry(); err != nil {
		errLg.Fatal(err)
	}

	// Keep a state of window geometry.
	xevent.ConfigureNotifyFun(
		func(X *xgbutil.XUtil, ev xevent.ConfigureNotifyEvent) {
			w.Geom.WidthSet(int(ev.Width))
			w.Geom.HeightSet(int(ev.Height))
		}).Connect(w.X, w.Id)

	// Repaint the window on expose events.
	xevent.ExposeFun(
		func(X *xgbutil.XUtil, ev xevent.ExposeEvent) {
			chans.ctl <- []string{"pan", "origin"}
		}).Connect(w.X, w.Id)

	// Setup a drag handler to allow panning.
	mousebind.Drag(w.X, w.Id, w.Id, "1", false,
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) (bool, xproto.Cursor) {
			chans.panStartChan <- image.Point{ex, ey}
			return true, 0
		},
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) {
			chans.panStepChan <- image.Point{ex, ey}
		},
		// We do nothing on mouse release
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) { return })

	for _, kb := range keybinds {
		k := kb // Needed because the callback closure will capture kb
		err := keybind.KeyPressFun(
			func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
				chans.ctl <- k.command
			}).Connect(w.X, w.Id, k.key, false)
		if err != nil {
			errLg.Println(err)
		}
	}
}
Exemple #11
0
func BindKeys(keymap map[string]string) {
	X, err := xgbutil.NewConn()
	if err != nil {
		log.Fatal(err)
	}
	keybind.Initialize(X)
	for k, v := range keymap {
		v := v
		err = keybind.KeyPressFun(func(X *xgbutil.XUtil, e xevent.KeyPressEvent) {
			h.broadcast <- v
		}).Connect(X, X.RootWin(), k, true)
		if err != nil {
			log.Fatal(err)
		}
	}
	log.Println("Program initialized. Start pressing keys!")
	xevent.Main(X)
}
Exemple #12
0
// setupEventHandlers attaches the canvas' channels to the window and
// sets the appropriate callbacks to some events:
// ConfigureNotify events will cause the window to update its state of geometry.
// Expose events will cause the window to repaint the current image.
// Button events to allow panning.
// Key events to perform various tasks when certain keys are pressed. Should
// these be configurable? Meh.
func (w *window) setupEventHandlers(chans chans) {
	w.chans = chans
	w.Listen(xproto.EventMaskStructureNotify | xproto.EventMaskExposure |
		xproto.EventMaskButtonPress | xproto.EventMaskButtonRelease |
		xproto.EventMaskKeyPress)

	// Get the current geometry in case we don't get a ConfigureNotify event
	// (or have already missed it).
	_, err := w.Geometry()
	if err != nil {
		errLg.Fatal(err)
	}

	// And ask the canvas to draw the first image when it gets around to it.
	go func() {
		w.chans.drawChan <- func(origin image.Point) image.Point {
			return image.Point{}
		}
	}()

	// Keep a state of window geometry.
	xevent.ConfigureNotifyFun(
		func(X *xgbutil.XUtil, ev xevent.ConfigureNotifyEvent) {
			w.Geom.WidthSet(int(ev.Width))
			w.Geom.HeightSet(int(ev.Height))
		}).Connect(w.X, w.Id)

	// Repaint the window on expose events.
	xevent.ExposeFun(
		func(X *xgbutil.XUtil, ev xevent.ExposeEvent) {
			w.chans.drawChan <- func(origin image.Point) image.Point {
				return origin
			}
		}).Connect(w.X, w.Id)

	// Setup a drag handler to allow panning.
	mousebind.Drag(w.X, w.Id, w.Id, "1", false,
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) (bool, xproto.Cursor) {
			w.chans.panStartChan <- image.Point{ex, ey}
			return true, 0
		},
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) {
			w.chans.panStepChan <- image.Point{ex, ey}
		},
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) {
			w.chans.panEndChan <- image.Point{ex, ey}
		})

	// Set up a map of keybindings to avoid a lot of boiler plate.
	// for keystring, fun := range kbs {
	for _, keyb := range keybinds {
		keyb := keyb
		err := keybind.KeyPressFun(
			func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
				keyb.action(w)
			}).Connect(w.X, w.Id, keyb.key, false)
		if err != nil {
			errLg.Println(err)
		}
	}
}
Exemple #13
0
func main() {
	X, err := xgbutil.NewConn()
	fatal(err)

	// Whenever the mousebind package is used, you must call Initialize.
	// Similarly for the keybind package.
	keybind.Initialize(X)
	mousebind.Initialize(X)

	// Easter egg! Use a right click to draw a gopher.
	gopherPng, _, err := image.Decode(bytes.NewBuffer(gopher.GopherPng()))
	fatal(err)

	// Now scale it to a reasonable size.
	gopher := xgraphics.Scale(gopherPng, gopherWidth, gopherHeight)

	// Create a new xgraphics.Image. It automatically creates an X pixmap for
	// you, and handles drawing to windows in the XDraw, XPaint and
	// XSurfaceSet functions.
	// N.B. An error is possible since X pixmap allocation can fail.
	canvas := xgraphics.New(X, image.Rect(0, 0, width, height))

	// Color in the background color.
	canvas.For(func(x, y int) xgraphics.BGRA {
		return bg
	})

	// Use the convenience function XShowExtra to create and map the
	// canvas window.
	// XShowExtra will also set the surface window of canvas for us.
	// We also use XShowExtra to set the name of the window and to quit the
	// main event loop when the window is closed.
	win := canvas.XShowExtra("Pointer painting", true)

	// Listen for pointer motion events and key press events.
	win.Listen(xproto.EventMaskButtonPress | xproto.EventMaskButtonRelease |
		xproto.EventMaskKeyPress)

	// The mousebind drag function runs three callbacks: one when the drag is
	// first started, another at each "step" in the drag, and a final one when
	// the drag is done.
	// The button sequence (in this case '1') is pressed, the first callback
	// is executed. If the first return value is true, the drag continues
	// and a pointer grab is initiated with the cursor id specified in the
	// second return value (use 0 to keep the cursor unchanged).
	// If it's false, the drag stops.
	// Note that Drag will automatically compress MotionNotify events.
	mousebind.Drag(X, win.Id, win.Id, "1", false,
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) (bool, xproto.Cursor) {
			drawPencil(canvas, win, ex, ey)
			return true, 0
		},
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) {
			drawPencil(canvas, win, ex, ey)
		},
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) {})

	mousebind.Drag(X, win.Id, win.Id, "3", false,
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) (bool, xproto.Cursor) {
			drawGopher(canvas, gopher, win, ex, ey)
			return true, 0
		},
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) {
			drawGopher(canvas, gopher, win, ex, ey)
		},
		func(X *xgbutil.XUtil, rx, ry, ex, ey int) {})

	// Bind to the clear key specified, and just redraw the bg color.
	keybind.KeyPressFun(
		func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
			clearCanvas(canvas, win)
		}).Connect(X, win.Id, clearKey, false)

	// Bind a callback to each key specified in the 'pencils' color map.
	// The response is to simply switch the pencil color.
	for key, clr := range pencils {
		c := clr
		keybind.KeyPressFun(
			func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
				log.Printf("Changing pencil color to: %#v", c)
				pencil = c
			}).Connect(X, win.Id, key, false)
	}

	// Output some basic directions.
	fmt.Println("Use the left or right buttons on your mouse to paint " +
		"squares and gophers.")
	fmt.Println("Pressing numbers 1, 2, 3, 4, 5 or 6 will switch your pencil " +
		"color.")
	fmt.Println("Pressing 'c' will clear the canvas.")

	xevent.Main(X)
}
Exemple #14
0
func (kcmd keyCommand) attach() {
	if kcmd.cmdName == "CycleClientPrev" || kcmd.cmdName == "CycleClientNext" {
		// We've got to parse the key string first and make sure
		// there are some modifiers; otherwise this utterly fails!
		mods, _, _ := keybind.ParseString(X, kcmd.keyStr)
		if mods == 0 {
			logger.Warning.Printf("Sorry but the key binding '%s' for the %s "+
				"command is invalid. It must have a modifier "+
				"to work properly. i.e., Mod1-tab where 'Mod1' "+
				"is the modifier.", kcmd.keyStr, kcmd.cmdName)
			return
		}

		run, err := cmdHacks.CycleClientRunWithKeyStr(kcmd.keyStr, kcmd.cmdStr)
		if err != nil {
			logger.Warning.Printf("Could not setup %s: %s", kcmd.cmdName, err)
			return
		}

		err = keybind.KeyPressFun(
			func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
				go run()
			}).Connect(X, Root.Id, kcmd.keyStr, true)
		if err != nil {
			logger.Warning.Printf("Could not bind '%s': %s", kcmd.keyStr, err)
		}

		err = keybind.KeyPressFun(
			func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
				go run()
			}).Connect(X, X.Dummy(), kcmd.keyStr, true)
		if err != nil {
			logger.Warning.Printf("Could not bind '%s': %s", kcmd.keyStr, err)
		}
	} else {
		run := func() {
			go func() {
				_, err := gribbleEnv.Run(kcmd.cmdStr)
				if err != nil {
					logger.Warning.Println(err)
				}
			}()
		}
		if kcmd.down {
			err := keybind.KeyPressFun(
				func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
					run()
				}).Connect(X, Root.Id, kcmd.keyStr, true)
			if err != nil {
				logger.Warning.Printf("Could not bind '%s': %s",
					kcmd.keyStr, err)
			}
		} else {
			err := keybind.KeyReleaseFun(
				func(X *xgbutil.XUtil, ev xevent.KeyReleaseEvent) {
					run()
				}).Connect(X, Root.Id, kcmd.keyStr, true)
			if err != nil {
				logger.Warning.Printf("Could not bind '%s': %s",
					kcmd.keyStr, err)
			}
		}
	}
}
Exemple #15
0
func makeWindow(ximage *xgraphics.Image) (*xwindow.Window, *bool) {
	w, h := ximage.Rect.Dx(), ximage.Rect.Dy()

	window, err := xwindow.Generate(ximage.X)
	if err != nil {
		xgbutil.Logger.Printf("Could not generate new window id: %s", err)
		return nil, nil
	}

	window.Create(ximage.X.RootWin(), 0, 0, w, h, xproto.CwBackPixel, 0x00000000)
	window.Listen(xproto.EventMaskExposure,
		xproto.EventMaskKeyPress,
		xproto.EventMaskStructureNotify,
		xproto.EventMaskVisibilityChange)

	window.WMGracefulClose(func(w *xwindow.Window) {
		xevent.Detach(w.X, w.Id)
		keybind.Detach(w.X, w.Id)
		mousebind.Detach(w.X, w.Id)
		w.Destroy()
		xevent.Quit(w.X)
	})

	err = icccm.WmStateSet(ximage.X, window.Id, &icccm.WmState{
		State: icccm.StateNormal,
	})
	if err != nil {
		xgbutil.Logger.Printf("Could not set WM_STATE: %s", err)
	}

	err = ewmh.WmNameSet(ximage.X, window.Id, "Computer System Monitor")
	if err != nil {
		xgbutil.Logger.Printf("Could not set _NET_WM_NAME: %s", err)
	}

	err = keybind.KeyPressFun(
		func(X *xgbutil.XUtil, ev xevent.KeyPressEvent) {
			err := ewmh.WmStateReq(ximage.X, window.Id, ewmh.StateToggle,
				"_NET_WM_STATE_FULLSCREEN")
			if err != nil {
				log.Fatal(err)
			}
		}).Connect(ximage.X, window.Id, "f", false)
	if err != nil {
		log.Fatal(err)
	}

	xevent.ExposeFun(
		func(xu *xgbutil.XUtil, event xevent.ExposeEvent) {
			ximage.XExpPaint(window.Id, 0, 0)
		}).Connect(ximage.X, window.Id)

	obscured := false
	xevent.VisibilityNotifyFun(
		func(xu *xgbutil.XUtil, event xevent.VisibilityNotifyEvent) {
			obscured = event.State == xproto.VisibilityFullyObscured
		}).Connect(ximage.X, window.Id)

	window.Map()

	return window, &obscured
}
Exemple #16
0
func main() {
	// Connect to the X server using the DISPLAY environment variable.
	X, err := xgbutil.NewConn()
	if err != nil {
		log.Fatal(err)
	}

	// Anytime the keybind (mousebind) package is used, keybind.Initialize
	// *should* be called once. It isn't strictly necessary, but allows your
	// keybindings to persist even if the keyboard mapping is changed during
	// run-time. (Assuming you're using the xevent package's event loop.)
	keybind.Initialize(X)

	// Before attaching callbacks, wrap them in a callback function type.
	// The keybind package exposes two such callback types: keybind.KeyPressFun
	// and keybind.KeyReleaseFun.
	cb1 := keybind.KeyPressFun(
		func(X *xgbutil.XUtil, e xevent.KeyPressEvent) {
			log.Println("Key press!")
		})

	// We can now attach the callback to a particular window and key
	// combination. This particular example grabs a key on the root window,
	// which makes it a global keybinding.
	// Also, "Mod4-j" typically corresponds to pressing down the "Super" or
	// "Windows" key on your keyboard, and then pressing the letter "j".
	// N.B. This approach works by issuing a passive grab on the window
	// specified. To respond to Key{Press,Release} events without a grab, use
	// the xevent.Key{Press,Release}Fun callback function types instead.
	err = cb1.Connect(X, X.RootWin(), "Mod4-j", true)

	// A keybinding can fail if the key string could not be parsed, or if you're
	// trying to bind a key that has already been grabbed by another client.
	if err != nil {
		log.Fatal(err)
	}

	// We can even attach multiple callbacks to the same key.
	err = keybind.KeyPressFun(
		func(X *xgbutil.XUtil, e xevent.KeyPressEvent) {
			log.Println("A second handler always happens after the first.")
		}).Connect(X, X.RootWin(), "Mod4-j", true)
	if err != nil {
		log.Fatal(err)
	}

	// Finally, if we want this client to stop responding to key events, we
	// can attach another handler that, when run, detaches all previous
	// handlers.
	// This time, we'll show an example of a KeyRelease binding.
	err = keybind.KeyReleaseFun(
		func(X *xgbutil.XUtil, e xevent.KeyReleaseEvent) {
			// Use keybind.Detach to detach the root window
			// from all KeyPress *and* KeyRelease handlers.
			keybind.Detach(X, X.RootWin())

			log.Printf("Detached all Key{Press,Release}Events from the "+
				"root window (%d).", X.RootWin())
		}).Connect(X, X.RootWin(), "Mod4-Shift-q", true)
	if err != nil {
		log.Fatal(err)
	}

	// Finally, start the main event loop. This will route any appropriate
	// KeyPressEvents to your callback function.
	log.Println("Program initialized. Start pressing keys!")
	xevent.Main(X)
}