Add machinery for INCR
This commit is contained in:
parent
a376482293
commit
54ad3ebee6
81
v2/claim.go
81
v2/claim.go
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user