Add machinery for INCR

This commit is contained in:
Sasha Koshka 2024-07-11 03:56:25 -04:00
parent a376482293
commit 54ad3ebee6

View File

@ -9,6 +9,16 @@ import "github.com/jezek/xgbutil/xprop"
import "github.com/jezek/xgbutil/xevent" import "github.com/jezek/xgbutil/xevent"
import "github.com/jezek/xgbutil/xwindow" 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. // Claim represents a claim that a window has on a particular selection.
type Claim struct { type Claim struct {
window *xwindow.Window window *xwindow.Window
@ -16,6 +26,8 @@ type Claim struct {
selection xproto.Atom selection xproto.Atom
timestamp xproto.Timestamp timestamp xproto.Timestamp
active bool active bool
requests map[xproto.Atom] claimRequest
} }
// NewClaim claims ownership of a specified selection, and allows using data // 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, data: data,
selection: selection, selection: selection,
timestamp: timestamp, timestamp: timestamp,
requests: make(map[xproto.Atom] claimRequest),
}, nil }, nil
} }
@ -165,7 +178,7 @@ func (claim *Claim) HandleSelectionRequest (
// send that list to the requestor // send that list to the requestor
atomAtom, err := xprop.Atm(claim.window.X, "ATOM") atomAtom, err := xprop.Atm(claim.window.X, "ATOM")
if err != nil { die(); return } if err != nil { die(); return }
claim.fulfillSelectionRequest(data, 32, atomAtom, event) claim.fulfillSelectionRequest(data, format32bit, atomAtom, event)
default: default:
// respond with data // respond with data
@ -175,7 +188,7 @@ func (claim *Claim) HandleSelectionRequest (
data, err := io.ReadAll(reader) data, err := io.ReadAll(reader)
reader.Close() reader.Close()
if err != nil { die() } 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, connection *xgbutil.XUtil,
event xevent.PropertyNotifyEvent, 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 // While the selection claim is active, HandleSelectionClear should be called