The repeated bool was removed and instead key release events are *only* sent when the key is actually let go. If an element wants to listen to repeat presses, it can just listen to press events.
		
			
				
	
	
		
			288 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package x
 | |
| 
 | |
| import "git.tebibyte.media/sashakoshka/tomo"
 | |
| 
 | |
| import "github.com/jezek/xgbutil"
 | |
| import "github.com/jezek/xgb/xproto"
 | |
| import "github.com/jezek/xgbutil/xevent"
 | |
| 
 | |
| type scrollSum struct {
 | |
| 	x, y int
 | |
| }
 | |
| 
 | |
| const scrollDistance = 16
 | |
| 
 | |
| func (sum *scrollSum) add (button xproto.Button, window *Window, state uint16) {
 | |
| 	shift := 
 | |
| 		(state & xproto.ModMaskShift)                    > 0 ||
 | |
| 		(state & window.backend.modifierMasks.shiftLock) > 0
 | |
| 	if shift {
 | |
| 		switch button {
 | |
| 		case 4:
 | |
| 			sum.x -= scrollDistance
 | |
| 		case 5:
 | |
| 			sum.x += scrollDistance
 | |
| 		case 6:
 | |
| 			sum.y -= scrollDistance
 | |
| 		case 7:
 | |
| 			sum.y += scrollDistance
 | |
| 		}
 | |
| 	} else {
 | |
| 		switch button {
 | |
| 		case 4:
 | |
| 			sum.y -= scrollDistance
 | |
| 		case 5:
 | |
| 			sum.y += scrollDistance
 | |
| 		case 6:
 | |
| 			sum.x -= scrollDistance
 | |
| 		case 7:
 | |
| 			sum.x += scrollDistance
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| func (window *Window) handleConfigureNotify (
 | |
| 	connection *xgbutil.XUtil,
 | |
| 	event xevent.ConfigureNotifyEvent,
 | |
| ) {
 | |
| 	if window.child == nil { return }
 | |
| 
 | |
| 	configureEvent := *event.ConfigureNotifyEvent
 | |
| 	
 | |
| 	newWidth  := int(configureEvent.Width)
 | |
| 	newHeight := int(configureEvent.Height)
 | |
| 	sizeChanged :=
 | |
| 		window.metrics.width  != newWidth ||
 | |
| 		window.metrics.height != newHeight
 | |
| 	window.metrics.width  = newWidth
 | |
| 	window.metrics.height = newHeight
 | |
| 
 | |
| 	if sizeChanged {
 | |
| 		configureEvent = window.compressConfigureNotify(configureEvent)
 | |
| 		window.metrics.width  = int(configureEvent.Width)
 | |
| 		window.metrics.height = int(configureEvent.Height)
 | |
| 		window.reallocateCanvas()
 | |
| 		window.resizeChildToFit()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (window *Window) modifiersFromState (
 | |
| 	state uint16,
 | |
| ) (
 | |
| 	modifiers tomo.Modifiers,
 | |
| ) {
 | |
| 	return tomo.Modifiers {
 | |
| 		Shift:
 | |
| 			(state & xproto.ModMaskShift)                    > 0 ||
 | |
| 			(state & window.backend.modifierMasks.shiftLock) > 0,
 | |
| 		Control: (state & xproto.ModMaskControl)              > 0,
 | |
| 		Alt:     (state & window.backend.modifierMasks.alt)   > 0,
 | |
| 		Meta:    (state & window.backend.modifierMasks.meta)  > 0,
 | |
| 		Super:   (state & window.backend.modifierMasks.super) > 0,
 | |
| 		Hyper:   (state & window.backend.modifierMasks.hyper) > 0,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (window *Window) handleKeyPress (
 | |
| 	connection *xgbutil.XUtil,
 | |
| 	event xevent.KeyPressEvent,
 | |
| ) {
 | |
| 	if window.child == nil { return }
 | |
| 	
 | |
| 	keyEvent := *event.KeyPressEvent
 | |
| 	key, numberPad := window.backend.keycodeToKey(keyEvent.Detail, keyEvent.State)
 | |
| 	modifiers := window.modifiersFromState(keyEvent.State)
 | |
| 	modifiers.NumberPad = numberPad
 | |
| 
 | |
| 	if key == tomo.KeyTab && modifiers.Alt {
 | |
| 		if child, ok := window.child.(tomo.Selectable); ok {
 | |
| 			direction := tomo.SelectionDirectionForward
 | |
| 			if modifiers.Shift {
 | |
| 				direction = tomo.SelectionDirectionBackward
 | |
| 			}
 | |
| 
 | |
| 			if !child.HandleSelection(direction) {
 | |
| 				child.HandleDeselection()
 | |
| 			}
 | |
| 		}
 | |
| 	} else if child, ok := window.child.(tomo.KeyboardTarget); ok {
 | |
| 		child.HandleKeyDown(key, modifiers)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (window *Window) handleKeyRelease (
 | |
| 	connection *xgbutil.XUtil,
 | |
| 	event xevent.KeyReleaseEvent,
 | |
| ) {
 | |
| 	if window.child == nil { return }
 | |
| 	
 | |
| 	keyEvent := *event.KeyReleaseEvent
 | |
| 
 | |
| 	// do not process this event if it was generated from a key repeat
 | |
| 	nextEvents := xevent.Peek(window.backend.connection)
 | |
| 	if len(nextEvents) > 0 {
 | |
| 		untypedEvent := nextEvents[0]
 | |
| 		if untypedEvent.Err == nil {
 | |
| 			typedEvent, ok :=
 | |
| 				untypedEvent.Event.(xproto.KeyReleaseEvent)
 | |
| 			
 | |
| 			if ok && typedEvent.Detail == keyEvent.Detail &&
 | |
| 				typedEvent.Event == keyEvent.Event &&
 | |
| 				typedEvent.State == keyEvent.State {
 | |
| 
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	key, numberPad := window.backend.keycodeToKey(keyEvent.Detail, keyEvent.State)
 | |
| 	modifiers := window.modifiersFromState(keyEvent.State)
 | |
| 	modifiers.NumberPad = numberPad
 | |
| 	
 | |
| 	if child, ok := window.child.(tomo.KeyboardTarget); ok {
 | |
| 		child.HandleKeyUp(key, modifiers)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (window *Window) handleButtonPress (
 | |
| 	connection *xgbutil.XUtil,
 | |
| 	event xevent.ButtonPressEvent,
 | |
| ) {
 | |
| 	if window.child == nil { return }
 | |
| 	
 | |
| 	if child, ok := window.child.(tomo.MouseTarget); ok {
 | |
| 		buttonEvent := *event.ButtonPressEvent
 | |
| 		if buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 {
 | |
| 			sum := scrollSum { }
 | |
| 			sum.add(buttonEvent.Detail, window, buttonEvent.State)
 | |
| 			window.compressScrollSum(buttonEvent, &sum)
 | |
| 			child.HandleMouseScroll (
 | |
| 				int(buttonEvent.EventX),
 | |
| 				int(buttonEvent.EventY),
 | |
| 				float64(sum.x), float64(sum.y))
 | |
| 		} else {
 | |
| 			child.HandleMouseDown (
 | |
| 				int(buttonEvent.EventX),
 | |
| 				int(buttonEvent.EventY),
 | |
| 				tomo.Button(buttonEvent.Detail))
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| }
 | |
| 
 | |
| func (window *Window) handleButtonRelease (
 | |
| 	connection *xgbutil.XUtil,
 | |
| 	event xevent.ButtonReleaseEvent,
 | |
| ) {
 | |
| 	if window.child == nil { return }
 | |
| 	
 | |
| 	if child, ok := window.child.(tomo.MouseTarget); ok {
 | |
| 		buttonEvent := *event.ButtonReleaseEvent
 | |
| 		if buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 { return }
 | |
| 		child.HandleMouseUp (
 | |
| 			int(buttonEvent.EventX),
 | |
| 			int(buttonEvent.EventY),
 | |
| 			tomo.Button(buttonEvent.Detail))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (window *Window) handleMotionNotify (
 | |
| 	connection *xgbutil.XUtil,
 | |
| 	event xevent.MotionNotifyEvent,
 | |
| ) {
 | |
| 	if window.child == nil { return }
 | |
| 	
 | |
| 	if child, ok := window.child.(tomo.MouseTarget); ok {
 | |
| 		motionEvent := window.compressMotionNotify(*event.MotionNotifyEvent)
 | |
| 		child.HandleMouseMove (
 | |
| 			int(motionEvent.EventX),
 | |
| 			int(motionEvent.EventY))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| func (window *Window) compressConfigureNotify (
 | |
| 	firstEvent xproto.ConfigureNotifyEvent,
 | |
| ) (
 | |
| 	lastEvent xproto.ConfigureNotifyEvent,
 | |
| ) {
 | |
| 	window.backend.connection.Sync()
 | |
| 	xevent.Read(window.backend.connection, false)
 | |
| 	lastEvent = firstEvent
 | |
| 	
 | |
| 	for index, untypedEvent := range xevent.Peek(window.backend.connection) {
 | |
| 		if untypedEvent.Err != nil { continue }
 | |
| 		
 | |
| 		typedEvent, ok := untypedEvent.Event.(xproto.ConfigureNotifyEvent)
 | |
| 		if !ok { continue }
 | |
| 		
 | |
| 		if firstEvent.Event == typedEvent.Event &&
 | |
| 			firstEvent.Window == typedEvent.Window {
 | |
| 
 | |
| 			lastEvent = typedEvent
 | |
| 			defer func (index int) {
 | |
| 				xevent.DequeueAt(window.backend.connection, index)
 | |
| 			} (index)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (window *Window) compressScrollSum (
 | |
| 	firstEvent xproto.ButtonPressEvent,
 | |
| 	sum *scrollSum,
 | |
| ) {
 | |
| 	window.backend.connection.Sync()
 | |
| 	xevent.Read(window.backend.connection, false)
 | |
| 	
 | |
| 	for index, untypedEvent := range xevent.Peek(window.backend.connection) {
 | |
| 		if untypedEvent.Err != nil { continue }
 | |
| 		
 | |
| 		typedEvent, ok := untypedEvent.Event.(xproto.ButtonPressEvent)
 | |
| 		if !ok { continue }
 | |
| 
 | |
| 		if firstEvent.Event == typedEvent.Event &&
 | |
| 			typedEvent.Detail >= 4 &&
 | |
| 			typedEvent.Detail <= 7 {
 | |
| 
 | |
| 			sum.add(typedEvent.Detail, window, typedEvent.State)
 | |
| 			defer func (index int) {
 | |
| 				xevent.DequeueAt(window.backend.connection, index)
 | |
| 			} (index)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (window *Window) compressMotionNotify (
 | |
| 	firstEvent xproto.MotionNotifyEvent,
 | |
| ) (
 | |
| 	lastEvent xproto.MotionNotifyEvent,
 | |
| ) {
 | |
| 	window.backend.connection.Sync()
 | |
| 	xevent.Read(window.backend.connection, false)
 | |
| 	lastEvent = firstEvent
 | |
| 	
 | |
| 	for index, untypedEvent := range xevent.Peek(window.backend.connection) {
 | |
| 		if untypedEvent.Err != nil { continue }
 | |
| 		
 | |
| 		typedEvent, ok := untypedEvent.Event.(xproto.MotionNotifyEvent)
 | |
| 		if !ok { continue }
 | |
| 
 | |
| 		if firstEvent.Event == typedEvent.Event &&
 | |
| 			typedEvent.Detail >= 4 &&
 | |
| 			typedEvent.Detail <= 7 {
 | |
| 
 | |
| 			lastEvent = typedEvent
 | |
| 			defer func (index int) {
 | |
| 				xevent.DequeueAt(window.backend.connection, index)
 | |
| 			} (index)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 |