// keyResponse translates key board input into two different actions: canceling // the current prompt and making a choice in the cycle prompt. // Canceling a prompt corresponds to the "CycleConfig.CancelKey" being pressed. // Making a choice in the cycle prompt corresponds to releasing all of the // modifiers used to initiate showing the prompt (when "CycleConfig.AutoChoose" // is true). // If CancelKey is empty, then no cancel key functionality is provided. // If AutoChoose is false, then releasing the modifiers will have no effect. // // For thos interested in the X details: // The prompt cycle dialog needs to choose the selection when the // modifiers (i.e., "alt" in "alt-tab") are released. // The only way to do this (generally) is to check the raw KeyRelease event. // Namely, if the keycode *released* is a modifier, we have to and-out // that modifier from the key release event data. If the modifiers // remaining aren't up to snuff with the original grabbed modifiers, // then we can finally "choose" the selection. // TL;DR - This is how we "exit" the prompt cycle dialog. func (cycle *Cycle) keyResponse() xevent.KeyReleaseFun { f := func(X *xgbutil.XUtil, ev xevent.KeyReleaseEvent) { if !cycle.showing { return } mods, kc := keybind.DeduceKeyInfo(ev.State, ev.Detail) // If the key release is the cancel key, quit the prompt and // don't do anything. if keybind.KeyMatch(X, cycle.config.CancelKey, mods, kc) { cycle.Hide() return } mods &= ^keybind.ModGet(X, ev.Detail) if cycle.grabMods > 0 { if mods&cycle.grabMods == 0 { cycle.Choose() } } else { if keybind.KeyMatch(X, cycle.config.ConfirmKey, mods, kc) { cycle.Choose() } } } return xevent.KeyReleaseFun(f) }
func grabKeyboardAndMouse(m *Manager) { if m == nil { return } //go func() { X, err := xgbutil.NewConn() if err != nil { logger.Info("Get New Connection Failed:", err) return } keybind.Initialize(X) mousebind.Initialize(X) err = keybind.GrabKeyboard(X, X.RootWin()) if err != nil { logger.Info("Grab Keyboard Failed:", err) return } grabAllMouseButton(X) xevent.ButtonPressFun( func(X *xgbutil.XUtil, e xevent.ButtonPressEvent) { dbus.Emit(m, "KeyReleaseEvent", "") ungrabAllMouseButton(X) keybind.UngrabKeyboard(X) logger.Info("Button Press Event") xevent.Quit(X) }).Connect(X, X.RootWin()) xevent.KeyPressFun( func(X *xgbutil.XUtil, e xevent.KeyPressEvent) { value := parseKeyEnvent(X, e.State, e.Detail) pressKeyStr = value dbus.Emit(m, "KeyPressEvent", value) }).Connect(X, X.RootWin()) xevent.KeyReleaseFun( func(X *xgbutil.XUtil, e xevent.KeyReleaseEvent) { if strings.ToLower(pressKeyStr) == "super_l" || strings.ToLower(pressKeyStr) == "super_r" { pressKeyStr = "Super" } dbus.Emit(m, "KeyReleaseEvent", pressKeyStr) pressKeyStr = "" ungrabAllMouseButton(X) keybind.UngrabKeyboard(X) logger.Infof("Key: %s\n", pressKeyStr) xevent.Quit(X) }).Connect(X, X.RootWin()) xevent.Main(X) //}() }
// connect is essentially 'Connect' for either KeyPress or KeyRelease events. // Namely, it parses the key string, issues a grab request if necessary, // sets up the appropriate event handlers for the main event loop, and attaches // the callback to the keybinding state. func connect(xu *xgbutil.XUtil, callback xgbutil.CallbackKey, evtype int, win xproto.Window, keyStr string, grab, reconnect bool) error { // Get the mods/key first mods, keycodes, err := ParseString(xu, keyStr) if err != nil { return err } // Only do the grab if we haven't yet on this window. for _, keycode := range keycodes { if grab && keyBindGrabs(xu, evtype, win, mods, keycode) == 0 { if err := GrabChecked(xu, win, mods, keycode); err != nil { // If a bad access, let's be nice and give a good error message. switch err.(type) { case xproto.AccessError: return fmt.Errorf("Got a bad access error when trying to "+ "bind '%s'. This usually means another client has "+ "already grabbed this keybinding.", keyStr) default: return fmt.Errorf("Could not bind '%s' because: %s", keyStr, err) } } } // If we've never grabbed anything on this window before, we need to // make sure we can respond to it in the main event loop. // Never do this if we're reconnecting. if !reconnect { var allCb xgbutil.Callback if evtype == xevent.KeyPress { allCb = xevent.KeyPressFun(runKeyPressCallbacks) } else { allCb = xevent.KeyReleaseFun(runKeyReleaseCallbacks) } // If this is the first Key{Press|Release}Event on this window, // then we need to listen to Key{Press|Release} events in the main // loop. if !connectedKeyBind(xu, evtype, win) { allCb.Connect(xu, win) } } // Finally, attach the callback. attachKeyBindCallback(xu, evtype, win, mods, keycode, callback) } // Keep track of all unique key connections. if !reconnect { addKeyString(xu, callback, evtype, win, keyStr, grab) } return nil }
func (m *Manager) listenKeyEvents() { xevent.KeyPressFun( func(X *xgbutil.XUtil, e xevent.KeyPressEvent) { modStr := keybind.ModifierString(e.State) keyStr := keybind.LookupString(X, e.State, e.Detail) if e.Detail == 65 { keyStr = "space" } logger.Infof("KeyStr: %s, modStr: %s", keyStr, modStr) if !m.mediaKey.emitMediaSignal(modStr, keyStr, true) { modStr = deleteSpecialMod(modStr) value := "" if len(modStr) < 1 { value = keyStr } else { value = modStr + "-" + keyStr } info, ok := newKeycodeInfo(value) if !ok { return } if v, ok := getExecCommand(info); ok { // 不然按键会阻塞,直到程序推出 go doAction(v) } } }).Connect(X, X.RootWin()) xevent.KeyReleaseFun( func(X *xgbutil.XUtil, e xevent.KeyReleaseEvent) { modStr := keybind.ModifierString(e.State) keyStr := keybind.LookupString(X, e.State, e.Detail) if e.Detail == 65 { keyStr = "space" } //modStr = deleteSpecialMod(modStr) m.mediaKey.emitMediaSignal(modStr, keyStr, false) }).Connect(X, X.RootWin()) }