diff options
Diffstat (limited to 'hpcu')
-rw-r--r-- | hpcu/main.go | 302 |
1 files changed, 158 insertions, 144 deletions
diff --git a/hpcu/main.go b/hpcu/main.go index 6240ef1..3aebb62 100644 --- a/hpcu/main.go +++ b/hpcu/main.go @@ -133,180 +133,194 @@ func requestOwnership(origin *selectionState, time xproto.Timestamp) { } } -func handleEvent(ev nexgb.Event) { - switch e := ev.(type) { - case xfixes.SelectionNotifyEvent: - state, ok := selections[e.Selection] - if !ok { - break - } +func handleXfixesSelectionNotify(e xfixes.SelectionNotifyEvent) { + state, ok := selections[e.Selection] + if !ok { + return + } - // Ownership request has been granted, don't ask ourselves for data. - if e.Owner == wid { - state.owning = e.SelectionTimestamp - break - } + // Ownership request has been granted, don't ask ourselves for data. + if e.Owner == wid { + state.owning = e.SelectionTimestamp + return + } - // This should always be true. - if state.owning < e.SelectionTimestamp { - state.owning = 0 - } + // This should always be true. + if state.owning < e.SelectionTimestamp { + state.owning = 0 + } - // Not checking whether we should give up when our current retrieval - // attempt is interrupted--the timeout mostly solves this. - if e.Owner == xproto.WindowNone { - break - } + // Not checking whether we should give up when our current retrieval + // attempt is interrupted--the timeout mostly solves this. + if e.Owner == xproto.WindowNone { + return + } - // 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 - // doesn't commence. Ideally we'd set up a separate queue for these - // skipped requests and process them later. - if state.inProgress != 0 && e.Timestamp-state.inProgress < 5000 { - break - } + // 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 + // doesn't commence. Ideally we'd set up a separate queue for these + // skipped requests and process them later. + if state.inProgress != 0 && e.Timestamp-state.inProgress < 5000 { + return + } - // ICCCM says we should ensure the named property doesn't exist. - _ = xproto.DeleteProperty(X, e.Window, e.Selection) + // ICCCM says we should ensure the named property doesn't exist. + _ = xproto.DeleteProperty(X, e.Window, e.Selection) - _ = xproto.ConvertSelection(X, e.Window, e.Selection, - atomUTF8String, e.Selection, e.Timestamp) + _ = xproto.ConvertSelection(X, e.Window, e.Selection, + atomUTF8String, e.Selection, e.Timestamp) - state.inProgress = e.Timestamp - state.incr = false + state.inProgress = e.Timestamp + state.incr = false +} - case xproto.SelectionNotifyEvent: - state, ok := selections[e.Selection] - if e.Requestor != wid || !ok || e.Time != state.inProgress { - break - } +func handleSelectionNotify(e xproto.SelectionNotifyEvent) { + state, ok := selections[e.Selection] + if e.Requestor != wid || !ok || e.Time != state.inProgress { + return + } - state.inProgress = 0 - if e.Property == xproto.AtomNone { - break - } + state.inProgress = 0 + if e.Property == xproto.AtomNone { + return + } - state.buffer = nil - reply, err := getProperty(e.Requestor, e.Property) - if err != nil { - break - } + state.buffer = nil + reply, err := getProperty(e.Requestor, e.Property) + if err != nil { + return + } - // When you select a lot of text in VIM, it starts the ICCCM - // INCR mechanism, from which there is no opt-out. - if reply.Type == atomINCR { - state.inProgress = e.Time - state.incr = true - state.incrFailed = false - } else if appendText(state, reply) { - requestOwnership(state, e.Time) - } + // When you select a lot of text in VIM, it starts the ICCCM + // INCR mechanism, from which there is no opt-out. + if reply.Type == atomINCR { + state.inProgress = e.Time + state.incr = true + state.incrFailed = false + } else if appendText(state, reply) { + requestOwnership(state, e.Time) + } - _ = xproto.DeleteProperty(X, e.Requestor, e.Property) + _ = xproto.DeleteProperty(X, e.Requestor, e.Property) +} - case xproto.PropertyNotifyEvent: - state, ok := selections[e.Atom] - if e.Window != wid || e.State != xproto.PropertyNewValue || - !ok || !state.incr { - break - } +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 - break - } + 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 !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 + if reply.ValueLen == 0 { + if !state.incrFailed { + requestOwnership(state, e.Time) } + state.inProgress = 0 + state.incr = false + } - _ = xproto.DeleteProperty(X, e.Window, e.Atom) + _ = xproto.DeleteProperty(X, e.Window, e.Atom) - case xproto.SelectionRequestEvent: - property := e.Property - if property == xproto.AtomNone { - property = e.Target - } +} - state, ok := selections[e.Selection] - if e.Owner != wid || !ok { - break - } +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 - ) + 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} + // 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 + 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)) - } + 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 + case atomTIMESTAMP: + typ = xproto.AtomInteger + format = 32 - data = make([]byte, 4) - nexgb.Put32(data, uint32(state.owning)) + data = make([]byte, 4) + nexgb.Put32(data, uint32(state.owning)) - case atomUTF8String: - typ = atomUTF8String - format = 8 + case atomUTF8String: + typ = atomUTF8String + format = 8 - data = []byte(contents) - } + data = []byte(contents) + } - response := xproto.SelectionNotifyEvent{ - Time: e.Time, - Requestor: e.Requestor, - Selection: e.Selection, - Target: e.Target, - Property: xproto.AtomNone, - } + 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 - } + 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())) + _ = 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: + handlePropertyNotify(e) + case xproto.SelectionRequestEvent: + handleSelectionRequest(e) } } @@ -345,7 +359,7 @@ func main() { return } if ev != nil { - handleEvent(ev) + handleXEvent(ev) } } } |