func (s SetStructWidgets) AfterEnd(code pcap.HandlerCode, app gowid.IApp) {
	if code&pcap.PdmlCode == 0 {
		return
	}
	setLowerWidgets(app)
	StopEmptyHexViewTimer()
	StopEmptyStructViewTimer()
}
Close the channel before the callback. When the global loader state is idle,
app.Quit() will stop accepting app callbacks, so the goroutine that waits
for ch to be closed will never terminate.
package ui
import (
	"fmt"
	"os"
	"strings"
	"time"
	"github.com/gcla/gowid"
	"github.com/gcla/gowid/widgets/table"
	"github.com/gcla/termshark/v2"
	"github.com/gcla/termshark/v2/pcap"
	log "github.com/sirupsen/logrus"
)
type NoHandlers struct{}
type updateCurrentCaptureInTitle struct {
	Ld *pcap.PacketLoader
}
var _ pcap.IBeforeBegin = updateCurrentCaptureInTitle{}
var _ pcap.IClear = updateCurrentCaptureInTitle{}
func MakeUpdateCurrentCaptureInTitle() updateCurrentCaptureInTitle {
	return updateCurrentCaptureInTitle{
		Ld: Loader,
	}
}
func (t updateCurrentCaptureInTitle) BeforeBegin(code pcap.HandlerCode, app gowid.IApp) {
	if code&pcap.PsmlCode != 0 {
		cap := t.Ld.String()
		if t.Ld.CaptureFilter() != "" {
			cap = fmt.Sprintf("%s (%s)", cap, t.Ld.CaptureFilter())
		}
		currentCapture.SetText(cap, app)
		currentCaptureWidgetHolder.SetSubWidget(currentCaptureWidget, app)
	}
}
func (t updateCurrentCaptureInTitle) OnClear(code pcap.HandlerCode, app gowid.IApp) {
	currentCaptureWidgetHolder.SetSubWidget(nullw, app)
}
type updatePacketViews struct {
	Ld *pcap.PacketLoader
}
var _ pcap.IOnError = updatePacketViews{}
var _ pcap.IClear = updatePacketViews{}
var _ pcap.IBeforeBegin = updatePacketViews{}
var _ pcap.IAfterEnd = updatePacketViews{}
func MakePacketViewUpdater() updatePacketViews {
	res := updatePacketViews{}
	res.Ld = Loader
	return res
}
func (t updatePacketViews) OnClear(code pcap.HandlerCode, app gowid.IApp) {
	clearPacketViews(app)
}
func (t updatePacketViews) BeforeBegin(code pcap.HandlerCode, app gowid.IApp) {
	if code&pcap.PsmlCode == 0 {
		return
	}
	ch2 := Loader.PsmlFinishedChan
	clearPacketViews(app)
	t.Ld.PsmlLoader.Lock()
	defer t.Ld.PsmlLoader.Unlock()
	setPacketListWidgets(t.Ld, app)
	termshark.TrackedGo(func() {
		fn2 := func() {
			app.Run(gowid.RunFunction(func(app gowid.IApp) {
				updatePacketListWithData(Loader, app)
			}))
		}
		termshark.RunOnDoubleTicker(ch2, fn2,
			time.Duration(100)*time.Millisecond,
			time.Duration(2000)*time.Millisecond,
			10)
	}, Goroutinewg)
}
func (t updatePacketViews) AfterEnd(code pcap.HandlerCode, app gowid.IApp) {
	if code&pcap.PsmlCode == 0 {
		return
	}
	updatePacketListWithData(t.Ld, app)
	StopEmptyStructViewTimer()
	StopEmptyHexViewTimer()
	log.Infof("Load operation complete")
}
func (t updatePacketViews) OnError(code pcap.HandlerCode, app gowid.IApp, err error) {
	if code&pcap.PsmlCode == 0 {
		return
	}
	log.Error(err)
	if !Running {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		RequestQuit()
	} else {
		if !termshark.ConfBool("main.suppress-tshark-errors", false) {
			var errstr string
			if kverr, ok := err.(gowid.KeyValueError); ok {
				errstr = fmt.Sprintf("%v\n\n", kverr.Cause())
				kvs := make([]string, 0, len(kverr.KeyVals))
				for k, v := range kverr.KeyVals {
					kvs = append(kvs, fmt.Sprintf("%v: %v", k, v))
				}
				errstr = errstr + strings.Join(kvs, "\n")
			} else {
				errstr = fmt.Sprintf("%v", err)
			}
			OpenLongError(errstr, app)
		}
		StopEmptyStructViewTimer()
		StopEmptyHexViewTimer()
	}
}
type SimpleErrors struct{}
var _ pcap.IOnError = SimpleErrors{}
func (t SimpleErrors) OnError(code pcap.HandlerCode, app gowid.IApp, err error) {
	if code&pcap.NoneCode == 0 {
		return
	}
	log.Error(err)
	if !termshark.ConfBool("main.suppress-tshark-errors", false) {
		app.Run(gowid.RunFunction(func(app gowid.IApp) {
			OpenError(fmt.Sprintf("%v", err), app)
		}))
	}
}
type SaveRecents struct {
	Pcap   string
	Filter string
}
var _ pcap.IBeforeBegin = SaveRecents{}
func MakeSaveRecents(pcap string, filter string) SaveRecents {
	return SaveRecents{
		Pcap:   pcap,
		Filter: filter,
	}
}
func (t SaveRecents) BeforeBegin(code pcap.HandlerCode, app gowid.IApp) {
	if code&pcap.PsmlCode == 0 {
		return
	}
	if t.Pcap != "" {
		termshark.AddToRecentFiles(t.Pcap)
	}
	if t.Filter != "" {
		termshark.AddToRecentFilters(t.Filter)
	}
}
type CancelledMessage struct{}
var _ pcap.IAfterEnd = CancelledMessage{}
func (t CancelledMessage) AfterEnd(code pcap.HandlerCode, app gowid.IApp) {
	if code&pcap.PsmlCode == 0 {
		return
	}
	if Loader.LoadWasCancelled() {
		if !QuitRequested {
			OpenError("Loading was cancelled.", app)
		}
	}
}
type StartUIWhenThereArePackets struct{}
var _ pcap.IPsmlHeader = StartUIWhenThereArePackets{}
func (t StartUIWhenThereArePackets) OnPsmlHeader(code pcap.HandlerCode, app gowid.IApp) {
	StartUIOnce.Do(func() {
		close(StartUIChan)
	})
}
type ClearWormholeState struct{}
var _ pcap.INewSource = ClearWormholeState{}
func (t ClearWormholeState) OnNewSource(code pcap.HandlerCode, app gowid.IApp) {
	if CurrentWormholeWidget != nil {
		CurrentWormholeWidget.Close()
	}
	CurrentWormholeWidget = nil
}
type ClearMarksHandler struct{}
var _ pcap.IClear = checkGlobalJumpAfterPsml{}
var _ pcap.INewSource = checkGlobalJumpAfterPsml{}
func clearMarks() {
	for k := range marksMap {
		delete(marksMap, k)
	}
	lastJumpPos = -1
}
func (t checkGlobalJumpAfterPsml) OnNewSource(code pcap.HandlerCode, app gowid.IApp) {
	clearMarks()
}
func (t checkGlobalJumpAfterPsml) OnClear(code pcap.HandlerCode, app gowid.IApp) {
	clearMarks()
}
type checkGlobalJumpAfterPsml struct {
	Jump termshark.GlobalJumpPos
}
var _ pcap.IAfterEnd = checkGlobalJumpAfterPsml{}
func MakeCheckGlobalJumpAfterPsml(jmp termshark.GlobalJumpPos) checkGlobalJumpAfterPsml {
	return checkGlobalJumpAfterPsml{
		Jump: jmp,
	}
}
func (t checkGlobalJumpAfterPsml) AfterEnd(code pcap.HandlerCode, app gowid.IApp) {
	if code&pcap.PsmlCode == 0 {
		return
	}
	if QuitRequested {
		return
	}
	if t.Jump.Filename == Loader.Pcap() {
		if packetListView != nil {
			tableRow, err := tableRowFromPacketNumber(t.Jump.Pos)
			if err != nil {
				OpenError(err.Error(), app)
			} else {
				tableCol := 0
				curTablePos, err := packetListView.FocusXY()
				if err == nil {
					tableCol = curTablePos.Column
				}
				packetListView.SetFocusXY(app, table.Coords{Column: tableCol, Row: tableRow})
			}
		}
	}
}
type SetStructWidgets struct {
	Ld *pcap.PacketLoader
}
var _ pcap.IOnError = SetStructWidgets{}
var _ pcap.IBeforeBegin = SetStructWidgets{}
var _ pcap.IAfterEnd = SetStructWidgets{}
func (s SetStructWidgets) BeforeBegin(code pcap.HandlerCode, app gowid.IApp) {
	if code&pcap.PdmlCode == 0 {
		return
	}
	s2ch := s.Ld.Stage2FinishedChan
	termshark.TrackedGo(func() {
		fn2 := func() {
			app.Run(gowid.RunFunction(func(app gowid.IApp) {
				setLowerWidgets(app)
			}))
		}
		termshark.RunOnDoubleTicker(s2ch, fn2,
			time.Duration(100)*time.Millisecond,
			time.Duration(2000)*time.Millisecond,
			10)
	}, Goroutinewg)
}