Implemented INCR selection properties

This commit is contained in:
Sasha Koshka 2023-03-29 12:27:23 -04:00
parent ab61615018
commit 1ebf5e1103

View File

@ -13,6 +13,7 @@ type selReqState int; const (
selReqStateClosed selReqState = iota
selReqStateAwaitTargets
selReqStateAwaitValue
selReqStateAwaitFirstChunk
selReqStateAwaitChunk
)
@ -22,6 +23,8 @@ type selectionRequest struct {
source xproto.Atom
destination xproto.Atom
accept []data.Mime
incrBuffer []byte
incrMime data.Mime
callback func (data.Data, error)
}
@ -138,9 +141,11 @@ func (request *selectionRequest) handleSelectionNotify (
event xevent.SelectionNotifyEvent,
) {
// the only valid states that we can process a SelectionNotify event in
if request.state != selReqStateAwaitValue && request.state != selReqStateAwaitTargets {
return
}
invalidState :=
request.state != selReqStateAwaitFirstChunk &&
request.state != selReqStateAwaitValue &&
request.state != selReqStateAwaitTargets
if invalidState { return }
// Follow:
// https://tronche.com/gui/x/icccm/sec-2.html#s-2.4
@ -152,6 +157,13 @@ func (request *selectionRequest) handleSelectionNotify (
// data.
if event.Property == 0 { request.die(nil); return }
// if we are waiting for the first INCR chunk, do the special stuff for
// that and not the other stuff.
if request.state == selReqStateAwaitFirstChunk {
request.handleINCRProperty(event.Property)
return
}
// When using GetProperty to retrieve the value of a selection, the
// property argument should be set to the corresponding value in the
// SelectionNotify event. Because the requestor has no way of knowing
@ -172,7 +184,30 @@ func (request *selectionRequest) handleSelectionNotify (
return
}
// TODO: handle INCR. do it here.
// https://tronche.com/gui/x/icccm/sec-2.html#s-2.7.2
// Requestors may receive a property of type INCR9 in response to any
// target that results in selection data. This indicates that the owner
// will send the actual data incrementally. The contents of the INCR
// property will be an integer, which represents a lower bound on the
// number of bytes of data in the selection. The requestor and the
// selection owner transfer the data in the selection in the following
// manner. The selection requestor starts the transfer process by
// deleting the (type==INCR) property forming the reply to the
// selection.
incr, err := xprop.Atm(request.window.backend.connection, "INCR")
if err != nil { request.die(err); return }
if reply.Type == incr {
// reply to the INCR selection
err = xproto.DeletePropertyChecked (
request.window.backend.connection.Conn(),
request.window.xWindow.Id,
request.destination).Check()
if err != nil { request.die(err); return }
// await the first chunk
request.state = selReqStateAwaitFirstChunk
return
}
// Once all the data in the selection has been retrieved (which may
// require getting the values of several properties &emdash; see section
@ -188,6 +223,8 @@ func (request *selectionRequest) handleSelectionNotify (
request.destination).Check()
if err != nil { request.die(err); return }
// depending on which state the selection request is in, do something
// different with the property's value
switch request.state {
case selReqStateAwaitValue:
// get the type from the property and convert that to the mime
@ -264,3 +301,40 @@ func (request *selectionRequest) handleSelectionNotify (
request.convertSelection(chosenTarget, selReqStateAwaitValue)
}
}
func (request *selectionRequest) handlePropertyNotify (
connection *xgbutil.XUtil,
event xevent.PropertyNotifyEvent,
) {
// the only valid state that we can process a PropertyNotify event in
if request.state != selReqStateAwaitChunk { return }
if event.State != xproto.PropertyNewValue { return }
request.handleINCRProperty(event.Atom)
}
func (request *selectionRequest) handleINCRProperty (property xproto.Atom) {
// Retrieving data using GetProperty with the delete argument True.
reply, err := xproto.GetProperty (
request.window.backend.connection.Conn(), true,
request.window.xWindow.Id, property, xproto.GetPropertyTypeAny,
0, (1 << 32) - 1).Reply()
if err != nil { request.die(err); return }
if len(reply.Value) == 0 {
// a zero length property means the transfer has finished. we
// finalize the request with the data we have, and don't wait
// for more.
request.finalize(data.Bytes(request.incrMime, request.incrBuffer))
} else {
// a property with content means the transfer is still ongoing.
// we append the data we got and wait for more.
request.state = selReqStateAwaitChunk
request.incrBuffer = append(request.incrBuffer, reply.Value...)
targetName, err := xprop.AtomName (
request.window.backend.connection, reply.Type)
if err != nil { request.die(err); return }
request.incrMime, _ = targetToMime(targetName)
}
}