func newWindow(X *xgbutil.XUtil) { win, err := xwindow.Generate(X) if err != nil { log.Fatal(err) } win.Create(X.RootWin(), 0, 0, 200, 200, xproto.CwBackPixel|xproto.CwEventMask, 0, xproto.EventMaskButtonRelease) win.WMGracefulClose( func(w *xwindow.Window) { xevent.Detach(w.X, w.Id) mousebind.Detach(w.X, w.Id) w.Destroy() xevent.Quit(X) Done <- true }) win.Map() err = mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { newWindow(X) }).Connect(X, win.Id, "1", false, false) if err != nil { log.Fatal(err) } }
func (mcmd mouseCommand) attachClick(wid xproto.Window, run func()) { mousebind.ButtonPressFun( func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) { // empty }).Connect(X, wid, mcmd.buttonStr, false, true) mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { run() }).Connect(X, wid, mcmd.buttonStr, false, false) }
// newWindow creates a new window with a random background color. It sets the // WM_PROTOCOLS property to contain the WM_DELETE_WINDOW atom. It also sets // up a ClientMessage event handler so that we know when to destroy the window. // We also set up a mouse binding so that clicking inside a window will // create another one. func newWindow(X *xgbutil.XUtil) { counter++ win, err := xwindow.Generate(X) if err != nil { log.Fatal(err) } // Get a random background color, create the window (ask to receive button // release events while we're at it) and map the window. bgColor := rand.Intn(0xffffff + 1) win.Create(X.RootWin(), 0, 0, 200, 200, xproto.CwBackPixel|xproto.CwEventMask, uint32(bgColor), xproto.EventMaskButtonRelease) // WMGracefulClose does all of the work for us. It sets the appropriate // values for WM_PROTOCOLS, and listens for ClientMessages that implement // the WM_DELETE_WINDOW protocol. When one is found, the provided callback // is executed. win.WMGracefulClose( func(w *xwindow.Window) { // Detach all event handlers. // This should always be done when a window can no longer // receive events. xevent.Detach(w.X, w.Id) mousebind.Detach(w.X, w.Id) w.Destroy() // Exit if there are no more windows left. counter-- if counter == 0 { os.Exit(0) } }) // It's important that the map comes after setting WMGracefulClose, since // the WM isn't obliged to watch updates to the WM_PROTOCOLS property. win.Map() // A mouse binding so that a left click will spawn a new window. // Note that we don't issue a grab here. Typically, window managers will // grab a button press on the client window (which usually activates the // window), so that we'd end up competing with the window manager if we // tried to grab it. // Instead, we set a ButtonRelease mask when creating the window and attach // a mouse binding *without* a grab. err = mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { newWindow(X) }).Connect(X, win.Id, "1", false, false) if err != nil { log.Fatal(err) } }
func (mcmd mouseCommand) attach(wid xproto.Window, run func(), propagate, grab bool) { if mcmd.down { mousebind.ButtonPressFun( func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) { run() }).Connect(X, wid, mcmd.buttonStr, propagate, grab) } else { mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { run() }).Connect(X, wid, mcmd.buttonStr, propagate, grab) } }
// attachGrabRelease is a special case of 'attach' that is necessary when // attaching a mouse release event to either the client or frame window. // // TODO: Recall and document *why* this is needed. func (mcmd mouseCommand) attachGrabRelease(wid xproto.Window, run func()) { var err error err = mousebind.ButtonPressFun( func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) { // empty }).Connect(X, wid, mcmd.buttonStr, false, true) if err != nil { logger.Warning.Printf("Could not bind '%s': %s", mcmd.buttonStr, err) } err = mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { run() }).Connect(X, wid, mcmd.buttonStr, false, false) if err != nil { logger.Warning.Printf("Could not bind '%s': %s", mcmd.buttonStr, err) } }
// attach sets up the event handlers for a mouse button press OR release. func (mcmd mouseCommand) attach(wid xproto.Window, run func(), propagate, grab bool) { if mcmd.down { err := mousebind.ButtonPressFun( func(X *xgbutil.XUtil, ev xevent.ButtonPressEvent) { run() }).Connect(X, wid, mcmd.buttonStr, propagate, grab) if err != nil { logger.Warning.Printf("Could not bind '%s': %s", mcmd.buttonStr, err) } } else { err := mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { run() }).Connect(X, wid, mcmd.buttonStr, propagate, grab) if err != nil { logger.Warning.Printf("Could not bind '%s': %s", mcmd.buttonStr, err) } } }
func NewSelect(X *xgbutil.XUtil, theme *SelectTheme, config SelectConfig) *Select { slct := &Select{ X: X, theme: theme, config: config, showing: false, selected: -1, tabComplete: TabCompletePrefix, } // Create all windows used for the base of the select prompt. This // obviously doesn't include the windows representing the items/groups. cwin := func(p xproto.Window) *xwindow.Window { return xwindow.Must(xwindow.Create(X, p)) } slct.win = cwin(X.RootWin()) slct.bInp = cwin(slct.win.Id) slct.bTop, slct.bBot = cwin(slct.win.Id), cwin(slct.win.Id) slct.bLft, slct.bRht = cwin(slct.win.Id), cwin(slct.win.Id) // Make the top-level window override redirect so the window manager // doesn't mess with us. slct.win.Change(xproto.CwOverrideRedirect, 1) slct.win.Listen(xproto.EventMaskFocusChange) slct.bInp.Listen(xproto.EventMaskKeyPress) // Create the text input window. slct.input = text.NewInput(X, slct.win.Id, 1000, 10, slct.theme.Font, slct.theme.FontSize, slct.theme.FontColor, slct.theme.BgColor) slct.input.Move(slct.theme.BorderSize, slct.theme.BorderSize) slct.input.StackSibling(slct.bRht.Id, xproto.StackModeBelow) // Colorize the windows. cclr := func(w *xwindow.Window, clr render.Color) { w.Change(xproto.CwBackPixel, clr.Uint32()) } cclr(slct.win, slct.theme.BgColor) cclr(slct.bInp, slct.theme.BorderColor) cclr(slct.bTop, slct.theme.BorderColor) cclr(slct.bBot, slct.theme.BorderColor) cclr(slct.bLft, slct.theme.BorderColor) cclr(slct.bRht, slct.theme.BorderColor) // Map the sub-windows once. (Real mapping only happens when // cycle.win is mapped.) slct.bInp.Map() slct.bTop.Map() slct.bBot.Map() slct.bLft.Map() slct.bRht.Map() slct.input.Map() // Connect the key response handler. // The handler is responsible for tab completion and quitting if the // cancel key has been pressed. slct.keyResponse().Connect(X, slct.bInp.Id) slct.focusResponse().Connect(X, slct.win.Id) // Attach a mouse handler so the user can re-focus the prompt window. mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, ev xevent.ButtonReleaseEvent) { slct.win.Focus() }).Connect(X, slct.win.Id, "1", false, true) return slct }
func main() { // Connect to the X server using the DISPLAY environment variable. X, err := xgbutil.NewConn() if err != nil { log.Fatal(err) } // Anytime the mousebind (keybind) package is used, mousebind.Initialize // *should* be called once. In the case of the mousebind package, this // isn't strictly necessary, but the 'Drag' features of the mousebind // package won't work without it. mousebind.Initialize(X) // Before attaching callbacks, wrap them in a callback function type. // The mouse package exposes two such callback types: // mousebind.ButtonPressFun and mousebind.ButtonReleaseFun. cb1 := mousebind.ButtonPressFun( func(X *xgbutil.XUtil, e xevent.ButtonPressEvent) { log.Println("Button press!") }) // We can now attach the callback to a particular window and button // combination. This particular example grabs a button on the root window, // which makes it a global mouse binding. // Also, "Mod4-1" typically corresponds to pressing down the "Super" or // "Windows" key on your keyboard, and then pressing the left mouse button. // The last two parameters are whether to make a synchronous grab and // whether to actually issue a grab, respectively. // (The parameters used here are the common case.) // See the documentation for the Connect method for more details. err = cb1.Connect(X, X.RootWin(), "Mod4-1", false, true) // A mouse binding can fail if the mouse string could not be parsed, or if // you're trying to bind a button that has already been grabbed by another // client. if err != nil { log.Fatal(err) } // We can even attach multiple callbacks to the same button. err = mousebind.ButtonPressFun( func(X *xgbutil.XUtil, e xevent.ButtonPressEvent) { log.Println("A second handler always happens after the first.") }).Connect(X, X.RootWin(), "Mod4-1", false, true) if err != nil { log.Fatal(err) } // Finally, if we want this client to stop responding to mouse events, we // can attach another handler that, when run, detaches all previous // handlers. // This time, we'll show an example of a ButtonRelease binding. err = mousebind.ButtonReleaseFun( func(X *xgbutil.XUtil, e xevent.ButtonReleaseEvent) { // Use mousebind.Detach to detach the root window // from all ButtonPress *and* ButtonRelease handlers. mousebind.Detach(X, X.RootWin()) mousebind.Detach(X, X.RootWin()) log.Printf("Detached all Button{Press,Release}Events from the "+ "root window (%d).", X.RootWin()) }).Connect(X, X.RootWin(), "Mod4-Shift-1", false, true) if err != nil { log.Fatal(err) } // Finally, start the main event loop. This will route any appropriate // ButtonPressEvents to your callback function. log.Println("Program initialized. Start pressing mouse buttons!") xevent.Main(X) }