hpcu: the slightest cleanup
This commit is contained in:
parent
f99615c850
commit
056391eeca
330
hpcu/main.go
330
hpcu/main.go
|
@ -133,180 +133,194 @@ func requestOwnership(origin *selectionState, time xproto.Timestamp) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleEvent(ev nexgb.Event) {
|
func handleXfixesSelectionNotify(e xfixes.SelectionNotifyEvent) {
|
||||||
switch e := ev.(type) {
|
state, ok := selections[e.Selection]
|
||||||
case xfixes.SelectionNotifyEvent:
|
if !ok {
|
||||||
state, ok := selections[e.Selection]
|
return
|
||||||
if !ok {
|
}
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ownership request has been granted, don't ask ourselves for data.
|
// Ownership request has been granted, don't ask ourselves for data.
|
||||||
if e.Owner == wid {
|
if e.Owner == wid {
|
||||||
state.owning = e.SelectionTimestamp
|
state.owning = e.SelectionTimestamp
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should always be true.
|
// This should always be true.
|
||||||
if state.owning < e.SelectionTimestamp {
|
if state.owning < e.SelectionTimestamp {
|
||||||
state.owning = 0
|
state.owning = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not checking whether we should give up when our current retrieval
|
// Not checking whether we should give up when our current retrieval
|
||||||
// attempt is interrupted--the timeout mostly solves this.
|
// attempt is interrupted--the timeout mostly solves this.
|
||||||
if e.Owner == xproto.WindowNone {
|
if e.Owner == xproto.WindowNone {
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't try to process two things at once. Each request gets a few
|
// Don't try to process two things at once. Each request gets a few
|
||||||
// seconds to finish, then we move on, hoping that a property race
|
// seconds to finish, then we move on, hoping that a property race
|
||||||
// doesn't commence. Ideally we'd set up a separate queue for these
|
// doesn't commence. Ideally we'd set up a separate queue for these
|
||||||
// skipped requests and process them later.
|
// skipped requests and process them later.
|
||||||
if state.inProgress != 0 && e.Timestamp-state.inProgress < 5000 {
|
if state.inProgress != 0 && e.Timestamp-state.inProgress < 5000 {
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ICCCM says we should ensure the named property doesn't exist.
|
// ICCCM says we should ensure the named property doesn't exist.
|
||||||
_ = xproto.DeleteProperty(X, e.Window, e.Selection)
|
_ = xproto.DeleteProperty(X, e.Window, e.Selection)
|
||||||
|
|
||||||
_ = xproto.ConvertSelection(X, e.Window, e.Selection,
|
_ = xproto.ConvertSelection(X, e.Window, e.Selection,
|
||||||
atomUTF8String, e.Selection, e.Timestamp)
|
atomUTF8String, e.Selection, e.Timestamp)
|
||||||
|
|
||||||
state.inProgress = e.Timestamp
|
state.inProgress = e.Timestamp
|
||||||
state.incr = false
|
state.incr = false
|
||||||
|
}
|
||||||
|
|
||||||
case xproto.SelectionNotifyEvent:
|
func handleSelectionNotify(e xproto.SelectionNotifyEvent) {
|
||||||
state, ok := selections[e.Selection]
|
state, ok := selections[e.Selection]
|
||||||
if e.Requestor != wid || !ok || e.Time != state.inProgress {
|
if e.Requestor != wid || !ok || e.Time != state.inProgress {
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state.inProgress = 0
|
state.inProgress = 0
|
||||||
if e.Property == xproto.AtomNone {
|
if e.Property == xproto.AtomNone {
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state.buffer = nil
|
state.buffer = nil
|
||||||
reply, err := getProperty(e.Requestor, e.Property)
|
reply, err := getProperty(e.Requestor, e.Property)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// When you select a lot of text in VIM, it starts the ICCCM
|
// When you select a lot of text in VIM, it starts the ICCCM
|
||||||
// INCR mechanism, from which there is no opt-out.
|
// INCR mechanism, from which there is no opt-out.
|
||||||
if reply.Type == atomINCR {
|
if reply.Type == atomINCR {
|
||||||
state.inProgress = e.Time
|
state.inProgress = e.Time
|
||||||
state.incr = true
|
state.incr = true
|
||||||
state.incrFailed = false
|
state.incrFailed = false
|
||||||
} else if appendText(state, reply) {
|
} else if appendText(state, reply) {
|
||||||
|
requestOwnership(state, e.Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = xproto.DeleteProperty(X, e.Requestor, e.Property)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlePropertyNotify(e xproto.PropertyNotifyEvent) {
|
||||||
|
state, ok := selections[e.Atom]
|
||||||
|
if e.Window != wid || e.State != xproto.PropertyNewValue ||
|
||||||
|
!ok || !state.incr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reply, err := getProperty(e.Window, e.Atom)
|
||||||
|
if err != nil {
|
||||||
|
state.incrFailed = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !appendText(state, reply) {
|
||||||
|
// We need to keep deleting the property.
|
||||||
|
state.incrFailed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if reply.ValueLen == 0 {
|
||||||
|
if !state.incrFailed {
|
||||||
requestOwnership(state, e.Time)
|
requestOwnership(state, e.Time)
|
||||||
}
|
}
|
||||||
|
state.inProgress = 0
|
||||||
|
state.incr = false
|
||||||
|
}
|
||||||
|
|
||||||
_ = xproto.DeleteProperty(X, e.Requestor, e.Property)
|
_ = xproto.DeleteProperty(X, e.Window, e.Atom)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSelectionRequest(e xproto.SelectionRequestEvent) {
|
||||||
|
property := e.Property
|
||||||
|
if property == xproto.AtomNone {
|
||||||
|
property = e.Target
|
||||||
|
}
|
||||||
|
|
||||||
|
state, ok := selections[e.Selection]
|
||||||
|
if e.Owner != wid || !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
typ xproto.Atom
|
||||||
|
format byte
|
||||||
|
data []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
// XXX: We should also support the MULTIPLE target but it seems to be
|
||||||
|
// unimportant and largely abandoned today.
|
||||||
|
targets := []xproto.Atom{atomTARGETS, atomTIMESTAMP, atomUTF8String}
|
||||||
|
|
||||||
|
switch e.Target {
|
||||||
|
case atomTARGETS:
|
||||||
|
typ = xproto.AtomAtom
|
||||||
|
format = 32
|
||||||
|
|
||||||
|
data = make([]byte, len(targets)*4)
|
||||||
|
for i, atom := range targets {
|
||||||
|
nexgb.Put32(data[i*4:], uint32(atom))
|
||||||
|
}
|
||||||
|
|
||||||
|
case atomTIMESTAMP:
|
||||||
|
typ = xproto.AtomInteger
|
||||||
|
format = 32
|
||||||
|
|
||||||
|
data = make([]byte, 4)
|
||||||
|
nexgb.Put32(data, uint32(state.owning))
|
||||||
|
|
||||||
|
case atomUTF8String:
|
||||||
|
typ = atomUTF8String
|
||||||
|
format = 8
|
||||||
|
|
||||||
|
data = []byte(contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := xproto.SelectionNotifyEvent{
|
||||||
|
Time: e.Time,
|
||||||
|
Requestor: e.Requestor,
|
||||||
|
Selection: e.Selection,
|
||||||
|
Target: e.Target,
|
||||||
|
Property: xproto.AtomNone,
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ == 0 || len(data) > int(setup.MaximumRequestLength)*4-64 ||
|
||||||
|
state.owning == 0 || e.Time < state.owning {
|
||||||
|
// TODO: Use the INCR mechanism for large data transfers instead
|
||||||
|
// of refusing the request, or at least use PropModeAppend.
|
||||||
|
//
|
||||||
|
// According to the ICCCM we need to set up a queue for concurrent
|
||||||
|
// (requestor, selection, target, timestamp) requests that differ
|
||||||
|
// only in the target property, and process them in order. The ICCCM
|
||||||
|
// has a nice rationale. It seems to only concern INCR. The queue
|
||||||
|
// might be a map[(who, what, how, when)][](where, data, offset).
|
||||||
|
//
|
||||||
|
// NOTE: Even with BigRequests support, it may technically be
|
||||||
|
// missing on the particular X server, and XGB copies buffers to yet
|
||||||
|
// another buffer, making very large transfers a very bad idea.
|
||||||
|
} else if xproto.ChangePropertyChecked(X, xproto.PropModeReplace,
|
||||||
|
e.Requestor, property, typ, format,
|
||||||
|
uint32(len(data)/int(format/8)), data).Check() == nil {
|
||||||
|
response.Property = property
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = xproto.SendEvent(X, false /* propagate */, e.Requestor,
|
||||||
|
0 /* event mask */, string(response.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleXEvent(ev nexgb.Event) {
|
||||||
|
switch e := ev.(type) {
|
||||||
|
case xfixes.SelectionNotifyEvent:
|
||||||
|
handleXfixesSelectionNotify(e)
|
||||||
|
case xproto.SelectionNotifyEvent:
|
||||||
|
handleSelectionNotify(e)
|
||||||
case xproto.PropertyNotifyEvent:
|
case xproto.PropertyNotifyEvent:
|
||||||
state, ok := selections[e.Atom]
|
handlePropertyNotify(e)
|
||||||
if e.Window != wid || e.State != xproto.PropertyNewValue ||
|
|
||||||
!ok || !state.incr {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
reply, err := getProperty(e.Window, e.Atom)
|
|
||||||
if err != nil {
|
|
||||||
state.incrFailed = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !appendText(state, reply) {
|
|
||||||
// We need to keep deleting the property.
|
|
||||||
state.incrFailed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if reply.ValueLen == 0 {
|
|
||||||
if !state.incrFailed {
|
|
||||||
requestOwnership(state, e.Time)
|
|
||||||
}
|
|
||||||
state.inProgress = 0
|
|
||||||
state.incr = false
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = xproto.DeleteProperty(X, e.Window, e.Atom)
|
|
||||||
|
|
||||||
case xproto.SelectionRequestEvent:
|
case xproto.SelectionRequestEvent:
|
||||||
property := e.Property
|
handleSelectionRequest(e)
|
||||||
if property == xproto.AtomNone {
|
|
||||||
property = e.Target
|
|
||||||
}
|
|
||||||
|
|
||||||
state, ok := selections[e.Selection]
|
|
||||||
if e.Owner != wid || !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
typ xproto.Atom
|
|
||||||
format byte
|
|
||||||
data []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
// XXX: We should also support the MULTIPLE target but it seems to be
|
|
||||||
// unimportant and largely abandoned today.
|
|
||||||
targets := []xproto.Atom{atomTARGETS, atomTIMESTAMP, atomUTF8String}
|
|
||||||
|
|
||||||
switch e.Target {
|
|
||||||
case atomTARGETS:
|
|
||||||
typ = xproto.AtomAtom
|
|
||||||
format = 32
|
|
||||||
|
|
||||||
data = make([]byte, len(targets)*4)
|
|
||||||
for i, atom := range targets {
|
|
||||||
nexgb.Put32(data[i*4:], uint32(atom))
|
|
||||||
}
|
|
||||||
|
|
||||||
case atomTIMESTAMP:
|
|
||||||
typ = xproto.AtomInteger
|
|
||||||
format = 32
|
|
||||||
|
|
||||||
data = make([]byte, 4)
|
|
||||||
nexgb.Put32(data, uint32(state.owning))
|
|
||||||
|
|
||||||
case atomUTF8String:
|
|
||||||
typ = atomUTF8String
|
|
||||||
format = 8
|
|
||||||
|
|
||||||
data = []byte(contents)
|
|
||||||
}
|
|
||||||
|
|
||||||
response := xproto.SelectionNotifyEvent{
|
|
||||||
Time: e.Time,
|
|
||||||
Requestor: e.Requestor,
|
|
||||||
Selection: e.Selection,
|
|
||||||
Target: e.Target,
|
|
||||||
Property: xproto.AtomNone,
|
|
||||||
}
|
|
||||||
|
|
||||||
if typ == 0 || len(data) > int(setup.MaximumRequestLength)*4-64 ||
|
|
||||||
state.owning == 0 || e.Time < state.owning {
|
|
||||||
// TODO: Use the INCR mechanism for large data transfers instead
|
|
||||||
// of refusing the request, or at least use PropModeAppend.
|
|
||||||
//
|
|
||||||
// According to the ICCCM we need to set up a queue for concurrent
|
|
||||||
// (requestor, selection, target, timestamp) requests that differ
|
|
||||||
// only in the target property, and process them in order. The ICCCM
|
|
||||||
// has a nice rationale. It seems to only concern INCR. The queue
|
|
||||||
// might be a map[(who, what, how, when)][](where, data, offset).
|
|
||||||
//
|
|
||||||
// NOTE: Even with BigRequests support, it may technically be
|
|
||||||
// missing on the particular X server, and XGB copies buffers to yet
|
|
||||||
// another buffer, making very large transfers a very bad idea.
|
|
||||||
} else if xproto.ChangePropertyChecked(X, xproto.PropModeReplace,
|
|
||||||
e.Requestor, property, typ, format,
|
|
||||||
uint32(len(data)/int(format/8)), data).Check() == nil {
|
|
||||||
response.Property = property
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = xproto.SendEvent(X, false /* propagate */, e.Requestor,
|
|
||||||
0 /* event mask */, string(response.Bytes()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +359,7 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ev != nil {
|
if ev != nil {
|
||||||
handleEvent(ev)
|
handleXEvent(ev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue