diff --git a/v2/claim.go b/v2/claim.go index d394207..d9304ea 100644 --- a/v2/claim.go +++ b/v2/claim.go @@ -9,6 +9,16 @@ import "github.com/jezek/xgbutil/xprop" import "github.com/jezek/xgbutil/xevent" import "github.com/jezek/xgbutil/xwindow" +const format8bit byte = 8 +const format16bit byte = 16 +const format32bit byte = 32 + +type claimRequest struct { + reader io.ReadSeekCloser + ty xproto.Atom // must be "INCR" + event xevent.SelectionRequestEvent +} + // Claim represents a claim that a window has on a particular selection. type Claim struct { window *xwindow.Window @@ -16,6 +26,8 @@ type Claim struct { selection xproto.Atom timestamp xproto.Timestamp active bool + + requests map[xproto.Atom] claimRequest } // NewClaim claims ownership of a specified selection, and allows using data @@ -57,6 +69,7 @@ func NewClaim (window *xwindow.Window, selection xproto.Atom, data Data, timesta data: data, selection: selection, timestamp: timestamp, + requests: make(map[xproto.Atom] claimRequest), }, nil } @@ -165,7 +178,7 @@ func (claim *Claim) HandleSelectionRequest ( // send that list to the requestor atomAtom, err := xprop.Atm(claim.window.X, "ATOM") if err != nil { die(); return } - claim.fulfillSelectionRequest(data, 32, atomAtom, event) + claim.fulfillSelectionRequest(data, format32bit, atomAtom, event) default: // respond with data @@ -175,7 +188,7 @@ func (claim *Claim) HandleSelectionRequest ( data, err := io.ReadAll(reader) reader.Close() if err != nil { die() } - claim.fulfillSelectionRequest(data, 8, event.Target, event) + claim.fulfillSelectionRequest(data, format8bit, event.Target, event) } } @@ -186,7 +199,69 @@ func (claim *Claim) HandlePropertyNotify ( connection *xgbutil.XUtil, event xevent.PropertyNotifyEvent, ) { - // TODO + // The selection owner then: + // ... Waits between each append for a PropertyNotify (state==Deleted) + // event that shows that the requestor has read the data. The reason for + // doing this is to limit the consumption of space in the server. + + // FIXME check for state==Deleted + // if the request does not exist, the property was either modified in + // error or the request has already finished. either way, we simply + // exit. + request, ok := claim.requests[event.Atom] + if !ok { return } + + done := func () { + request.reader.Close() + delete(claim.requests, request.event.Property) + } + + die := func () { + done() + claim.refuseSelectionRequest(request.event) + } + + // FIXME change the length of this if maximum-request-size is smaller + buffer := [1024]byte { } + size, err := request.reader.Read(buffer[:]) + if err != nil { die(); return } + dataRead := buffer[:size] + + if len(dataRead) > 0 { + // there is more data to send + + // ... Appends the data in suitable-size chunks to the same property on + // the same window as the selection reply with a type corresponding to + // the actual type of the converted selection. The size should be less + // than the maximum-request-size in the connection handshake. + format := format8bit + err = xproto.ChangePropertyChecked ( + claim.window.X.Conn(), + xproto.PropModeAppend, request.event.Requestor, + request.event.Property, + request.ty, format, + uint32(len(dataRead) / (int(format) / 8)), dataRead).Check() + if err != nil { die(); return } + + } else { + // all data has been sent + + // ... Waits (after the entire data has been transferred to the + // server) until a PropertyNotify (state==Deleted) event that + // shows that the data has been read by the requestor and then + // writes zero-length data to the property. + + format := format8bit + err := xproto.ChangePropertyChecked ( + claim.window.X.Conn(), + xproto.PropModeAppend, request.event.Requestor, + request.event.Property, + request.ty, format, + 0, nil).Check() + if err != nil { die(); return } + + done() + } } // While the selection claim is active, HandleSelectionClear should be called