Skip to content

Commit a84ee10

Browse files
committed
added AsyncPath to archivefs. allows asynchronous use of archivefs
ROM select could noticeable stall the GUI if there were many files in a directory
1 parent e09d16c commit a84ee10

File tree

3 files changed

+129
-35
lines changed

3 files changed

+129
-35
lines changed

archivefs/async.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// This file is part of Gopher2600.
2+
//
3+
// Gopher2600 is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// Gopher2600 is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
15+
16+
package archivefs
17+
18+
// SetSelectedFilename is called after a successful Set()
19+
type FilenameSetter interface {
20+
SetSelectedFilename(string)
21+
}
22+
23+
// AsyncResults are copies of archivefs path information that are safe to access asynchronously
24+
type AsyncResults struct {
25+
Entries []Node
26+
Selected string
27+
Dir string
28+
Base string
29+
}
30+
31+
// AsyncPath provides asynchronous access to an archivefs
32+
type AsyncPath struct {
33+
setter FilenameSetter
34+
afs Path
35+
36+
results chan AsyncResults
37+
err chan error
38+
39+
// Results of most recent change of path settings
40+
Results AsyncResults
41+
}
42+
43+
// NewAsyncPath is the preferred method of initialisation for the AsyncPath type
44+
func NewAsyncPath(setter FilenameSetter) AsyncPath {
45+
return AsyncPath{
46+
setter: setter,
47+
results: make(chan AsyncResults, 1),
48+
err: make(chan error, 1),
49+
}
50+
}
51+
52+
// Close any open zip files and reset path
53+
func (pth *AsyncPath) Close() {
54+
// not sure how safe this is if it is called when the Set() goroutine is running
55+
pth.afs.Close()
56+
}
57+
58+
// Set archivefs path. Process() must be called in order to retreive the results
59+
// of the Set()
60+
func (pth *AsyncPath) Set(path string) error {
61+
go func() {
62+
pth.afs.Set(path)
63+
64+
entries, err := pth.afs.List()
65+
if err != nil {
66+
pth.err <- err
67+
return
68+
}
69+
70+
pth.results <- AsyncResults{
71+
Entries: entries,
72+
Selected: pth.afs.String(),
73+
Dir: pth.afs.Dir(),
74+
Base: pth.afs.Base(),
75+
}
76+
}()
77+
78+
return nil
79+
}
80+
81+
// Process asynchronous requests. Must be called in order to receive the results
82+
// of a Set(). Suitable to be called as part of a render loop
83+
func (pth *AsyncPath) Process() error {
84+
select {
85+
case err := <-pth.err:
86+
return err
87+
88+
case results := <-pth.results:
89+
pth.Results = results
90+
91+
if pth.setter != nil {
92+
if pth.afs.IsDir() {
93+
pth.setter.SetSelectedFilename("")
94+
} else {
95+
pth.setter.SetSelectedFilename(pth.Results.Selected)
96+
}
97+
}
98+
default:
99+
}
100+
101+
return nil
102+
}

archivefs/path.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ func (afs *Path) List() ([]Node, error) {
219219
return ent, nil
220220
}
221221

222+
// Set archivefs path
222223
func (afs *Path) Set(path string) error {
223224
afs.Close()
224225

gui/sdlimgui/win_rom_select.go

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,8 @@ type winSelectROM struct {
4545
playmodeWin
4646
debuggerWin
4747

48-
img *SdlImgui
49-
50-
path archivefs.Path
51-
pathEntries []archivefs.Node
48+
img *SdlImgui
49+
path archivefs.AsyncPath
5250

5351
// selectedName is the name of the ROM in a normalised form
5452
selectedName string
@@ -98,6 +96,7 @@ func newSelectROM(img *SdlImgui) (window, error) {
9896
propertyResult: make(chan properties.Entry, 1),
9997
}
10098
win.debuggerGeom.noFocusTracking = true
99+
win.path = archivefs.NewAsyncPath(win)
101100

102101
var err error
103102

@@ -150,7 +149,7 @@ func (win *winSelectROM) setOpen(open bool) {
150149

151150
// open at the most recently selected ROM
152151
recent := win.img.dbg.Prefs.RecentROM.String()
153-
err := win.setPath(recent)
152+
err := win.path.Set(recent)
154153
if err != nil {
155154
logger.Logf("sdlimgui", err.Error())
156155
}
@@ -252,14 +251,19 @@ func (win *winSelectROM) render() {
252251
}
253252

254253
func (win *winSelectROM) draw() {
254+
err := win.path.Process()
255+
if err != nil {
256+
logger.Logf("sdlimgui", err.Error())
257+
}
258+
255259
imgui.BeginGroup()
256260

257261
// check for new property information
258262
select {
259263
case win.selectedProperties = <-win.propertyResult:
260264
win.selectedName = win.selectedProperties.Name
261265
if win.selectedName == "" {
262-
win.selectedName = win.path.Base()
266+
win.selectedName = win.path.Results.Base
263267
win.selectedName = cartridgeloader.NameFromFilename(win.selectedName)
264268
}
265269

@@ -281,16 +285,15 @@ func (win *winSelectROM) draw() {
281285
}()
282286

283287
if imgui.Button("Parent") {
284-
d := filepath.Dir(win.path.Dir())
285-
err := win.setPath(d)
288+
err := win.path.Set(filepath.Dir(win.path.Results.Dir))
286289
if err != nil {
287-
logger.Logf("sdlimgui", "error setting path (%s)", d)
290+
logger.Logf("sdlimgui", err.Error())
288291
}
289292
win.scrollToTop = true
290293
}
291294

292295
imgui.SameLine()
293-
imgui.Text(archivefs.RemoveArchiveExt(win.path.Dir()))
296+
imgui.Text(archivefs.RemoveArchiveExt(win.path.Results.Dir))
294297

295298
if imgui.BeginTable("romSelector", 2) {
296299
imgui.TableSetupColumnV("filelist", imgui.TableColumnFlagsWidthStretch, -1, 0)
@@ -309,7 +312,7 @@ func (win *winSelectROM) draw() {
309312

310313
// list directories
311314
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.ROMSelectDir)
312-
for _, e := range win.pathEntries {
315+
for _, e := range win.path.Results.Entries {
313316
// ignore dot files
314317
if !win.showHidden && e.Name[0] == '.' {
315318
continue
@@ -328,10 +331,9 @@ func (win *winSelectROM) draw() {
328331
}
329332

330333
if imgui.Selectable(s.String()) {
331-
d := filepath.Join(win.path.Dir(), e.Name)
332-
err := win.setPath(d)
334+
err := win.path.Set(filepath.Join(win.path.Results.Dir, e.Name))
333335
if err != nil {
334-
logger.Logf("sdlimgui", "error setting path (%s)", d)
336+
logger.Logf("sdlimgui", err.Error())
335337
}
336338
win.scrollToTop = true
337339
}
@@ -341,7 +343,7 @@ func (win *winSelectROM) draw() {
341343

342344
// list files
343345
imgui.PushStyleColor(imgui.StyleColorText, win.img.cols.ROMSelectFile)
344-
for _, e := range win.pathEntries {
346+
for _, e := range win.path.Results.Entries {
345347
// ignore dot files
346348
if !win.showHidden && e.Name[0] == '.' {
347349
continue
@@ -371,14 +373,17 @@ func (win *winSelectROM) draw() {
371373
}
372374

373375
if !e.IsDir {
374-
selected := e.Name == win.path.Base()
376+
selected := e.Name == win.path.Results.Base
375377

376378
if selected && win.centreOnFile {
377379
imgui.SetScrollHereY(0.0)
378380
}
379381

380382
if imgui.SelectableV(e.Name, selected, 0, imgui.Vec2{0, 0}) {
381-
win.setPath(filepath.Join(win.path.Dir(), e.Name))
383+
err := win.path.Set(filepath.Join(win.path.Results.Dir, e.Name))
384+
if err != nil {
385+
logger.Logf("sdlimgui", err.Error())
386+
}
382387
}
383388
if imgui.IsItemHovered() && imgui.IsMouseDoubleClicked(0) {
384389
win.insertCartridge()
@@ -580,7 +585,7 @@ func (win *winSelectROM) draw() {
580585
var s string
581586

582587
// load or reload button
583-
if win.path.String() == win.img.cache.VCS.Mem.Cart.Filename {
588+
if win.path.Results.Selected == win.img.cache.VCS.Mem.Cart.Filename {
584589
s = fmt.Sprintf("Reload %s", win.selectedName)
585590
} else {
586591
s = fmt.Sprintf("Load %s", win.selectedName)
@@ -622,29 +627,15 @@ func (win *winSelectROM) insertCartridge() {
622627
return
623628
}
624629

625-
win.img.dbg.InsertCartridge(win.path.String())
630+
win.img.dbg.InsertCartridge(win.path.Results.Selected)
626631

627632
// close rom selected in both the debugger and playmode
628633
win.debuggerSetOpen(false)
629634
win.playmodeSetOpen(false)
630635
}
631636

632-
func (win *winSelectROM) setPath(path string) error {
633-
var err error
634-
win.path.Set(path)
635-
win.pathEntries, err = win.path.List()
636-
if err != nil {
637-
return err
638-
}
639-
if win.path.IsDir() {
640-
win.setSelectedFile("")
641-
} else {
642-
win.setSelectedFile(win.path.String())
643-
}
644-
return nil
645-
}
646-
647-
func (win *winSelectROM) setSelectedFile(filename string) {
637+
// imnplements the archivefs.FilenameSetter interface
638+
func (win *winSelectROM) SetSelectedFilename(filename string) {
648639
// return immediately if the filename is empty
649640
if filename == "" {
650641
return

0 commit comments

Comments
 (0)