diff --git a/core/internal/distros/manual_packages.go b/core/internal/distros/manual_packages.go index 5c032c36..13cb6f00 100644 --- a/core/internal/distros/manual_packages.go +++ b/core/internal/distros/manual_packages.go @@ -74,10 +74,6 @@ func (m *ManualPackageInstaller) InstallManualPackages(ctx context.Context, pack if err := m.installHyprland(ctx, sudoPassword, progressChan); err != nil { return fmt.Errorf("failed to install hyprland: %w", err) } - case "hyprpicker": - if err := m.installHyprpicker(ctx, sudoPassword, progressChan); err != nil { - return fmt.Errorf("failed to install hyprpicker: %w", err) - } case "ghostty": if err := m.installGhostty(ctx, sudoPassword, progressChan); err != nil { return fmt.Errorf("failed to install ghostty: %w", err) @@ -401,184 +397,6 @@ func (m *ManualPackageInstaller) installHyprland(ctx context.Context, sudoPasswo return nil } -func (m *ManualPackageInstaller) installHyprpicker(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { - m.log("Installing hyprpicker from source...") - - homeDir := os.Getenv("HOME") - if homeDir == "" { - return fmt.Errorf("HOME environment variable not set") - } - - cacheDir := filepath.Join(homeDir, ".cache", "dankinstall") - if err := os.MkdirAll(cacheDir, 0755); err != nil { - return fmt.Errorf("failed to create cache directory: %w", err) - } - - // Install hyprutils first - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.05, - Step: "Building hyprutils dependency...", - IsComplete: false, - CommandInfo: "git clone https://github.com/hyprwm/hyprutils.git", - } - - hyprutilsDir := filepath.Join(cacheDir, "hyprutils-build") - if err := os.MkdirAll(hyprutilsDir, 0755); err != nil { - return fmt.Errorf("failed to create hyprutils directory: %w", err) - } - defer os.RemoveAll(hyprutilsDir) - - cloneUtilsCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/hyprwm/hyprutils.git", hyprutilsDir) - if err := cloneUtilsCmd.Run(); err != nil { - return fmt.Errorf("failed to clone hyprutils: %w", err) - } - - configureUtilsCmd := exec.CommandContext(ctx, "cmake", - "--no-warn-unused-cli", - "-DCMAKE_BUILD_TYPE:STRING=Release", - "-DCMAKE_INSTALL_PREFIX:PATH=/usr", - "-DBUILD_TESTING=off", - "-S", ".", - "-B", "./build") - configureUtilsCmd.Dir = hyprutilsDir - configureUtilsCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(configureUtilsCmd, progressChan, PhaseSystemPackages, 0.05, 0.1, "Configuring hyprutils..."); err != nil { - return fmt.Errorf("failed to configure hyprutils: %w", err) - } - - buildUtilsCmd := exec.CommandContext(ctx, "cmake", "--build", "./build", "--config", "Release", "--target", "all") - buildUtilsCmd.Dir = hyprutilsDir - buildUtilsCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(buildUtilsCmd, progressChan, PhaseSystemPackages, 0.1, 0.2, "Building hyprutils..."); err != nil { - return fmt.Errorf("failed to build hyprutils: %w", err) - } - - installUtilsCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install ./build") - installUtilsCmd.Dir = hyprutilsDir - if err := installUtilsCmd.Run(); err != nil { - return fmt.Errorf("failed to install hyprutils: %w", err) - } - - // Install hyprwayland-scanner - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.2, - Step: "Building hyprwayland-scanner dependency...", - IsComplete: false, - CommandInfo: "git clone https://github.com/hyprwm/hyprwayland-scanner.git", - } - - scannerDir := filepath.Join(cacheDir, "hyprwayland-scanner-build") - if err := os.MkdirAll(scannerDir, 0755); err != nil { - return fmt.Errorf("failed to create scanner directory: %w", err) - } - defer os.RemoveAll(scannerDir) - - cloneScannerCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/hyprwm/hyprwayland-scanner.git", scannerDir) - if err := cloneScannerCmd.Run(); err != nil { - return fmt.Errorf("failed to clone hyprwayland-scanner: %w", err) - } - - configureScannerCmd := exec.CommandContext(ctx, "cmake", - "-DCMAKE_INSTALL_PREFIX=/usr", - "-B", "build") - configureScannerCmd.Dir = scannerDir - configureScannerCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(configureScannerCmd, progressChan, PhaseSystemPackages, 0.2, 0.25, "Configuring hyprwayland-scanner..."); err != nil { - return fmt.Errorf("failed to configure hyprwayland-scanner: %w", err) - } - - buildScannerCmd := exec.CommandContext(ctx, "cmake", "--build", "build", "-j") - buildScannerCmd.Dir = scannerDir - buildScannerCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(buildScannerCmd, progressChan, PhaseSystemPackages, 0.25, 0.35, "Building hyprwayland-scanner..."); err != nil { - return fmt.Errorf("failed to build hyprwayland-scanner: %w", err) - } - - installScannerCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install build") - installScannerCmd.Dir = scannerDir - if err := installScannerCmd.Run(); err != nil { - return fmt.Errorf("failed to install hyprwayland-scanner: %w", err) - } - - // Now build hyprpicker - tmpDir := filepath.Join(cacheDir, "hyprpicker-build") - if err := os.MkdirAll(tmpDir, 0755); err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tmpDir) - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.35, - Step: "Cloning hyprpicker repository...", - IsComplete: false, - CommandInfo: "git clone https://github.com/hyprwm/hyprpicker.git", - } - - cloneCmd := exec.CommandContext(ctx, "git", "clone", "https://github.com/hyprwm/hyprpicker.git", tmpDir) - if err := cloneCmd.Run(); err != nil { - return fmt.Errorf("failed to clone hyprpicker: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.45, - Step: "Configuring hyprpicker build...", - IsComplete: false, - CommandInfo: "cmake -B build -S . -DCMAKE_BUILD_TYPE=Release", - } - - configureCmd := exec.CommandContext(ctx, "cmake", - "--no-warn-unused-cli", - "-DCMAKE_BUILD_TYPE:STRING=Release", - "-DCMAKE_INSTALL_PREFIX:PATH=/usr", - "-S", ".", - "-B", "./build") - configureCmd.Dir = tmpDir - configureCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - - output, err := configureCmd.CombinedOutput() - if err != nil { - m.log(fmt.Sprintf("cmake configure failed. Output:\n%s", string(output))) - return fmt.Errorf("failed to configure hyprpicker: %w\nCMake output:\n%s", err, string(output)) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.55, - Step: "Building hyprpicker...", - IsComplete: false, - CommandInfo: "cmake --build build --target hyprpicker", - } - - buildCmd := exec.CommandContext(ctx, "cmake", "--build", "./build", "--config", "Release", "--target", "hyprpicker") - buildCmd.Dir = tmpDir - buildCmd.Env = append(os.Environ(), "TMPDIR="+cacheDir) - if err := m.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.55, 0.8, "Building hyprpicker..."); err != nil { - return fmt.Errorf("failed to build hyprpicker: %w", err) - } - - progressChan <- InstallProgressMsg{ - Phase: PhaseSystemPackages, - Progress: 0.8, - Step: "Installing hyprpicker...", - IsComplete: false, - NeedsSudo: true, - CommandInfo: "sudo cmake --install build", - } - - installCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install ./build") - installCmd.Dir = tmpDir - if err := installCmd.Run(); err != nil { - return fmt.Errorf("failed to install hyprpicker: %w", err) - } - - m.log("hyprpicker installed successfully from source") - return nil -} - func (m *ManualPackageInstaller) installGhostty(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { m.log("Installing Ghostty from source...") diff --git a/core/internal/dms/views_features.go b/core/internal/dms/views_features.go index b013267d..0cb96d03 100644 --- a/core/internal/dms/views_features.go +++ b/core/internal/dms/views_features.go @@ -518,7 +518,7 @@ func (m Model) categorizeDependencies() map[string][]DependencyInfo { categories["Hyprland Components"] = append(categories["Hyprland Components"], dep) case "niri": categories["Niri Components"] = append(categories["Niri Components"], dep) - case "kitty", "alacritty", "ghostty", "hyprpicker": + case "kitty", "alacritty", "ghostty": categories["Shared Components"] = append(categories["Shared Components"], dep) default: categories["Shared Components"] = append(categories["Shared Components"], dep) diff --git a/core/internal/proto/ext_data_control/data_control.go b/core/internal/proto/ext_data_control/data_control.go index 2200402b..3a4de9f2 100644 --- a/core/internal/proto/ext_data_control/data_control.go +++ b/core/internal/proto/ext_data_control/data_control.go @@ -1,3 +1,34 @@ +// Generated by go-wayland-scanner +// https://github.com/yaslama/go-wayland/cmd/go-wayland-scanner +// XML file : internal/proto/xml/ext-data-control-v1.xml +// +// ext_data_control_v1 Protocol Copyright: +// +// Copyright © 2018 Simon Ser +// Copyright © 2019 Ivan Molodetskikh +// Copyright © 2024 Neal Gompa +// +// Permission to use, copy, modify, distribute, and sell this +// software and its documentation for any purpose is hereby granted +// without fee, provided that the above copyright notice appear in +// all copies and that both that copyright notice and this permission +// notice appear in supporting documentation, and that the name of +// the copyright holders not be used in advertising or publicity +// pertaining to distribution of the software without specific, +// written prior permission. The copyright holders make no +// representations about the suitability of this software for any +// purpose. It is provided "as is" without express or implied +// warranty. +// +// THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS +// SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY +// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +// THIS SOFTWARE. + package ext_data_control import ( @@ -5,83 +36,117 @@ import ( "golang.org/x/sys/unix" ) +// ExtDataControlManagerV1InterfaceName is the name of the interface as it appears in the [client.Registry]. +// It can be used to match the [client.RegistryGlobalEvent.Interface] in the +// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. const ExtDataControlManagerV1InterfaceName = "ext_data_control_manager_v1" +// ExtDataControlManagerV1 : manager to control data devices +// +// This interface is a manager that allows creating per-seat data device +// controls. type ExtDataControlManagerV1 struct { client.BaseProxy } +// NewExtDataControlManagerV1 : manager to control data devices +// +// This interface is a manager that allows creating per-seat data device +// controls. func NewExtDataControlManagerV1(ctx *client.Context) *ExtDataControlManagerV1 { - m := &ExtDataControlManagerV1{} - ctx.Register(m) - return m + extDataControlManagerV1 := &ExtDataControlManagerV1{} + ctx.Register(extDataControlManagerV1) + return extDataControlManagerV1 } -func (m *ExtDataControlManagerV1) CreateDataSource() (*ExtDataControlSourceV1, error) { - id := NewExtDataControlSourceV1(m.Context()) +// CreateDataSource : create a new data source +// +// Create a new data source. +func (i *ExtDataControlManagerV1) CreateDataSource() (*ExtDataControlSourceV1, error) { + id := NewExtDataControlSourceV1(i.Context()) const opcode = 0 - const reqBufLen = 8 + 4 - var reqBuf [reqBufLen]byte + const _reqBufLen = 8 + 4 + var _reqBuf [_reqBufLen]byte l := 0 - client.PutUint32(reqBuf[l:4], m.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 - client.PutUint32(reqBuf[l:l+4], id.ID()) + client.PutUint32(_reqBuf[l:l+4], id.ID()) l += 4 - err := m.Context().WriteMsg(reqBuf[:], nil) + err := i.Context().WriteMsg(_reqBuf[:], nil) return id, err } -func (m *ExtDataControlManagerV1) GetDataDevice(seat *client.Seat) (*ExtDataControlDeviceV1, error) { - id := NewExtDataControlDeviceV1(m.Context()) +// GetDataDevice : get a data device for a seat +// +// Create a data device that can be used to manage a seat's selection. +func (i *ExtDataControlManagerV1) GetDataDevice(seat *client.Seat) (*ExtDataControlDeviceV1, error) { + id := NewExtDataControlDeviceV1(i.Context()) const opcode = 1 - const reqBufLen = 8 + 4 + 4 - var reqBuf [reqBufLen]byte + const _reqBufLen = 8 + 4 + 4 + var _reqBuf [_reqBufLen]byte l := 0 - client.PutUint32(reqBuf[l:4], m.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 - client.PutUint32(reqBuf[l:l+4], id.ID()) + client.PutUint32(_reqBuf[l:l+4], id.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], seat.ID()) + client.PutUint32(_reqBuf[l:l+4], seat.ID()) l += 4 - err := m.Context().WriteMsg(reqBuf[:], nil) + err := i.Context().WriteMsg(_reqBuf[:], nil) return id, err } -func (m *ExtDataControlManagerV1) GetDataDeviceWithProxy(device *ExtDataControlDeviceV1, seat *client.Seat) error { +// GetDataDeviceWithProxy : get a data device for a seat using a pre-created proxy +// +// Like GetDataDevice, but uses a pre-created ExtDataControlDeviceV1 proxy. +// This allows setting up event handlers before the request is sent. +func (i *ExtDataControlManagerV1) GetDataDeviceWithProxy(device *ExtDataControlDeviceV1, seat *client.Seat) error { const opcode = 1 - const reqBufLen = 8 + 4 + 4 - var reqBuf [reqBufLen]byte + const _reqBufLen = 8 + 4 + 4 + var _reqBuf [_reqBufLen]byte l := 0 - client.PutUint32(reqBuf[l:4], m.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 - client.PutUint32(reqBuf[l:l+4], device.ID()) + client.PutUint32(_reqBuf[l:l+4], device.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], seat.ID()) + client.PutUint32(_reqBuf[l:l+4], seat.ID()) l += 4 - return m.Context().WriteMsg(reqBuf[:], nil) + return i.Context().WriteMsg(_reqBuf[:], nil) } -func (m *ExtDataControlManagerV1) Destroy() error { - defer m.MarkZombie() +// Destroy : destroy the manager +// +// All objects created by the manager will still remain valid, until their +// appropriate destroy request has been called. +func (i *ExtDataControlManagerV1) Destroy() error { + defer i.MarkZombie() const opcode = 2 - const reqBufLen = 8 - var reqBuf [reqBufLen]byte + const _reqBufLen = 8 + var _reqBuf [_reqBufLen]byte l := 0 - client.PutUint32(reqBuf[l:4], m.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 - return m.Context().WriteMsg(reqBuf[:], nil) + err := i.Context().WriteMsg(_reqBuf[:], nil) + return err } +// ExtDataControlDeviceV1InterfaceName is the name of the interface as it appears in the [client.Registry]. +// It can be used to match the [client.RegistryGlobalEvent.Interface] in the +// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. const ExtDataControlDeviceV1InterfaceName = "ext_data_control_device_v1" +// ExtDataControlDeviceV1 : manage a data device for a seat +// +// This interface allows a client to manage a seat's selection. +// +// When the seat is destroyed, this object becomes inert. type ExtDataControlDeviceV1 struct { client.BaseProxy dataOfferHandler ExtDataControlDeviceV1DataOfferHandlerFunc @@ -90,298 +155,541 @@ type ExtDataControlDeviceV1 struct { primarySelectionHandler ExtDataControlDeviceV1PrimarySelectionHandlerFunc } +// NewExtDataControlDeviceV1 : manage a data device for a seat +// +// This interface allows a client to manage a seat's selection. +// +// When the seat is destroyed, this object becomes inert. func NewExtDataControlDeviceV1(ctx *client.Context) *ExtDataControlDeviceV1 { - d := &ExtDataControlDeviceV1{} - ctx.Register(d) - return d + extDataControlDeviceV1 := &ExtDataControlDeviceV1{} + ctx.Register(extDataControlDeviceV1) + return extDataControlDeviceV1 } -func (d *ExtDataControlDeviceV1) SetSelection(source *ExtDataControlSourceV1) error { +// SetSelection : copy data to the selection +// +// This request asks the compositor to set the selection to the data from +// the source on behalf of the client. +// +// The given source may not be used in any further set_selection or +// set_primary_selection requests. Attempting to use a previously used +// source triggers the used_source protocol error. +// +// To unset the selection, set the source to NULL. +func (i *ExtDataControlDeviceV1) SetSelection(source *ExtDataControlSourceV1) error { const opcode = 0 - const reqBufLen = 8 + 4 - var reqBuf [reqBufLen]byte + const _reqBufLen = 8 + 4 + var _reqBuf [_reqBufLen]byte l := 0 - client.PutUint32(reqBuf[l:4], d.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 if source == nil { - client.PutUint32(reqBuf[l:l+4], 0) + client.PutUint32(_reqBuf[l:l+4], 0) + l += 4 } else { - client.PutUint32(reqBuf[l:l+4], source.ID()) + client.PutUint32(_reqBuf[l:l+4], source.ID()) + l += 4 } - l += 4 - return d.Context().WriteMsg(reqBuf[:], nil) + err := i.Context().WriteMsg(_reqBuf[:], nil) + return err } -func (d *ExtDataControlDeviceV1) Destroy() error { - defer d.MarkZombie() +// Destroy : destroy this data device +// +// Destroys the data device object. +func (i *ExtDataControlDeviceV1) Destroy() error { + defer i.MarkZombie() const opcode = 1 - const reqBufLen = 8 - var reqBuf [reqBufLen]byte + const _reqBufLen = 8 + var _reqBuf [_reqBufLen]byte l := 0 - client.PutUint32(reqBuf[l:4], d.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 - return d.Context().WriteMsg(reqBuf[:], nil) + err := i.Context().WriteMsg(_reqBuf[:], nil) + return err } -func (d *ExtDataControlDeviceV1) SetPrimarySelection(source *ExtDataControlSourceV1) error { +// SetPrimarySelection : copy data to the primary selection +// +// This request asks the compositor to set the primary selection to the +// data from the source on behalf of the client. +// +// The given source may not be used in any further set_selection or +// set_primary_selection requests. Attempting to use a previously used +// source triggers the used_source protocol error. +// +// To unset the primary selection, set the source to NULL. +// +// The compositor will ignore this request if it does not support primary +// selection. +func (i *ExtDataControlDeviceV1) SetPrimarySelection(source *ExtDataControlSourceV1) error { const opcode = 2 - const reqBufLen = 8 + 4 - var reqBuf [reqBufLen]byte + const _reqBufLen = 8 + 4 + var _reqBuf [_reqBufLen]byte l := 0 - client.PutUint32(reqBuf[l:4], d.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 if source == nil { - client.PutUint32(reqBuf[l:l+4], 0) + client.PutUint32(_reqBuf[l:l+4], 0) + l += 4 } else { - client.PutUint32(reqBuf[l:l+4], source.ID()) + client.PutUint32(_reqBuf[l:l+4], source.ID()) + l += 4 } - l += 4 - return d.Context().WriteMsg(reqBuf[:], nil) + err := i.Context().WriteMsg(_reqBuf[:], nil) + return err } +type ExtDataControlDeviceV1Error uint32 + +// ExtDataControlDeviceV1Error : +const ( + // ExtDataControlDeviceV1ErrorUsedSource : source given to set_selection or set_primary_selection was already used before + ExtDataControlDeviceV1ErrorUsedSource ExtDataControlDeviceV1Error = 1 +) + +func (e ExtDataControlDeviceV1Error) Name() string { + switch e { + case ExtDataControlDeviceV1ErrorUsedSource: + return "used_source" + default: + return "" + } +} + +func (e ExtDataControlDeviceV1Error) Value() string { + switch e { + case ExtDataControlDeviceV1ErrorUsedSource: + return "1" + default: + return "" + } +} + +func (e ExtDataControlDeviceV1Error) String() string { + return e.Name() + "=" + e.Value() +} + +// ExtDataControlDeviceV1DataOfferEvent : introduce a new ext_data_control_offer +// +// The data_offer event introduces a new ext_data_control_offer object, +// which will subsequently be used in either the +// ext_data_control_device.selection event (for the regular clipboard +// selections) or the ext_data_control_device.primary_selection event (for +// the primary clipboard selections). Immediately following the +// ext_data_control_device.data_offer event, the new data_offer object +// will send out ext_data_control_offer.offer events to describe the MIME +// types it offers. type ExtDataControlDeviceV1DataOfferEvent struct { Id *ExtDataControlOfferV1 } type ExtDataControlDeviceV1DataOfferHandlerFunc func(ExtDataControlDeviceV1DataOfferEvent) -func (d *ExtDataControlDeviceV1) SetDataOfferHandler(f ExtDataControlDeviceV1DataOfferHandlerFunc) { - d.dataOfferHandler = f +// SetDataOfferHandler : sets handler for ExtDataControlDeviceV1DataOfferEvent +func (i *ExtDataControlDeviceV1) SetDataOfferHandler(f ExtDataControlDeviceV1DataOfferHandlerFunc) { + i.dataOfferHandler = f } +// ExtDataControlDeviceV1SelectionEvent : advertise new selection +// +// The selection event is sent out to notify the client of a new +// ext_data_control_offer for the selection for this device. The +// ext_data_control_device.data_offer and the ext_data_control_offer.offer +// events are sent out immediately before this event to introduce the data +// offer object. The selection event is sent to a client when a new +// selection is set. The ext_data_control_offer is valid until a new +// ext_data_control_offer or NULL is received. The client must destroy the +// previous selection ext_data_control_offer, if any, upon receiving this +// event. Regardless, the previous selection will be ignored once a new +// selection ext_data_control_offer is received. +// +// The first selection event is sent upon binding the +// ext_data_control_device object. type ExtDataControlDeviceV1SelectionEvent struct { Id *ExtDataControlOfferV1 - OfferId uint32 + OfferId uint32 // Raw object ID for external registry lookups } type ExtDataControlDeviceV1SelectionHandlerFunc func(ExtDataControlDeviceV1SelectionEvent) -func (d *ExtDataControlDeviceV1) SetSelectionHandler(f ExtDataControlDeviceV1SelectionHandlerFunc) { - d.selectionHandler = f +// SetSelectionHandler : sets handler for ExtDataControlDeviceV1SelectionEvent +func (i *ExtDataControlDeviceV1) SetSelectionHandler(f ExtDataControlDeviceV1SelectionHandlerFunc) { + i.selectionHandler = f } +// ExtDataControlDeviceV1FinishedEvent : this data control is no longer valid +// +// This data control object is no longer valid and should be destroyed by +// the client. type ExtDataControlDeviceV1FinishedEvent struct{} type ExtDataControlDeviceV1FinishedHandlerFunc func(ExtDataControlDeviceV1FinishedEvent) -func (d *ExtDataControlDeviceV1) SetFinishedHandler(f ExtDataControlDeviceV1FinishedHandlerFunc) { - d.finishedHandler = f +// SetFinishedHandler : sets handler for ExtDataControlDeviceV1FinishedEvent +func (i *ExtDataControlDeviceV1) SetFinishedHandler(f ExtDataControlDeviceV1FinishedHandlerFunc) { + i.finishedHandler = f } +// ExtDataControlDeviceV1PrimarySelectionEvent : advertise new primary selection +// +// The primary_selection event is sent out to notify the client of a new +// ext_data_control_offer for the primary selection for this device. The +// ext_data_control_device.data_offer and the ext_data_control_offer.offer +// events are sent out immediately before this event to introduce the data +// offer object. The primary_selection event is sent to a client when a +// new primary selection is set. The ext_data_control_offer is valid until +// a new ext_data_control_offer or NULL is received. The client must +// destroy the previous primary selection ext_data_control_offer, if any, +// upon receiving this event. Regardless, the previous primary selection +// will be ignored once a new primary selection ext_data_control_offer is +// received. +// +// If the compositor supports primary selection, the first +// primary_selection event is sent upon binding the +// ext_data_control_device object. type ExtDataControlDeviceV1PrimarySelectionEvent struct { Id *ExtDataControlOfferV1 - OfferId uint32 + OfferId uint32 // Raw object ID for external registry lookups } type ExtDataControlDeviceV1PrimarySelectionHandlerFunc func(ExtDataControlDeviceV1PrimarySelectionEvent) -func (d *ExtDataControlDeviceV1) SetPrimarySelectionHandler(f ExtDataControlDeviceV1PrimarySelectionHandlerFunc) { - d.primarySelectionHandler = f +// SetPrimarySelectionHandler : sets handler for ExtDataControlDeviceV1PrimarySelectionEvent +func (i *ExtDataControlDeviceV1) SetPrimarySelectionHandler(f ExtDataControlDeviceV1PrimarySelectionHandlerFunc) { + i.primarySelectionHandler = f } -func (d *ExtDataControlDeviceV1) Dispatch(opcode uint32, fd int, data []byte) { +func (i *ExtDataControlDeviceV1) Dispatch(opcode uint32, fd int, data []byte) { switch opcode { case 0: - if d.dataOfferHandler == nil { + // data_offer event: server creates a new object (new_id) + if i.dataOfferHandler == nil { return } + var e ExtDataControlDeviceV1DataOfferEvent l := 0 newID := client.Uint32(data[l : l+4]) l += 4 - ctx := d.Context() + ctx := i.Context() offer := &ExtDataControlOfferV1{} offer.SetContext(ctx) offer.SetID(newID) ctx.RegisterWithID(offer, newID) + e.Id = offer - d.dataOfferHandler(ExtDataControlDeviceV1DataOfferEvent{Id: offer}) + i.dataOfferHandler(e) case 1: - if d.selectionHandler == nil { + // selection event: nullable object reference + if i.selectionHandler == nil { return } + var e ExtDataControlDeviceV1SelectionEvent l := 0 objID := client.Uint32(data[l : l+4]) l += 4 - var offer *ExtDataControlOfferV1 + e.OfferId = objID if objID != 0 { - if p := d.Context().GetProxy(objID); p != nil { - offer = p.(*ExtDataControlOfferV1) + if p := i.Context().GetProxy(objID); p != nil { + e.Id = p.(*ExtDataControlOfferV1) } } - d.selectionHandler(ExtDataControlDeviceV1SelectionEvent{Id: offer, OfferId: objID}) + + i.selectionHandler(e) case 2: - if d.finishedHandler == nil { + if i.finishedHandler == nil { return } - d.finishedHandler(ExtDataControlDeviceV1FinishedEvent{}) + var e ExtDataControlDeviceV1FinishedEvent + + i.finishedHandler(e) case 3: - if d.primarySelectionHandler == nil { + // primary_selection event: nullable object reference + if i.primarySelectionHandler == nil { return } + var e ExtDataControlDeviceV1PrimarySelectionEvent l := 0 objID := client.Uint32(data[l : l+4]) l += 4 - var offer *ExtDataControlOfferV1 + e.OfferId = objID if objID != 0 { - if p := d.Context().GetProxy(objID); p != nil { - offer = p.(*ExtDataControlOfferV1) + if p := i.Context().GetProxy(objID); p != nil { + e.Id = p.(*ExtDataControlOfferV1) } } - d.primarySelectionHandler(ExtDataControlDeviceV1PrimarySelectionEvent{Id: offer, OfferId: objID}) + + i.primarySelectionHandler(e) } } +// ExtDataControlSourceV1InterfaceName is the name of the interface as it appears in the [client.Registry]. +// It can be used to match the [client.RegistryGlobalEvent.Interface] in the +// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. const ExtDataControlSourceV1InterfaceName = "ext_data_control_source_v1" +// ExtDataControlSourceV1 : offer to transfer data +// +// The ext_data_control_source object is the source side of a +// ext_data_control_offer. It is created by the source client in a data +// transfer and provides a way to describe the offered data and a way to +// respond to requests to transfer the data. type ExtDataControlSourceV1 struct { client.BaseProxy sendHandler ExtDataControlSourceV1SendHandlerFunc cancelledHandler ExtDataControlSourceV1CancelledHandlerFunc } +// NewExtDataControlSourceV1 : offer to transfer data +// +// The ext_data_control_source object is the source side of a +// ext_data_control_offer. It is created by the source client in a data +// transfer and provides a way to describe the offered data and a way to +// respond to requests to transfer the data. func NewExtDataControlSourceV1(ctx *client.Context) *ExtDataControlSourceV1 { - s := &ExtDataControlSourceV1{} - ctx.Register(s) - return s + extDataControlSourceV1 := &ExtDataControlSourceV1{} + ctx.Register(extDataControlSourceV1) + return extDataControlSourceV1 } -func (s *ExtDataControlSourceV1) Offer(mimeType string) error { +// Offer : add an offered MIME type +// +// This request adds a MIME type to the set of MIME types advertised to +// targets. Can be called several times to offer multiple types. +// +// Calling this after ext_data_control_device.set_selection is a protocol +// error. +// +// mimeType: MIME type offered by the data source +func (i *ExtDataControlSourceV1) Offer(mimeType string) error { const opcode = 0 mimeTypeLen := client.PaddedLen(len(mimeType) + 1) - reqBufLen := 8 + (4 + mimeTypeLen) - reqBuf := make([]byte, reqBufLen) + _reqBufLen := 8 + (4 + mimeTypeLen) + _reqBuf := make([]byte, _reqBufLen) l := 0 - client.PutUint32(reqBuf[l:4], s.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 - client.PutString(reqBuf[l:l+(4+mimeTypeLen)], mimeType) + client.PutString(_reqBuf[l:l+(4+mimeTypeLen)], mimeType) l += (4 + mimeTypeLen) - return s.Context().WriteMsg(reqBuf, nil) + err := i.Context().WriteMsg(_reqBuf, nil) + return err } -func (s *ExtDataControlSourceV1) Destroy() error { - defer s.MarkZombie() +// Destroy : destroy this source +// +// Destroys the data source object. +func (i *ExtDataControlSourceV1) Destroy() error { + defer i.MarkZombie() const opcode = 1 - const reqBufLen = 8 - var reqBuf [reqBufLen]byte + const _reqBufLen = 8 + var _reqBuf [_reqBufLen]byte l := 0 - client.PutUint32(reqBuf[l:4], s.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 - return s.Context().WriteMsg(reqBuf[:], nil) + err := i.Context().WriteMsg(_reqBuf[:], nil) + return err } +type ExtDataControlSourceV1Error uint32 + +// ExtDataControlSourceV1Error : +const ( + // ExtDataControlSourceV1ErrorInvalidOffer : offer sent after ext_data_control_device.set_selection + ExtDataControlSourceV1ErrorInvalidOffer ExtDataControlSourceV1Error = 1 +) + +func (e ExtDataControlSourceV1Error) Name() string { + switch e { + case ExtDataControlSourceV1ErrorInvalidOffer: + return "invalid_offer" + default: + return "" + } +} + +func (e ExtDataControlSourceV1Error) Value() string { + switch e { + case ExtDataControlSourceV1ErrorInvalidOffer: + return "1" + default: + return "" + } +} + +func (e ExtDataControlSourceV1Error) String() string { + return e.Name() + "=" + e.Value() +} + +// ExtDataControlSourceV1SendEvent : send the data +// +// Request for data from the client. Send the data as the specified MIME +// type over the passed file descriptor, then close it. type ExtDataControlSourceV1SendEvent struct { MimeType string Fd int } type ExtDataControlSourceV1SendHandlerFunc func(ExtDataControlSourceV1SendEvent) -func (s *ExtDataControlSourceV1) SetSendHandler(f ExtDataControlSourceV1SendHandlerFunc) { - s.sendHandler = f +// SetSendHandler : sets handler for ExtDataControlSourceV1SendEvent +func (i *ExtDataControlSourceV1) SetSendHandler(f ExtDataControlSourceV1SendHandlerFunc) { + i.sendHandler = f } +// ExtDataControlSourceV1CancelledEvent : selection was cancelled +// +// This data source is no longer valid. The data source has been replaced +// by another data source. +// +// The client should clean up and destroy this data source. type ExtDataControlSourceV1CancelledEvent struct{} type ExtDataControlSourceV1CancelledHandlerFunc func(ExtDataControlSourceV1CancelledEvent) -func (s *ExtDataControlSourceV1) SetCancelledHandler(f ExtDataControlSourceV1CancelledHandlerFunc) { - s.cancelledHandler = f +// SetCancelledHandler : sets handler for ExtDataControlSourceV1CancelledEvent +func (i *ExtDataControlSourceV1) SetCancelledHandler(f ExtDataControlSourceV1CancelledHandlerFunc) { + i.cancelledHandler = f } -func (s *ExtDataControlSourceV1) Dispatch(opcode uint32, fd int, data []byte) { +func (i *ExtDataControlSourceV1) Dispatch(opcode uint32, fd int, data []byte) { switch opcode { case 0: - if s.sendHandler == nil { + if i.sendHandler == nil { if fd != -1 { unix.Close(fd) } return } + var e ExtDataControlSourceV1SendEvent l := 0 mimeTypeLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) l += 4 - mimeType := client.String(data[l : l+mimeTypeLen]) + e.MimeType = client.String(data[l : l+mimeTypeLen]) l += mimeTypeLen + e.Fd = fd - s.sendHandler(ExtDataControlSourceV1SendEvent{MimeType: mimeType, Fd: fd}) + i.sendHandler(e) case 1: - if s.cancelledHandler == nil { + if i.cancelledHandler == nil { return } - s.cancelledHandler(ExtDataControlSourceV1CancelledEvent{}) + var e ExtDataControlSourceV1CancelledEvent + + i.cancelledHandler(e) } } +// ExtDataControlOfferV1InterfaceName is the name of the interface as it appears in the [client.Registry]. +// It can be used to match the [client.RegistryGlobalEvent.Interface] in the +// [Registry.SetGlobalHandler] and can be used in [Registry.Bind] if this applies. const ExtDataControlOfferV1InterfaceName = "ext_data_control_offer_v1" +// ExtDataControlOfferV1 : offer to transfer data +// +// A ext_data_control_offer represents a piece of data offered for transfer +// by another client (the source client). The offer describes the different +// MIME types that the data can be converted to and provides the mechanism +// for transferring the data directly from the source client. type ExtDataControlOfferV1 struct { client.BaseProxy offerHandler ExtDataControlOfferV1OfferHandlerFunc } +// NewExtDataControlOfferV1 : offer to transfer data +// +// A ext_data_control_offer represents a piece of data offered for transfer +// by another client (the source client). The offer describes the different +// MIME types that the data can be converted to and provides the mechanism +// for transferring the data directly from the source client. func NewExtDataControlOfferV1(ctx *client.Context) *ExtDataControlOfferV1 { - o := &ExtDataControlOfferV1{} - ctx.Register(o) - return o + extDataControlOfferV1 := &ExtDataControlOfferV1{} + ctx.Register(extDataControlOfferV1) + return extDataControlOfferV1 } -func (o *ExtDataControlOfferV1) Receive(mimeType string, fd int) error { +// Receive : request that the data is transferred +// +// To transfer the offered data, the client issues this request and +// indicates the MIME type it wants to receive. The transfer happens +// through the passed file descriptor (typically created with the pipe +// system call). The source client writes the data in the MIME type +// representation requested and then closes the file descriptor. +// +// The receiving client reads from the read end of the pipe until EOF and +// then closes its end, at which point the transfer is complete. +// +// This request may happen multiple times for different MIME types. +// +// mimeType: MIME type desired by receiver +// fd: file descriptor for data transfer +func (i *ExtDataControlOfferV1) Receive(mimeType string, fd int) error { const opcode = 0 mimeTypeLen := client.PaddedLen(len(mimeType) + 1) - reqBufLen := 8 + (4 + mimeTypeLen) - reqBuf := make([]byte, reqBufLen) + _reqBufLen := 8 + (4 + mimeTypeLen) + _reqBuf := make([]byte, _reqBufLen) l := 0 - client.PutUint32(reqBuf[l:4], o.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 - client.PutString(reqBuf[l:l+(4+mimeTypeLen)], mimeType) + client.PutString(_reqBuf[l:l+(4+mimeTypeLen)], mimeType) l += (4 + mimeTypeLen) - oob := unix.UnixRights(fd) - return o.Context().WriteMsg(reqBuf, oob) + oob := unix.UnixRights(int(fd)) + err := i.Context().WriteMsg(_reqBuf, oob) + return err } -func (o *ExtDataControlOfferV1) Destroy() error { - defer o.MarkZombie() +// Destroy : destroy this offer +// +// Destroys the data offer object. +func (i *ExtDataControlOfferV1) Destroy() error { + defer i.MarkZombie() const opcode = 1 - const reqBufLen = 8 - var reqBuf [reqBufLen]byte + const _reqBufLen = 8 + var _reqBuf [_reqBufLen]byte l := 0 - client.PutUint32(reqBuf[l:4], o.ID()) + client.PutUint32(_reqBuf[l:4], i.ID()) l += 4 - client.PutUint32(reqBuf[l:l+4], uint32(reqBufLen<<16|opcode&0x0000ffff)) + client.PutUint32(_reqBuf[l:l+4], uint32(_reqBufLen<<16|opcode&0x0000ffff)) l += 4 - return o.Context().WriteMsg(reqBuf[:], nil) + err := i.Context().WriteMsg(_reqBuf[:], nil) + return err } +// ExtDataControlOfferV1OfferEvent : advertise offered MIME type +// +// Sent immediately after creating the ext_data_control_offer object. +// One event per offered MIME type. type ExtDataControlOfferV1OfferEvent struct { MimeType string } type ExtDataControlOfferV1OfferHandlerFunc func(ExtDataControlOfferV1OfferEvent) -func (o *ExtDataControlOfferV1) SetOfferHandler(f ExtDataControlOfferV1OfferHandlerFunc) { - o.offerHandler = f +// SetOfferHandler : sets handler for ExtDataControlOfferV1OfferEvent +func (i *ExtDataControlOfferV1) SetOfferHandler(f ExtDataControlOfferV1OfferHandlerFunc) { + i.offerHandler = f } -func (o *ExtDataControlOfferV1) Dispatch(opcode uint32, fd int, data []byte) { +func (i *ExtDataControlOfferV1) Dispatch(opcode uint32, fd int, data []byte) { switch opcode { case 0: - if o.offerHandler == nil { + if i.offerHandler == nil { return } + var e ExtDataControlOfferV1OfferEvent l := 0 mimeTypeLen := client.PaddedLen(int(client.Uint32(data[l : l+4]))) l += 4 - mimeType := client.String(data[l : l+mimeTypeLen]) + e.MimeType = client.String(data[l : l+mimeTypeLen]) l += mimeTypeLen - o.offerHandler(ExtDataControlOfferV1OfferEvent{MimeType: mimeType}) + i.offerHandler(e) } } diff --git a/core/internal/proto/xml/ext-data-control-v1.xml b/core/internal/proto/xml/ext-data-control-v1.xml new file mode 100644 index 00000000..37ee577e --- /dev/null +++ b/core/internal/proto/xml/ext-data-control-v1.xml @@ -0,0 +1,276 @@ + + + + Copyright © 2018 Simon Ser + Copyright © 2019 Ivan Molodetskikh + Copyright © 2024 Neal Gompa + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + This protocol allows a privileged client to control data devices. In + particular, the client will be able to manage the current selection and take + the role of a clipboard manager. + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + + + + + This interface is a manager that allows creating per-seat data device + controls. + + + + + Create a new data source. + + + + + + + Create a data device that can be used to manage a seat's selection. + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This interface allows a client to manage a seat's selection. + + When the seat is destroyed, this object becomes inert. + + + + + This request asks the compositor to set the selection to the data from + the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source triggers the used_source protocol error. + + To unset the selection, set the source to NULL. + + + + + + + Destroys the data device object. + + + + + + The data_offer event introduces a new ext_data_control_offer object, + which will subsequently be used in either the + ext_data_control_device.selection event (for the regular clipboard + selections) or the ext_data_control_device.primary_selection event (for + the primary clipboard selections). Immediately following the + ext_data_control_device.data_offer event, the new data_offer object + will send out ext_data_control_offer.offer events to describe the MIME + types it offers. + + + + + + + The selection event is sent out to notify the client of a new + ext_data_control_offer for the selection for this device. The + ext_data_control_device.data_offer and the ext_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The selection event is sent to a client when a new + selection is set. The ext_data_control_offer is valid until a new + ext_data_control_offer or NULL is received. The client must destroy the + previous selection ext_data_control_offer, if any, upon receiving this + event. Regardless, the previous selection will be ignored once a new + selection ext_data_control_offer is received. + + The first selection event is sent upon binding the + ext_data_control_device object. + + + + + + + This data control object is no longer valid and should be destroyed by + the client. + + + + + + The primary_selection event is sent out to notify the client of a new + ext_data_control_offer for the primary selection for this device. The + ext_data_control_device.data_offer and the ext_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The primary_selection event is sent to a client when a + new primary selection is set. The ext_data_control_offer is valid until + a new ext_data_control_offer or NULL is received. The client must + destroy the previous primary selection ext_data_control_offer, if any, + upon receiving this event. Regardless, the previous primary selection + will be ignored once a new primary selection ext_data_control_offer is + received. + + If the compositor supports primary selection, the first + primary_selection event is sent upon binding the + ext_data_control_device object. + + + + + + + This request asks the compositor to set the primary selection to the + data from the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source triggers the used_source protocol error. + + To unset the primary selection, set the source to NULL. + + The compositor will ignore this request if it does not support primary + selection. + + + + + + + + + + + + The ext_data_control_source object is the source side of a + ext_data_control_offer. It is created by the source client in a data + transfer and provides a way to describe the offered data and a way to + respond to requests to transfer the data. + + + + + + + + + This request adds a MIME type to the set of MIME types advertised to + targets. Can be called several times to offer multiple types. + + Calling this after ext_data_control_device.set_selection is a protocol + error. + + + + + + + Destroys the data source object. + + + + + + Request for data from the client. Send the data as the specified MIME + type over the passed file descriptor, then close it. + + + + + + + + This data source is no longer valid. The data source has been replaced + by another data source. + + The client should clean up and destroy this data source. + + + + + + + A ext_data_control_offer represents a piece of data offered for transfer + by another client (the source client). The offer describes the different + MIME types that the data can be converted to and provides the mechanism + for transferring the data directly from the source client. + + + + + To transfer the offered data, the client issues this request and + indicates the MIME type it wants to receive. The transfer happens + through the passed file descriptor (typically created with the pipe + system call). The source client writes the data in the MIME type + representation requested and then closes the file descriptor. + + The receiving client reads from the read end of the pipe until EOF and + then closes its end, at which point the transfer is complete. + + This request may happen multiple times for different MIME types. + + + + + + + + Destroys the data offer object. + + + + + + Sent immediately after creating the ext_data_control_offer object. + One event per offered MIME type. + + + + +