// WMGracefulClose will do all the necessary setup to implement the // WM_DELETE_WINDOW protocol. This will prevent well-behaving window managers // from killing your client whenever one of your windows is closed. (Killing // a client is bad because it will destroy your X connection and any other // clients you have open.) // You must provide a callback function that is called when the window manager // asks you to close your window. (You may provide some means of confirmation // to the user, i.e., "Do you really want to quit?", but you should probably // just wrap things up and call DestroyWindow.) func (w *Window) WMGracefulClose(cb func(w *Window)) { // Get the current protocols so we don't overwrite anything. prots, _ := icccm.WmProtocolsGet(w.X, w.Id) // If WM_DELETE_WINDOW isn't here, add it. Otherwise, move on. wmdelete := false for _, prot := range prots { if prot == "WM_DELETE_WINDOW" { wmdelete = true break } } if !wmdelete { icccm.WmProtocolsSet(w.X, w.Id, append(prots, "WM_DELETE_WINDOW")) } // Attach a ClientMessage event handler. It will determine whether the // ClientMessage is a 'close' request, and if so, run the callback 'cb' // provided. xevent.ClientMessageFun( func(X *xgbutil.XUtil, ev xevent.ClientMessageEvent) { if icccm.IsDeleteProtocol(X, ev) { cb(w) } }).Connect(w.X, w.Id) }
func (w *Window) handleEvents() { var noX int32 = 1<<31 - 1 noX++ var lastX, lastY int32 = noX, 0 var button wde.Button downKeys := map[string]bool{} for { e, err := w.conn.WaitForEvent() if err != nil { fmt.Println("[go.wde X error] ", err) continue } switch e := e.(type) { case xproto.ButtonPressEvent: button = button | buttonForDetail(e.Detail) var bpe wde.MouseDownEvent bpe.Which = buttonForDetail(e.Detail) bpe.Where.X = int(e.EventX) bpe.Where.Y = int(e.EventY) lastX = int32(e.EventX) lastY = int32(e.EventY) w.events <- bpe case xproto.ButtonReleaseEvent: button = button & ^buttonForDetail(e.Detail) var bue wde.MouseUpEvent bue.Which = buttonForDetail(e.Detail) bue.Where.X = int(e.EventX) bue.Where.Y = int(e.EventY) lastX = int32(e.EventX) lastY = int32(e.EventY) w.events <- bue case xproto.LeaveNotifyEvent: var wee wde.MouseExitedEvent wee.Where.X = int(e.EventX) wee.Where.Y = int(e.EventY) if lastX != noX { wee.From.X = int(lastX) wee.From.Y = int(lastY) } else { wee.From.X = wee.Where.X wee.From.Y = wee.Where.Y } lastX = int32(e.EventX) lastY = int32(e.EventY) w.events <- wee case xproto.EnterNotifyEvent: var wee wde.MouseEnteredEvent wee.Where.X = int(e.EventX) wee.Where.Y = int(e.EventY) if lastX != noX { wee.From.X = int(lastX) wee.From.Y = int(lastY) } else { wee.From.X = wee.Where.X wee.From.Y = wee.Where.Y } lastX = int32(e.EventX) lastY = int32(e.EventY) w.events <- wee case xproto.MotionNotifyEvent: var mme wde.MouseMovedEvent mme.Where.X = int(e.EventX) mme.Where.Y = int(e.EventY) if lastX != noX { mme.From.X = int(lastX) mme.From.Y = int(lastY) } else { mme.From.X = mme.Where.X mme.From.Y = mme.Where.Y } lastX = int32(e.EventX) lastY = int32(e.EventY) if button == 0 { w.events <- mme } else { var mde wde.MouseDraggedEvent mde.MouseMovedEvent = mme mde.Which = button w.events <- mde } case xproto.KeyPressEvent: var ke wde.KeyEvent code := keybind.LookupString(w.xu, e.State, e.Detail) ke.Key = keyForCode(code) w.events <- wde.KeyDownEvent(ke) downKeys[ke.Key] = true kpe := wde.KeyTypedEvent{ KeyEvent: ke, Glyph: letterForCode(code), Chord: wde.ConstructChord(downKeys), } w.events <- kpe case xproto.KeyReleaseEvent: var ke wde.KeyUpEvent ke.Key = keyForCode(keybind.LookupString(w.xu, e.State, e.Detail)) delete(downKeys, ke.Key) w.events <- ke case xproto.ConfigureNotifyEvent: var re wde.ResizeEvent re.Width = int(e.Width) re.Height = int(e.Height) if re.Width != w.width || re.Height != w.height { w.width, w.height = re.Width, re.Height w.bufferLck.Lock() w.buffer.Destroy() w.buffer = xgraphics.New(w.xu, image.Rect(0, 0, re.Width, re.Height)) w.bufferLck.Unlock() w.events <- re } case xproto.ClientMessageEvent: if icccm.IsDeleteProtocol(w.xu, xevent.ClientMessageEvent{&e}) { w.events <- wde.CloseEvent{} } case xproto.DestroyNotifyEvent: case xproto.ReparentNotifyEvent: case xproto.MapNotifyEvent: case xproto.UnmapNotifyEvent: case xproto.PropertyNotifyEvent: default: fmt.Printf("unhandled event: type %T\n%+v\n", e, e) } } close(w.events) }