Skip to content

Commit 259ecee

Browse files
author
Donovan Solms
committed
[v0.0.2] Working for Windows, Linux and Mac
Mac requires some attention to the .app package
1 parent 53e5b07 commit 259ecee

15 files changed

+253
-55
lines changed

Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ run: build
1818
run_debug: build
1919
./bin/linux-amd64/'${APP_NAME}' -d
2020

21+
run_only_debug:
22+
./bin/linux-amd64/'${APP_NAME}' -d
23+
2124
webdev:
2225
./scripts/run_browsersync.sh
2326

@@ -27,3 +30,5 @@ fmt:
2730

2831
clean:
2932
rm -Rf bin/
33+
rm src/windows.syso
34+
rm src/bind*

src/bundler.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"app_name": "Stellite GUI Miner v0.0.1",
2+
"app_name": "Stellite GUI Miner v0.0.2",
33
"environments": [
44
{"arch": "amd64", "os": "darwin"},
55
{"arch": "amd64", "os": "linux"},

src/gui/gui.go

+73-49
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"os/user"
1111
"path/filepath"
12+
"runtime"
1213
"time"
1314

1415
astilectron "github.com/asticode/go-astilectron"
@@ -22,8 +23,6 @@ import (
2223
type GUI struct {
2324
// window is the main Astilectron window
2425
window *astilectron.Window
25-
// // minerCmd is a reference to the xmr-stak miner process
26-
// minerCmd *exec.Cmd
2726
// astilectronOptions holds the Astilectron options
2827
astilectronOptions bootstrap.Options
2928
// config for the miner
@@ -51,21 +50,13 @@ func New(
5150
asset bootstrap.Asset,
5251
restoreAssets bootstrap.RestoreAssets,
5352
apiEndpoint string,
53+
workingDir string,
5454
isDebug bool) (*GUI, error) {
5555

5656
if apiEndpoint == "" {
5757
return nil, errors.New("The API Endpoint must be specified")
5858
}
5959

60-
workingDir, err := os.Getwd()
61-
if err != nil {
62-
return nil, fmt.Errorf("Can't read current directory: %s", err)
63-
}
64-
workingDir, err = filepath.Abs(workingDir)
65-
if err != nil {
66-
return nil, fmt.Errorf("Can't read current directory: %s", err)
67-
}
68-
6960
gui := GUI{
7061
config: config,
7162
workingDir: workingDir,
@@ -89,6 +80,65 @@ func New(
8980
Mid: uuid.New().String(),
9081
}
9182
}
83+
var menu []*astilectron.MenuItemOptions
84+
85+
// Setup the logging, by default we log to stdout
86+
logrus.SetFormatter(&logrus.TextFormatter{
87+
FullTimestamp: true,
88+
TimestampFormat: "Jan 02 15:04:05",
89+
})
90+
logrus.SetLevel(logrus.InfoLevel)
91+
92+
logrus.SetOutput(os.Stdout)
93+
if isDebug {
94+
logrus.SetLevel(logrus.DebugLevel)
95+
debugLog, err := os.OpenFile(
96+
filepath.Join(gui.workingDir, "debug.log"),
97+
os.O_CREATE|os.O_TRUNC|os.O_WRONLY,
98+
0644)
99+
if err != nil {
100+
panic(err)
101+
}
102+
logrus.SetOutput(debugLog)
103+
104+
// We only show the menu bar in debug mode
105+
menu = append(menu, &astilectron.MenuItemOptions{
106+
Label: astilectron.PtrStr("File"),
107+
SubMenu: []*astilectron.MenuItemOptions{
108+
{
109+
Role: astilectron.MenuItemRoleClose,
110+
},
111+
},
112+
})
113+
}
114+
// To make copy and paste work on Mac, the copy and paste entries need to
115+
// be defined, the alternative is to implement the clipboard API
116+
// https://github.com/electron/electron/blob/master/docs/api/clipboard.md
117+
if runtime.GOOS == "darwin" {
118+
menu = append(menu, &astilectron.MenuItemOptions{
119+
Label: astilectron.PtrStr("Edit"),
120+
SubMenu: []*astilectron.MenuItemOptions{
121+
{
122+
Role: astilectron.MenuItemRoleCut,
123+
},
124+
{
125+
Role: astilectron.MenuItemRoleCopy,
126+
},
127+
{
128+
Role: astilectron.MenuItemRolePaste,
129+
},
130+
{
131+
Role: astilectron.MenuItemRoleSelectAll,
132+
},
133+
},
134+
})
135+
}
136+
137+
// Setting the WithFields now will ensure all log entries from this point
138+
// includes the fields
139+
gui.logger = logrus.WithFields(logrus.Fields{
140+
"service": "stellite-gui-miner",
141+
})
92142

93143
gui.astilectronOptions = bootstrap.Options{
94144
Debug: isDebug,
@@ -101,20 +151,20 @@ func New(
101151
AppIconDefaultPath: "resources/icon.png",
102152
},
103153
WindowOptions: &astilectron.WindowOptions{
104-
Title: astilectron.PtrStr("Stellite GUI Miner"),
154+
// TODO: Frameless looks amazing, first I'd need to implement draggable
155+
// sections and control buttons
156+
//Frame: astilectron.PtrBool(false),
105157
BackgroundColor: astilectron.PtrStr("#0B0C22"),
106158
Center: astilectron.PtrBool(true),
107159
Height: astilectron.PtrInt(700),
108160
Width: astilectron.PtrInt(1175),
109161
},
110-
MenuOptions: []*astilectron.MenuItemOptions{{
111-
Label: astilectron.PtrStr("File"),
112-
SubMenu: []*astilectron.MenuItemOptions{
113-
{
114-
Role: astilectron.MenuItemRoleClose,
115-
},
116-
},
117-
}},
162+
// TODO: Fix this tray to display nicely
163+
/*TrayOptions: &astilectron.TrayOptions{
164+
Image: astilectron.PtrStr("/static/i/miner-logo.png"),
165+
Tooltip: astilectron.PtrStr(appName),
166+
},*/
167+
MenuOptions: menu,
118168
// OnWait is triggered as soon as the electron window is ready and running
119169
OnWait: func(
120170
_ *astilectron.Astilectron,
@@ -139,32 +189,6 @@ func New(
139189
MessageHandler: gui.handleElectronCommands,
140190
}
141191

142-
// Setup the logging, by default we log to stdout
143-
logrus.SetFormatter(&logrus.TextFormatter{
144-
FullTimestamp: true,
145-
TimestampFormat: "Jan 02 15:04:05",
146-
})
147-
logrus.SetLevel(logrus.InfoLevel)
148-
149-
logrus.SetOutput(os.Stdout)
150-
if isDebug {
151-
logrus.SetLevel(logrus.DebugLevel)
152-
// TODO: Handle this debug log better
153-
debugLog, err := os.OpenFile(
154-
fmt.Sprintf(".%c%s", os.PathSeparator, "debug.log"),
155-
os.O_CREATE|os.O_TRUNC|os.O_WRONLY,
156-
0644)
157-
if err != nil {
158-
panic(err)
159-
}
160-
logrus.SetOutput(debugLog)
161-
}
162-
// Setting the WithFields now will ensure all log entries from this point
163-
// includes the fields
164-
gui.logger = logrus.WithFields(logrus.Fields{
165-
"service": "stellite-gui-miner",
166-
})
167-
168192
gui.logger.Info("Setup complete")
169193
return &gui, nil
170194
}
@@ -315,10 +339,10 @@ func (gui *GUI) configureMiner(command bootstrap.MessageIn) {
315339
gui.logger.Fatalf("Unable to configure miner: '%s'", err)
316340
}
317341
scanPath := filepath.Join(gui.workingDir, "miner")
318-
if gui.config.Miner.Path != "" {
319-
// TODO: Fix own miner paths option
342+
// TODO: Fix own miner paths option
343+
/*if gui.config.Miner.Path != "" {
320344
//scanPath = path.Base(gui.config.Miner.Path)
321-
}
345+
}*/
322346
gui.logger.WithField(
323347
"scan_path", scanPath,
324348
).Debug("Determining miner type")

src/gui/helper.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io/ioutil"
99
"log"
1010
"net/http"
11+
"path/filepath"
1112
)
1213

1314
// GetPoolList returns the list of pools available to the GUI miner
@@ -40,13 +41,15 @@ func (gui *GUI) GetPool(id int) (PoolData, error) {
4041
}
4142

4243
// SaveConfig saves the configuration to disk
43-
// TODO: Specify path here
4444
func (gui *GUI) SaveConfig(config Config) error {
4545
configBytes, err := json.Marshal(&config)
4646
if err != nil {
4747
return err
4848
}
49-
err = ioutil.WriteFile("config.json", configBytes, 0644)
49+
err = ioutil.WriteFile(
50+
filepath.Join(gui.workingDir, "config.json"),
51+
configBytes,
52+
0644)
5053
if err != nil {
5154
return err
5255
}
File renamed without changes.

src/gui/miner/base_linux.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package miner
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"strings"
8+
9+
ps "github.com/mitchellh/go-ps"
10+
)
11+
12+
// Base implements core functionality common to all miners
13+
type Base struct {
14+
executableName string
15+
executablePath string
16+
command *exec.Cmd
17+
}
18+
19+
// Start the miner
20+
func (b *Base) Start() error {
21+
params := []string{}
22+
commandName := fmt.Sprintf(".%c%s", os.PathSeparator, b.executableName)
23+
commandDir := b.executablePath
24+
b.command = exec.Command(commandName, params...)
25+
b.command.Dir = commandDir
26+
return b.command.Start()
27+
}
28+
29+
// Stop the miner
30+
func (b *Base) Stop() error {
31+
if b.command != nil {
32+
// Some of the miners fork in a way that we loose track of the actual
33+
// miner's pid. To make sure the miner is stopped, we find all processes
34+
// that match the original executable name
35+
processes, err := ps.Processes()
36+
if err != nil {
37+
// If for some reason we can't get the process list, we use the
38+
// standard kill available
39+
return b.command.Process.Kill()
40+
}
41+
for _, process := range processes {
42+
if strings.Contains(strings.ToLower(process.Executable()), b.executableName) {
43+
p, err := os.FindProcess(process.Pid())
44+
if err != nil {
45+
// If the process is in the list, but we can't find it by Pid, then
46+
// it probably died or something weird is going on
47+
return err
48+
}
49+
// Kill the process we found, then continue searching - just in case
50+
// there is still others lingering around. Not worried about any errors
51+
// here since there is nothing we can do about it at this point
52+
_ = p.Kill()
53+
}
54+
}
55+
56+
}
57+
return nil
58+
}

src/gui/miner/base_windows.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package miner
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"strings"
8+
"syscall"
9+
10+
ps "github.com/mitchellh/go-ps"
11+
)
12+
13+
// Base implements core functionality common to all miners
14+
type Base struct {
15+
executableName string
16+
executablePath string
17+
command *exec.Cmd
18+
}
19+
20+
// Start the miner
21+
func (b *Base) Start() error {
22+
params := []string{}
23+
commandName := fmt.Sprintf(".%c%s", os.PathSeparator, b.executableName)
24+
commandDir := b.executablePath
25+
b.command = exec.Command(commandName, params...)
26+
b.command.Dir = commandDir
27+
// This hides the syscall section on Linux where SysProcAttr defines different
28+
// attributes
29+
b.command.SysProcAttr = &syscall.SysProcAttr{
30+
HideWindow: true,
31+
}
32+
return b.command.Start()
33+
}
34+
35+
// Stop the miner
36+
func (b *Base) Stop() error {
37+
if b.command != nil {
38+
// Some of the miners fork in a way that we loose track of the actual
39+
// miner's pid. To make sure the miner is stopped, we find all processes
40+
// that match the original executable name
41+
processes, err := ps.Processes()
42+
if err != nil {
43+
// If for some reason we can't get the process list, we use the
44+
// standard kill available
45+
return b.command.Process.Kill()
46+
}
47+
for _, process := range processes {
48+
if strings.Contains(strings.ToLower(process.Executable()), b.executableName) {
49+
p, err := os.FindProcess(process.Pid())
50+
if err != nil {
51+
// If the process is in the list, but we can't find it by Pid, then
52+
// it probably died or something weird is going on
53+
return err
54+
}
55+
// Kill the process we found, then continue searching - just in case
56+
// there is still others lingering around. Not worried about any errors
57+
// here since there is nothing we can do about it at this point
58+
_ = p.Kill()
59+
}
60+
}
61+
62+
}
63+
return nil
64+
}

src/gui/miner/xmrig.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"io/ioutil"
66
"net/http"
77
"path/filepath"
8+
"runtime"
89
)
910

1011
// Xmrig implements the miner interface for the xmrig miner, including
@@ -210,10 +211,17 @@ func (miner *Xmrig) defaultConfig(
210211
poolEndpoint string,
211212
walletAddress string) XmrigConfig {
212213

214+
runInBackground := true
215+
// On Mac OSX xmrig doesn't run is we fork the process to the background and
216+
// xmrig forks to the background again
217+
if runtime.GOOS == "darwin" {
218+
runInBackground = false
219+
}
220+
213221
return XmrigConfig{
214222
Algo: "cryptonight",
215223
Av: 0,
216-
Background: true,
224+
Background: runInBackground,
217225
Colors: true,
218226
CPUAffinity: nil,
219227
CPUPriority: nil,

0 commit comments

Comments
 (0)