mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-19 05:30:06 +08:00
add extra core &&
fix speedtest current
This commit is contained in:
parent
83413c19e8
commit
78a78cf38e
@ -148,6 +148,10 @@ set(PROJECT_SOURCES
|
||||
src/ui/profile/edit_custom.cpp
|
||||
include/ui/profile/edit_custom.ui
|
||||
|
||||
include/ui/profile/edit_extra_core.h
|
||||
src/ui/profile/edit_extra_core.cpp
|
||||
include/ui/profile/edit_extra_core.ui
|
||||
|
||||
include/ui/profile/edit_wireguard.h
|
||||
src/ui/profile/edit_wireguard.cpp
|
||||
include/ui/profile/edit_wireguard.ui
|
||||
|
||||
@ -138,11 +138,17 @@ func (x *ErrorResp) GetError() string {
|
||||
}
|
||||
|
||||
type LoadConfigReq struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
CoreConfig string `protobuf:"bytes,1,opt,name=core_config,json=coreConfig,proto3" json:"core_config,omitempty"`
|
||||
DisableStats bool `protobuf:"varint,2,opt,name=disable_stats,json=disableStats,proto3" json:"disable_stats,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
CoreConfig string `protobuf:"bytes,1,opt,name=core_config,json=coreConfig,proto3" json:"core_config,omitempty"`
|
||||
DisableStats bool `protobuf:"varint,2,opt,name=disable_stats,json=disableStats,proto3" json:"disable_stats,omitempty"`
|
||||
NeedExtraProcess bool `protobuf:"varint,3,opt,name=need_extra_process,json=needExtraProcess,proto3" json:"need_extra_process,omitempty"`
|
||||
ExtraProcessPath string `protobuf:"bytes,4,opt,name=extra_process_path,json=extraProcessPath,proto3" json:"extra_process_path,omitempty"`
|
||||
ExtraProcessArgs string `protobuf:"bytes,5,opt,name=extra_process_args,json=extraProcessArgs,proto3" json:"extra_process_args,omitempty"`
|
||||
ExtraProcessConf string `protobuf:"bytes,6,opt,name=extra_process_conf,json=extraProcessConf,proto3" json:"extra_process_conf,omitempty"`
|
||||
ExtraProcessConfDir string `protobuf:"bytes,7,opt,name=extra_process_conf_dir,json=extraProcessConfDir,proto3" json:"extra_process_conf_dir,omitempty"`
|
||||
ExtraNoOut bool `protobuf:"varint,8,opt,name=extra_no_out,json=extraNoOut,proto3" json:"extra_no_out,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *LoadConfigReq) Reset() {
|
||||
@ -189,6 +195,48 @@ func (x *LoadConfigReq) GetDisableStats() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *LoadConfigReq) GetNeedExtraProcess() bool {
|
||||
if x != nil {
|
||||
return x.NeedExtraProcess
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *LoadConfigReq) GetExtraProcessPath() string {
|
||||
if x != nil {
|
||||
return x.ExtraProcessPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LoadConfigReq) GetExtraProcessArgs() string {
|
||||
if x != nil {
|
||||
return x.ExtraProcessArgs
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LoadConfigReq) GetExtraProcessConf() string {
|
||||
if x != nil {
|
||||
return x.ExtraProcessConf
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LoadConfigReq) GetExtraProcessConfDir() string {
|
||||
if x != nil {
|
||||
return x.ExtraProcessConfDir
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LoadConfigReq) GetExtraNoOut() bool {
|
||||
if x != nil {
|
||||
return x.ExtraNoOut
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type URLTestResp struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
OutboundTag string `protobuf:"bytes,1,opt,name=outbound_tag,json=outboundTag,proto3" json:"outbound_tag,omitempty"`
|
||||
@ -1193,12 +1241,29 @@ var file_libcore_proto_rawDesc = string([]byte{
|
||||
0x79, 0x52, 0x65, 0x71, 0x22, 0x0b, 0x0a, 0x09, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73,
|
||||
0x70, 0x22, 0x21, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14,
|
||||
0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x22, 0x55, 0x0a, 0x0d, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66,
|
||||
0x69, 0x67, 0x52, 0x65, 0x71, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64,
|
||||
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x22, 0x65, 0x0a, 0x0b, 0x55,
|
||||
0x72, 0x72, 0x6f, 0x72, 0x22, 0xe4, 0x02, 0x0a, 0x0d, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c,
|
||||
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12,
|
||||
0x6e, 0x65, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65,
|
||||
0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6e, 0x65, 0x65, 0x64, 0x45, 0x78,
|
||||
0x74, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x65, 0x78,
|
||||
0x74, 0x72, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x72, 0x6f,
|
||||
0x63, 0x65, 0x73, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x2c, 0x0a, 0x12, 0x65, 0x78, 0x74, 0x72,
|
||||
0x61, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x05,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x63, 0x65,
|
||||
0x73, 0x73, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f,
|
||||
0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x12, 0x33, 0x0a, 0x16, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x70, 0x72,
|
||||
0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x07,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x63, 0x65,
|
||||
0x73, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x44, 0x69, 0x72, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x78, 0x74,
|
||||
0x72, 0x61, 0x5f, 0x6e, 0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x4e, 0x6f, 0x4f, 0x75, 0x74, 0x22, 0x65, 0x0a, 0x0b, 0x55,
|
||||
0x52, 0x4c, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75,
|
||||
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1d, 0x0a,
|
||||
|
||||
@ -38,6 +38,12 @@ message ErrorResp {
|
||||
message LoadConfigReq {
|
||||
string core_config = 1;
|
||||
bool disable_stats = 2;
|
||||
bool need_extra_process = 3;
|
||||
string extra_process_path = 4;
|
||||
string extra_process_args = 5;
|
||||
string extra_process_conf = 6;
|
||||
string extra_process_conf_dir = 7;
|
||||
bool extra_no_out = 8;
|
||||
}
|
||||
|
||||
message URLTestResp {
|
||||
|
||||
15
core/server/internal/process/logger.go
Normal file
15
core/server/internal/process/logger.go
Normal file
@ -0,0 +1,15 @@
|
||||
package process
|
||||
|
||||
import "log"
|
||||
|
||||
type pipeLogger struct {
|
||||
prefix string
|
||||
noOut bool
|
||||
}
|
||||
|
||||
func (p *pipeLogger) Write(b []byte) (int, error) {
|
||||
if !p.noOut {
|
||||
log.Println(p.prefix + ":" + string(b))
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
47
core/server/internal/process/process.go
Normal file
47
core/server/internal/process/process.go
Normal file
@ -0,0 +1,47 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Process struct {
|
||||
path string
|
||||
args string
|
||||
noOut bool
|
||||
cmd *exec.Cmd
|
||||
stopped atomic.Bool
|
||||
}
|
||||
|
||||
func NewProcess(path string, args string, noOut bool) *Process {
|
||||
return &Process{path: path, args: args, noOut: noOut}
|
||||
}
|
||||
|
||||
func (p *Process) Start() error {
|
||||
p.cmd = exec.Command(p.path, strings.Split(p.args, " ")...)
|
||||
|
||||
p.cmd.Stdout = &pipeLogger{prefix: "Extra Core", noOut: p.noOut}
|
||||
p.cmd.Stderr = &pipeLogger{prefix: "Extra Core", noOut: p.noOut}
|
||||
|
||||
err := p.cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.stopped.Store(false)
|
||||
|
||||
go func() {
|
||||
fmt.Println(p.path, ":", "process started, waiting for it to end")
|
||||
_ = p.cmd.Wait()
|
||||
if !p.stopped.Load() {
|
||||
fmt.Println("Extra process exited unexpectedly")
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Process) Stop() {
|
||||
p.stopped.Store(true)
|
||||
_ = p.cmd.Process.Kill()
|
||||
}
|
||||
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/settings"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||
@ -13,6 +14,7 @@ import (
|
||||
"nekobox_core/gen"
|
||||
"nekobox_core/internal/boxbox"
|
||||
"nekobox_core/internal/boxmain"
|
||||
"nekobox_core/internal/process"
|
||||
"nekobox_core/internal/sys"
|
||||
"os"
|
||||
"runtime"
|
||||
@ -21,6 +23,7 @@ import (
|
||||
)
|
||||
|
||||
var boxInstance *boxbox.Box
|
||||
var extraProcess *process.Process
|
||||
var needUnsetDNS bool
|
||||
var systemProxyController settings.SystemProxy
|
||||
var systemProxyAddr metadata.Socksaddr
|
||||
@ -59,6 +62,28 @@ func (s *server) Start(ctx context.Context, in *gen.LoadConfigReq) (out *gen.Err
|
||||
return
|
||||
}
|
||||
|
||||
if in.NeedExtraProcess {
|
||||
extraConfPath := in.ExtraProcessConfDir + string(os.PathSeparator) + "extra.conf"
|
||||
f, e := os.OpenFile(extraConfPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 700)
|
||||
if e != nil {
|
||||
err = E.Cause(e, "Failed to open extra.conf")
|
||||
return
|
||||
}
|
||||
_, e = f.WriteString(in.ExtraProcessConf)
|
||||
if e != nil {
|
||||
err = E.Cause(e, "Failed to write extra.conf")
|
||||
return
|
||||
}
|
||||
_ = f.Close()
|
||||
args := fmt.Sprintf(in.ExtraProcessArgs, extraConfPath)
|
||||
|
||||
extraProcess = process.NewProcess(in.ExtraProcessPath, args, in.ExtraNoOut)
|
||||
err = extraProcess.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
boxInstance, instanceCancel, err = boxmain.Create([]byte(in.CoreConfig))
|
||||
if err != nil {
|
||||
return
|
||||
@ -99,6 +124,11 @@ func (s *server) Stop(ctx context.Context, in *gen.EmptyReq) (out *gen.ErrorResp
|
||||
|
||||
boxInstance = nil
|
||||
|
||||
if extraProcess != nil {
|
||||
extraProcess.Stop()
|
||||
extraProcess = nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -4,10 +4,21 @@
|
||||
#include "include/sys/Process.hpp"
|
||||
|
||||
namespace NekoGui {
|
||||
class ExtraCoreData
|
||||
{
|
||||
public:
|
||||
QString path;
|
||||
QString args;
|
||||
QString config;
|
||||
QString configDir;
|
||||
bool noLog;
|
||||
};
|
||||
|
||||
class BuildConfigResult {
|
||||
public:
|
||||
QString error;
|
||||
QJsonObject coreConfig;
|
||||
std::shared_ptr<ExtraCoreData> extraCoreData;
|
||||
|
||||
QList<std::shared_ptr<NekoGui_traffic::TrafficData>> outboundStats; // all, but not including "bypass" "block"
|
||||
};
|
||||
|
||||
34
include/configs/proxy/ExtraCore.h
Normal file
34
include/configs/proxy/ExtraCore.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbstractBean.hpp"
|
||||
|
||||
namespace NekoGui_fmt {
|
||||
class ExtraCoreBean : public AbstractBean {
|
||||
public:
|
||||
QString socksAddress = "127.0.0.1";
|
||||
int socksPort;
|
||||
QString extraCorePath;
|
||||
QString extraCoreArgs;
|
||||
QString extraCoreConf;
|
||||
bool noLogs;
|
||||
|
||||
ExtraCoreBean() : AbstractBean(0) {
|
||||
_add(new configItem("socks_address", &socksAddress, itemType::string));
|
||||
_add(new configItem("socks_port", &socksPort, itemType::integer));
|
||||
_add(new configItem("extra_core_path", &extraCorePath, itemType::string));
|
||||
_add(new configItem("extra_core_args", &extraCoreArgs, itemType::string));
|
||||
_add(new configItem("extra_core_conf", &extraCoreConf, itemType::string));
|
||||
_add(new configItem("no_logs", &noLogs, itemType::boolean));
|
||||
};
|
||||
|
||||
QString DisplayType() override { return "ExtraCore"; };
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjSingBox() override;
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
bool TryParseJson(const QJsonObject &obj);
|
||||
|
||||
QString ToShareLink() override;
|
||||
};
|
||||
}
|
||||
@ -9,3 +9,4 @@
|
||||
#include "WireguardBean.h"
|
||||
#include "SSHBean.h"
|
||||
#include "CustomBean.hpp"
|
||||
#include "ExtraCore.h"
|
||||
|
||||
@ -56,11 +56,16 @@ namespace NekoGui {
|
||||
|
||||
void UpdateRouteChains(const QList<std::shared_ptr<RoutingChain>>& newChain);
|
||||
|
||||
QStringList GetExtraCorePaths() const;
|
||||
|
||||
bool AddExtraCorePath(const QString &path);
|
||||
|
||||
private:
|
||||
// sort by id
|
||||
QList<int> profilesIdOrder;
|
||||
QList<int> groupsIdOrder;
|
||||
QList<int> routesIdOrder;
|
||||
QSet<QString> extraCorePaths;
|
||||
|
||||
[[nodiscard]] int NewProfileID() const;
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "include/global/NekoGui.hpp"
|
||||
#include "include/stats/traffic/TrafficData.hpp"
|
||||
#include "include/configs/proxy/AbstractBean.hpp"
|
||||
#include "include/configs/proxy/ExtraCore.h"
|
||||
|
||||
namespace NekoGui_fmt {
|
||||
class SocksHttpBean;
|
||||
@ -86,5 +87,9 @@ namespace NekoGui {
|
||||
[[nodiscard]] NekoGui_fmt::CustomBean *CustomBean() const {
|
||||
return (NekoGui_fmt::CustomBean *) bean.get();
|
||||
};
|
||||
|
||||
[[nodiscard]] NekoGui_fmt::ExtraCoreBean *ExtraCoreBean() const {
|
||||
return (NekoGui_fmt::ExtraCoreBean *) bean.get();
|
||||
};
|
||||
};
|
||||
} // namespace NekoGui
|
||||
|
||||
@ -241,9 +241,9 @@ private:
|
||||
|
||||
void url_test_current();
|
||||
|
||||
void speedtest_current_group(const QList<std::shared_ptr<NekoGui::ProxyEntity>>& profiles);
|
||||
void speedtest_current_group(const QList<std::shared_ptr<NekoGui::ProxyEntity>>& profiles, bool testCurrent = false);
|
||||
|
||||
void runSpeedTest(const QString& config, bool useDefault, const QStringList& outboundTags, const QMap<QString, int>& tag2entID, int entID = -1);
|
||||
void runSpeedTest(const QString& config, bool useDefault, bool testCurrent, const QStringList& outboundTags, const QMap<QString, int>& tag2entID, int entID = -1);
|
||||
|
||||
static void stop_core_daemon();
|
||||
|
||||
|
||||
28
include/ui/profile/edit_extra_core.h
Normal file
28
include/ui/profile/edit_extra_core.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include "profile_editor.h"
|
||||
#include "ui_edit_extra_core.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class EditExtraCore;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class EditExtraCore : public QWidget, public ProfileEditor {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EditExtraCore(QWidget *parent = nullptr);
|
||||
|
||||
~EditExtraCore() override;
|
||||
|
||||
void onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) override;
|
||||
|
||||
bool onEnd() override;
|
||||
|
||||
private:
|
||||
Ui::EditExtraCore *ui;
|
||||
std::shared_ptr<NekoGui::ProxyEntity> ent;
|
||||
};
|
||||
155
include/ui/profile/edit_extra_core.ui
Normal file
155
include/ui/profile/edit_extra_core.ui
Normal file
@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>EditExtraCore</class>
|
||||
<widget class="QWidget" name="EditExtraCore">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_2">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Socks address</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="socks_address">
|
||||
<property name="text">
|
||||
<string>127.0.0.1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Socks port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="socks_port">
|
||||
<property name="text">
|
||||
<string>1080</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_3">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Core path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="path_combo">
|
||||
<property name="frame">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="path_button">
|
||||
<property name="text">
|
||||
<string>Choose from file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_4">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>args to pass to the executable, with a %s placed in place of the config file path, for example:<br/>./sing-box run -c conf.json<br/>will have its args like:<br/>run -c %s</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Args</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="args">
|
||||
<property name="placeholderText">
|
||||
<string>run -confPath %s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>contents of the config file that will be passed to the extra core process</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Config</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPlainTextEdit" name="config"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="no_logs">
|
||||
<property name="text">
|
||||
<string>No logs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@ -46,6 +46,7 @@ namespace NekoGui {
|
||||
|
||||
std::shared_ptr<BuildConfigResult> BuildConfig(const std::shared_ptr<ProxyEntity> &ent, bool forTest, bool forExport, int chainID) {
|
||||
auto result = std::make_shared<BuildConfigResult>();
|
||||
result->extraCoreData = std::make_shared<ExtraCoreData>();
|
||||
auto status = std::make_shared<BuildConfigStatus>();
|
||||
status->ent = ent;
|
||||
status->result = result;
|
||||
@ -127,6 +128,11 @@ namespace NekoGui {
|
||||
|
||||
QJsonArray directDomainArray;
|
||||
for (const auto &item: profiles) {
|
||||
if (item->type == "extracore")
|
||||
{
|
||||
MW_show_log("Skipping ExtraCore conf");
|
||||
continue;
|
||||
}
|
||||
if (!IsValid(item)) {
|
||||
MW_show_log("Skipping invalid config: " + item->bean->name);
|
||||
item->latency = -1;
|
||||
@ -423,6 +429,15 @@ namespace NekoGui {
|
||||
// Outbounds
|
||||
auto tagProxy = BuildChain(status->chainID, status);
|
||||
if (!status->result->error.isEmpty()) return;
|
||||
if (status->ent->type == "extracore")
|
||||
{
|
||||
auto bean = status->ent->ExtraCoreBean();
|
||||
status->result->extraCoreData->path = bean->extraCorePath;
|
||||
status->result->extraCoreData->args = bean->extraCoreArgs;
|
||||
status->result->extraCoreData->config = bean->extraCoreConf;
|
||||
status->result->extraCoreData->configDir = GetBasePath();
|
||||
status->result->extraCoreData->noLog = bean->noLogs;
|
||||
}
|
||||
|
||||
// Direct domains
|
||||
bool needDirectDnsRules = false;
|
||||
|
||||
@ -334,4 +334,17 @@ namespace NekoGui_fmt {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CoreObjOutboundBuildResult ExtraCoreBean::BuildCoreObjSingBox()
|
||||
{
|
||||
CoreObjOutboundBuildResult result;
|
||||
QJsonObject outbound{
|
||||
{"type", "socks"},
|
||||
{"server", socksAddress},
|
||||
{"server_port", socksPort},
|
||||
};
|
||||
result.outbound = outbound;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace NekoGui_fmt
|
||||
|
||||
@ -296,4 +296,9 @@ namespace NekoGui_fmt {
|
||||
return url.toString(QUrl::FullyEncoded);
|
||||
}
|
||||
|
||||
QString ExtraCoreBean::ToShareLink()
|
||||
{
|
||||
return "Unsupported for now";
|
||||
}
|
||||
|
||||
} // namespace NekoGui_fmt
|
||||
@ -7,6 +7,7 @@
|
||||
#include <include/configs/proxy/VMessBean.hpp>
|
||||
#include <include/configs/proxy/WireguardBean.h>
|
||||
|
||||
#include "include/configs/proxy/ExtraCore.h"
|
||||
#include "include/configs/proxy/SSHBean.h"
|
||||
|
||||
namespace NekoGui_fmt
|
||||
@ -224,4 +225,9 @@ namespace NekoGui_fmt
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExtraCoreBean::TryParseJson(const QJsonObject& obj)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -394,4 +394,10 @@ namespace NekoGui_fmt {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExtraCoreBean::TryParseLink(const QString& link)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace NekoGui_fmt
|
||||
@ -51,6 +51,7 @@ namespace NekoGui {
|
||||
continue;
|
||||
}
|
||||
profiles[id] = ent;
|
||||
if (ent->type == "extracore") extraCorePaths.insert(ent->ExtraCoreBean()->extraCorePath);
|
||||
}
|
||||
// Clear Corrupted profile
|
||||
for (auto id: delProfile) {
|
||||
@ -227,6 +228,8 @@ namespace NekoGui {
|
||||
bean = new NekoGui_fmt::SSHBean(NekoGui_fmt::SSHBean());
|
||||
} else if (type == "custom") {
|
||||
bean = new NekoGui_fmt::CustomBean();
|
||||
} else if (type == "extracore") {
|
||||
bean = new NekoGui_fmt::ExtraCoreBean();
|
||||
} else {
|
||||
bean = new NekoGui_fmt::AbstractBean(-114514);
|
||||
}
|
||||
@ -342,6 +345,19 @@ namespace NekoGui {
|
||||
return profiles.count(id) ? profiles[id] : nullptr;
|
||||
}
|
||||
|
||||
QStringList ProfileManager::GetExtraCorePaths() const {
|
||||
return extraCorePaths.values();
|
||||
}
|
||||
|
||||
bool ProfileManager::AddExtraCorePath(const QString &path)
|
||||
{
|
||||
if (extraCorePaths.contains(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
extraCorePaths.insert(path);
|
||||
return true;
|
||||
}
|
||||
// Group
|
||||
|
||||
Group::Group() {
|
||||
|
||||
@ -36,6 +36,11 @@ namespace NekoGui_sys {
|
||||
kill();
|
||||
}
|
||||
}
|
||||
if (log.contains("Extra process exited unexpectedly"))
|
||||
{
|
||||
MW_show_log("Extra Core exited, stopping profile...");
|
||||
MW_dialog_message("ExternalProcess", "Crashed");
|
||||
}
|
||||
if (logCounter.fetchAndAddRelaxed(log.count("\n")) > NekoGui::dataStore->max_log_line) return;
|
||||
MW_show_log(log);
|
||||
});
|
||||
@ -74,8 +79,8 @@ namespace NekoGui_sys {
|
||||
|
||||
// Restart
|
||||
start_profile_when_core_is_up = NekoGui::dataStore->started_id;
|
||||
MW_show_log("[ERROR] " + QObject::tr("Core exited, restarting."));
|
||||
setTimeout([=] { Restart(); }, this, 1000);
|
||||
MW_show_log("[Fatal] " + QObject::tr("Core exited, restarting."));
|
||||
setTimeout([=] { Restart(); }, this, 200);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -454,7 +454,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
{
|
||||
if (running != nullptr)
|
||||
{
|
||||
speedtest_current_group({running});
|
||||
speedtest_current_group({}, true);
|
||||
}
|
||||
});
|
||||
connect(ui->actionSpeedtest_Selected, &QAction::triggered, this, [=]()
|
||||
|
||||
@ -170,9 +170,9 @@ void MainWindow::url_test_current() {
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::speedtest_current_group(const QList<std::shared_ptr<NekoGui::ProxyEntity>>& profiles)
|
||||
void MainWindow::speedtest_current_group(const QList<std::shared_ptr<NekoGui::ProxyEntity>>& profiles, bool testCurrent)
|
||||
{
|
||||
if (profiles.isEmpty()) {
|
||||
if (profiles.isEmpty() && !testCurrent) {
|
||||
return;
|
||||
}
|
||||
if (!speedtestRunning.tryLock()) {
|
||||
@ -180,22 +180,29 @@ void MainWindow::speedtest_current_group(const QList<std::shared_ptr<NekoGui::Pr
|
||||
return;
|
||||
}
|
||||
|
||||
runOnNewThread([this, profiles]() {
|
||||
auto buildObject = NekoGui::BuildTestConfig(profiles);
|
||||
if (!buildObject->error.isEmpty()) {
|
||||
MW_show_log(tr("Failed to build test config: ") + buildObject->error);
|
||||
speedtestRunning.unlock();
|
||||
return;
|
||||
}
|
||||
runOnNewThread([this, profiles, testCurrent]() {
|
||||
if (!testCurrent)
|
||||
{
|
||||
auto buildObject = NekoGui::BuildTestConfig(profiles);
|
||||
if (!buildObject->error.isEmpty()) {
|
||||
MW_show_log(tr("Failed to build test config: ") + buildObject->error);
|
||||
speedtestRunning.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
stopSpeedtest.store(false);
|
||||
for (const auto &entID: buildObject->fullConfigs.keys()) {
|
||||
auto configStr = buildObject->fullConfigs[entID];
|
||||
runSpeedTest(configStr, true, {}, {}, entID);
|
||||
}
|
||||
stopSpeedtest.store(false);
|
||||
for (const auto &entID: buildObject->fullConfigs.keys()) {
|
||||
auto configStr = buildObject->fullConfigs[entID];
|
||||
runSpeedTest(configStr, true, false, {}, {}, entID);
|
||||
}
|
||||
|
||||
if (!buildObject->outboundTags.empty()) {
|
||||
runSpeedTest(QJsonObject2QString(buildObject->coreConfig, false), false, buildObject->outboundTags, buildObject->tag2entID);
|
||||
if (!buildObject->outboundTags.empty()) {
|
||||
runSpeedTest(QJsonObject2QString(buildObject->coreConfig, false), false, false, buildObject->outboundTags, buildObject->tag2entID);
|
||||
}
|
||||
} else
|
||||
{
|
||||
stopSpeedtest.store(false);
|
||||
runSpeedTest("", true, true, {}, {});
|
||||
}
|
||||
|
||||
speedtestRunning.unlock();
|
||||
@ -206,7 +213,7 @@ void MainWindow::speedtest_current_group(const QList<std::shared_ptr<NekoGui::Pr
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::runSpeedTest(const QString& config, bool useDefault, const QStringList& outboundTags, const QMap<QString, int>& tag2entID, int entID)
|
||||
void MainWindow::runSpeedTest(const QString& config, bool useDefault, bool testCurrent, const QStringList& outboundTags, const QMap<QString, int>& tag2entID, int entID)
|
||||
{
|
||||
if (stopSpeedtest.load()) {
|
||||
MW_show_log(tr("Profile speed test aborted"));
|
||||
@ -222,6 +229,7 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, const QStr
|
||||
req.set_use_default_outbound(useDefault);
|
||||
req.set_test_download(speedtestConf == NekoGui::TestConfig::FULL || speedtestConf == NekoGui::TestConfig::DL);
|
||||
req.set_test_upload(speedtestConf == NekoGui::TestConfig::FULL || speedtestConf == NekoGui::TestConfig::UL);
|
||||
req.set_test_current(testCurrent);
|
||||
|
||||
// loop query result
|
||||
auto doneMu = new QMutex;
|
||||
@ -240,7 +248,7 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, const QStr
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto profile = NekoGui::profileManager->GetProfile(tag2entID[res.result().outbound_tag().c_str()]);
|
||||
auto profile = testCurrent ? running : NekoGui::profileManager->GetProfile(tag2entID[res.result().outbound_tag().c_str()]);
|
||||
if (profile == nullptr)
|
||||
{
|
||||
continue;
|
||||
@ -268,7 +276,8 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, const QStr
|
||||
if (!rpcOK) return;
|
||||
|
||||
for (const auto &res: result.results()) {
|
||||
if (!tag2entID.empty()) {
|
||||
if (testCurrent) entID = running ? running->id : -1;
|
||||
else {
|
||||
entID = tag2entID.count(QString(res.outbound_tag().c_str())) == 0 ? -1 : tag2entID[QString(res.outbound_tag().c_str())];
|
||||
}
|
||||
if (entID == -1) {
|
||||
@ -361,6 +370,15 @@ void MainWindow::neko_start(int _id) {
|
||||
libcore::LoadConfigReq req;
|
||||
req.set_core_config(QJsonObject2QString(result->coreConfig, true).toStdString());
|
||||
req.set_disable_stats(NekoGui::dataStore->disable_traffic_stats);
|
||||
if (ent->type == "extracore")
|
||||
{
|
||||
req.set_need_extra_process(true);
|
||||
req.set_extra_process_path(result->extraCoreData->path.toStdString());
|
||||
req.set_extra_process_args(result->extraCoreData->args.toStdString());
|
||||
req.set_extra_process_conf(result->extraCoreData->config.toStdString());
|
||||
req.set_extra_process_conf_dir(result->extraCoreData->configDir.toStdString());
|
||||
req.set_extra_no_out(result->extraCoreData->noLog);
|
||||
}
|
||||
//
|
||||
bool rpcOK;
|
||||
QString error = defaultClient->Start(&rpcOK, req);
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include "include/ui/profile/edit_wireguard.h"
|
||||
#include "include/ui/profile/edit_ssh.h"
|
||||
#include "include/ui/profile/edit_custom.h"
|
||||
#include "include/ui/profile/edit_extra_core.h"
|
||||
|
||||
#include "include/configs/proxy/includes.h"
|
||||
#include "include/configs/proxy/Preset.hpp"
|
||||
@ -162,6 +163,7 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
|
||||
LOAD_TYPE("ssh")
|
||||
ui->type->addItem(tr("Custom (%1 outbound)").arg(software_core_name), "internal");
|
||||
ui->type->addItem(tr("Custom (%1 config)").arg(software_core_name), "internal-full");
|
||||
ui->type->addItem(tr("Extra Core"), "extracore");
|
||||
LOAD_TYPE("chain")
|
||||
|
||||
// type changed
|
||||
@ -229,6 +231,13 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
||||
customType = newEnt ? type : ent->CustomBean()->core;
|
||||
if (customType != "custom") _innerWidget->preset_core = customType;
|
||||
type = "custom";
|
||||
ui->apply_to_group->hide();
|
||||
} else if (type == "extracore")
|
||||
{
|
||||
auto _innerWidget = new EditExtraCore(this);
|
||||
innerWidget = _innerWidget;
|
||||
innerEditor = _innerWidget;
|
||||
ui->apply_to_group->hide();
|
||||
} else {
|
||||
validType = false;
|
||||
}
|
||||
@ -244,7 +253,7 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
||||
}
|
||||
|
||||
// hide some widget
|
||||
auto showAddressPort = type != "chain" && customType != "internal" && customType != "internal-full";
|
||||
auto showAddressPort = type != "chain" && customType != "internal" && customType != "internal-full" && type != "extracore";
|
||||
ui->address->setVisible(showAddressPort);
|
||||
ui->address_l->setVisible(showAddressPort);
|
||||
ui->port->setVisible(showAddressPort);
|
||||
@ -296,6 +305,10 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
||||
show_custom_outbound = false;
|
||||
show_custom_config = false;
|
||||
}
|
||||
} else if (type == "extracore")
|
||||
{
|
||||
show_custom_outbound = false;
|
||||
show_custom_config = false;
|
||||
}
|
||||
ui->custom_box->setVisible(show_custom_outbound);
|
||||
ui->custom_global_box->setVisible(show_custom_config);
|
||||
@ -309,7 +322,7 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
||||
delete old;
|
||||
|
||||
// 左边 bean inner editor
|
||||
innerEditor->get_edit_dialog = [&]() { return (QWidget *) this; };
|
||||
innerEditor->get_edit_dialog = [&]() { return static_cast<QWidget*>(this); };
|
||||
innerEditor->get_edit_text_name = [&]() { return ui->name->text(); };
|
||||
innerEditor->get_edit_text_serverAddress = [&]() { return ui->address->text(); };
|
||||
innerEditor->get_edit_text_serverPort = [&]() { return ui->port->text(); };
|
||||
|
||||
@ -50,7 +50,7 @@ void EditChain::on_select_profile_clicked() {
|
||||
|
||||
void EditChain::AddProfileToListIfExist(int profileId) {
|
||||
auto _ent = NekoGui::profileManager->GetProfile(profileId);
|
||||
if (_ent != nullptr && _ent->type != "chain") {
|
||||
if (_ent != nullptr && _ent->type != "chain" && _ent->type != "extracore") {
|
||||
auto wI = new QListWidgetItem();
|
||||
wI->setData(114514, profileId);
|
||||
auto w = new ProxyItem(this, _ent, wI);
|
||||
@ -69,7 +69,7 @@ void EditChain::AddProfileToListIfExist(int profileId) {
|
||||
|
||||
void EditChain::ReplaceProfile(ProxyItem *w, int profileId) {
|
||||
auto _ent = NekoGui::profileManager->GetProfile(profileId);
|
||||
if (_ent != nullptr && _ent->type != "chain") {
|
||||
if (_ent != nullptr && _ent->type != "chain" && _ent->type != "extracore") {
|
||||
w->item->setData(114514, profileId);
|
||||
w->ent = _ent;
|
||||
w->refresh_data();
|
||||
|
||||
61
src/ui/profile/edit_extra_core.cpp
Normal file
61
src/ui/profile/edit_extra_core.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include "include/ui/profile/edit_extra_core.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "include/configs/proxy/ExtraCore.h"
|
||||
#include "include/configs/proxy/Preset.hpp"
|
||||
#include "include/ui/profile/dialog_edit_profile.h"
|
||||
|
||||
EditExtraCore::EditExtraCore(QWidget *parent) : QWidget(parent),
|
||||
ui(new Ui::EditExtraCore) {
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
EditExtraCore::~EditExtraCore() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void EditExtraCore::onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) {
|
||||
this->ent = _ent;
|
||||
|
||||
auto bean = ent->ExtraCoreBean();
|
||||
ui->socks_address->setText(bean->socksAddress);
|
||||
ui->socks_port->setValidator(new QIntValidator(1, 65534));
|
||||
ui->socks_port->setText(Int2String(bean->socksPort));
|
||||
ui->config->setPlainText(bean->extraCoreConf);
|
||||
ui->args->setText(bean->extraCoreArgs);
|
||||
ui->path_combo->addItems(NekoGui::profileManager->GetExtraCorePaths());
|
||||
ui->path_combo->setCurrentText(bean->extraCorePath);
|
||||
|
||||
connect(ui->path_button, &QPushButton::pressed, this, [=]
|
||||
{
|
||||
auto f = QFileDialog::getOpenFileName();
|
||||
if (f.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (NekoGui::profileManager->AddExtraCorePath(f)) ui->path_combo->addItem(f);
|
||||
ui->path_combo->setCurrentText(f);
|
||||
ui->path_combo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
adjustSize();
|
||||
});
|
||||
}
|
||||
|
||||
bool EditExtraCore::onEnd() {
|
||||
auto bean = ent->ExtraCoreBean();
|
||||
if (!ui->args->text().contains("%s"))
|
||||
{
|
||||
runOnUiThread([=]
|
||||
{
|
||||
MessageBoxWarning("Invalid args", "Args should have a %s as the placeholder for config file path.");
|
||||
});
|
||||
return false;
|
||||
}
|
||||
bean->socksAddress = ui->socks_address->text();
|
||||
bean->socksPort = ui->socks_port->text().toInt();
|
||||
bean->extraCoreConf = ui->config->toPlainText();
|
||||
bean->extraCorePath = ui->path_combo->currentText();
|
||||
bean->extraCoreArgs = ui->args->text();
|
||||
|
||||
return true;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user