Pasting implemented (nonworking)
This commit is contained in:
		
							parent
							
								
									8abc4defa7
								
							
						
					
					
						commit
						f9e5503320
					
				@ -257,12 +257,15 @@ func (window *window) handleSelectionClear (
 | 
				
			|||||||
	connection *xgbutil.XUtil,
 | 
						connection *xgbutil.XUtil,
 | 
				
			||||||
	event xevent.SelectionClearEvent,
 | 
						event xevent.SelectionClearEvent,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
	// TODO: schedule the claim to be deleted. when the event loop fires we
 | 
						window.selectionClaim = nil
 | 
				
			||||||
	// will check to see if the claim is scheduled to be deleted and if it
 | 
					}
 | 
				
			||||||
	// is, delete it.
 | 
					
 | 
				
			||||||
	if window.selectionClaim != nil {
 | 
					func (window *window) handleSelectionRequest (
 | 
				
			||||||
		window.selectionClaim.scheduledDelete = true
 | 
						connection *xgbutil.XUtil,
 | 
				
			||||||
	}
 | 
						event xevent.SelectionRequestEvent,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						if window.selectionClaim == nil { return }
 | 
				
			||||||
 | 
						window.selectionClaim.handleSelectionRequest(connection, event)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (window *window) compressExpose (
 | 
					func (window *window) compressExpose (
 | 
				
			||||||
 | 
				
			|||||||
@ -146,6 +146,23 @@ func targetToMime (name string) (data.Mime, confidence) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func mimeToTargets (mime data.Mime) (names []string) {
 | 
				
			||||||
 | 
						names = append(names, mime.String())
 | 
				
			||||||
 | 
						switch mime {
 | 
				
			||||||
 | 
						case data.M("application", "pdf"):
 | 
				
			||||||
 | 
							names = append(names, "ADOBE_PORTABLE_DOCUMENT_FORMAT")
 | 
				
			||||||
 | 
						case data.M("image", "pict"):
 | 
				
			||||||
 | 
							names = append(names, "APPLE_PICT")
 | 
				
			||||||
 | 
						case data.M("application", "postscript"):
 | 
				
			||||||
 | 
							names = append(names, "POSTSCRIPT")
 | 
				
			||||||
 | 
						case data.MimeFile:
 | 
				
			||||||
 | 
							names = append(names, "FILE_NAME")
 | 
				
			||||||
 | 
						case data.MimePlain:
 | 
				
			||||||
 | 
							names = append(names, "UTF8_STRING", "TEXT", "STRING")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (request *selectionRequest) handleSelectionNotify (
 | 
					func (request *selectionRequest) handleSelectionNotify (
 | 
				
			||||||
	connection *xgbutil.XUtil,
 | 
						connection *xgbutil.XUtil,
 | 
				
			||||||
	event xevent.SelectionNotifyEvent,
 | 
						event xevent.SelectionNotifyEvent,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,24 +1,155 @@
 | 
				
			|||||||
package x
 | 
					package x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "io"
 | 
				
			||||||
 | 
					import "github.com/jezek/xgb"
 | 
				
			||||||
 | 
					import "github.com/jezek/xgbutil"
 | 
				
			||||||
 | 
					import "github.com/jezek/xgb/xproto"
 | 
				
			||||||
 | 
					import "github.com/jezek/xgbutil/xprop"
 | 
				
			||||||
 | 
					import "github.com/jezek/xgbutil/xevent"
 | 
				
			||||||
import "git.tebibyte.media/sashakoshka/tomo/data"
 | 
					import "git.tebibyte.media/sashakoshka/tomo/data"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type selectionClaim struct {
 | 
					type selectionClaim struct {
 | 
				
			||||||
 | 
						window *window
 | 
				
			||||||
	data data.Data
 | 
						data data.Data
 | 
				
			||||||
	scheduledDelete bool
 | 
						name xproto.Atom
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (window *window) newSelectionClaim (data data.Data) *selectionClaim {
 | 
					func (window *window) claimSelection (name xproto.Atom, data data.Data) *selectionClaim {
 | 
				
			||||||
	return &selectionClaim{
 | 
						// Follow:
 | 
				
			||||||
 | 
						// https://tronche.com/gui/x/icccm/sec-2.html#s-2.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// A client wishing to acquire ownership of a particular selection
 | 
				
			||||||
 | 
						// should call SetSelectionOwner. The client should set the specified
 | 
				
			||||||
 | 
						// selection to the atom that represents the selection, set the
 | 
				
			||||||
 | 
						// specified owner to some window that the client created, and set the
 | 
				
			||||||
 | 
						// specified time to some time between the current last-change time of
 | 
				
			||||||
 | 
						// the selection concerned and the current server time. This time value
 | 
				
			||||||
 | 
						// usually will be obtained from the timestamp of the event that
 | 
				
			||||||
 | 
						// triggers the acquisition of the selection. Clients should not set the
 | 
				
			||||||
 | 
						// time value to CurrentTime, because if they do so, they have no way of
 | 
				
			||||||
 | 
						// finding when they gained ownership of the selection. Clients must use
 | 
				
			||||||
 | 
						// a window they created so that requestors can route events to the
 | 
				
			||||||
 | 
						// owner of the selection.
 | 
				
			||||||
 | 
						err := xproto.SetSelectionOwnerChecked (
 | 
				
			||||||
 | 
							window.backend.connection.Conn(),
 | 
				
			||||||
 | 
							window.xWindow.Id, name, 0).Check() // FIXME: should not be zero
 | 
				
			||||||
 | 
						if err != nil { return nil }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &selectionClaim {
 | 
				
			||||||
 | 
							window: window,
 | 
				
			||||||
		data: data,
 | 
							data: data,
 | 
				
			||||||
 | 
							name: name,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (claim *selectionClaim) idle () bool {
 | 
					func (window *window) refuseSelectionRequest (request xevent.SelectionRequestEvent) {
 | 
				
			||||||
	// TODO
 | 
						// ... refuse the SelectionRequest by sending the requestor window a
 | 
				
			||||||
 | 
						// SelectionNotify event with the property set to None (by means of a
 | 
				
			||||||
 | 
						// SendEvent request with an empty event mask).
 | 
				
			||||||
 | 
						event := xproto.SelectionNotifyEvent {
 | 
				
			||||||
 | 
							Requestor: request.Requestor,
 | 
				
			||||||
 | 
							Selection: request.Selection,
 | 
				
			||||||
 | 
							Target:    request.Target,
 | 
				
			||||||
 | 
							Property:  0,
 | 
				
			||||||
 | 
						}.Bytes()
 | 
				
			||||||
 | 
						xproto.SendEvent (
 | 
				
			||||||
 | 
							window.backend.connection.Conn(),
 | 
				
			||||||
 | 
							false, request.Requestor, 0, string(event))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (window *window) fulfillSelectionRequest (
 | 
				
			||||||
 | 
						data []byte,
 | 
				
			||||||
 | 
						format byte,
 | 
				
			||||||
 | 
						request xevent.SelectionRequestEvent,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						die := func () { window.refuseSelectionRequest(request) }
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// If the specified property is not None, the owner should place the
 | 
				
			||||||
 | 
						// data resulting from converting the selection into the specified
 | 
				
			||||||
 | 
						// property on the requestor window and should set the property's type
 | 
				
			||||||
 | 
						// to some appropriate value, which need not be the same as the
 | 
				
			||||||
 | 
						// specified target.
 | 
				
			||||||
 | 
						err := xproto.ChangePropertyChecked (
 | 
				
			||||||
 | 
							window.backend.connection.Conn(),
 | 
				
			||||||
 | 
							xproto.PropModeReplace, window.xWindow.Id,
 | 
				
			||||||
 | 
							request.Property,
 | 
				
			||||||
 | 
							request.Target, format,
 | 
				
			||||||
 | 
							uint32(len(data) / (int(format) / 8)), data).Check()
 | 
				
			||||||
 | 
						if err != nil { die() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the property is successfully stored, the owner should acknowledge
 | 
				
			||||||
 | 
						// the successful conversion by sending the requestor window a
 | 
				
			||||||
 | 
						// SelectionNotify event (by means of a SendEvent request with an empty
 | 
				
			||||||
 | 
						// mask).
 | 
				
			||||||
 | 
						event := xproto.SelectionNotifyEvent {
 | 
				
			||||||
 | 
							Requestor: request.Requestor,
 | 
				
			||||||
 | 
							Selection: request.Selection,
 | 
				
			||||||
 | 
							Target:    request.Target,
 | 
				
			||||||
 | 
							Property:  request.Property,
 | 
				
			||||||
 | 
						}.Bytes()
 | 
				
			||||||
 | 
						xproto.SendEvent (
 | 
				
			||||||
 | 
							window.backend.connection.Conn(),
 | 
				
			||||||
 | 
							false, request.Requestor, 0, string(event))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (claim *selectionClaim) handleSelectionRequest (
 | 
					func (claim *selectionClaim) handleSelectionRequest (
 | 
				
			||||||
	// TODO
 | 
						connection *xgbutil.XUtil,
 | 
				
			||||||
 | 
						event xevent.SelectionRequestEvent,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
	// TODO
 | 
						// Follow:
 | 
				
			||||||
 | 
						// https://tronche.com/gui/x/icccm/sec-2.html#s-2.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						die := func () { claim.window.refuseSelectionRequest(event) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// When a requestor wants the value of a selection, the owner receives a
 | 
				
			||||||
 | 
						// SelectionRequest event. The specified owner and selection will be the
 | 
				
			||||||
 | 
						// values that were specified in the SetSelectionOwner request. The
 | 
				
			||||||
 | 
						// owner should compare the timestamp with the period it has owned the
 | 
				
			||||||
 | 
						// selection and, if the time is outside, refuse the SelectionRequest.
 | 
				
			||||||
 | 
						if event.Selection != claim.name { die(); return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the specified property is None , the requestor is an obsolete
 | 
				
			||||||
 | 
						// client. Owners are encouraged to support these clients by using the
 | 
				
			||||||
 | 
						// specified target atom as the property name to be used for the reply.
 | 
				
			||||||
 | 
						if event.Property == 0 {
 | 
				
			||||||
 | 
							event.Property = event.Target
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Otherwise, the owner should use the target to decide the form into
 | 
				
			||||||
 | 
						// which the selection should be converted. Some targets may be defined
 | 
				
			||||||
 | 
						// such that requestors can pass parameters along with the request. The
 | 
				
			||||||
 | 
						// owner will find these parameters in the property named in the
 | 
				
			||||||
 | 
						// selection request. The type, format, and contents of this property
 | 
				
			||||||
 | 
						// are dependent upon the definition of the target. If the target is not
 | 
				
			||||||
 | 
						// defined to have parameters, the owner should ignore the property if
 | 
				
			||||||
 | 
						// it is present. If the selection cannot be converted into a form based
 | 
				
			||||||
 | 
						// on the target (and parameters, if any), the owner should refuse the
 | 
				
			||||||
 | 
						// SelectionRequest as previously described.
 | 
				
			||||||
 | 
						targetName, err := xprop.AtomName (
 | 
				
			||||||
 | 
							claim.window.backend.connection, event.Target)
 | 
				
			||||||
 | 
						if err != nil { die(); return }
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						switch targetName {
 | 
				
			||||||
 | 
						case "TARGETS":
 | 
				
			||||||
 | 
							targetNames := []string { }
 | 
				
			||||||
 | 
							for mime := range claim.data {
 | 
				
			||||||
 | 
								targetNames = append(targetNames, mimeToTargets(mime)...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							data := make([]byte, len(targetNames) * 4)
 | 
				
			||||||
 | 
							for index, name := range targetNames {
 | 
				
			||||||
 | 
								atom, err := xprop.Atm(claim.window.backend.connection, name)
 | 
				
			||||||
 | 
								if err != nil { die(); return }
 | 
				
			||||||
 | 
								xgb.Put32(data[:index * 4], uint32(atom))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							claim.window.fulfillSelectionRequest(data, 8, event)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							mime, confidence := targetToMime(targetName)
 | 
				
			||||||
 | 
							if confidence == confidenceNone { die(); return }
 | 
				
			||||||
 | 
							reader, ok := claim.data[mime]
 | 
				
			||||||
 | 
							if !ok { die(); return }
 | 
				
			||||||
 | 
							data, err := io.ReadAll(reader)
 | 
				
			||||||
 | 
							if err != nil { die() }
 | 
				
			||||||
 | 
							claim.window.fulfillSelectionRequest(data, 32, event)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -102,6 +102,8 @@ func (backend *Backend) newWindow (
 | 
				
			|||||||
		Connect(backend.connection, window.xWindow.Id)
 | 
							Connect(backend.connection, window.xWindow.Id)
 | 
				
			||||||
	xevent.SelectionClearFun(window.handleSelectionClear).
 | 
						xevent.SelectionClearFun(window.handleSelectionClear).
 | 
				
			||||||
		Connect(backend.connection, window.xWindow.Id)
 | 
							Connect(backend.connection, window.xWindow.Id)
 | 
				
			||||||
 | 
						xevent.SelectionRequestFun(window.handleSelectionRequest).
 | 
				
			||||||
 | 
							Connect(backend.connection, window.xWindow.Id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	window.SetTheme(backend.theme)
 | 
						window.SetTheme(backend.theme)
 | 
				
			||||||
	window.SetConfig(backend.config)
 | 
						window.SetConfig(backend.config)
 | 
				
			||||||
@ -288,7 +290,10 @@ func (window *window) Hide () {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (window *window) Copy (data data.Data) {
 | 
					func (window *window) Copy (data data.Data) {
 | 
				
			||||||
	// TODO
 | 
						selectionName := "CLIPBOARD"
 | 
				
			||||||
 | 
						selectionAtom, err := xprop.Atm(window.backend.connection, selectionName)
 | 
				
			||||||
 | 
						if err != nil { return }
 | 
				
			||||||
 | 
						window.selectionClaim = window.claimSelection(selectionAtom, data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (window *window) Paste (callback func (data.Data, error), accept ...data.Mime) {
 | 
					func (window *window) Paste (callback func (data.Data, error), accept ...data.Mime) {
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user