This repository has been archived on 2023-08-08. You can view files and clone it, but cannot push or open issues or pull requests.
tomo-old/backends/x/event.go
Sasha Koshka 72f604e819 Repeated keys are detected properly
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.
2023-01-20 17:40:28 -05:00

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
}