├── .clang-format ├── .clang-tidy ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report-en.md │ ├── bug-report-zh_cn.md │ ├── feature_request-en.md │ └── feature_request-zh_cn.md └── workflows │ ├── build-nekoray-cmake.yml │ └── update-pkgbuild.yml ├── .gitignore ├── .gitmodules ├── 3rdparty ├── QThreadCreateThread.hpp ├── QtExtKeySequenceEdit.cpp ├── QtExtKeySequenceEdit.h ├── RunGuard.hpp ├── VT100Parser.hpp ├── WinCommander.cpp ├── WinCommander.hpp ├── ZxingQtReader.hpp ├── base64.cpp ├── base64.h ├── fix_old_qt.h ├── qrcodegen.cpp ├── qrcodegen.hpp ├── qscopeguard.h └── qv2ray │ ├── v2 │ ├── components │ │ └── proxy │ │ │ ├── QvProxyConfigurator.cpp │ │ │ └── QvProxyConfigurator.hpp │ └── ui │ │ ├── QvAutoCompleteTextEdit.cpp │ │ ├── QvAutoCompleteTextEdit.hpp │ │ └── widgets │ │ ├── common │ │ ├── QJsonModel.cpp │ │ └── QJsonModel.hpp │ │ └── editors │ │ ├── w_JsonEditor.cpp │ │ ├── w_JsonEditor.hpp │ │ └── w_JsonEditor.ui │ ├── v3 │ └── components │ │ └── GeositeReader │ │ ├── GeositeReader.cpp │ │ ├── GeositeReader.hpp │ │ ├── picoproto.cpp │ │ └── picoproto.hpp │ └── wrapper.hpp ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── linux │ └── linux.cmake ├── myproto.cmake ├── nkr.cmake ├── print.cmake └── windows │ ├── VersionInfo.in │ ├── VersionResource.rc │ ├── generate_product_version.cmake │ └── windows.cmake ├── db ├── ConfigBuilder.cpp ├── ConfigBuilder.hpp ├── Database.cpp ├── Database.hpp ├── Group.hpp ├── ProfileFilter.cpp ├── ProfileFilter.hpp ├── ProxyEntity.hpp └── traffic │ ├── TrafficData.hpp │ ├── TrafficLooper.cpp │ └── TrafficLooper.hpp ├── docs ├── Build_Core.md ├── Build_Linux.md ├── Build_Windows.md ├── RunFlags.md ├── Run_Linux.md └── readme.md ├── fmt ├── AbstractBean.cpp ├── AbstractBean.hpp ├── Bean2CoreObj_box.cpp ├── Bean2External.cpp ├── Bean2Link.cpp ├── ChainBean.hpp ├── CustomBean.hpp ├── Link2Bean.cpp ├── NaiveBean.hpp ├── Preset.hpp ├── QUICBean.hpp ├── ShadowSocksBean.hpp ├── SocksHttpBean.hpp ├── TrojanVLESSBean.hpp ├── V2RayStreamSettings.hpp ├── VMessBean.hpp └── includes.h ├── go ├── .gitignore ├── cmd │ ├── nekobox_core │ │ ├── core_box.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── grpc_box.go │ │ └── main.go │ └── updater │ │ ├── .gitignore │ │ ├── go.mod │ │ ├── go.sum │ │ ├── launcher.go │ │ ├── launcher_linux.go │ │ ├── main.go │ │ ├── msgbox.go │ │ ├── msgbox_windows.go │ │ └── updater.go └── grpc_server │ ├── auth │ └── auth.go │ ├── fulltest.go │ ├── gen │ ├── libcore.pb.go │ ├── libcore.proto │ ├── libcore_grpc.pb.go │ └── update_proto.sh │ ├── go.mod │ ├── go.sum │ ├── grpc.go │ └── update.go ├── libs ├── .gitignore ├── build_deps_all.sh ├── build_go.sh ├── build_public_res.sh ├── deploy_linux64.sh ├── deploy_windows64.sh ├── download_qtsdk_win.sh ├── env_deploy.sh ├── env_qtsdk.sh ├── format_cpp.sh ├── get_source.sh ├── get_source_env.sh ├── package_appimage.sh └── package_debian.sh ├── main ├── Const.hpp ├── GuiUtils.hpp ├── HTTPRequestHelper.cpp ├── HTTPRequestHelper.hpp ├── NekoGui.cpp ├── NekoGui.hpp ├── NekoGui_ConfigItem.hpp ├── NekoGui_DataStore.hpp ├── NekoGui_Utils.cpp ├── NekoGui_Utils.hpp └── main.cpp ├── nekoray_version.txt ├── res ├── dashboard-notice.html ├── icon │ ├── dialog-question.svg │ ├── internet-web-browser.svg │ ├── material │ │ ├── cancel.svg │ │ ├── delete.svg │ │ ├── history.svg │ │ ├── lock-open-outline.svg │ │ ├── lock-outline.svg │ │ ├── swap-horizontal.svg │ │ └── swap-vertical.svg │ ├── network-server.svg │ ├── preferences.svg │ ├── system-run.svg │ └── system-software-update.svg ├── neko.css ├── neko.qrc ├── nekobox.ico ├── public │ ├── nekobox.png │ └── qtbase_zh_CN.qm ├── theme │ └── feiyangqingyun │ │ ├── qss.qrc │ │ └── qss │ │ ├── blacksoft.css │ │ ├── blacksoft │ │ ├── add_bottom.png │ │ ├── add_left.png │ │ ├── add_right.png │ │ ├── add_top.png │ │ ├── arrow_bottom.png │ │ ├── arrow_left.png │ │ ├── arrow_right.png │ │ ├── arrow_top.png │ │ ├── branch_close.png │ │ ├── branch_open.png │ │ ├── calendar_nextmonth.png │ │ ├── calendar_prevmonth.png │ │ ├── checkbox_checked.png │ │ ├── checkbox_checked_disable.png │ │ ├── checkbox_parcial.png │ │ ├── checkbox_parcial_disable.png │ │ ├── checkbox_unchecked.png │ │ ├── checkbox_unchecked_disable.png │ │ ├── menu_checked.png │ │ ├── radiobutton_checked.png │ │ ├── radiobutton_checked_disable.png │ │ ├── radiobutton_unchecked.png │ │ └── radiobutton_unchecked_disable.png │ │ ├── flatgray.css │ │ ├── flatgray │ │ ├── add_bottom.png │ │ ├── add_left.png │ │ ├── add_right.png │ │ ├── add_top.png │ │ ├── arrow_bottom.png │ │ ├── arrow_left.png │ │ ├── arrow_right.png │ │ ├── arrow_top.png │ │ ├── branch_close.png │ │ ├── branch_open.png │ │ ├── calendar_nextmonth.png │ │ ├── calendar_prevmonth.png │ │ ├── checkbox_checked.png │ │ ├── checkbox_checked_disable.png │ │ ├── checkbox_parcial.png │ │ ├── checkbox_parcial_disable.png │ │ ├── checkbox_unchecked.png │ │ ├── checkbox_unchecked_disable.png │ │ ├── menu_checked.png │ │ ├── radiobutton_checked.png │ │ ├── radiobutton_checked_disable.png │ │ ├── radiobutton_unchecked.png │ │ └── radiobutton_unchecked_disable.png │ │ ├── lightblue.css │ │ └── lightblue │ │ ├── add_bottom.png │ │ ├── add_left.png │ │ ├── add_right.png │ │ ├── add_top.png │ │ ├── arrow_bottom.png │ │ ├── arrow_left.png │ │ ├── arrow_right.png │ │ ├── arrow_top.png │ │ ├── branch_close.png │ │ ├── branch_open.png │ │ ├── calendar_nextmonth.png │ │ ├── calendar_prevmonth.png │ │ ├── checkbox_checked.png │ │ ├── checkbox_checked_disable.png │ │ ├── checkbox_parcial.png │ │ ├── checkbox_parcial_disable.png │ │ ├── checkbox_unchecked.png │ │ ├── checkbox_unchecked_disable.png │ │ ├── menu_checked.png │ │ ├── radiobutton_checked.png │ │ ├── radiobutton_checked_disable.png │ │ ├── radiobutton_unchecked.png │ │ └── radiobutton_unchecked_disable.png └── vpn │ ├── sing-box-vpn.json │ └── vpn-run-root.sh ├── rpc ├── gRPC.cpp └── gRPC.h ├── sub ├── GroupUpdater.cpp └── GroupUpdater.hpp ├── sys ├── AutoRun.cpp ├── AutoRun.hpp ├── ExternalProcess.cpp ├── ExternalProcess.hpp ├── linux │ ├── LinuxCap.cpp │ └── LinuxCap.h └── windows │ ├── MiniDump.cpp │ ├── MiniDump.h │ ├── guihelper.cpp │ └── guihelper.h ├── test ├── test-qt512-sdk-build.sh └── test-qt6-build.sh ├── translations ├── fa_IR.ts ├── ru_RU.ts ├── translations.qrc └── zh_CN.ts └── ui ├── GroupSort.hpp ├── Icon.cpp ├── Icon.hpp ├── ThemeManager.cpp ├── ThemeManager.hpp ├── dialog_basic_settings.cpp ├── dialog_basic_settings.h ├── dialog_basic_settings.ui ├── dialog_hotkey.cpp ├── dialog_hotkey.h ├── dialog_hotkey.ui ├── dialog_manage_groups.cpp ├── dialog_manage_groups.h ├── dialog_manage_groups.ui ├── dialog_manage_routes.cpp ├── dialog_manage_routes.h ├── dialog_manage_routes.ui ├── dialog_vpn_settings.cpp ├── dialog_vpn_settings.h ├── dialog_vpn_settings.ui ├── edit ├── dialog_edit_group.cpp ├── dialog_edit_group.h ├── dialog_edit_group.ui ├── dialog_edit_profile.cpp ├── dialog_edit_profile.h ├── dialog_edit_profile.ui ├── edit_chain.cpp ├── edit_chain.h ├── edit_chain.ui ├── edit_custom.cpp ├── edit_custom.h ├── edit_custom.ui ├── edit_naive.cpp ├── edit_naive.h ├── edit_naive.ui ├── edit_quic.cpp ├── edit_quic.h ├── edit_quic.ui ├── edit_shadowsocks.cpp ├── edit_shadowsocks.h ├── edit_shadowsocks.ui ├── edit_socks_http.cpp ├── edit_socks_http.h ├── edit_socks_http.ui ├── edit_trojan_vless.cpp ├── edit_trojan_vless.h ├── edit_trojan_vless.ui ├── edit_vmess.cpp ├── edit_vmess.h ├── edit_vmess.ui └── profile_editor.h ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── mainwindow_grpc.cpp ├── mainwindow_interface.h └── widget ├── FloatCheckBox.h ├── GroupItem.cpp ├── GroupItem.h ├── GroupItem.ui ├── MessageBoxTimer.h ├── MyLineEdit.h ├── MyTableWidget.h ├── ProxyItem.cpp ├── ProxyItem.h └── ProxyItem.ui /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | ColumnLimit: 0 3 | IndentWidth: 4 4 | SortIncludes: Never 5 | SpacesBeforeTrailingComments: 1 6 | NamespaceIndentation: All 7 | AccessModifierOffset: -4 8 | SpaceAfterCStyleCast: true 9 | SpaceAfterTemplateKeyword: false 10 | SpaceBeforeRangeBasedForLoopColon: false 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report-en.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Bug Report' 3 | about: 'Please troubleshoot server-side issues and upgrade to the latest client before raising a question.' 4 | title: 'BUG: ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the problem 11 | 12 | Expected behavior: 13 | 14 | Actual behavior: 15 | 16 | ## How to reproduce 17 | 18 | Provide helpful screenshots, videos, text descriptions, subscription links, etc. 19 | 20 | ## log 21 | 22 | If you have logs, please upload them. Please see the detailed steps for exporting logs in the documentation. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report-zh_cn.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '问题反馈' 3 | about: '在提出问题前请先自行排除服务器端问题和升级到最新客户端。' 4 | title: 'BUG: ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 描述问题 11 | 12 | 预期行为: 13 | 14 | 实际行为: 15 | 16 | ## 如何复现 17 | 18 | 提供有帮助的截图,录像,文字说明,订阅链接等。 19 | 20 | ## 日志 21 | 22 | 如果有日志,请上传。请在文档内查看导出日志的详细步骤。 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request-en.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Feature Request' 3 | about: 'Make suggestions for new features of the software' 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Description suggestions 11 | 12 | ## Necessity of recommendations 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request-zh_cn.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '功能请求' 3 | about: '对软件的新功能提出建议。' 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 描述建议 11 | 12 | ## 建议的必要性 13 | -------------------------------------------------------------------------------- /.github/workflows/update-pkgbuild.yml: -------------------------------------------------------------------------------- 1 | name: AUR CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths-ignore: 7 | - '**.md' 8 | - 'LICENSE' 9 | - '!.github/workflows/**' 10 | 11 | jobs: 12 | update: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: chitang233/aur-pkgbuild-builder@main 16 | with: 17 | deploy_key: ${{ secrets.DEPLOY_KEY }} 18 | package_name: 'nekoray-git' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | *~ 5 | *.autosave 6 | *.a 7 | *.core 8 | *.moc 9 | *.o 10 | *.obj 11 | *.orig 12 | *.rej 13 | *.so 14 | *.so.* 15 | *_pch.h.cpp 16 | *_resource.rc 17 | .#* 18 | *.*# 19 | core 20 | !core/ 21 | tags 22 | .DS_Store 23 | .directory 24 | *.debug 25 | /Makefile* 26 | *.prl 27 | *.app 28 | moc_*.cpp 29 | ui_*.h 30 | qrc_*.cpp 31 | Thumbs.db 32 | *.res 33 | /.qmake.cache 34 | /.qmake.stash 35 | 36 | # qtcreator generated files 37 | *.pro.user* 38 | 39 | # xemacs temporary files 40 | *.flc 41 | 42 | # Vim temporary files 43 | .*.swp 44 | 45 | # Visual Studio generated files 46 | *.ib_pdb_index 47 | *.idb 48 | *.ilk 49 | *.pdb 50 | *.sln 51 | *.suo 52 | *.vcproj 53 | *vcproj.*.*.user 54 | *.ncb 55 | *.sdf 56 | *.opensdf 57 | *.vcxproj 58 | *vcxproj.* 59 | 60 | # MinGW generated files 61 | *.Debug 62 | *.Release 63 | 64 | # Python byte code 65 | *.pyc 66 | 67 | # Binaries 68 | # -------- 69 | *.dll 70 | *.exe 71 | 72 | # Custom 73 | /nekoray 74 | /build 75 | CMakeLists.txt.user* 76 | /cmake-build-* 77 | /build-* 78 | .vscode 79 | .idea 80 | 81 | # Deploy 82 | /deployment 83 | /neko*.sh 84 | /qtsdk 85 | 86 | .vs 87 | out -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/QHotkey"] 2 | path = 3rdparty/QHotkey 3 | url = https://github.com/Skycoder42/QHotkey.git 4 | -------------------------------------------------------------------------------- /3rdparty/QThreadCreateThread.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // FOR OLD QT 7 | 8 | class QThreadCreateThread : public QThread { 9 | public: 10 | explicit QThreadCreateThread(std::future &&future) 11 | : m_future(std::move(future)) { 12 | // deleteLater 13 | connect(this, &QThread::finished, this, &QThread::deleteLater); 14 | } 15 | 16 | private: 17 | void run() override { 18 | m_future.get(); 19 | } 20 | 21 | std::future m_future; 22 | }; 23 | 24 | inline QThread *createThreadImpl(std::future &&future) { 25 | return new QThreadCreateThread(std::move(future)); 26 | } 27 | 28 | template 29 | QThread *createQThread(Function &&f, Args &&... args) { 30 | using DecayedFunction = typename std::decay::type; 31 | auto threadFunction = 32 | [f = static_cast(std::forward(f))](auto &&... largs) mutable -> void { 33 | (void) std::invoke(std::move(f), std::forward(largs)...); 34 | }; 35 | 36 | return createThreadImpl(std::async(std::launch::deferred, 37 | std::move(threadFunction), 38 | std::forward(args)...)); 39 | } 40 | -------------------------------------------------------------------------------- /3rdparty/QtExtKeySequenceEdit.cpp: -------------------------------------------------------------------------------- 1 | #include "QtExtKeySequenceEdit.h" 2 | 3 | QtExtKeySequenceEdit::QtExtKeySequenceEdit(QWidget *parent) 4 | : QKeySequenceEdit(parent) { 5 | } 6 | 7 | QtExtKeySequenceEdit::~QtExtKeySequenceEdit() { 8 | } 9 | 10 | void QtExtKeySequenceEdit::keyPressEvent(QKeyEvent *pEvent) { 11 | QKeySequenceEdit::keyPressEvent(pEvent); 12 | 13 | QKeySequence keySeq = keySequence(); 14 | if (keySeq.count() <= 0) { 15 | return; 16 | } 17 | int key = keySeq[0]; 18 | if (key == Qt::Key_Backspace || key == Qt::Key_Delete) { 19 | key = 0; 20 | } 21 | setKeySequence(key); 22 | } 23 | -------------------------------------------------------------------------------- /3rdparty/QtExtKeySequenceEdit.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class QtExtKeySequenceEdit : public QKeySequenceEdit { 4 | public: 5 | QtExtKeySequenceEdit(QWidget *parent); 6 | 7 | ~QtExtKeySequenceEdit(); 8 | 9 | protected: 10 | virtual void keyPressEvent(QKeyEvent *pEvent); 11 | }; 12 | -------------------------------------------------------------------------------- /3rdparty/RunGuard.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RUNGUARD_H 2 | #define RUNGUARD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class RunGuard { 10 | public: 11 | RunGuard(const QString &key); 12 | 13 | ~RunGuard(); 14 | 15 | bool isAnotherRunning(quint64 *data_out); 16 | 17 | bool tryToRun(quint64 *data_in); 18 | 19 | void release(); 20 | 21 | private: 22 | const QString key; 23 | const QString memLockKey; 24 | const QString sharedmemKey; 25 | 26 | QSharedMemory sharedMem; 27 | QSystemSemaphore memLock; 28 | 29 | Q_DISABLE_COPY(RunGuard) 30 | }; 31 | 32 | namespace { 33 | 34 | QString generateKeyHash(const QString &key, const QString &salt) { 35 | QByteArray data; 36 | 37 | data.append(key.toUtf8()); 38 | data.append(salt.toUtf8()); 39 | data = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex(); 40 | 41 | return data; 42 | } 43 | 44 | } // namespace 45 | 46 | RunGuard::RunGuard(const QString &key) 47 | : key(key), memLockKey(generateKeyHash(key, "_memLockKey")), sharedmemKey(generateKeyHash(key, "_sharedmemKey")), sharedMem(sharedmemKey), memLock(memLockKey, 1) { 48 | memLock.acquire(); 49 | { 50 | QSharedMemory fix(sharedmemKey); // Fix for *nix: http://habrahabr.ru/post/173281/ 51 | fix.attach(); 52 | } 53 | memLock.release(); 54 | } 55 | 56 | RunGuard::~RunGuard() { 57 | release(); 58 | } 59 | 60 | bool RunGuard::isAnotherRunning(quint64 *data_out) { 61 | if (sharedMem.isAttached()) 62 | return false; 63 | 64 | memLock.acquire(); 65 | const bool isRunning = sharedMem.attach(); 66 | if (isRunning) { 67 | if (data_out != nullptr) { 68 | memcpy(data_out, sharedMem.data(), sizeof(quint64)); 69 | } 70 | sharedMem.detach(); 71 | } 72 | memLock.release(); 73 | 74 | return isRunning; 75 | } 76 | 77 | bool RunGuard::tryToRun(quint64 *data_in) { 78 | memLock.acquire(); 79 | const bool result = sharedMem.create(sizeof(quint64)); 80 | if (result) memcpy(sharedMem.data(), data_in, sizeof(quint64)); 81 | memLock.release(); 82 | 83 | if (!result) { 84 | release(); 85 | return false; 86 | } 87 | 88 | return true; 89 | } 90 | 91 | void RunGuard::release() { 92 | memLock.acquire(); 93 | if (sharedMem.isAttached()) 94 | sharedMem.detach(); 95 | memLock.release(); 96 | } 97 | 98 | #endif // RUNGUARD_H 99 | -------------------------------------------------------------------------------- /3rdparty/VT100Parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | inline QString cleanVT100String(const QString &in) { 6 | QString out; 7 | bool in_033 = false; 8 | for (auto &&chr: in) { 9 | if (chr == '\033') { 10 | in_033 = true; 11 | continue; 12 | } 13 | if (in_033) { 14 | if (chr == 'm') { 15 | in_033 = false; 16 | } 17 | continue; 18 | } 19 | out += chr; 20 | } 21 | return out; 22 | } 23 | -------------------------------------------------------------------------------- /3rdparty/WinCommander.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2014 UpdateNode UG (haftungsbeschränkt) 4 | ** Contact: code@updatenode.com 5 | ** 6 | ** This file is part of the UpdateNode Client. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial UpdateNode license may use this file 10 | ** under the terms of the the Apache License, Version 2.0 11 | ** Full license description file: LICENSE.COM 12 | ** 13 | ** GNU General Public License Usage 14 | ** Alternatively, this file may be used under the terms of the GNU 15 | ** General Public License version 3.0 as published by the Free Software 16 | ** Foundation. Please review the following information to ensure the 17 | ** GNU General Public License version 3.0 requirements will be met: 18 | ** http://www.gnu.org/copyleft/gpl.html. 19 | ** Full license description file: LICENSE.GPL 20 | ** 21 | ****************************************************************************/ 22 | 23 | #include "WinCommander.hpp" 24 | 25 | #include 26 | #include 27 | 28 | #ifdef Q_OS_WIN 29 | #ifndef WIN32_LEAN_AND_MEAN 30 | #define WIN32_LEAN_AND_MEAN 31 | #endif 32 | #include 33 | #include 34 | #include 35 | #define MAX_KEY_LENGTH 255 36 | #define MAX_VALUE_NAME 16383 37 | #endif 38 | 39 | 40 | /*! 41 | Executes a command elevated specified by \apath , using paramters \aparameters. 42 | \n 43 | Parameter /aaWait decides if the function should return immediatelly after it's\n 44 | execution or wait for the exit of the launched process 45 | \n 46 | Returns the return value of the executed command 47 | */ 48 | uint WinCommander::runProcessElevated(const QString &path, 49 | const QStringList ¶meters, 50 | const QString &workingDir, 51 | int nShow, bool aWait) { 52 | uint result = 0; 53 | 54 | #ifdef Q_OS_WIN 55 | QString params; 56 | HWND hwnd = NULL; 57 | LPCTSTR pszPath = (LPCTSTR)path.utf16(); 58 | foreach(QString item, parameters) 59 | params += "\"" + item + "\" "; 60 | 61 | LPCTSTR pszParameters = (LPCTSTR)params.utf16(); 62 | QString dir; 63 | if (workingDir.count() == 0) 64 | dir = QDir::toNativeSeparators(QDir::currentPath()); 65 | else 66 | dir = QDir::toNativeSeparators(workingDir); 67 | LPCTSTR pszDirectory = (LPCTSTR)dir.utf16(); 68 | 69 | SHELLEXECUTEINFO shex; 70 | DWORD dwCode = 0; 71 | 72 | ZeroMemory(&shex, sizeof(shex)); 73 | 74 | shex.cbSize = sizeof(shex); 75 | shex.fMask = SEE_MASK_NOCLOSEPROCESS; 76 | shex.hwnd = hwnd; 77 | shex.lpVerb = TEXT("runas"); 78 | shex.lpFile = pszPath; 79 | shex.lpParameters = pszParameters; 80 | shex.lpDirectory = pszDirectory; 81 | // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow 82 | shex.nShow = nShow; 83 | 84 | ShellExecuteEx(&shex); 85 | if (shex.hProcess) 86 | { 87 | if(aWait) 88 | { 89 | WaitForSingleObject(shex.hProcess, INFINITE ); 90 | GetExitCodeProcess(shex.hProcess, &dwCode); 91 | } 92 | CloseHandle (shex.hProcess) ; 93 | } 94 | else 95 | return -1; 96 | 97 | result = (uint)dwCode; 98 | #else 99 | Q_UNUSED(path); 100 | Q_UNUSED(parameters); 101 | Q_UNUSED(workingDir); 102 | Q_UNUSED(aWait); 103 | #endif 104 | return result; 105 | } 106 | -------------------------------------------------------------------------------- /3rdparty/WinCommander.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2014 UpdateNode UG (haftungsbeschränkt) 4 | ** Contact: code@updatenode.com 5 | ** 6 | ** This file is part of the UpdateNode Client. 7 | ** 8 | ** Commercial License Usage 9 | ** Licensees holding valid commercial UpdateNode license may use this file 10 | ** under the terms of the the Apache License, Version 2.0 11 | ** Full license description file: LICENSE.COM 12 | ** 13 | ** GNU General Public License Usage 14 | ** Alternatively, this file may be used under the terms of the GNU 15 | ** General Public License version 3.0 as published by the Free Software 16 | ** Foundation. Please review the following information to ensure the 17 | ** GNU General Public License version 3.0 requirements will be met: 18 | ** http://www.gnu.org/copyleft/gpl.html. 19 | ** Full license description file: LICENSE.GPL 20 | ** 21 | ****************************************************************************/ 22 | 23 | #ifndef WINCOMMANDER_H 24 | #define WINCOMMANDER_H 25 | 26 | #include 27 | #include 28 | 29 | class WinCommander { 30 | public: 31 | static const int SW_HIDE = 0; 32 | static const int SW_NORMAL = 1; 33 | static const int SW_SHOWMINIMIZED = 2; 34 | 35 | static uint runProcessElevated(const QString &path, 36 | const QStringList ¶meters = QStringList(), 37 | const QString &workingDir = QString(), 38 | int nShow = SW_SHOWMINIMIZED, bool aWait = true); 39 | }; 40 | 41 | #endif // WINCOMMANDER_H -------------------------------------------------------------------------------- /3rdparty/base64.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Qt515Base64 { 4 | enum Base64Option { 5 | Base64Encoding = 0, 6 | Base64UrlEncoding = 1, 7 | 8 | KeepTrailingEquals = 0, 9 | OmitTrailingEquals = 2, 10 | 11 | IgnoreBase64DecodingErrors = 0, 12 | AbortOnBase64DecodingErrors = 4, 13 | }; 14 | Q_DECLARE_FLAGS(Base64Options, Base64Option) 15 | Q_DECLARE_OPERATORS_FOR_FLAGS(Base64Options) 16 | 17 | enum class Base64DecodingStatus { 18 | Ok, 19 | IllegalInputLength, 20 | IllegalCharacter, 21 | IllegalPadding, 22 | }; 23 | 24 | class FromBase64Result { 25 | public: 26 | QByteArray decoded; 27 | Base64DecodingStatus decodingStatus; 28 | 29 | void swap(FromBase64Result &other) noexcept { 30 | qSwap(decoded, other.decoded); 31 | qSwap(decodingStatus, other.decodingStatus); 32 | } 33 | 34 | explicit operator bool() const noexcept { return decodingStatus == Base64DecodingStatus::Ok; } 35 | 36 | #if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(Q_QDOC) 37 | QByteArray &operator*() &noexcept { return decoded; } 38 | const QByteArray &operator*() const &noexcept { return decoded; } 39 | QByteArray &&operator*() &&noexcept { return std::move(decoded); } 40 | #else 41 | QByteArray &operator*() noexcept { return decoded; } 42 | const QByteArray &operator*() const noexcept { return decoded; } 43 | #endif 44 | }; 45 | 46 | FromBase64Result QByteArray_fromBase64Encoding(const QByteArray &base64, Base64Options options); 47 | } // namespace Qt515Base64 48 | -------------------------------------------------------------------------------- /3rdparty/fix_old_qt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) 6 | 7 | inline QString qEnvironmentVariable(const char *varName) { 8 | return qgetenv(varName); 9 | } 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /3rdparty/qscopeguard.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sérgio Martins 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the QtCore module of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:LGPL$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** GNU Lesser General Public License Usage 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser 19 | ** General Public License version 3 as published by the Free Software 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the 21 | ** packaging of this file. Please review the following information to 22 | ** ensure the GNU Lesser General Public License version 3 requirements 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24 | ** 25 | ** GNU General Public License Usage 26 | ** Alternatively, this file may be used under the terms of the GNU 27 | ** General Public License version 2.0 or (at your option) the GNU General 28 | ** Public license version 3 or any later version approved by the KDE Free 29 | ** Qt Foundation. The licenses are as published by the Free Software 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31 | ** included in the packaging of this file. Please review the following 32 | ** information to ensure the GNU General Public License requirements will 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. 35 | ** 36 | ** $QT_END_LICENSE$ 37 | ** 38 | ****************************************************************************/ 39 | 40 | #ifndef QSCOPEGUARD_H 41 | #define QSCOPEGUARD_H 42 | 43 | #include 44 | 45 | 46 | QT_BEGIN_NAMESPACE 47 | 48 | 49 | template class QScopeGuard; 50 | template QScopeGuard qScopeGuard(F f); 51 | 52 | template 53 | class QScopeGuard 54 | { 55 | public: 56 | QScopeGuard(QScopeGuard &&other) Q_DECL_NOEXCEPT 57 | : m_func(std::move(other.m_func)) 58 | , m_invoke(other.m_invoke) 59 | { 60 | other.dismiss(); 61 | } 62 | 63 | ~QScopeGuard() 64 | { 65 | if (m_invoke) 66 | m_func(); 67 | } 68 | 69 | void dismiss() Q_DECL_NOEXCEPT 70 | { 71 | m_invoke = false; 72 | } 73 | 74 | private: 75 | explicit QScopeGuard(F f) Q_DECL_NOEXCEPT 76 | : m_func(std::move(f)) 77 | { 78 | } 79 | 80 | Q_DISABLE_COPY(QScopeGuard) 81 | 82 | F m_func; 83 | bool m_invoke = true; 84 | friend QScopeGuard qScopeGuard(F); 85 | }; 86 | 87 | 88 | template 89 | QScopeGuard qScopeGuard(F f) 90 | { 91 | return QScopeGuard(std::move(f)); 92 | } 93 | 94 | QT_END_NAMESPACE 95 | 96 | #endif // QSCOPEGUARD_H 97 | -------------------------------------------------------------------------------- /3rdparty/qv2ray/v2/components/proxy/QvProxyConfigurator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | // 6 | namespace Qv2ray::components::proxy { 7 | void ClearSystemProxy(); 8 | void SetSystemProxy(int http_port, int socks_port); 9 | } // namespace Qv2ray::components::proxy 10 | 11 | using namespace Qv2ray::components; 12 | using namespace Qv2ray::components::proxy; 13 | -------------------------------------------------------------------------------- /3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "w_JsonEditor.hpp" 2 | 3 | #include "main/NekoGui.hpp" 4 | 5 | JsonEditor::JsonEditor(const QJsonObject& rootObject, QWidget* parent) : QDialog(parent) { 6 | setupUi(this); 7 | // QvMessageBusConnect(JsonEditor); 8 | // 9 | original = rootObject; 10 | final = rootObject; 11 | QString jsonString = JsonToString(rootObject); 12 | 13 | if (VerifyJsonString(jsonString).isEmpty()) { 14 | jsonTree->setModel(&model); 15 | model.loadJson(QJsonDocument(rootObject).toJson()); 16 | } else { 17 | QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), 18 | tr("Original Json may contain syntax errors. Json tree is disabled.")); 19 | } 20 | 21 | jsonEditor->setText(JsonToString(rootObject)); 22 | jsonTree->expandAll(); 23 | jsonTree->resizeColumnToContents(0); 24 | } 25 | 26 | // QvMessageBusSlotImpl(JsonEditor) 27 | // { 28 | // switch (msg) 29 | // { 30 | // MBShowDefaultImpl; 31 | // MBHideDefaultImpl; 32 | // MBRetranslateDefaultImpl; 33 | // case UPDATE_COLORSCHEME: 34 | // break; 35 | // } 36 | // } 37 | 38 | QJsonObject JsonEditor::OpenEditor() { 39 | int resultCode = this->exec(); 40 | auto string = jsonEditor->toPlainText(); 41 | 42 | while (resultCode == QDialog::Accepted && !VerifyJsonString(string).isEmpty()) { 43 | if (string.isEmpty()) { 44 | resultCode = QDialog::Accepted; 45 | final = {}; 46 | break; 47 | } 48 | QvMessageBoxWarn(this, tr("Json Contains Syntax Errors"), 49 | tr("You must correct these errors before continuing.")); 50 | resultCode = this->exec(); 51 | string = jsonEditor->toPlainText(); 52 | } 53 | 54 | return resultCode == QDialog::Accepted ? final : original; 55 | } 56 | 57 | JsonEditor::~JsonEditor() { 58 | } 59 | 60 | void JsonEditor::on_jsonEditor_textChanged() { 61 | auto string = jsonEditor->toPlainText(); 62 | auto VerifyResult = VerifyJsonString(string); 63 | jsonValidateStatus->setText(VerifyResult); 64 | 65 | if (VerifyResult.isEmpty()) { 66 | BLACK(jsonEditor); 67 | final = JsonFromString(string); 68 | model.loadJson(QJsonDocument(final).toJson()); 69 | jsonTree->expandAll(); 70 | jsonTree->resizeColumnToContents(0); 71 | } else { 72 | RED(jsonEditor); 73 | } 74 | } 75 | 76 | void JsonEditor::on_formatJsonBtn_clicked() { 77 | auto string = jsonEditor->toPlainText(); 78 | auto VerifyResult = VerifyJsonString(string); 79 | jsonValidateStatus->setText(VerifyResult); 80 | 81 | if (VerifyResult.isEmpty()) { 82 | BLACK(jsonEditor); 83 | jsonEditor->setPlainText(JsonToString(JsonFromString(string))); 84 | model.loadJson(QJsonDocument(JsonFromString(string)).toJson()); 85 | jsonTree->setModel(&model); 86 | jsonTree->expandAll(); 87 | jsonTree->resizeColumnToContents(0); 88 | } else { 89 | RED(jsonEditor); 90 | QvMessageBoxWarn(this, tr("Syntax Errors"), 91 | tr("Please fix the JSON errors or remove the comments before continue")); 92 | } 93 | } 94 | 95 | void JsonEditor::on_removeCommentsBtn_clicked() { 96 | jsonEditor->setPlainText(JsonToString(JsonFromString(jsonEditor->toPlainText()))); 97 | } 98 | -------------------------------------------------------------------------------- /3rdparty/qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "3rdparty/qv2ray/wrapper.hpp" 4 | #include "3rdparty/qv2ray/v2/ui/widgets/common/QJsonModel.hpp" 5 | #include "ui_w_JsonEditor.h" 6 | 7 | #include 8 | 9 | class JsonEditor 10 | : public QDialog, 11 | private Ui::JsonEditor { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit JsonEditor(const QJsonObject& rootObject, QWidget* parent = nullptr); 16 | ~JsonEditor(); 17 | QJsonObject OpenEditor(); 18 | 19 | private slots: 20 | void on_jsonEditor_textChanged(); 21 | 22 | void on_formatJsonBtn_clicked(); 23 | 24 | void on_removeCommentsBtn_clicked(); 25 | 26 | private: 27 | QJsonModel model; 28 | QJsonObject original; 29 | QJsonObject final; 30 | }; 31 | -------------------------------------------------------------------------------- /3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.cpp: -------------------------------------------------------------------------------- 1 | #include "GeositeReader.hpp" 2 | 3 | #include "3rdparty/qv2ray/wrapper.hpp" 4 | #include "picoproto.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace Qv2ray::components::GeositeReader { 10 | QMap GeositeEntries; 11 | 12 | QStringList ReadGeoSiteFromFile(const QString &filepath, bool allowCache) { 13 | if (GeositeEntries.contains(filepath) && allowCache) 14 | return GeositeEntries.value(filepath); 15 | 16 | QStringList list; 17 | qInfo() << "Reading geosites from:" << filepath; 18 | QFile f(filepath); 19 | bool opened = f.open(QFile::OpenModeFlag::ReadOnly); 20 | 21 | if (!opened) { 22 | qInfo() << "File cannot be opened:" << filepath; 23 | return list; 24 | } 25 | 26 | const auto content = f.readAll(); 27 | f.close(); 28 | { 29 | picoproto::Message root; 30 | root.ParseFromBytes((unsigned char *) content.data(), content.size()); 31 | 32 | list.reserve(root.GetMessageArray(1).size()); 33 | for (const auto &geosite: root.GetMessageArray(1)) 34 | list << QString::fromStdString(geosite->GetString(1)); 35 | } 36 | 37 | qInfo() << "Loaded" << list.count() << "geosite entries from data file."; 38 | list.sort(); 39 | GeositeEntries[filepath] = list; 40 | return list; 41 | } 42 | } // namespace Qv2ray::components::GeositeReader 43 | -------------------------------------------------------------------------------- /3rdparty/qv2ray/v3/components/GeositeReader/GeositeReader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Qv2ray::components::GeositeReader { 6 | QStringList ReadGeoSiteFromFile(const QString &filepath, bool allowCache = true); 7 | } // namespace Qv2ray::components::GeositeReader 8 | -------------------------------------------------------------------------------- /3rdparty/qv2ray/wrapper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Qv2ray wrapper 4 | 5 | #include 6 | #include 7 | 8 | #define LOG(...) Qv2ray::base::log_internal(__VA_ARGS__) 9 | #define DEBUG(...) Qv2ray::base::log_internal(__VA_ARGS__) 10 | namespace Qv2ray { 11 | namespace base { 12 | template 13 | inline void log_internal(T... v) {} 14 | } // namespace base 15 | } // namespace Qv2ray 16 | 17 | #define JsonToString(a) QJsonObject2QString(a, false) 18 | #define JsonFromString(a) QString2QJsonObject(a) 19 | #define QvMessageBoxWarn(a, b, c) MessageBoxWarning(b, c) 20 | 21 | inline QString VerifyJsonString(const QString &source) { 22 | QJsonParseError error{}; 23 | QJsonDocument doc = QJsonDocument::fromJson(source.toUtf8(), &error); 24 | Q_UNUSED(doc) 25 | 26 | if (error.error == QJsonParseError::NoError) { 27 | return ""; 28 | } else { 29 | // LOG("WARNING: Json parse returns: " + error.errorString()); 30 | return error.errorString(); 31 | } 32 | } 33 | 34 | #define RED(obj) \ 35 | { \ 36 | auto _temp = obj->palette(); \ 37 | _temp.setColor(QPalette::Text, Qt::red); \ 38 | obj->setPalette(_temp); \ 39 | } 40 | 41 | #define BLACK(obj) obj->setPalette(QWidget::palette()); 42 | -------------------------------------------------------------------------------- /cmake/linux/linux.cmake: -------------------------------------------------------------------------------- 1 | set(PLATFORM_SOURCES sys/linux/LinuxCap.cpp) 2 | set(PLATFORM_LIBRARIES dl) 3 | -------------------------------------------------------------------------------- /cmake/myproto.cmake: -------------------------------------------------------------------------------- 1 | find_package(Protobuf CONFIG REQUIRED) 2 | 3 | set(PROTO_FILES 4 | go/grpc_server/gen/libcore.proto 5 | ) 6 | 7 | add_library(myproto STATIC ${PROTO_FILES}) 8 | target_link_libraries(myproto 9 | PUBLIC 10 | protobuf::libprotobuf 11 | ) 12 | target_include_directories(myproto PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) 13 | 14 | protobuf_generate(TARGET myproto LANGUAGE cpp) 15 | -------------------------------------------------------------------------------- /cmake/nkr.cmake: -------------------------------------------------------------------------------- 1 | # Release 2 | file(STRINGS nekoray_version.txt NKR_VERSION) 3 | add_compile_definitions(NKR_VERSION=\"${NKR_VERSION}\") 4 | 5 | # Debug 6 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DNKR_CPP_DEBUG") 7 | 8 | # Func 9 | function(nkr_add_compile_definitions arg) 10 | message("[add_compile_definitions] ${ARGV}") 11 | add_compile_definitions(${ARGV}) 12 | endfunction() 13 | -------------------------------------------------------------------------------- /cmake/print.cmake: -------------------------------------------------------------------------------- 1 | macro(print_all_variables) 2 | message(STATUS "print_all_variables------------------------------------------{") 3 | get_cmake_property(_variableNames VARIABLES) 4 | foreach (_variableName ${_variableNames}) 5 | message(STATUS "${_variableName}=${${_variableName}}") 6 | endforeach() 7 | message(STATUS "print_all_variables------------------------------------------}") 8 | endmacro() 9 | 10 | # Get all propreties that cmake supports 11 | if(NOT CMAKE_PROPERTY_LIST) 12 | execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST) 13 | 14 | # Convert command output into a CMake list 15 | string(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") 16 | string(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") 17 | endif() 18 | 19 | function(print_properties) 20 | message("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}") 21 | endfunction() 22 | 23 | function(print_target_properties target) 24 | if(NOT TARGET ${target}) 25 | message(STATUS "There is no target named '${target}'") 26 | return() 27 | endif() 28 | 29 | foreach(property ${CMAKE_PROPERTY_LIST}) 30 | string(REPLACE "" "${CMAKE_BUILD_TYPE}" property ${property}) 31 | 32 | # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i 33 | if(property STREQUAL "LOCATION" OR property MATCHES "^LOCATION_" OR property MATCHES "_LOCATION$") 34 | continue() 35 | endif() 36 | 37 | get_property(was_set TARGET ${target} PROPERTY ${property} SET) 38 | if(was_set) 39 | get_target_property(value ${target} ${property}) 40 | message("${target} ${property} = ${value}") 41 | endif() 42 | endforeach() 43 | endfunction() 44 | -------------------------------------------------------------------------------- /cmake/windows/VersionInfo.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef PRODUCT_VERSION_MAJOR 4 | #define PRODUCT_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ 5 | #endif 6 | 7 | #ifndef PRODUCT_VERSION_MINOR 8 | #define PRODUCT_VERSION_MINOR @PRODUCT_VERSION_MINOR@ 9 | #endif 10 | 11 | #ifndef PRODUCT_VERSION_PATCH 12 | #define PRODUCT_VERSION_PATCH @PRODUCT_VERSION_PATCH@ 13 | #endif 14 | 15 | #ifndef PRODUCT_VERSION_BUILD 16 | #define PRODUCT_VERSION_BUILD @PRODUCT_VERSION_REVISION@ 17 | #endif 18 | 19 | #ifndef FILE_VERSION_MAJOR 20 | #define FILE_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ 21 | #endif 22 | 23 | #ifndef FILE_VERSION_MINOR 24 | #define FILE_VERSION_MINOR @PRODUCT_VERSION_MINOR@ 25 | #endif 26 | 27 | #ifndef FILE_VERSION_PATCH 28 | #define FILE_VERSION_PATCH @PRODUCT_VERSION_PATCH@ 29 | #endif 30 | 31 | #ifndef FILE_VERSION_BUILD 32 | #define FILE_VERSION_BUILD @PRODUCT_VERSION_REVISION@ 33 | #endif 34 | 35 | #ifndef __TO_STRING 36 | #define __TO_STRING_IMPL(x) #x 37 | #define __TO_STRING(x) __TO_STRING_IMPL(x) 38 | #endif 39 | 40 | #define PRODUCT_VERSION_MAJOR_MINOR_STR __TO_STRING(PRODUCT_VERSION_MAJOR) "." __TO_STRING(PRODUCT_VERSION_MINOR) 41 | #define PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR PRODUCT_VERSION_MAJOR_MINOR_STR "." __TO_STRING(PRODUCT_VERSION_PATCH) 42 | #define PRODUCT_VERSION_FULL_STR PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(PRODUCT_VERSION_BUILD) 43 | #define PRODUCT_VERSION_RESOURCE PRODUCT_VERSION_MAJOR,PRODUCT_VERSION_MINOR,PRODUCT_VERSION_PATCH,PRODUCT_VERSION_BUILD 44 | #define PRODUCT_VERSION_RESOURCE_STR PRODUCT_VERSION_FULL_STR "\0" 45 | 46 | #define FILE_VERSION_MAJOR_MINOR_STR __TO_STRING(FILE_VERSION_MAJOR) "." __TO_STRING(FILE_VERSION_MINOR) 47 | #define FILE_VERSION_MAJOR_MINOR_PATCH_STR FILE_VERSION_MAJOR_MINOR_STR "." __TO_STRING(FILE_VERSION_PATCH) 48 | #define FILE_VERSION_FULL_STR FILE_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(FILE_VERSION_BUILD) 49 | #define FILE_VERSION_RESOURCE FILE_VERSION_MAJOR,FILE_VERSION_MINOR,FILE_VERSION_PATCH,FILE_VERSION_BUILD 50 | #define FILE_VERSION_RESOURCE_STR FILE_VERSION_FULL_STR "\0" 51 | 52 | #ifndef PRODUCT_ICON 53 | #define PRODUCT_ICON "@PRODUCT_ICON@" 54 | #endif 55 | 56 | #ifndef PRODUCT_COMMENTS 57 | #define PRODUCT_COMMENTS "@PRODUCT_COMMENTS@\0" 58 | #endif 59 | 60 | #ifndef PRODUCT_COMPANY_NAME 61 | #define PRODUCT_COMPANY_NAME "@PRODUCT_COMPANY_NAME@\0" 62 | #endif 63 | 64 | #ifndef PRODUCT_COMPANY_COPYRIGHT 65 | #define PRODUCT_COMPANY_COPYRIGHT "@PRODUCT_COMPANY_COPYRIGHT@\0" 66 | #endif 67 | 68 | #ifndef PRODUCT_FILE_DESCRIPTION 69 | #define PRODUCT_FILE_DESCRIPTION "@PRODUCT_FILE_DESCRIPTION@\0" 70 | #endif 71 | 72 | #ifndef PRODUCT_INTERNAL_NAME 73 | #define PRODUCT_INTERNAL_NAME "@PRODUCT_NAME@\0" 74 | #endif 75 | 76 | #ifndef PRODUCT_ORIGINAL_FILENAME 77 | #define PRODUCT_ORIGINAL_FILENAME "@PRODUCT_ORIGINAL_FILENAME@\0" 78 | #endif 79 | 80 | #ifndef PRODUCT_BUNDLE 81 | #define PRODUCT_BUNDLE "@PRODUCT_BUNDLE@\0" 82 | #endif 83 | -------------------------------------------------------------------------------- /cmake/windows/VersionResource.rc: -------------------------------------------------------------------------------- 1 | #include "VersionInfo.h" 2 | 3 | #if defined(__MINGW64__) || defined(__MINGW32__) 4 | // MinGW-w64, MinGW 5 | #if defined(__has_include) && __has_include() 6 | #include 7 | #else 8 | #include 9 | #include 10 | #endif 11 | #else 12 | // MSVC, Windows SDK 13 | #include 14 | #endif 15 | 16 | IDI_ICON1 ICON PRODUCT_ICON 17 | 18 | LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT 19 | 20 | VS_VERSION_INFO VERSIONINFO 21 | FILEVERSION FILE_VERSION_RESOURCE 22 | PRODUCTVERSION PRODUCT_VERSION_RESOURCE 23 | FILEFLAGSMASK 0x3fL 24 | #ifdef _DEBUG 25 | FILEFLAGS 0x1L 26 | #else 27 | FILEFLAGS 0x0L 28 | #endif 29 | FILEOS 0x4L 30 | FILETYPE 0x1L 31 | FILESUBTYPE 0x0L 32 | BEGIN 33 | BLOCK "StringFileInfo" 34 | BEGIN 35 | BLOCK "000904b0" 36 | BEGIN 37 | VALUE "Comments", PRODUCT_COMMENTS 38 | VALUE "CompanyName", PRODUCT_COMPANY_NAME 39 | VALUE "FileDescription", PRODUCT_FILE_DESCRIPTION 40 | VALUE "FileVersion", FILE_VERSION_RESOURCE_STR 41 | VALUE "InternalName", PRODUCT_INTERNAL_NAME 42 | VALUE "LegalCopyright", PRODUCT_COMPANY_COPYRIGHT 43 | VALUE "OriginalFilename", PRODUCT_ORIGINAL_FILENAME 44 | VALUE "ProductName", PRODUCT_BUNDLE 45 | VALUE "ProductVersion", PRODUCT_VERSION_RESOURCE_STR 46 | END 47 | END 48 | BLOCK "VarFileInfo" 49 | BEGIN 50 | VALUE "Translation", 0x9, 1200 51 | END 52 | END 53 | -------------------------------------------------------------------------------- /cmake/windows/windows.cmake: -------------------------------------------------------------------------------- 1 | set(PLATFORM_SOURCES 3rdparty/WinCommander.cpp sys/windows/guihelper.cpp sys/windows/MiniDump.cpp) 2 | set(PLATFORM_LIBRARIES wininet wsock32 ws2_32 user32 rasapi32 iphlpapi) 3 | 4 | include(cmake/windows/generate_product_version.cmake) 5 | generate_product_version( 6 | QV2RAY_RC 7 | ICON "${CMAKE_SOURCE_DIR}/res/nekobox.ico" 8 | NAME "nekobox" 9 | BUNDLE "nekobox" 10 | COMPANY_NAME "nekobox" 11 | COMPANY_COPYRIGHT "nekobox" 12 | FILE_DESCRIPTION "nekobox" 13 | ) 14 | add_definitions(-DUNICODE -D_UNICODE -DNOMINMAX) 15 | set(GUI_TYPE WIN32) 16 | if (MINGW) 17 | if (NOT DEFINED MinGW_ROOT) 18 | set(MinGW_ROOT "C:/msys64/mingw64") 19 | endif () 20 | else () 21 | add_compile_options("/utf-8") 22 | add_compile_options("/std:c++17") 23 | add_definitions(-D_WIN32_WINNT=0x600 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS) 24 | endif () 25 | -------------------------------------------------------------------------------- /db/ConfigBuilder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ProxyEntity.hpp" 4 | #include "sys/ExternalProcess.hpp" 5 | 6 | namespace NekoGui { 7 | class BuildConfigResult { 8 | public: 9 | QString error; 10 | QJsonObject coreConfig; 11 | 12 | QList> outboundStats; // all, but not including "bypass" "block" 13 | std::shared_ptr outboundStat; // main 14 | QStringList ignoreConnTag; 15 | 16 | std::list> extRs; 17 | }; 18 | 19 | class BuildConfigStatus { 20 | public: 21 | std::shared_ptr result; 22 | std::shared_ptr ent; 23 | bool forTest; 24 | bool forExport; 25 | 26 | // priv 27 | QList globalProfiles; 28 | 29 | // xxList is V2Ray format string list 30 | 31 | QStringList domainListDNSRemote; 32 | QStringList domainListDNSDirect; 33 | QStringList domainListRemote; 34 | QStringList domainListDirect; 35 | QStringList ipListRemote; 36 | QStringList ipListDirect; 37 | QStringList domainListBlock; 38 | QStringList ipListBlock; 39 | 40 | // config format 41 | 42 | QJsonArray routingRules; 43 | QJsonArray inbounds; 44 | QJsonArray outbounds; 45 | }; 46 | 47 | std::shared_ptr BuildConfig(const std::shared_ptr &ent, bool forTest, bool forExport); 48 | 49 | void BuildConfigSingBox(const std::shared_ptr &status); 50 | 51 | QString BuildChain(int chainId, const std::shared_ptr &status); 52 | 53 | QString BuildChainInternal(int chainId, const QList> &ents, 54 | const std::shared_ptr &status); 55 | 56 | QString WriteVPNSingBoxConfig(); 57 | 58 | QString WriteVPNLinuxScript(const QString &configPath); 59 | } // namespace NekoGui 60 | -------------------------------------------------------------------------------- /db/Database.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "main/NekoGui.hpp" 4 | #include "ProxyEntity.hpp" 5 | #include "Group.hpp" 6 | 7 | namespace NekoGui { 8 | class ProfileManager : private JsonStore { 9 | public: 10 | // JsonStore 11 | 12 | // order -> id 13 | QList groupsTabOrder; 14 | 15 | // Manager 16 | 17 | std::map> profiles; 18 | std::map> groups; 19 | 20 | ProfileManager(); 21 | 22 | // LoadManager Reset and loads profiles & groups 23 | void LoadManager(); 24 | 25 | void SaveManager(); 26 | 27 | [[nodiscard]] static std::shared_ptr NewProxyEntity(const QString &type); 28 | 29 | [[nodiscard]] static std::shared_ptr NewGroup(); 30 | 31 | bool AddProfile(const std::shared_ptr &ent, int gid = -1); 32 | 33 | void DeleteProfile(int id); 34 | 35 | void MoveProfile(const std::shared_ptr &ent, int gid); 36 | 37 | std::shared_ptr GetProfile(int id); 38 | 39 | bool AddGroup(const std::shared_ptr &ent); 40 | 41 | void DeleteGroup(int gid); 42 | 43 | std::shared_ptr GetGroup(int id); 44 | 45 | std::shared_ptr CurrentGroup(); 46 | 47 | private: 48 | // sort by id 49 | QList profilesIdOrder; 50 | QList groupsIdOrder; 51 | 52 | [[nodiscard]] int NewProfileID() const; 53 | 54 | [[nodiscard]] int NewGroupID() const; 55 | 56 | static std::shared_ptr LoadProxyEntity(const QString &jsonPath); 57 | 58 | static std::shared_ptr LoadGroup(const QString &jsonPath); 59 | }; 60 | 61 | extern ProfileManager *profileManager; 62 | } // namespace NekoGui 63 | -------------------------------------------------------------------------------- /db/Group.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "main/NekoGui.hpp" 4 | #include "ProxyEntity.hpp" 5 | 6 | namespace NekoGui { 7 | class Group : public JsonStore { 8 | public: 9 | int id = -1; 10 | bool archive = false; 11 | bool skip_auto_update = false; 12 | QString name = ""; 13 | QString url = ""; 14 | QString info = ""; 15 | qint64 sub_last_update = 0; 16 | int front_proxy_id = -1; 17 | 18 | // list ui 19 | bool manually_column_width = false; 20 | QList column_width; 21 | QList order; 22 | 23 | Group(); 24 | 25 | // 按 id 顺序 26 | [[nodiscard]] QList> Profiles() const; 27 | 28 | // 按 显示 顺序 29 | [[nodiscard]] QList> ProfilesWithOrder() const; 30 | }; 31 | } // namespace NekoGui 32 | -------------------------------------------------------------------------------- /db/ProfileFilter.cpp: -------------------------------------------------------------------------------- 1 | #include "ProfileFilter.hpp" 2 | 3 | namespace NekoGui { 4 | 5 | QString ProfileFilter_ent_key(const std::shared_ptr &ent, bool by_address) { 6 | by_address &= ent->type != "custom"; 7 | return by_address ? (ent->bean->DisplayAddress() + ent->bean->DisplayType()) 8 | : QJsonObject2QString(ent->bean->ToJson({"c_cfg", "c_out"}), true) + ent->bean->DisplayType(); 9 | } 10 | 11 | void ProfileFilter::Uniq(const QList> &in, 12 | QList> &out, 13 | bool by_address, bool keep_last) { 14 | QMap> hashMap; 15 | 16 | for (const auto &ent: in) { 17 | QString key = ProfileFilter_ent_key(ent, by_address); 18 | if (hashMap.contains(key)) { 19 | if (keep_last) { 20 | out.removeAll(hashMap[key]); 21 | hashMap[key] = ent; 22 | out += ent; 23 | } 24 | } else { 25 | hashMap[key] = ent; 26 | out += ent; 27 | } 28 | } 29 | } 30 | 31 | void ProfileFilter::Common(const QList> &src, 32 | const QList> &dst, 33 | QList> &outSrc, 34 | QList> &outDst, 35 | bool by_address) { 36 | QMap> hashMap; 37 | 38 | for (const auto &ent: src) { 39 | QString key = ProfileFilter_ent_key(ent, by_address); 40 | hashMap[key] = ent; 41 | } 42 | for (const auto &ent: dst) { 43 | QString key = ProfileFilter_ent_key(ent, by_address); 44 | if (hashMap.contains(key)) { 45 | outDst += ent; 46 | outSrc += hashMap[key]; 47 | } 48 | } 49 | } 50 | 51 | void ProfileFilter::OnlyInSrc(const QList> &src, 52 | const QList> &dst, 53 | QList> &out, 54 | bool by_address) { 55 | QMap hashMap; 56 | 57 | for (const auto &ent: dst) { 58 | QString key = ProfileFilter_ent_key(ent, by_address); 59 | hashMap[key] = true; 60 | } 61 | for (const auto &ent: src) { 62 | QString key = ProfileFilter_ent_key(ent, by_address); 63 | if (!hashMap.contains(key)) out += ent; 64 | } 65 | } 66 | 67 | void ProfileFilter::OnlyInSrc_ByPointer(const QList> &src, 68 | const QList> &dst, 69 | QList> &out) { 70 | for (const auto &ent: src) { 71 | if (!dst.contains(ent)) out += ent; 72 | } 73 | } 74 | 75 | } // namespace NekoGui -------------------------------------------------------------------------------- /db/ProfileFilter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ProxyEntity.hpp" 4 | 5 | namespace NekoGui { 6 | class ProfileFilter { 7 | public: 8 | static void Uniq( 9 | const QList> &in, 10 | QList> &out, 11 | bool by_address = false, // def by bean 12 | bool keep_last = false // def keep first 13 | ); 14 | 15 | static void Common( 16 | const QList> &src, 17 | const QList> &dst, 18 | QList> &outSrc, 19 | QList> &outDst, 20 | bool by_address = false // def by bean 21 | ); 22 | 23 | static void OnlyInSrc( 24 | const QList> &src, 25 | const QList> &dst, 26 | QList> &out, 27 | bool by_address = false // def by bean 28 | ); 29 | 30 | static void OnlyInSrc_ByPointer( 31 | const QList> &src, 32 | const QList> &dst, 33 | QList> &out); 34 | }; 35 | } // namespace NekoGui 36 | -------------------------------------------------------------------------------- /db/ProxyEntity.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "main/NekoGui.hpp" 4 | #include "db/traffic/TrafficData.hpp" 5 | #include "fmt/AbstractBean.hpp" 6 | 7 | namespace NekoGui_fmt { 8 | class SocksHttpBean; 9 | 10 | class ShadowSocksBean; 11 | 12 | class VMessBean; 13 | 14 | class TrojanVLESSBean; 15 | 16 | class NaiveBean; 17 | 18 | class QUICBean; 19 | 20 | class CustomBean; 21 | 22 | class ChainBean; 23 | }; // namespace NekoGui_fmt 24 | 25 | namespace NekoGui { 26 | class ProxyEntity : public JsonStore { 27 | public: 28 | QString type; 29 | 30 | int id = -1; 31 | int gid = 0; 32 | int latency = 0; 33 | std::shared_ptr bean; 34 | std::shared_ptr traffic_data = std::make_shared(""); 35 | 36 | QString full_test_report; 37 | 38 | ProxyEntity(NekoGui_fmt::AbstractBean *bean, const QString &type_); 39 | 40 | [[nodiscard]] QString DisplayLatency() const; 41 | 42 | [[nodiscard]] QColor DisplayLatencyColor() const; 43 | 44 | [[nodiscard]] NekoGui_fmt::ChainBean *ChainBean() const { 45 | return (NekoGui_fmt::ChainBean *) bean.get(); 46 | }; 47 | 48 | [[nodiscard]] NekoGui_fmt::SocksHttpBean *SocksHTTPBean() const { 49 | return (NekoGui_fmt::SocksHttpBean *) bean.get(); 50 | }; 51 | 52 | [[nodiscard]] NekoGui_fmt::ShadowSocksBean *ShadowSocksBean() const { 53 | return (NekoGui_fmt::ShadowSocksBean *) bean.get(); 54 | }; 55 | 56 | [[nodiscard]] NekoGui_fmt::VMessBean *VMessBean() const { 57 | return (NekoGui_fmt::VMessBean *) bean.get(); 58 | }; 59 | 60 | [[nodiscard]] NekoGui_fmt::TrojanVLESSBean *TrojanVLESSBean() const { 61 | return (NekoGui_fmt::TrojanVLESSBean *) bean.get(); 62 | }; 63 | 64 | [[nodiscard]] NekoGui_fmt::NaiveBean *NaiveBean() const { 65 | return (NekoGui_fmt::NaiveBean *) bean.get(); 66 | }; 67 | 68 | [[nodiscard]] NekoGui_fmt::QUICBean *QUICBean() const { 69 | return (NekoGui_fmt::QUICBean *) bean.get(); 70 | }; 71 | 72 | [[nodiscard]] NekoGui_fmt::CustomBean *CustomBean() const { 73 | return (NekoGui_fmt::CustomBean *) bean.get(); 74 | }; 75 | }; 76 | } // namespace NekoGui 77 | -------------------------------------------------------------------------------- /db/traffic/TrafficData.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "main/NekoGui.hpp" 4 | 5 | namespace NekoGui_traffic { 6 | class TrafficData : public JsonStore { 7 | public: 8 | int id = -1; // ent id 9 | std::string tag; 10 | 11 | long long downlink = 0; 12 | long long uplink = 0; 13 | long long downlink_rate = 0; 14 | long long uplink_rate = 0; 15 | 16 | long long last_update; 17 | 18 | explicit TrafficData(std::string tag) { 19 | this->tag = std::move(tag); 20 | _add(new configItem("dl", &downlink, itemType::integer64)); 21 | _add(new configItem("ul", &uplink, itemType::integer64)); 22 | }; 23 | 24 | void Reset() { 25 | downlink = 0; 26 | uplink = 0; 27 | downlink_rate = 0; 28 | uplink_rate = 0; 29 | } 30 | 31 | [[nodiscard]] QString DisplaySpeed() const { 32 | return UNICODE_LRO + QStringLiteral("%1↑ %2↓").arg(ReadableSize(uplink_rate), ReadableSize(downlink_rate)); 33 | } 34 | 35 | [[nodiscard]] QString DisplayTraffic() const { 36 | if (downlink + uplink == 0) return ""; 37 | return UNICODE_LRO + QStringLiteral("%1↑ %2↓").arg(ReadableSize(uplink), ReadableSize(downlink)); 38 | } 39 | }; 40 | } // namespace NekoGui_traffic 41 | -------------------------------------------------------------------------------- /db/traffic/TrafficLooper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "TrafficData.hpp" 8 | 9 | namespace NekoGui_traffic { 10 | class TrafficLooper { 11 | public: 12 | bool loop_enabled = false; 13 | bool looping = false; 14 | QMutex loop_mutex; 15 | 16 | QList> items; 17 | TrafficData *proxy = nullptr; 18 | 19 | void UpdateAll(); 20 | 21 | void Loop(); 22 | 23 | private: 24 | TrafficData *bypass = new TrafficData("bypass"); 25 | 26 | [[nodiscard]] static TrafficData *update_stats(TrafficData *item); 27 | 28 | [[nodiscard]] static QJsonArray get_connection_list(); 29 | }; 30 | 31 | extern TrafficLooper *trafficLooper; 32 | } // namespace NekoGui_traffic 33 | -------------------------------------------------------------------------------- /docs/Build_Core.md: -------------------------------------------------------------------------------- 1 | ## 构建 nekobox_core 2 | 3 | ### 目录结构 4 | 5 | ``` 6 | | nekoray 7 | | go/cmd/* 8 | | sing-box-extra 9 | | sing-box 10 | | ...... 11 | ``` 12 | 13 | ### 常规构建 14 | 15 | 1. `bash libs/get_source.sh` (自动下载目录结构,自动 checkout commit) 16 | 2. `GOOS=windows GOARCH=amd64 bash libs/build_go.sh` 17 | 18 | 具体支持的 GOOS 和 GOARCH 请看 `libs/build_go.sh` 19 | 20 | 非官方构建无需编译 `updater` `launcher` 21 | 22 | ### sing-box tags 23 | 24 | 具体使用的 tags 请看 `libs/build_go.sh` 25 | -------------------------------------------------------------------------------- /docs/Build_Linux.md: -------------------------------------------------------------------------------- 1 | 在 Linux 下编译 Nekoray 2 | 3 | ## git clone 源码 4 | 5 | ``` 6 | git clone https://github.com/MatsuriDayo/nekoray.git --recursive 7 | ``` 8 | 9 | ## 简单编译法 10 | 11 | 条件: 12 | 13 | 1. C++ 依赖:`protobuf yaml-cpp zxing-cpp` 已用包管理器安装,并符合版本要求。 14 | 2. 已安装 `qtbase` `qtsvg` `qttools` `qtx11extras` 15 | 3. 已安装 Qt `5.12.x` 或 `5.15.x` 16 | 4. 系统为 `x86-64-linux-gnu` 17 | 18 | ```shell 19 | mkdir build 20 | cd build 21 | cmake -GNinja .. 22 | ninja 23 | ``` 24 | 25 | 编译完成后得到 `nekobox` 26 | 27 | 解压 Release 的压缩包,替换其中的 `nekobox`,删除 `launcher` 即可使用。 28 | 29 | ## 复杂编译法 30 | 31 | ### CMake 参数 32 | 33 | | CMake 参数 | 默认值 | 含义 | 34 | |-------------------|-------------------|-----------------------| 35 | | QT_VERSION_MAJOR | 5 | QT版本 | 36 | | NKR_NO_EXTERNAL | | 不包含外部 C/C++ 依赖 (以下所有) | 37 | | NKR_NO_YAML | | 不包含 yaml-cpp | 38 | | NKR_NO_QHOTKEY | | 不包含 qhotkey | 39 | | NKR_NO_ZXING | | 不包含 zxing | 40 | | NKR_NO_GRPC | | 不包含 gRPC | 41 | | NKR_PACKAGE | | 编译 package 版本 (aur) | 42 | | NKR_LIBS | ./libs/deps/built | 依赖搜索目录 | 43 | | NKR_DISABLE_LIBS | | 禁用 NKR_LIBS | 44 | 45 | 1. `NKR_LIBS` 的值会被追加到 `CMAKE_PREFIX_PATH` 46 | 2. `NKR_PACKAGE` 打开后,`NKR_LIBS` 的默认值为 `./libs/deps/package` ,具体依赖请看 `build_deps_all.sh` 47 | 3. `NKR_PACKAGE` 打开后,应用将使用 appdata 目录存放配置,自动更新等功能将被禁用。 48 | 49 | ### C++ 部分 50 | 51 | 当您的发行版没有上面几个 C++ 依赖包,或者版本不符合要求时,可以参考 `build_deps_all.sh` 编译脚本自行编译。 52 | 53 | 条件: 已安装 Qt `5.12.x` 或 `5.15.x` 54 | 55 | #### 编译安装 C/C++ 依赖 56 | 57 | (这一步可能要挂梯) 58 | 59 | ```shell 60 | ./libs/build_deps_all.sh 61 | ``` 62 | 63 | #### 编译本体 64 | 65 | ```shell 66 | mkdir build 67 | cd build 68 | cmake -GNinja .. 69 | ninja 70 | ``` 71 | 72 | 编译完成后得到 `nekobox` 73 | 74 | ### Go 部分编译 75 | 76 | 请看 [Build_Core.md](./Build_Core.md) 77 | -------------------------------------------------------------------------------- /docs/Build_Windows.md: -------------------------------------------------------------------------------- 1 | 在 Windows 下编译 Nekoray 2 | 3 | ### git clone 源码 4 | 5 | ``` 6 | git clone https://github.com/MatsuriDayo/nekoray.git --recursive 7 | ``` 8 | 9 | ### 安装 Visual Studio 10 | 11 | 从微软官网安装,可以使用 2019 和 2022 版本,安装 Win32 C++ 开发环境。 12 | 13 | 安装好后可以在「开始」菜单找到 `x64 Native Tools Command Prompt` 14 | 15 | 本文之后的命令均在该 cmd 内执行。`cmake` `ninja` 等工具使用 VS 自带的即可。 16 | 17 | ### 下载 Qt SDK 18 | 19 | 目前 Windows Release 使用的版本是 Qt 6.5.x 20 | 21 | 下载解压后,将 bin 目录添加到环境变量。 22 | 23 | #### Release 编译用到的 Qt 包下载 (MSVC2019 x86_64) 24 | 25 | https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/Qt6.5.0-Windows-x86_64-VS2022-17.5.5-20230507.7z 26 | 27 | #### 官方签名版 Qt 5.15.2 (可选,已知有内存泄漏的BUG) 28 | 29 | 在此下载 `qtbase` `qtsvg` `qttools` 的包并解压到同一个目录。 30 | 31 | https://download.qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5152/qt.qt5.5152.win64_msvc2019_64/ 32 | 33 | ### C++ 部分编译 34 | 35 | #### 编译安装 C/C++ 依赖 36 | 37 | (这一步可能要挂梯) 38 | 39 | ```shell 40 | bash ./libs/build_deps_all.sh 41 | ``` 42 | 43 | 目前只有 bash 脚本,没有批处理或 powershell,如果 Windows 没有带 bash 建议自行安装。 44 | 45 | CMake 参数等细节与 Linux 大同小异,有问题可以参照 Build_Linux 文档。 46 | 47 | #### 编译本体 48 | 49 | 请根据你的 QT Sdk 的位置替换命令 50 | 51 | ```shell 52 | mkdir build 53 | cd build 54 | cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=D:/path/to/qt/5.15.2/msvc2019_64 .. 55 | ninja 56 | ``` 57 | 58 | 编译完成后得到 `nekobox.exe` 59 | 60 | 最后运行 `windeployqt nekobox.exe` 自动复制所需 DLL 等文件到当前目录 61 | 62 | ### Go 部分编译 63 | 64 | 请看 [Build_Core.md](./Build_Core.md) 65 | -------------------------------------------------------------------------------- /docs/RunFlags.md: -------------------------------------------------------------------------------- 1 | # 运行参数 2 | 3 | - `-many` 无视同目录正在运行的实例,强行开启新的实例。 4 | - `-appdata` 开启后配置文件会指定目录,未指定目录则使用共享目录,无法多开和自动升级。 5 | - `-flag_reorder` 进行重新整理配置文件的顺序,并删除损坏和孤立的配置。 6 | -------------------------------------------------------------------------------- /docs/Run_Linux.md: -------------------------------------------------------------------------------- 1 | ## Linux 安装 2 | 3 | ### Debian 系发行版 4 | 5 | 使用 Debian 系发行版时,推荐使用 .deb 包安装: 6 | 7 | ```shell 8 | sudo apt install ./nekoray-*-debian-x64.deb 9 | ``` 10 | 11 | 安装完成后,桌面快捷方式启动自带参数 `-appdata`,如果想要直接启动并使用之前的配置,注意附带本参数。 12 | 13 | ### Arch 系发行版 14 | 15 | 使用 Arch 系发行版时,推荐从 ```aur``` 或 ```archlinuxcn``` 安装: 16 | 17 | #### AUR 18 | ##### 最新稳定版 19 | 20 | ```shell 21 | [yay/paru] -S nekoray 22 | ``` 23 | 24 | ##### 最新 Git 版 (开发版) 25 | 26 | ```shell 27 | [yay/paru] -S nekoray-git 28 | ``` 29 | 30 | #### archlinuxcn 31 | 32 | ##### 最新稳定版 33 | 34 | ```shell 35 | sudo pacman -S nekoray 36 | ``` 37 | 38 | ##### 最新 Git 版 (开发版) 39 | 40 | ```shell 41 | sudo pacman -S nekoray-git 42 | ``` 43 | 44 | ### 其他发行版 45 | 46 | 下载 .zip 文件,解压到合适的路径,开箱即用。 47 | 48 | 或下载 .AppImage,并使用 `chmod +x nekoray-*-AppImage-x64.AppImage` 给予可执行权限。 49 | 50 | 具体使用方法见下文。 51 | 52 | ## Linux 运行 53 | 54 | **使用 Linux 系统相信您已具备基本的排错能力, 55 | 本项目不提供特定发行版/架构的支持,预编译文件不能满足您的需求时,请自行编译/适配。** 56 | 57 | 已知部分 Linux 发行版无法使用、非 x86_64 暂无适配,可以尝试自行编译。 58 | 59 | 目前 Release 便携包解压后,有两种使用方法: 60 | 61 | 1. System: 若要使用系统的 Qt5 运行库,请执行 `./nekoray` 62 | 2. Bundle: 若要使用预编译的 Qt 运行库,请执行 `./launcher` 63 | 64 | ### Bundle 65 | 66 | 要求:已安装主流的发行版和 xcb 桌面环境。 67 | 68 | 运行: `./launcher` 或 部分系统可双击打开 69 | 70 | launcher 参数 71 | 72 | * `./launcher -- -appdata` ( `--` 后的参数传递给主程序 ) 73 | * `-debug` Debug mode 74 | 75 | Ubuntu 22.04: `sudo apt install libxcb-xinerama0` 76 | 77 | ### System 78 | 79 | 要求:已安装主流的发行版和 xcb 桌面环境,已安装 Qt5.12 ~ Qt5.15 环境。 80 | 81 | 运行: `./nekoray` 或 部分系统可双击打开。如果无法运行,建议使用 Bundle 版。 82 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | # 技术文档 2 | 3 | # Technical documentation 4 | 5 | 1. Build GUI: `Build_*.md` 6 | 2. Build Core: `Build_Core.md` 7 | -------------------------------------------------------------------------------- /fmt/AbstractBean.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace NekoGui_fmt { 8 | AbstractBean::AbstractBean(int version) { 9 | this->version = version; 10 | _add(new configItem("_v", &this->version, itemType::integer)); 11 | _add(new configItem("name", &name, itemType::string)); 12 | _add(new configItem("addr", &serverAddress, itemType::string)); 13 | _add(new configItem("port", &serverPort, itemType::integer)); 14 | _add(new configItem("c_cfg", &custom_config, itemType::string)); 15 | _add(new configItem("c_out", &custom_outbound, itemType::string)); 16 | } 17 | 18 | QString AbstractBean::ToNekorayShareLink(const QString &type) { 19 | auto b = ToJson(); 20 | QUrl url; 21 | url.setScheme("nekoray"); 22 | url.setHost(type); 23 | url.setFragment(QJsonObject2QString(b, true) 24 | .toUtf8() 25 | .toBase64(QByteArray::Base64UrlEncoding)); 26 | return url.toString(); 27 | } 28 | 29 | QString AbstractBean::DisplayAddress() { 30 | return ::DisplayAddress(serverAddress, serverPort); 31 | } 32 | 33 | QString AbstractBean::DisplayName() { 34 | if (name.isEmpty()) { 35 | return DisplayAddress(); 36 | } 37 | return name; 38 | } 39 | 40 | QString AbstractBean::DisplayTypeAndName() { 41 | return QStringLiteral("[%1] %2").arg(DisplayType(), DisplayName()); 42 | } 43 | 44 | void AbstractBean::ResolveDomainToIP(const std::function &onFinished) { 45 | bool noResolve = false; 46 | if (dynamic_cast(this) != nullptr) noResolve = true; 47 | if (dynamic_cast(this) != nullptr) noResolve = true; 48 | if (dynamic_cast(this) != nullptr) noResolve = true; 49 | if (IsIpAddress(serverAddress)) noResolve = true; 50 | if (noResolve) { 51 | onFinished(); 52 | return; 53 | } 54 | 55 | #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) // TODO older QT 56 | QHostInfo::lookupHost(serverAddress, QApplication::instance(), [=](const QHostInfo &host) { 57 | auto addr = host.addresses(); 58 | if (!addr.isEmpty()) { 59 | auto domain = serverAddress; 60 | auto stream = GetStreamSettings(this); 61 | 62 | // replace serverAddress 63 | serverAddress = addr.first().toString(); 64 | 65 | // replace ws tls 66 | if (stream != nullptr) { 67 | if (stream->security == "tls" && stream->sni.isEmpty()) { 68 | stream->sni = domain; 69 | } 70 | if (stream->network == "ws" && stream->host.isEmpty()) { 71 | stream->host = domain; 72 | } 73 | } 74 | } 75 | onFinished(); 76 | }); 77 | #endif 78 | } 79 | } // namespace NekoGui_fmt 80 | -------------------------------------------------------------------------------- /fmt/AbstractBean.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "main/NekoGui.hpp" 7 | 8 | namespace NekoGui_fmt { 9 | struct CoreObjOutboundBuildResult { 10 | public: 11 | QJsonObject outbound; 12 | QString error; 13 | }; 14 | 15 | struct ExternalBuildResult { 16 | public: 17 | QString program; 18 | QStringList env; 19 | QStringList arguments; 20 | // 21 | QString tag; 22 | // 23 | QString error; 24 | QString config_export; 25 | }; 26 | 27 | class AbstractBean : public JsonStore { 28 | public: 29 | int version; 30 | 31 | QString name = ""; 32 | QString serverAddress = "127.0.0.1"; 33 | int serverPort = 1080; 34 | 35 | QString custom_config = ""; 36 | QString custom_outbound = ""; 37 | 38 | explicit AbstractBean(int version); 39 | 40 | // 41 | 42 | QString ToNekorayShareLink(const QString &type); 43 | 44 | void ResolveDomainToIP(const std::function &onFinished); 45 | 46 | // 47 | 48 | [[nodiscard]] virtual QString DisplayAddress(); 49 | 50 | [[nodiscard]] virtual QString DisplayName(); 51 | 52 | virtual QString DisplayCoreType() { return software_core_name; }; 53 | 54 | virtual QString DisplayType() { return {}; }; 55 | 56 | virtual QString DisplayTypeAndName(); 57 | 58 | // 59 | 60 | virtual int NeedExternal(bool isFirstProfile) { return 0; }; 61 | 62 | virtual CoreObjOutboundBuildResult BuildCoreObjSingBox() { return {}; }; 63 | 64 | virtual ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) { return {}; }; 65 | 66 | virtual QString ToShareLink() { return {}; }; 67 | }; 68 | 69 | } // namespace NekoGui_fmt 70 | -------------------------------------------------------------------------------- /fmt/ChainBean.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "main/NekoGui.hpp" 4 | 5 | namespace NekoGui_fmt { 6 | class ChainBean : public AbstractBean { 7 | public: 8 | QList list; // in to out 9 | 10 | ChainBean() : AbstractBean(0) { 11 | _add(new configItem("list", &list, itemType::integerList)); 12 | }; 13 | 14 | QString DisplayType() override { return QObject::tr("Chain Proxy"); }; 15 | 16 | QString DisplayAddress() override { return ""; }; 17 | }; 18 | } // namespace NekoGui_fmt 19 | -------------------------------------------------------------------------------- /fmt/CustomBean.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fmt/AbstractBean.hpp" 4 | 5 | namespace NekoGui_fmt { 6 | class CustomBean : public AbstractBean { 7 | public: 8 | QString core; 9 | QList command; 10 | QString config_suffix; 11 | QString config_simple; 12 | int mapping_port = 0; 13 | int socks_port = 0; 14 | 15 | CustomBean() : AbstractBean(0) { 16 | _add(new configItem("core", &core, itemType::string)); 17 | _add(new configItem("cmd", &command, itemType::stringList)); 18 | _add(new configItem("cs", &config_simple, itemType::string)); 19 | _add(new configItem("cs_suffix", &config_suffix, itemType::string)); 20 | _add(new configItem("mapping_port", &mapping_port, itemType::integer)); 21 | _add(new configItem("socks_port", &socks_port, itemType::integer)); 22 | }; 23 | 24 | QString DisplayType() override { 25 | if (core == "internal") { 26 | auto obj = QString2QJsonObject(config_simple); 27 | return obj["type"].toString(); 28 | } else if (core == "internal-full") { 29 | return software_core_name + " config"; 30 | } 31 | return core; 32 | }; 33 | 34 | QString DisplayCoreType() override { return NeedExternal(true) == 0 ? software_core_name : core; }; 35 | 36 | QString DisplayAddress() override { 37 | if (core == "internal") { 38 | auto obj = QString2QJsonObject(config_simple); 39 | return ::DisplayAddress(obj["server"].toString(), obj["server_port"].toInt()); 40 | } else if (core == "internal-full") { 41 | return {}; 42 | } 43 | return AbstractBean::DisplayAddress(); 44 | }; 45 | 46 | int NeedExternal(bool isFirstProfile) override; 47 | 48 | ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; 49 | 50 | CoreObjOutboundBuildResult BuildCoreObjSingBox() override; 51 | }; 52 | } // namespace NekoGui_fmt -------------------------------------------------------------------------------- /fmt/NaiveBean.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fmt/AbstractBean.hpp" 4 | 5 | namespace NekoGui_fmt { 6 | class NaiveBean : public AbstractBean { 7 | public: 8 | QString username = ""; 9 | QString password = ""; 10 | QString protocol = "https"; 11 | QString extra_headers = ""; 12 | QString sni = ""; 13 | QString certificate = ""; 14 | int insecure_concurrency = 0; 15 | 16 | bool disable_log = false; 17 | 18 | NaiveBean() : AbstractBean(0) { 19 | _add(new configItem("username", &username, itemType::string)); 20 | _add(new configItem("password", &password, itemType::string)); 21 | _add(new configItem("protocol", &protocol, itemType::string)); 22 | _add(new configItem("extra_headers", &extra_headers, itemType::string)); 23 | _add(new configItem("sni", &sni, itemType::string)); 24 | _add(new configItem("certificate", &certificate, itemType::string)); 25 | _add(new configItem("insecure_concurrency", &insecure_concurrency, itemType::integer)); 26 | _add(new configItem("disable_log", &disable_log, itemType::boolean)); 27 | }; 28 | 29 | QString DisplayCoreType() override { return "Naive"; }; 30 | 31 | QString DisplayType() override { return "Naive"; }; 32 | 33 | int NeedExternal(bool isFirstProfile) override; 34 | 35 | ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override; 36 | 37 | bool TryParseLink(const QString &link); 38 | 39 | QString ToShareLink() override; 40 | }; 41 | } // namespace NekoGui_fmt -------------------------------------------------------------------------------- /fmt/Preset.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Preset { 4 | namespace SingBox { 5 | inline QStringList VpnImplementation = {"gvisor", "system", "mixed"}; 6 | inline QStringList DomainStrategy = {"", "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6"}; 7 | inline QStringList UtlsFingerPrint = {"", "chrome", "firefox", "edge", "safari", "360", "qq", "ios", "android", "random", "randomized"}; 8 | inline QStringList ShadowsocksMethods = {"2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20"}; 9 | inline QStringList Flows = {"xtls-rprx-vision"}; 10 | } // namespace SingBox 11 | 12 | namespace Windows { 13 | inline QStringList system_proxy_format{"{ip}:{http_port}", 14 | "socks={ip}:{socks_port}", 15 | "http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}", 16 | "http=http://{ip}:{http_port};https=http://{ip}:{http_port}"}; 17 | } // namespace Windows 18 | } // namespace Preset 19 | -------------------------------------------------------------------------------- /fmt/ShadowSocksBean.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fmt/AbstractBean.hpp" 4 | #include "fmt/V2RayStreamSettings.hpp" 5 | 6 | namespace NekoGui_fmt { 7 | class ShadowSocksBean : public AbstractBean { 8 | public: 9 | QString method = "aes-128-gcm"; 10 | QString password = ""; 11 | QString plugin = ""; 12 | int uot = 0; 13 | 14 | std::shared_ptr stream = std::make_shared(); 15 | 16 | ShadowSocksBean() : AbstractBean(0) { 17 | _add(new configItem("method", &method, itemType::string)); 18 | _add(new configItem("pass", &password, itemType::string)); 19 | _add(new configItem("plugin", &plugin, itemType::string)); 20 | _add(new configItem("uot", &uot, itemType::integer)); 21 | _add(new configItem("stream", dynamic_cast(stream.get()), itemType::jsonStore)); 22 | }; 23 | 24 | QString DisplayType() override { return "Shadowsocks"; }; 25 | 26 | CoreObjOutboundBuildResult BuildCoreObjSingBox() override; 27 | 28 | bool TryParseLink(const QString &link); 29 | 30 | QString ToShareLink() override; 31 | }; 32 | } // namespace NekoGui_fmt 33 | -------------------------------------------------------------------------------- /fmt/SocksHttpBean.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fmt/AbstractBean.hpp" 4 | #include "fmt/V2RayStreamSettings.hpp" 5 | 6 | namespace NekoGui_fmt { 7 | class SocksHttpBean : public AbstractBean { 8 | public: 9 | static constexpr int type_HTTP = -80; 10 | static constexpr int type_Socks4 = 4; 11 | static constexpr int type_Socks5 = 5; 12 | 13 | int socks_http_type = type_Socks5; 14 | QString username = ""; 15 | QString password = ""; 16 | 17 | std::shared_ptr stream = std::make_shared(); 18 | 19 | explicit SocksHttpBean(int _socks_http_type) : AbstractBean(0) { 20 | this->socks_http_type = _socks_http_type; 21 | _add(new configItem("v", &socks_http_type, itemType::integer)); 22 | _add(new configItem("username", &username, itemType::string)); 23 | _add(new configItem("password", &password, itemType::string)); 24 | _add(new configItem("stream", dynamic_cast(stream.get()), itemType::jsonStore)); 25 | }; 26 | 27 | QString DisplayType() override { return socks_http_type == type_HTTP ? "HTTP" : "Socks"; }; 28 | 29 | CoreObjOutboundBuildResult BuildCoreObjSingBox() override; 30 | 31 | bool TryParseLink(const QString &link); 32 | 33 | QString ToShareLink() override; 34 | }; 35 | } // namespace NekoGui_fmt 36 | -------------------------------------------------------------------------------- /fmt/TrojanVLESSBean.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fmt/AbstractBean.hpp" 4 | #include "fmt/V2RayStreamSettings.hpp" 5 | 6 | namespace NekoGui_fmt { 7 | class TrojanVLESSBean : public AbstractBean { 8 | public: 9 | static constexpr int proxy_Trojan = 0; 10 | static constexpr int proxy_VLESS = 1; 11 | int proxy_type = proxy_Trojan; 12 | 13 | QString password = ""; 14 | QString flow = ""; 15 | 16 | std::shared_ptr stream = std::make_shared(); 17 | 18 | explicit TrojanVLESSBean(int _proxy_type) : AbstractBean(0) { 19 | proxy_type = _proxy_type; 20 | _add(new configItem("pass", &password, itemType::string)); 21 | _add(new configItem("flow", &flow, itemType::string)); 22 | _add(new configItem("stream", dynamic_cast(stream.get()), itemType::jsonStore)); 23 | }; 24 | 25 | QString DisplayType() override { return proxy_type == proxy_VLESS ? "VLESS" : "Trojan"; }; 26 | 27 | CoreObjOutboundBuildResult BuildCoreObjSingBox() override; 28 | 29 | bool TryParseLink(const QString &link); 30 | 31 | QString ToShareLink() override; 32 | }; 33 | } // namespace NekoGui_fmt -------------------------------------------------------------------------------- /fmt/V2RayStreamSettings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AbstractBean.hpp" 4 | 5 | namespace NekoGui_fmt { 6 | class V2rayStreamSettings : public JsonStore { 7 | public: 8 | QString network = "tcp"; 9 | QString security = ""; 10 | QString packet_encoding = ""; 11 | // ws/http/grpc/tcp-http/httpupgrade 12 | QString path = ""; 13 | QString host = ""; 14 | // kcp/quic/tcp-http 15 | QString header_type = ""; 16 | // tls 17 | QString sni = ""; 18 | QString alpn = ""; 19 | QString certificate = ""; 20 | QString utlsFingerprint = ""; 21 | bool allow_insecure = false; 22 | // ws early data 23 | QString ws_early_data_name = ""; 24 | int ws_early_data_length = 0; 25 | // reality 26 | QString reality_pbk = ""; 27 | QString reality_sid = ""; 28 | QString reality_spx = ""; 29 | // multiplex 30 | int multiplex_status = 0; 31 | 32 | V2rayStreamSettings() : JsonStore() { 33 | _add(new configItem("net", &network, itemType::string)); 34 | _add(new configItem("sec", &security, itemType::string)); 35 | _add(new configItem("pac_enc", &packet_encoding, itemType::string)); 36 | _add(new configItem("path", &path, itemType::string)); 37 | _add(new configItem("host", &host, itemType::string)); 38 | _add(new configItem("sni", &sni, itemType::string)); 39 | _add(new configItem("alpn", &alpn, itemType::string)); 40 | _add(new configItem("cert", &certificate, itemType::string)); 41 | _add(new configItem("insecure", &allow_insecure, itemType::boolean)); 42 | _add(new configItem("h_type", &header_type, itemType::string)); 43 | _add(new configItem("ed_name", &ws_early_data_name, itemType::string)); 44 | _add(new configItem("ed_len", &ws_early_data_length, itemType::integer)); 45 | _add(new configItem("utls", &utlsFingerprint, itemType::string)); 46 | _add(new configItem("pbk", &reality_pbk, itemType::string)); 47 | _add(new configItem("sid", &reality_sid, itemType::string)); 48 | _add(new configItem("spx", &reality_spx, itemType::string)); 49 | _add(new configItem("mux_s", &multiplex_status, itemType::integer)); 50 | } 51 | 52 | void BuildStreamSettingsSingBox(QJsonObject *outbound); 53 | }; 54 | 55 | inline V2rayStreamSettings *GetStreamSettings(AbstractBean *bean) { 56 | if (bean == nullptr) return nullptr; 57 | auto stream_item = bean->_get("stream"); 58 | if (stream_item != nullptr) { 59 | auto stream_store = (JsonStore *) stream_item->ptr; 60 | auto stream = (NekoGui_fmt::V2rayStreamSettings *) stream_store; 61 | return stream; 62 | } 63 | return nullptr; 64 | } 65 | } // namespace NekoGui_fmt 66 | -------------------------------------------------------------------------------- /fmt/VMessBean.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fmt/AbstractBean.hpp" 4 | #include "fmt/V2RayStreamSettings.hpp" 5 | 6 | namespace NekoGui_fmt { 7 | class VMessBean : public AbstractBean { 8 | public: 9 | QString uuid = ""; 10 | int aid = 0; 11 | QString security = "auto"; 12 | 13 | std::shared_ptr stream = std::make_shared(); 14 | 15 | VMessBean() : AbstractBean(0) { 16 | _add(new configItem("id", &uuid, itemType::string)); 17 | _add(new configItem("aid", &aid, itemType::integer)); 18 | _add(new configItem("sec", &security, itemType::string)); 19 | _add(new configItem("stream", dynamic_cast(stream.get()), itemType::jsonStore)); 20 | }; 21 | 22 | QString DisplayType() override { return "VMess"; }; 23 | 24 | CoreObjOutboundBuildResult BuildCoreObjSingBox() override; 25 | 26 | bool TryParseLink(const QString &link); 27 | 28 | QString ToShareLink() override; 29 | }; 30 | } // namespace NekoGui_fmt 31 | -------------------------------------------------------------------------------- /fmt/includes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SocksHttpBean.hpp" 4 | #include "ShadowSocksBean.hpp" 5 | #include "ChainBean.hpp" 6 | #include "VMessBean.hpp" 7 | #include "TrojanVLESSBean.hpp" 8 | #include "NaiveBean.hpp" 9 | #include "QUICBean.hpp" 10 | #include "CustomBean.hpp" 11 | -------------------------------------------------------------------------------- /go/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.pem 3 | *.json 4 | *.exe 5 | *.dat 6 | /cmd/nekoray_core/nekoray_core 7 | /cmd/nekobox_core/nekobox_core 8 | *.db 9 | -------------------------------------------------------------------------------- /go/cmd/nekobox_core/core_box.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/http" 7 | 8 | "github.com/matsuridayo/libneko/neko_common" 9 | "github.com/matsuridayo/libneko/neko_log" 10 | box "github.com/sagernet/sing-box" 11 | "github.com/sagernet/sing-box/boxapi" 12 | boxmain "github.com/sagernet/sing-box/cmd/sing-box" 13 | ) 14 | 15 | var instance *box.Box 16 | var instance_cancel context.CancelFunc 17 | 18 | func setupCore() { 19 | boxmain.SetDisableColor(true) 20 | // 21 | neko_log.SetupLog(50*1024, "./neko.log") 22 | // 23 | neko_common.GetCurrentInstance = func() interface{} { 24 | return instance 25 | } 26 | neko_common.DialContext = func(ctx context.Context, specifiedInstance interface{}, network, addr string) (net.Conn, error) { 27 | if i, ok := specifiedInstance.(*box.Box); ok { 28 | return boxapi.DialContext(ctx, i, network, addr) 29 | } 30 | if instance != nil { 31 | return boxapi.DialContext(ctx, instance, network, addr) 32 | } 33 | return neko_common.DialContextSystem(ctx, network, addr) 34 | } 35 | neko_common.DialUDP = func(ctx context.Context, specifiedInstance interface{}) (net.PacketConn, error) { 36 | if i, ok := specifiedInstance.(*box.Box); ok { 37 | return boxapi.DialUDP(ctx, i) 38 | } 39 | if instance != nil { 40 | return boxapi.DialUDP(ctx, instance) 41 | } 42 | return neko_common.DialUDPSystem(ctx) 43 | } 44 | neko_common.CreateProxyHttpClient = func(specifiedInstance interface{}) *http.Client { 45 | if i, ok := specifiedInstance.(*box.Box); ok { 46 | return boxapi.CreateProxyHttpClient(i) 47 | } 48 | return boxapi.CreateProxyHttpClient(instance) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /go/cmd/nekobox_core/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | _ "unsafe" 7 | 8 | "grpc_server" 9 | 10 | "github.com/matsuridayo/libneko/neko_common" 11 | boxmain "github.com/sagernet/sing-box/cmd/sing-box" 12 | "github.com/sagernet/sing-box/constant" 13 | ) 14 | 15 | func main() { 16 | fmt.Println("sing-box:", constant.Version, "NekoBox:", neko_common.Version_neko) 17 | fmt.Println() 18 | 19 | // nekobox_core 20 | if len(os.Args) > 1 && os.Args[1] == "nekobox" { 21 | neko_common.RunMode = neko_common.RunMode_NekoBox_Core 22 | grpc_server.RunCore(setupCore, &server{}) 23 | return 24 | } 25 | 26 | // sing-box 27 | boxmain.Main() 28 | } 29 | -------------------------------------------------------------------------------- /go/cmd/updater/.gitignore: -------------------------------------------------------------------------------- 1 | /updater 2 | /launcher 3 | -------------------------------------------------------------------------------- /go/cmd/updater/go.mod: -------------------------------------------------------------------------------- 1 | module updater 2 | 3 | go 1.18 4 | 5 | require github.com/codeclysm/extract v2.2.0+incompatible 6 | 7 | require ( 8 | github.com/h2non/filetype v1.1.3 // indirect 9 | github.com/juju/errors v0.0.0-20220331221717-b38fca44723b // indirect 10 | github.com/stretchr/testify v1.7.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /go/cmd/updater/go.sum: -------------------------------------------------------------------------------- 1 | github.com/codeclysm/extract v2.2.0+incompatible h1:q3wyckoA30bhUSiwdQezMqVhwd8+WGE64/GL//LtUhI= 2 | github.com/codeclysm/extract v2.2.0+incompatible/go.mod h1:2nhFMPHiU9At61hz+12bfrlpXSUrOnK+wR+KlGO4Uks= 3 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= 6 | github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= 7 | github.com/juju/errors v0.0.0-20220331221717-b38fca44723b h1:AxFeSQJfcm2O3ov1wqAkTKYFsnMw2g1B4PkYujfAdkY= 8 | github.com/juju/errors v0.0.0-20220331221717-b38fca44723b/go.mod h1:jMGj9DWF/qbo91ODcfJq6z/RYc3FX3taCBZMCcpI4Ls= 9 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 10 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 11 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 14 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 15 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 17 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 18 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 19 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 20 | -------------------------------------------------------------------------------- /go/cmd/updater/launcher.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package main 4 | 5 | import ( 6 | "log" 7 | "runtime" 8 | ) 9 | 10 | func Launcher() { 11 | log.Fatalln("launcher is not for your platform", runtime.GOOS) 12 | } 13 | -------------------------------------------------------------------------------- /go/cmd/updater/launcher_linux.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | ) 10 | 11 | var local_qt_theme bool 12 | 13 | func Launcher() { 14 | log.Println("Running as launcher") 15 | wd, _ := filepath.Abs(".") 16 | 17 | _debug := flag.Bool("debug", false, "Debug mode") 18 | flag.Parse() 19 | 20 | cmd := exec.Command("./nekobox", flag.Args()...) 21 | 22 | ld_env := "LD_LIBRARY_PATH=" + filepath.Join(wd, "./usr/lib") 23 | qt_plugin_env := "QT_PLUGIN_PATH=" + filepath.Join(wd, "./usr/plugins") 24 | 25 | // Qt 5.12 abi is usually compatible with system Qt 5.15 26 | // But use package Qt 5.12 by default. 27 | cmd.Env = os.Environ() 28 | cmd.Env = append(cmd.Env, "NKR_FROM_LAUNCHER=1") 29 | cmd.Env = append(cmd.Env, ld_env, qt_plugin_env) 30 | log.Println(ld_env, qt_plugin_env, cmd) 31 | 32 | if *_debug { 33 | cmd.Env = append(cmd.Env, "QT_DEBUG_PLUGINS=1") 34 | cmd.Stdin = os.Stdin 35 | cmd.Stderr = os.Stderr 36 | cmd.Stdout = os.Stdout 37 | cmd.Run() 38 | } else { 39 | cmd.Start() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /go/cmd/updater/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "runtime" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | func main() { 15 | // update & launcher 16 | exe, err := os.Executable() 17 | if err != nil { 18 | panic(err.Error()) 19 | } 20 | 21 | wd := filepath.Dir(exe) 22 | os.Chdir(wd) 23 | exe = filepath.Base(os.Args[0]) 24 | log.Println("exe:", exe, "exe dir:", wd) 25 | 26 | if strings.HasPrefix(strings.ToLower(exe), "updater") { 27 | if runtime.GOOS == "windows" { 28 | if strings.HasPrefix(strings.ToLower(exe), "updater.old") { 29 | // 2. "updater.old" update files 30 | time.Sleep(time.Second) 31 | Updater() 32 | // 3. start 33 | exec.Command("./nekobox.exe").Start() 34 | } else { 35 | // 1. main prog quit and run "updater.exe" 36 | Copy("./updater.exe", "./updater.old") 37 | exec.Command("./updater.old", os.Args[1:]...).Start() 38 | } 39 | } else { 40 | // 1. update files 41 | Updater() 42 | // 2. start 43 | if os.Getenv("NKR_FROM_LAUNCHER") == "1" { 44 | Launcher() 45 | } else { 46 | exec.Command("./nekobox").Start() 47 | } 48 | } 49 | return 50 | } else if strings.HasPrefix(strings.ToLower(exe), "launcher") { 51 | Launcher() 52 | return 53 | } 54 | log.Fatalf("wrong name") 55 | } 56 | 57 | func Copy(src string, dst string) { 58 | // Read all content of src to data 59 | data, _ := ioutil.ReadFile(src) 60 | // Write data to dst 61 | ioutil.WriteFile(dst, data, 0644) 62 | } 63 | -------------------------------------------------------------------------------- /go/cmd/updater/msgbox.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package main 4 | 5 | func MessageBoxPlain(title, caption string) int { 6 | return 0 7 | } 8 | -------------------------------------------------------------------------------- /go/cmd/updater/msgbox_windows.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | // MessageBoxPlain of Win32 API. 9 | func MessageBoxPlain(title, caption string) int { 10 | const ( 11 | NULL = 0 12 | MB_OK = 0 13 | ) 14 | return MessageBox(NULL, caption, title, MB_OK) 15 | } 16 | 17 | // MessageBox of Win32 API. 18 | func MessageBox(hwnd uintptr, caption, title string, flags uint) int { 19 | ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call( 20 | uintptr(hwnd), 21 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))), 22 | uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))), 23 | uintptr(flags)) 24 | 25 | return int(ret) 26 | } 27 | -------------------------------------------------------------------------------- /go/cmd/updater/updater.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "runtime" 9 | "strings" 10 | 11 | "github.com/codeclysm/extract" 12 | ) 13 | 14 | func Updater() { 15 | pre_cleanup := func() { 16 | if runtime.GOOS == "linux" { 17 | os.RemoveAll("./usr") 18 | } 19 | os.RemoveAll("./nekoray_update") 20 | } 21 | 22 | // find update package 23 | var updatePackagePath string 24 | if len(os.Args) == 2 && Exist(os.Args[1]) { 25 | updatePackagePath = os.Args[1] 26 | } else if Exist("./nekoray.zip") { 27 | updatePackagePath = "./nekoray.zip" 28 | } else if Exist("./nekoray.tar.gz") { 29 | updatePackagePath = "./nekoray.tar.gz" 30 | } else { 31 | log.Fatalln("no update") 32 | } 33 | log.Println("updating from", updatePackagePath) 34 | 35 | // extract update package 36 | if strings.HasSuffix(updatePackagePath, ".zip") { 37 | pre_cleanup() 38 | f, err := os.Open(updatePackagePath) 39 | if err != nil { 40 | log.Fatalln(err.Error()) 41 | } 42 | err = extract.Zip(context.Background(), f, "./nekoray_update", nil) 43 | if err != nil { 44 | log.Fatalln(err.Error()) 45 | } 46 | f.Close() 47 | } else if strings.HasSuffix(updatePackagePath, ".tar.gz") { 48 | pre_cleanup() 49 | f, err := os.Open(updatePackagePath) 50 | if err != nil { 51 | log.Fatalln(err.Error()) 52 | } 53 | err = extract.Gz(context.Background(), f, "./nekoray_update", nil) 54 | if err != nil { 55 | log.Fatalln(err.Error()) 56 | } 57 | f.Close() 58 | } 59 | 60 | // remove old file 61 | removeAll("./*.dll") 62 | removeAll("./*.dmp") 63 | 64 | // update move 65 | err := Mv("./nekoray_update/nekoray", "./") 66 | if err != nil { 67 | MessageBoxPlain("NekoGui Updater", "Update failed. Please close the running instance and run the updater again.\n\n"+err.Error()) 68 | log.Fatalln(err.Error()) 69 | } 70 | 71 | os.RemoveAll("./nekoray_update") 72 | os.RemoveAll("./nekoray.zip") 73 | os.RemoveAll("./nekoray.tar.gz") 74 | 75 | // nekoray -> nekobox 76 | os.Remove("./nekoray.exe") 77 | os.Remove("./nekoray.png") 78 | os.Remove("./nekoray_core.exe") 79 | } 80 | 81 | func Exist(path string) bool { 82 | _, err := os.Stat(path) 83 | return err == nil 84 | } 85 | 86 | func FindExist(paths []string) string { 87 | for _, path := range paths { 88 | if Exist(path) { 89 | return path 90 | } 91 | } 92 | return "" 93 | } 94 | 95 | func Mv(src, dst string) error { 96 | s, err := os.Stat(src) 97 | if err != nil { 98 | return err 99 | } 100 | if s.IsDir() { 101 | es, err := os.ReadDir(src) 102 | if err != nil { 103 | return err 104 | } 105 | for _, e := range es { 106 | err = Mv(filepath.Join(src, e.Name()), filepath.Join(dst, e.Name())) 107 | if err != nil { 108 | return err 109 | } 110 | } 111 | } else { 112 | err = os.MkdirAll(filepath.Dir(dst), 0755) 113 | if err != nil { 114 | return err 115 | } 116 | err = os.Rename(src, dst) 117 | if err != nil { 118 | return err 119 | } 120 | } 121 | return nil 122 | } 123 | 124 | func removeAll(glob string) { 125 | files, _ := filepath.Glob(glob) 126 | for _, f := range files { 127 | os.Remove(f) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /go/grpc_server/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc/codes" 7 | "google.golang.org/grpc/metadata" 8 | "google.golang.org/grpc/status" 9 | ) 10 | 11 | // Authenticator exposes a function for authenticating requests. 12 | type Authenticator struct { 13 | Token string 14 | } 15 | 16 | // Authenticate checks that a token exists and is valid. It stores the user 17 | // metadata in the returned context and removes the token from the context. 18 | func (a Authenticator) Authenticate(ctx context.Context) (newCtx context.Context, err error) { 19 | auth, err := extractHeader(ctx, "nekoray_auth") 20 | if err != nil { 21 | return ctx, err 22 | } 23 | 24 | if auth != a.Token { 25 | return ctx, status.Error(codes.Unauthenticated, "invalid token") 26 | } 27 | 28 | return purgeHeader(ctx, "nekoray_auth"), nil 29 | } 30 | 31 | func extractHeader(ctx context.Context, header string) (string, error) { 32 | md, ok := metadata.FromIncomingContext(ctx) 33 | if !ok { 34 | return "", status.Error(codes.Unauthenticated, "no headers in request") 35 | } 36 | 37 | authHeaders, ok := md[header] 38 | if !ok { 39 | return "", status.Error(codes.Unauthenticated, "no header in request") 40 | } 41 | 42 | if len(authHeaders) != 1 { 43 | return "", status.Error(codes.Unauthenticated, "more than 1 header in request") 44 | } 45 | 46 | return authHeaders[0], nil 47 | } 48 | 49 | func purgeHeader(ctx context.Context, header string) context.Context { 50 | md, _ := metadata.FromIncomingContext(ctx) 51 | mdCopy := md.Copy() 52 | mdCopy[header] = nil 53 | return metadata.NewIncomingContext(ctx, mdCopy) 54 | } 55 | -------------------------------------------------------------------------------- /go/grpc_server/gen/libcore.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package libcore; 4 | option go_package = "grpc_server/gen"; 5 | 6 | service LibcoreService { 7 | rpc Exit(EmptyReq) returns (EmptyResp) {} 8 | rpc Update(UpdateReq) returns (UpdateResp) {} 9 | // 10 | rpc Start(LoadConfigReq) returns (ErrorResp) {} 11 | rpc Stop(EmptyReq) returns (ErrorResp) {} 12 | rpc Test(TestReq) returns (TestResp) {} 13 | rpc QueryStats(QueryStatsReq) returns (QueryStatsResp) {} 14 | rpc ListConnections(EmptyReq) returns (ListConnectionsResp) {} 15 | } 16 | 17 | message EmptyReq {} 18 | 19 | message EmptyResp {} 20 | 21 | message ErrorResp { 22 | string error = 1; 23 | } 24 | 25 | message LoadConfigReq { 26 | string core_config = 1; 27 | bool enable_nekoray_connections = 2; 28 | repeated string stats_outbounds = 3; 29 | } 30 | 31 | enum TestMode { 32 | TcpPing = 0; 33 | UrlTest = 1; 34 | FullTest = 2; 35 | } 36 | 37 | message TestReq { 38 | TestMode mode = 1; 39 | int32 timeout = 6; 40 | // TcpPing 41 | string address = 2; 42 | // UrlTest 43 | LoadConfigReq config = 3; 44 | string inbound = 4; 45 | string url = 5; 46 | // FullTest 47 | string in_address = 7; 48 | bool full_latency = 8; 49 | bool full_speed = 9; 50 | string full_speed_url = 13; 51 | int32 full_speed_timeout = 14; 52 | bool full_in_out = 10; 53 | bool full_udp_latency = 12; 54 | // 55 | bool full_nat = 11 [deprecated = true]; 56 | } 57 | 58 | message TestResp { 59 | string error = 1; 60 | int32 ms = 2; 61 | string full_report = 3; 62 | } 63 | 64 | message QueryStatsReq{ 65 | string tag = 1; 66 | string direct = 2; 67 | } 68 | 69 | message QueryStatsResp{ 70 | int64 traffic = 1; 71 | } 72 | 73 | enum UpdateAction { 74 | Check = 0; 75 | Download = 1; 76 | } 77 | 78 | message UpdateReq { 79 | UpdateAction action = 1; 80 | bool check_pre_release = 2; 81 | } 82 | 83 | message UpdateResp { 84 | string error = 1; 85 | string assets_name = 2; 86 | string download_url = 3; 87 | string release_url = 4; 88 | string release_note = 5; 89 | bool is_pre_release = 6; 90 | } 91 | 92 | message ListConnectionsResp { 93 | string nekoray_connections_json = 1; 94 | } 95 | -------------------------------------------------------------------------------- /go/grpc_server/gen/update_proto.sh: -------------------------------------------------------------------------------- 1 | protoc -I . --go_out=. --go_opt paths=source_relative --go-grpc_out=. --go-grpc_opt paths=source_relative libcore.proto 2 | 3 | # protoc -I . --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` libcore.proto 4 | -------------------------------------------------------------------------------- /go/grpc_server/go.mod: -------------------------------------------------------------------------------- 1 | module grpc_server 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 7 | github.com/matsuridayo/libneko v1.0.0 // replaced 8 | google.golang.org/grpc v1.49.0 9 | google.golang.org/protobuf v1.28.1 10 | ) 11 | 12 | require ( 13 | github.com/golang/protobuf v1.5.2 // indirect 14 | github.com/google/go-cmp v0.5.8 // indirect 15 | golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect 16 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect 17 | golang.org/x/text v0.3.7 // indirect 18 | google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect 19 | ) 20 | 21 | replace github.com/matsuridayo/libneko v1.0.0 => ../../../libneko 22 | -------------------------------------------------------------------------------- /go/grpc_server/grpc.go: -------------------------------------------------------------------------------- 1 | package grpc_server 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "flag" 7 | "fmt" 8 | "grpc_server/auth" 9 | "grpc_server/gen" 10 | "log" 11 | "net" 12 | "os" 13 | "runtime" 14 | "strconv" 15 | "strings" 16 | "syscall" 17 | "time" 18 | 19 | "github.com/matsuridayo/libneko/neko_common" 20 | 21 | grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" 22 | "google.golang.org/grpc" 23 | ) 24 | 25 | type BaseServer struct { 26 | gen.LibcoreServiceServer 27 | } 28 | 29 | func (s *BaseServer) Exit(ctx context.Context, in *gen.EmptyReq) (out *gen.EmptyResp, _ error) { 30 | out = &gen.EmptyResp{} 31 | 32 | // Connection closed 33 | os.Exit(0) 34 | return 35 | } 36 | 37 | func RunCore(setupCore func(), server gen.LibcoreServiceServer) { 38 | _token := flag.String("token", "", "") 39 | _port := flag.Int("port", 19810, "") 40 | _debug := flag.Bool("debug", false, "") 41 | flag.CommandLine.Parse(os.Args[2:]) 42 | 43 | neko_common.Debug = *_debug 44 | 45 | go func() { 46 | parent, err := os.FindProcess(os.Getppid()) 47 | if err != nil { 48 | log.Fatalln("find parent:", err) 49 | } 50 | if runtime.GOOS == "windows" { 51 | state, err := parent.Wait() 52 | log.Fatalln("parent exited:", state, err) 53 | } else { 54 | for { 55 | time.Sleep(time.Second * 10) 56 | err = parent.Signal(syscall.Signal(0)) 57 | if err != nil { 58 | log.Fatalln("parent exited:", err) 59 | } 60 | } 61 | } 62 | }() 63 | 64 | // Libcore 65 | setupCore() 66 | 67 | // GRPC 68 | lis, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(*_port)) 69 | if err != nil { 70 | log.Fatalf("failed to listen: %v", err) 71 | } 72 | 73 | token := *_token 74 | if token == "" { 75 | os.Stderr.WriteString("Please set a token: ") 76 | s := bufio.NewScanner(os.Stdin) 77 | if s.Scan() { 78 | token = strings.TrimSpace(s.Text()) 79 | } 80 | } 81 | if token == "" { 82 | fmt.Println("You must set a token") 83 | os.Exit(0) 84 | } 85 | os.Stderr.WriteString("token is set\n") 86 | 87 | auther := auth.Authenticator{ 88 | Token: token, 89 | } 90 | 91 | s := grpc.NewServer( 92 | grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(auther.Authenticate)), 93 | grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(auther.Authenticate)), 94 | ) 95 | gen.RegisterLibcoreServiceServer(s, server) 96 | 97 | name := "nekobox_core" 98 | 99 | log.Printf("%s grpc server listening at %v\n", name, lis.Addr()) 100 | if err := s.Serve(lis); err != nil { 101 | log.Fatalf("failed to serve: %v", err) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /go/grpc_server/update.go: -------------------------------------------------------------------------------- 1 | package grpc_server 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "grpc_server/gen" 7 | "io" 8 | "net/http" 9 | "os" 10 | "runtime" 11 | "strings" 12 | "time" 13 | 14 | "github.com/matsuridayo/libneko/neko_common" 15 | ) 16 | 17 | var update_download_url string 18 | 19 | func (s *BaseServer) Update(ctx context.Context, in *gen.UpdateReq) (*gen.UpdateResp, error) { 20 | ret := &gen.UpdateResp{} 21 | 22 | client := neko_common.CreateProxyHttpClient(neko_common.GetCurrentInstance()) 23 | 24 | if in.Action == gen.UpdateAction_Check { // Check update 25 | ctx, cancel := context.WithTimeout(ctx, time.Second*10) 26 | defer cancel() 27 | 28 | req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.github.com/repos/MatsuriDayo/nekoray/releases", nil) 29 | resp, err := client.Do(req) 30 | if err != nil { 31 | ret.Error = err.Error() 32 | return ret, nil 33 | } 34 | defer resp.Body.Close() 35 | 36 | v := []struct { 37 | HtmlUrl string `json:"html_url"` 38 | Assets []struct { 39 | Name string `json:"name"` 40 | BrowserDownloadUrl string `json:"browser_download_url"` 41 | } `json:"assets"` 42 | Prerelease bool `json:"prerelease"` 43 | Body string `json:"body"` 44 | }{} 45 | err = json.NewDecoder(resp.Body).Decode(&v) 46 | if err != nil { 47 | ret.Error = err.Error() 48 | return ret, nil 49 | } 50 | 51 | nowVer := strings.TrimLeft(neko_common.Version_neko, "nekoray-") 52 | 53 | var search string 54 | if runtime.GOOS == "windows" && runtime.GOARCH == "amd64" { 55 | search = "windows64" 56 | } else if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" { 57 | search = "linux64" 58 | } else if runtime.GOOS == "darwin" { 59 | search = "macos-" + runtime.GOARCH 60 | } else { 61 | ret.Error = "Not official support platform" 62 | return ret, nil 63 | } 64 | 65 | for _, release := range v { 66 | if len(release.Assets) > 0 { 67 | for _, asset := range release.Assets { 68 | if strings.Contains(asset.Name, nowVer) { 69 | return ret, nil // No update 70 | } 71 | if strings.Contains(asset.Name, search) { 72 | if release.Prerelease && !in.CheckPreRelease { 73 | continue 74 | } 75 | update_download_url = asset.BrowserDownloadUrl 76 | ret.AssetsName = asset.Name 77 | ret.DownloadUrl = asset.BrowserDownloadUrl 78 | ret.ReleaseUrl = release.HtmlUrl 79 | ret.ReleaseNote = release.Body 80 | ret.IsPreRelease = release.Prerelease 81 | return ret, nil // update 82 | } 83 | } 84 | } 85 | } 86 | } else { // Download update 87 | if update_download_url == "" { 88 | ret.Error = "?" 89 | return ret, nil 90 | } 91 | 92 | req, _ := http.NewRequestWithContext(ctx, "GET", update_download_url, nil) 93 | resp, err := client.Do(req) 94 | if err != nil { 95 | ret.Error = err.Error() 96 | return ret, nil 97 | } 98 | defer resp.Body.Close() 99 | 100 | f, err := os.OpenFile("../nekoray.zip", os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0644) 101 | if err != nil { 102 | ret.Error = err.Error() 103 | return ret, nil 104 | } 105 | defer f.Close() 106 | 107 | _, err = io.Copy(f, resp.Body) 108 | if err != nil { 109 | ret.Error = err.Error() 110 | return ret, nil 111 | } 112 | f.Sync() 113 | } 114 | 115 | return ret, nil 116 | } 117 | -------------------------------------------------------------------------------- /libs/.gitignore: -------------------------------------------------------------------------------- 1 | /deps* 2 | downloaded -------------------------------------------------------------------------------- /libs/build_deps_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd libs 5 | 6 | # 参数 7 | if [ -z $cmake ]; then 8 | cmake="cmake" 9 | fi 10 | if [ -z $deps ]; then 11 | deps="deps" 12 | fi 13 | 14 | # libs/deps/... 15 | mkdir -p $deps 16 | cd $deps 17 | if [ -z $NKR_PACKAGE ]; then 18 | INSTALL_PREFIX=$PWD/built 19 | else 20 | INSTALL_PREFIX=$PWD/package 21 | fi 22 | rm -rf $INSTALL_PREFIX 23 | mkdir -p $INSTALL_PREFIX 24 | 25 | #### clean #### 26 | clean() { 27 | rm -rf dl.zip yaml-* zxing-* protobuf 28 | } 29 | 30 | #### ZXing v2.0.0 #### 31 | curl -L -o dl.zip https://github.com/nu-book/zxing-cpp/archive/refs/tags/v2.0.0.zip 32 | unzip dl.zip 33 | 34 | cd zxing-* 35 | mkdir -p build 36 | cd build 37 | 38 | $cmake .. -GNinja -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLES=OFF -DBUILD_BLACKBOX_TESTS=OFF -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX 39 | ninja && ninja install 40 | 41 | cd ../.. 42 | 43 | #### yaml-cpp #### 44 | curl -L -o dl.zip https://github.com/jbeder/yaml-cpp/archive/refs/tags/yaml-cpp-0.7.0.zip 45 | unzip dl.zip 46 | 47 | cd yaml-* 48 | mkdir -p build 49 | cd build 50 | 51 | $cmake .. -GNinja -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX 52 | ninja && ninja install 53 | 54 | cd ../.. 55 | 56 | #### protobuf #### 57 | git clone --recurse-submodules -b v21.4 --depth 1 --shallow-submodules https://github.com/protocolbuffers/protobuf 58 | 59 | #备注:交叉编译要在 host 也安装 protobuf 并且版本一致,编译安装,同参数,安装到 /usr/local 60 | 61 | mkdir -p protobuf/build 62 | cd protobuf/build 63 | 64 | $cmake .. -GNinja \ 65 | -DCMAKE_BUILD_TYPE=Release \ 66 | -DBUILD_SHARED_LIBS=OFF \ 67 | -Dprotobuf_MSVC_STATIC_RUNTIME=OFF \ 68 | -Dprotobuf_BUILD_TESTS=OFF \ 69 | -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX 70 | ninja && ninja install 71 | 72 | cd ../.. 73 | 74 | #### 75 | clean 76 | -------------------------------------------------------------------------------- /libs/build_go.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source libs/env_deploy.sh 5 | [ "$GOOS" == "windows" ] && [ "$GOARCH" == "amd64" ] && DEST=$DEPLOYMENT/windows64 || true 6 | [ "$GOOS" == "windows" ] && [ "$GOARCH" == "arm64" ] && DEST=$DEPLOYMENT/windows-arm64 || true 7 | [ "$GOOS" == "linux" ] && [ "$GOARCH" == "amd64" ] && DEST=$DEPLOYMENT/linux64 || true 8 | [ "$GOOS" == "linux" ] && [ "$GOARCH" == "arm64" ] && DEST=$DEPLOYMENT/linux-arm64 || true 9 | if [ -z $DEST ]; then 10 | echo "Please set GOOS GOARCH" 11 | exit 1 12 | fi 13 | rm -rf $DEST 14 | mkdir -p $DEST 15 | 16 | export CGO_ENABLED=0 17 | 18 | #### Go: updater #### 19 | pushd go/cmd/updater 20 | [ "$GOOS" == "darwin" ] || go build -o $DEST -trimpath -ldflags "-w -s" 21 | [ "$GOOS" == "linux" ] && mv $DEST/updater $DEST/launcher || true 22 | popd 23 | 24 | #### Go: nekobox_core #### 25 | pushd go/cmd/nekobox_core 26 | go build -v -o $DEST -trimpath -ldflags "-w -s -X github.com/matsuridayo/libneko/neko_common.Version_neko=$version_standalone" -tags "with_clash_api,with_gvisor,with_quic,with_wireguard,with_utls,with_ech" 27 | popd 28 | -------------------------------------------------------------------------------- /libs/build_public_res.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source libs/env_deploy.sh 5 | DEST=$DEPLOYMENT/public_res 6 | rm -rf $DEST 7 | mkdir -p $DEST 8 | 9 | #### Download geodata #### 10 | curl -fLso $DEST/geoip.dat "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" 11 | curl -fLso $DEST/geosite.dat "https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat" 12 | curl -fLso $DEST/geoip.db "https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db" 13 | curl -fLso $DEST/geosite.db "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db" 14 | 15 | #### copy res/public #### 16 | cp res/public/* $DEST 17 | -------------------------------------------------------------------------------- /libs/deploy_linux64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source libs/env_deploy.sh 5 | DEST=$DEPLOYMENT/linux64 6 | rm -rf $DEST 7 | mkdir -p $DEST 8 | 9 | #### copy binary #### 10 | cp $BUILD/nekobox $DEST 11 | 12 | #### Download: prebuilt runtime #### 13 | curl -Lso usr.zip https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/20230202-5.12.8-ubuntu20.04-linux64.zip 14 | unzip usr.zip 15 | mv usr $DEST 16 | 17 | 18 | #### copy so #### 19 | # 5.11 looks buggy on new systems... 20 | exit 21 | 22 | USR_LIB=/usr/lib/x86_64-linux-gnu 23 | mkdir usr 24 | pushd usr 25 | mkdir lib 26 | pushd lib 27 | cp $USR_LIB/libQt5Core.so.5 . 28 | cp $USR_LIB/libQt5DBus.so.5 . 29 | cp $USR_LIB/libQt5Gui.so.5 . 30 | cp $USR_LIB/libQt5Network.so.5 . 31 | cp $USR_LIB/libQt5Svg.so.5 . 32 | cp $USR_LIB/libQt5Widgets.so.5 . 33 | cp $USR_LIB/libQt5X11Extras.so.5 . 34 | cp $USR_LIB/libQt5XcbQpa.so.5 . 35 | cp $USR_LIB/libdouble-conversion.so.? . 36 | cp $USR_LIB/libxcb-util.so.? . 37 | cp $USR_LIB/libicuuc.so.?? . 38 | cp $USR_LIB/libicui18n.so.?? . 39 | cp $USR_LIB/libicudata.so.?? . 40 | popd 41 | mkdir plugins 42 | pushd plugins 43 | cp -r $USR_LIB/qt5/plugins/bearer . 44 | cp -r $USR_LIB/qt5/plugins/iconengines . 45 | cp -r $USR_LIB/qt5/plugins/imageformats . 46 | cp -r $USR_LIB/qt5/plugins/platforminputcontexts . 47 | cp -r $USR_LIB/qt5/plugins/platforms . 48 | cp -r $USR_LIB/qt5/plugins/xcbglintegrations . 49 | popd 50 | popd 51 | mv usr $DEST 52 | -------------------------------------------------------------------------------- /libs/deploy_windows64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source libs/env_deploy.sh 5 | DEST=$DEPLOYMENT/windows64 6 | rm -rf $DEST 7 | mkdir -p $DEST 8 | 9 | #### copy exe #### 10 | cp $BUILD/nekobox.exe $DEST 11 | 12 | #### deploy qt & DLL runtime #### 13 | pushd $DEST 14 | windeployqt nekobox.exe --no-compiler-runtime --no-system-d3d-compiler --no-opengl-sw --verbose 2 15 | rm -rf translations 16 | rm -rf libEGL.dll libGLESv2.dll Qt6Pdf.dll 17 | 18 | if [ "$DL_QT_VER" != "5.15" ]; then 19 | cp $SRC_ROOT/qtsdk/Qt/bin/libcrypto-3-x64.dll . 20 | cp $SRC_ROOT/qtsdk/Qt/bin/libssl-3-x64.dll . 21 | fi 22 | 23 | popd 24 | 25 | #### prepare deployment #### 26 | cp $BUILD/*.pdb $DEPLOYMENT 27 | -------------------------------------------------------------------------------- /libs/download_qtsdk_win.sh: -------------------------------------------------------------------------------- 1 | mkdir qtsdk 2 | cd qtsdk 3 | if [ "$DL_QT_VER" == "5.15" ]; then 4 | curl -LSO https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/Qt5.15.7-Windows-x86_64-VS2019-16.11.20-20221103.7z 5 | else 6 | curl -LSO https://github.com/MatsuriDayo/nekoray_qt_runtime/releases/download/20220503/Qt6.7.2-Windows-x86_64-VS2022-17.10.3-20240621.7z 7 | fi 8 | 7z x *.7z 9 | rm *.7z 10 | mv Qt* Qt 11 | -------------------------------------------------------------------------------- /libs/env_deploy.sh: -------------------------------------------------------------------------------- 1 | SRC_ROOT="$PWD" 2 | DEPLOYMENT="$SRC_ROOT/deployment" 3 | BUILD="$SRC_ROOT/build" 4 | version_standalone="nekoray-"$(cat nekoray_version.txt) # 下次改 5 | -------------------------------------------------------------------------------- /libs/env_qtsdk.sh: -------------------------------------------------------------------------------- 1 | echo "Setting Qt Sdk Dir to" "$1" 2 | export Qt5_DIR="$1" 3 | export Qt6_DIR=$Qt5_DIR 4 | export PATH=$PATH:$Qt5_DIR/bin 5 | export LD_LIBRARY_PATH=$Qt5_DIR/lib 6 | export PKG_CONFIG_PATH=$Qt5_DIR/lib/pkgconfig 7 | export QT_PLUGIN_PATH=$Qt5_DIR/plugins 8 | export QML2_IMPORT_PATH=$Qt5_DIR/lib/qml 9 | -------------------------------------------------------------------------------- /libs/format_cpp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | git ls-files | grep -E "\.cpp|\.h" | grep -v "3rdparty" | xargs -n1 clang-format -i 3 | -------------------------------------------------------------------------------- /libs/get_source.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source libs/env_deploy.sh 5 | ENV_NEKORAY=1 6 | source libs/get_source_env.sh 7 | pushd .. 8 | 9 | #### 10 | 11 | if [ ! -d "sing-box" ]; then 12 | git clone --no-checkout https://github.com/MatsuriDayo/sing-box.git 13 | fi 14 | pushd sing-box 15 | git checkout "$COMMIT_SING_BOX" 16 | 17 | popd 18 | 19 | #### 20 | 21 | if [ ! -d "sing-quic" ]; then 22 | git clone --no-checkout https://github.com/MatsuriDayo/sing-quic.git 23 | fi 24 | pushd sing-quic 25 | git checkout "$COMMIT_SING_QUIC" 26 | 27 | popd 28 | 29 | #### 30 | 31 | if [ ! -d "libneko" ]; then 32 | git clone --no-checkout https://github.com/MatsuriDayo/libneko.git 33 | fi 34 | pushd libneko 35 | git checkout "$COMMIT_LIBNEKO" 36 | 37 | popd 38 | 39 | #### 40 | 41 | popd 42 | -------------------------------------------------------------------------------- /libs/get_source_env.sh: -------------------------------------------------------------------------------- 1 | export COMMIT_SING_BOX="06557f6cef23160668122a17a818b378b5a216b5" 2 | export COMMIT_SING_QUIC="b49ce60d9b3622d5238fee96bfd3c5f6e3915b42" 3 | export COMMIT_LIBNEKO="1c47a3af71990a7b2192e03292b4d246c308ef0b" 4 | -------------------------------------------------------------------------------- /libs/package_appimage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo apt-get install fuse -y 4 | 5 | cp -r linux64 nekobox.AppDir 6 | 7 | # The file for Appimage 8 | 9 | rm nekobox.AppDir/launcher 10 | 11 | cat >nekobox.AppDir/nekobox.desktop <<-EOF 12 | [Desktop Entry] 13 | Name=nekobox 14 | Exec=echo "nekobox started" 15 | Icon=nekobox 16 | Type=Application 17 | Categories=Network 18 | EOF 19 | 20 | cat >nekobox.AppDir/AppRun <<-EOF 21 | #!/bin/bash 22 | echo "PATH: \${PATH}" 23 | echo "nekobox runing on: \$APPDIR" 24 | LD_LIBRARY_PATH=\${APPDIR}/usr/lib QT_PLUGIN_PATH=\${APPDIR}/usr/plugins \${APPDIR}/nekobox -appdata "\$@" 25 | EOF 26 | 27 | chmod +x nekobox.AppDir/AppRun 28 | 29 | # build 30 | 31 | curl -fLSO https://github.com/AppImage/AppImageKit/releases/latest/download/appimagetool-x86_64.AppImage 32 | chmod +x appimagetool-x86_64.AppImage 33 | ./appimagetool-x86_64.AppImage nekobox.AppDir 34 | 35 | # clean 36 | 37 | rm appimagetool-x86_64.AppImage 38 | rm -rf nekobox.AppDir 39 | -------------------------------------------------------------------------------- /libs/package_debian.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version="$1" 4 | 5 | mkdir -p nekoray/DEBIAN 6 | mkdir -p nekoray/opt 7 | cp -r linux64 nekoray/opt/ 8 | mv nekoray/opt/linux64 nekoray/opt/nekoray 9 | rm -rf nekoray/opt/nekoray/usr 10 | rm nekoray/opt/nekoray/launcher 11 | 12 | # basic 13 | cat >nekoray/DEBIAN/control <<-EOF 14 | Package: nekoray 15 | Version: $version 16 | Architecture: amd64 17 | Maintainer: MatsuriDayo nekoha_matsuri@protonmail.com 18 | Depends: libxcb-xinerama0, libqt5core5a, libqt5gui5, libqt5network5, libqt5widgets5, libqt5svg5, libqt5x11extras5, desktop-file-utils 19 | Description: Qt based cross-platform GUI proxy configuration manager (backend: v2ray / sing-box) 20 | EOF 21 | 22 | cat >nekoray/DEBIAN/postinst <<-EOF 23 | if [ ! -s /usr/share/applications/nekoray.desktop ]; then 24 | cat >/usr/share/applications/nekoray.desktop<<-END 25 | [Desktop Entry] 26 | Name=nekoray 27 | Comment=Qt based cross-platform GUI proxy configuration manager (backend: sing-box) 28 | Exec=sh -c "PATH=/opt/nekoray:\$PATH /opt/nekoray/nekobox -appdata" 29 | Icon=/opt/nekoray/nekobox.png 30 | Terminal=false 31 | Type=Application 32 | Categories=Network;Application; 33 | END 34 | fi 35 | 36 | setcap cap_net_admin=ep /opt/nekoray/nekobox_core 37 | 38 | update-desktop-database 39 | EOF 40 | 41 | sudo chmod 0755 nekoray/DEBIAN/postinst 42 | 43 | # desktop && PATH 44 | 45 | sudo dpkg-deb -Zxz --build nekoray 46 | -------------------------------------------------------------------------------- /main/Const.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace NekoGui { 4 | namespace DomainMatcher { 5 | enum DomainMatcher { 6 | DEFAULT, 7 | MPH, 8 | }; 9 | } 10 | 11 | namespace SniffingMode { 12 | enum SniffingMode { 13 | DISABLE, 14 | FOR_ROUTING, 15 | FOR_DESTINATION, 16 | }; 17 | } 18 | 19 | namespace CoreType { 20 | enum CoreType { 21 | V2RAY, // DO NOT USE 22 | SING_BOX, 23 | }; 24 | } 25 | } // namespace NekoGui 26 | -------------------------------------------------------------------------------- /main/HTTPRequestHelper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace NekoGui_network { 10 | struct NekoHTTPResponse { 11 | QString error; 12 | QByteArray data; 13 | QList> header; 14 | }; 15 | 16 | class NetworkRequestHelper : QObject { 17 | Q_OBJECT 18 | 19 | explicit NetworkRequestHelper(QObject *parent) : QObject(parent){}; 20 | 21 | ~NetworkRequestHelper() override = default; 22 | ; 23 | 24 | public: 25 | static NekoHTTPResponse HttpGet(const QUrl &url); 26 | 27 | static QString GetHeader(const QList> &header, const QString &name); 28 | }; 29 | } // namespace NekoGui_network 30 | 31 | using namespace NekoGui_network; 32 | -------------------------------------------------------------------------------- /main/NekoGui.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Const.hpp" 4 | #include "NekoGui_Utils.hpp" 5 | #include "NekoGui_ConfigItem.hpp" 6 | #include "NekoGui_DataStore.hpp" 7 | 8 | // Switch core support 9 | 10 | namespace NekoGui { 11 | inline int coreType = CoreType::SING_BOX; 12 | 13 | QString FindCoreAsset(const QString &name); 14 | 15 | QString FindNekoBoxCoreRealPath(); 16 | 17 | bool IsAdmin(); 18 | } // namespace NekoGui 19 | 20 | #define ROUTES_PREFIX_NAME QStringLiteral("routes_box") 21 | #define ROUTES_PREFIX QString(ROUTES_PREFIX_NAME + "/") 22 | -------------------------------------------------------------------------------- /main/NekoGui_ConfigItem.hpp: -------------------------------------------------------------------------------- 1 | // DO NOT INCLUDE THIS 2 | 3 | namespace NekoGui_ConfigItem { 4 | // config 工具 5 | enum itemType { 6 | string, 7 | integer, 8 | integer64, 9 | boolean, 10 | stringList, 11 | integerList, 12 | jsonStore, 13 | }; 14 | 15 | class configItem { 16 | public: 17 | QString name; 18 | void *ptr; 19 | itemType type; 20 | 21 | configItem(QString n, void *p, itemType t) { 22 | name = std::move(n); 23 | ptr = p; 24 | type = t; 25 | } 26 | }; 27 | 28 | // 可格式化对象 29 | class JsonStore { 30 | public: 31 | QMap> _map; 32 | 33 | std::function callback_after_load = nullptr; 34 | std::function callback_before_save = nullptr; 35 | 36 | QString fn; 37 | bool load_control_must = false; // must load from file 38 | bool save_control_compact = false; 39 | bool save_control_no_save = false; 40 | QByteArray last_save_content; 41 | 42 | JsonStore() = default; 43 | 44 | explicit JsonStore(QString fileName) { 45 | fn = std::move(fileName); 46 | } 47 | 48 | void _add(configItem *item); 49 | 50 | QString _name(void *p); 51 | 52 | std::shared_ptr _get(const QString &name); 53 | 54 | void _setValue(const QString &name, void *p); 55 | 56 | QJsonObject ToJson(const QStringList &without = {}); 57 | 58 | QByteArray ToJsonBytes(); 59 | 60 | void FromJson(QJsonObject object); 61 | 62 | void FromJsonBytes(const QByteArray &data); 63 | 64 | bool Save(); 65 | 66 | bool Load(); 67 | }; 68 | } // namespace NekoGui_ConfigItem 69 | 70 | using namespace NekoGui_ConfigItem; 71 | -------------------------------------------------------------------------------- /nekoray_version.txt: -------------------------------------------------------------------------------- 1 | 4.0.1-2024-12-12 2 | -------------------------------------------------------------------------------- /res/dashboard-notice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

7 |

Please put your clash dashboard files to "./config/dashboard" dir.

8 |

For example, you can download from the following URL.

9 |

10 | Download Yacd-meta 11 | or 12 | Use online 13 |

14 |

15 | 16 | 17 | -------------------------------------------------------------------------------- /res/icon/internet-web-browser.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icon/material/cancel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icon/material/delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icon/material/history.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icon/material/lock-open-outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icon/material/lock-outline.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icon/material/swap-horizontal.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icon/material/swap-vertical.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/icon/network-server.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 17 | 21 | 25 | 30 | 35 | 36 | -------------------------------------------------------------------------------- /res/icon/system-software-update.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /res/neko.css: -------------------------------------------------------------------------------- 1 | QMessageBox { 2 | messagebox-text-interaction-flags: 5; 3 | } 4 | -------------------------------------------------------------------------------- /res/neko.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icon/internet-web-browser.svg 4 | icon/system-run.svg 5 | icon/preferences.svg 6 | icon/network-server.svg 7 | icon/dialog-question.svg 8 | icon/system-software-update.svg 9 | icon/material/lock-open-outline.svg 10 | icon/material/lock-outline.svg 11 | icon/material/cancel.svg 12 | icon/material/history.svg 13 | icon/material/swap-vertical.svg 14 | icon/material/delete.svg 15 | icon/material/swap-horizontal.svg 16 | 17 | 18 | public/nekobox.png 19 | neko.css 20 | vpn/vpn-run-root.sh 21 | vpn/sing-box-vpn.json 22 | dashboard-notice.html 23 | 24 | 25 | -------------------------------------------------------------------------------- /res/nekobox.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/nekobox.ico -------------------------------------------------------------------------------- /res/public/nekobox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/public/nekobox.png -------------------------------------------------------------------------------- /res/public/qtbase_zh_CN.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/public/qtbase_zh_CN.qm -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/add_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/add_bottom.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/add_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/add_left.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/add_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/add_right.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/add_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/add_top.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/arrow_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/arrow_bottom.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/arrow_left.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/arrow_right.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/arrow_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/arrow_top.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/branch_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/branch_close.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/branch_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/branch_open.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/calendar_nextmonth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/calendar_nextmonth.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/calendar_prevmonth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/calendar_prevmonth.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/checkbox_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/checkbox_checked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/checkbox_checked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/checkbox_checked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/checkbox_parcial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/checkbox_parcial.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/checkbox_parcial_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/checkbox_parcial_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/checkbox_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/checkbox_unchecked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/checkbox_unchecked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/checkbox_unchecked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/menu_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/menu_checked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/radiobutton_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/radiobutton_checked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/radiobutton_checked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/radiobutton_checked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/radiobutton_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/radiobutton_unchecked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/blacksoft/radiobutton_unchecked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/blacksoft/radiobutton_unchecked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/add_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/add_bottom.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/add_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/add_left.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/add_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/add_right.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/add_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/add_top.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/arrow_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/arrow_bottom.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/arrow_left.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/arrow_right.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/arrow_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/arrow_top.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/branch_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/branch_close.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/branch_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/branch_open.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/calendar_nextmonth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/calendar_nextmonth.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/calendar_prevmonth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/calendar_prevmonth.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/checkbox_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/checkbox_checked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/checkbox_checked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/checkbox_checked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/checkbox_parcial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/checkbox_parcial.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/checkbox_parcial_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/checkbox_parcial_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/checkbox_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/checkbox_unchecked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/checkbox_unchecked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/checkbox_unchecked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/menu_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/menu_checked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/radiobutton_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/radiobutton_checked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/radiobutton_checked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/radiobutton_checked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/radiobutton_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/radiobutton_unchecked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/flatgray/radiobutton_unchecked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/flatgray/radiobutton_unchecked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/add_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/add_bottom.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/add_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/add_left.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/add_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/add_right.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/add_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/add_top.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/arrow_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/arrow_bottom.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/arrow_left.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/arrow_right.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/arrow_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/arrow_top.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/branch_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/branch_close.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/branch_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/branch_open.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/calendar_nextmonth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/calendar_nextmonth.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/calendar_prevmonth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/calendar_prevmonth.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/checkbox_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/checkbox_checked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/checkbox_checked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/checkbox_checked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/checkbox_parcial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/checkbox_parcial.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/checkbox_parcial_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/checkbox_parcial_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/checkbox_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/checkbox_unchecked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/checkbox_unchecked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/checkbox_unchecked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/menu_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/menu_checked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/radiobutton_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/radiobutton_checked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/radiobutton_checked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/radiobutton_checked_disable.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/radiobutton_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/radiobutton_unchecked.png -------------------------------------------------------------------------------- /res/theme/feiyangqingyun/qss/lightblue/radiobutton_unchecked_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatsuriDayo/nekoray/adef6cd4af7dffc77235c524086f3dc4100d8457/res/theme/feiyangqingyun/qss/lightblue/radiobutton_unchecked_disable.png -------------------------------------------------------------------------------- /res/vpn/vpn-run-root.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -x 4 | 5 | if [ "$EUID" -ne 0 ]; then 6 | echo "[Warning] Tun script not running as root" 7 | fi 8 | 9 | command -v pkill >/dev/null 2>&1 || echo "[Warning] pkill not found" 10 | 11 | BASEDIR=$(dirname "$0") 12 | cd $BASEDIR 13 | 14 | pre_start_linux() { 15 | # for Tun2Socket 16 | iptables -I INPUT -s 172.19.0.2 -d 172.19.0.1 -p tcp -j ACCEPT 17 | ip6tables -I INPUT -s fdfe:dcba:9876::2 -d fdfe:dcba:9876::1 -p tcp -j ACCEPT 18 | } 19 | 20 | start() { 21 | pre_start_linux 22 | "./nekobox_core" run -c "$CONFIG_PATH" 23 | } 24 | 25 | stop() { 26 | iptables -D INPUT -s 172.19.0.2 -d 172.19.0.1 -p tcp -j ACCEPT 27 | ip6tables -D INPUT -s fdfe:dcba:9876::2 -d fdfe:dcba:9876::1 -p tcp -j ACCEPT 28 | } 29 | 30 | if [ "$1" != "stop" ]; then 31 | start || true 32 | fi 33 | 34 | stop || true 35 | -------------------------------------------------------------------------------- /rpc/gRPC.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef NKR_NO_GRPC 4 | 5 | #include "go/grpc_server/gen/libcore.pb.h" 6 | #include 7 | 8 | namespace QtGrpc { 9 | class Http2GrpcChannelPrivate; 10 | } 11 | 12 | namespace NekoGui_rpc { 13 | class Client { 14 | public: 15 | explicit Client(std::function onError, const QString &target, const QString &token); 16 | 17 | void Exit(); 18 | 19 | bool KeepAlive(); 20 | 21 | // QString returns is error string 22 | 23 | QString Start(bool *rpcOK, const libcore::LoadConfigReq &request); 24 | 25 | QString Stop(bool *rpcOK); 26 | 27 | long long QueryStats(const std::string &tag, const std::string &direct); 28 | 29 | std::string ListConnections(); 30 | 31 | libcore::TestResp Test(bool *rpcOK, const libcore::TestReq &request); 32 | 33 | libcore::UpdateResp Update(bool *rpcOK, const libcore::UpdateReq &request); 34 | 35 | private: 36 | std::function()> make_grpc_channel; 37 | std::unique_ptr default_grpc_channel; 38 | std::function onError; 39 | }; 40 | 41 | inline Client *defaultClient; 42 | } // namespace NekoGui_rpc 43 | #endif 44 | -------------------------------------------------------------------------------- /sub/GroupUpdater.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "db/Database.hpp" 4 | 5 | namespace NekoGui_sub { 6 | class RawUpdater { 7 | public: 8 | void updateClash(const QString &str); 9 | 10 | void update(const QString &str); 11 | 12 | int gid_add_to = -1; // 导入到指定组 -1 为当前选中组 13 | 14 | QList> updated_order; // 新增的配置,按照导入时处理的先后排序 15 | }; 16 | 17 | class GroupUpdater : public QObject { 18 | Q_OBJECT 19 | 20 | public: 21 | void AsyncUpdate(const QString &str, int _sub_gid = -1, const std::function &finish = nullptr); 22 | 23 | void Update(const QString &_str, int _sub_gid = -1, bool _not_sub_as_url = false); 24 | 25 | signals: 26 | 27 | void asyncUpdateCallback(int gid); 28 | }; 29 | 30 | extern GroupUpdater *groupUpdater; 31 | } // namespace NekoGui_sub 32 | 33 | // 更新所有订阅 关闭分组窗口时 更新动作继续执行 34 | void UI_update_all_groups(bool onlyAllowed = false); 35 | -------------------------------------------------------------------------------- /sys/AutoRun.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void AutoRun_SetEnabled(bool enable); 4 | 5 | bool AutoRun_IsEnabled(); 6 | -------------------------------------------------------------------------------- /sys/ExternalProcess.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace NekoGui_sys { 7 | class ExternalProcess : public QProcess { 8 | public: 9 | QString tag; 10 | QString program; 11 | QStringList arguments; 12 | QStringList env; 13 | 14 | bool managed = true; // MW_dialog_message 15 | 16 | ExternalProcess(); 17 | ~ExternalProcess(); 18 | 19 | // start & kill is one time 20 | 21 | virtual void Start(); 22 | 23 | void Kill(); 24 | 25 | protected: 26 | bool started = false; 27 | bool killed = false; 28 | bool crashed = false; 29 | }; 30 | 31 | class CoreProcess : public ExternalProcess { 32 | public: 33 | CoreProcess(const QString &core_path, const QStringList &args); 34 | 35 | void Start() override; 36 | 37 | void Restart(); 38 | 39 | int start_profile_when_core_is_up = -1; 40 | 41 | private: 42 | bool show_stderr = false; 43 | bool failed_to_start = false; 44 | bool restarting = false; 45 | }; 46 | 47 | // 手动管理 48 | inline std::list> running_ext; 49 | 50 | inline QAtomicInt logCounter; 51 | } // namespace NekoGui_sys 52 | -------------------------------------------------------------------------------- /sys/linux/LinuxCap.cpp: -------------------------------------------------------------------------------- 1 | #include "LinuxCap.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define EXIT_CODE(p) (p.exitStatus() == QProcess::NormalExit ? p.exitCode() : -1) 8 | 9 | QString Linux_GetCapString(const QString &path) { 10 | QProcess p; 11 | p.setProgram(Linux_FindCapProgsExec("getcap")); 12 | p.setArguments({path}); 13 | p.start(); 14 | p.waitForFinished(500); 15 | return p.readAllStandardOutput(); 16 | } 17 | 18 | int Linux_Pkexec_SetCapString(const QString &path, const QString &cap) { 19 | QProcess p; 20 | p.setProgram("pkexec"); 21 | p.setArguments({Linux_FindCapProgsExec("setcap"), cap, path}); 22 | p.start(); 23 | p.waitForFinished(-1); 24 | return EXIT_CODE(p); 25 | } 26 | 27 | bool Linux_HavePkexec() { 28 | QProcess p; 29 | p.setProgram("pkexec"); 30 | p.setArguments({"--help"}); 31 | p.setProcessChannelMode(QProcess::SeparateChannels); 32 | p.start(); 33 | p.waitForFinished(500); 34 | return EXIT_CODE(p) == 0; 35 | } 36 | 37 | QString Linux_FindCapProgsExec(const QString &name) { 38 | QString exec = QStandardPaths::findExecutable(name); 39 | if (exec.isEmpty()) 40 | exec = QStandardPaths::findExecutable(name, {"/usr/sbin", "/sbin"}); 41 | 42 | if (exec.isEmpty()) 43 | qDebug() << "Executable" << name << "could not be resolved"; 44 | else 45 | qDebug() << "Found exec" << name << "at" << exec; 46 | 47 | return exec.isEmpty() ? name : exec; 48 | } 49 | -------------------------------------------------------------------------------- /sys/linux/LinuxCap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | QString Linux_GetCapString(const QString &path); 6 | 7 | int Linux_Pkexec_SetCapString(const QString &path, const QString &cap); 8 | 9 | bool Linux_HavePkexec(); 10 | 11 | QString Linux_FindCapProgsExec(const QString &name); 12 | -------------------------------------------------------------------------------- /sys/windows/MiniDump.cpp: -------------------------------------------------------------------------------- 1 | #include "MiniDump.h" 2 | 3 | #ifndef WIN32_LEAN_AND_MEAN 4 | #define WIN32_LEAN_AND_MEAN 5 | #endif 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | typedef BOOL(WINAPI *MINIDUMPWRITEDUMP)( 16 | HANDLE hProcess, 17 | DWORD dwPid, 18 | HANDLE hFile, 19 | MINIDUMP_TYPE DumpType, 20 | CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 21 | CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 22 | CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); 23 | 24 | LONG __stdcall CreateCrashHandler(EXCEPTION_POINTERS *pException) { 25 | QDir::setCurrent(QApplication::applicationDirPath()); 26 | 27 | HMODULE DllHandle = NULL; 28 | DllHandle = LoadLibrary(_T("DBGHELP.DLL")); 29 | 30 | if (DllHandle) { 31 | MINIDUMPWRITEDUMP Dump = (MINIDUMPWRITEDUMP) GetProcAddress(DllHandle, "MiniDumpWriteDump"); 32 | if (Dump) { 33 | // 创建 Dump 文件 34 | QDateTime CurDTime = QDateTime::currentDateTime(); 35 | QString current_date = CurDTime.toString("yyyy_MM_dd_hh_mm_ss"); 36 | // dmp文件的命名 37 | QString dumpText = "Dump_" + current_date + ".dmp"; 38 | EXCEPTION_RECORD *record = pException->ExceptionRecord; 39 | QString errCode(QString::number(record->ExceptionCode, 16)); 40 | QString errAddr(QString::number((uintptr_t) record->ExceptionAddress, 16)); 41 | QString errFlag(QString::number(record->ExceptionFlags, 16)); 42 | QString errPara(QString::number(record->NumberParameters, 16)); 43 | HANDLE DumpHandle = CreateFile((LPCWSTR) dumpText.utf16(), 44 | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 45 | if (DumpHandle != INVALID_HANDLE_VALUE) { 46 | MINIDUMP_EXCEPTION_INFORMATION dumpInfo; 47 | dumpInfo.ExceptionPointers = pException; 48 | dumpInfo.ThreadId = GetCurrentThreadId(); 49 | dumpInfo.ClientPointers = TRUE; 50 | // 将dump信息写入dmp文件 51 | Dump(GetCurrentProcess(), GetCurrentProcessId(), DumpHandle, MiniDumpNormal, &dumpInfo, 52 | NULL, NULL); 53 | CloseHandle(DumpHandle); 54 | } else { 55 | dumpText = ""; 56 | } 57 | // 创建消息提示 58 | QMessageBox::warning(NULL, "Application crashed", 59 | QStringLiteral("ErrorCode: %1 ErrorAddr:%2 ErrorFlag: %3 ErrorPara: %4\nVersion: %5\nDump file at %6") 60 | .arg(errCode) 61 | .arg(errAddr) 62 | .arg(errFlag) 63 | .arg(errPara) 64 | .arg(NKR_VERSION) 65 | .arg(dumpText), 66 | QMessageBox::Ok); 67 | } 68 | } 69 | return EXCEPTION_EXECUTE_HANDLER; 70 | } 71 | 72 | void Windows_SetCrashHandler() { 73 | SetErrorMode(SEM_FAILCRITICALERRORS); 74 | SetUnhandledExceptionFilter(CreateCrashHandler); 75 | } 76 | -------------------------------------------------------------------------------- /sys/windows/MiniDump.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void Windows_SetCrashHandler(); 4 | -------------------------------------------------------------------------------- /sys/windows/guihelper.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "guihelper.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | void Windows_QWidget_SetForegroundWindow(QWidget *w) { 10 | HWND hForgroundWnd = GetForegroundWindow(); 11 | DWORD dwForeID = ::GetWindowThreadProcessId(hForgroundWnd, NULL); 12 | DWORD dwCurID = ::GetCurrentThreadId(); 13 | ::AttachThreadInput(dwCurID, dwForeID, TRUE); 14 | ::SetForegroundWindow((HWND) w->winId()); 15 | ::AttachThreadInput(dwCurID, dwForeID, FALSE); 16 | } 17 | 18 | int isThisAdmin = -1; // cached 19 | 20 | bool Windows_IsInAdmin() { 21 | if (isThisAdmin >= 0) return isThisAdmin; 22 | isThisAdmin = IsUserAnAdmin(); 23 | return isThisAdmin; 24 | } 25 | -------------------------------------------------------------------------------- /sys/windows/guihelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QWidget; 4 | 5 | void Windows_QWidget_SetForegroundWindow(QWidget* w); 6 | 7 | bool Windows_IsInAdmin(); 8 | -------------------------------------------------------------------------------- /test/test-qt512-sdk-build.sh: -------------------------------------------------------------------------------- 1 | QT=$PWD/qtsdk/5.12.12/gcc_64 2 | BUILD=build-sdk-qt512 3 | rm -rf $BUILD 4 | mkdir -p $BUILD 5 | cd $BUILD 6 | cmake -GNinja -DCMAKE_PREFIX_PATH=$QT .. 7 | ninja 8 | -------------------------------------------------------------------------------- /test/test-qt6-build.sh: -------------------------------------------------------------------------------- 1 | rm -rf build-qt6 2 | mkdir -p build-qt6 3 | cd build-qt6 4 | cmake -GNinja -DQT_VERSION_MAJOR=6 .. 5 | ninja 6 | -------------------------------------------------------------------------------- /translations/translations.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | zh_CN.qm 4 | fa_IR.qm 5 | ru_RU.qm 6 | 7 | 8 | -------------------------------------------------------------------------------- /ui/GroupSort.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // implement in mainwindow 4 | namespace GroupSortMethod { 5 | enum GroupSortMethod { 6 | Raw, 7 | ByType, 8 | ByAddress, 9 | ByName, 10 | ByLatency, 11 | ById, 12 | }; 13 | } 14 | 15 | struct GroupSortAction { 16 | GroupSortMethod::GroupSortMethod method = GroupSortMethod::Raw; 17 | bool save_sort = false; // 保存到文件 18 | bool descending = false; // 默认升序,开这个就是降序 19 | bool scroll_to_started = false; 20 | }; 21 | -------------------------------------------------------------------------------- /ui/Icon.cpp: -------------------------------------------------------------------------------- 1 | #include "Icon.hpp" 2 | 3 | #include "main/NekoGui.hpp" 4 | 5 | #include 6 | 7 | QPixmap Icon::GetTrayIcon(Icon::TrayIconStatus status) { 8 | QPixmap pixmap; 9 | 10 | // software embedded icon 11 | auto pixmap_read = QPixmap(":/neko/" + software_name.toLower() + ".png"); 12 | if (!pixmap_read.isNull()) pixmap = pixmap_read; 13 | 14 | // software pack icon 15 | pixmap_read = QPixmap("../" + software_name.toLower() + ".png"); 16 | if (!pixmap_read.isNull()) pixmap = pixmap_read; 17 | 18 | // user icon 19 | pixmap_read = QPixmap("./" + software_name.toLower() + ".png"); 20 | if (!pixmap_read.isNull()) pixmap = pixmap_read; 21 | 22 | if (status == TrayIconStatus::NONE) return pixmap; 23 | 24 | auto p = QPainter(&pixmap); 25 | 26 | auto side = pixmap.width(); 27 | auto radius = side * 0.4; 28 | auto d = side * 0.3; 29 | auto margin = side * 0.05; 30 | 31 | if (status == TrayIconStatus::RUNNING) { 32 | p.setBrush(QBrush(Qt::darkGreen)); 33 | } else if (status == TrayIconStatus::SYSTEM_PROXY) { 34 | p.setBrush(QBrush(Qt::blue)); 35 | } else if (status == TrayIconStatus::VPN) { 36 | p.setBrush(QBrush(Qt::red)); 37 | } 38 | p.drawRoundedRect( 39 | QRect(side - d - margin, 40 | side - d - margin, 41 | d, 42 | d), 43 | radius, 44 | radius); 45 | p.end(); 46 | 47 | return pixmap; 48 | } 49 | 50 | QPixmap Icon::GetMaterialIcon(const QString &name) { 51 | QPixmap pixmap(":/icon/material/" + name + ".svg"); 52 | return pixmap; 53 | } 54 | -------------------------------------------------------------------------------- /ui/Icon.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Icon { 6 | 7 | enum TrayIconStatus { 8 | NONE, 9 | RUNNING, 10 | SYSTEM_PROXY, 11 | VPN, 12 | }; 13 | 14 | QPixmap GetTrayIcon(TrayIconStatus status); 15 | 16 | QPixmap GetMaterialIcon(const QString &name); 17 | 18 | } // namespace Icon 19 | -------------------------------------------------------------------------------- /ui/ThemeManager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "ThemeManager.hpp" 6 | 7 | ThemeManager *themeManager = new ThemeManager; 8 | 9 | extern QString ReadFileText(const QString &path); 10 | 11 | void ThemeManager::ApplyTheme(const QString &theme) { 12 | auto internal = [=] { 13 | if (this->system_style_name.isEmpty()) { 14 | this->system_style_name = qApp->style()->objectName(); 15 | } 16 | if (this->current_theme == theme) { 17 | return; 18 | } 19 | 20 | bool ok; 21 | auto themeId = theme.toInt(&ok); 22 | 23 | if (ok) { 24 | // System & Built-in 25 | QString qss; 26 | 27 | if (themeId != 0) { 28 | QString path; 29 | std::map replace; 30 | switch (themeId) { 31 | case 1: 32 | path = ":/themes/feiyangqingyun/qss/flatgray.css"; 33 | replace[":/qss/"] = ":/themes/feiyangqingyun/qss/"; 34 | break; 35 | case 2: 36 | path = ":/themes/feiyangqingyun/qss/lightblue.css"; 37 | replace[":/qss/"] = ":/themes/feiyangqingyun/qss/"; 38 | break; 39 | case 3: 40 | path = ":/themes/feiyangqingyun/qss/blacksoft.css"; 41 | replace[":/qss/"] = ":/themes/feiyangqingyun/qss/"; 42 | break; 43 | default: 44 | return; 45 | } 46 | qss = ReadFileText(path); 47 | for (auto const &[a, b]: replace) { 48 | qss = qss.replace(a, b); 49 | } 50 | } 51 | 52 | auto system_style = QStyleFactory::create(this->system_style_name); 53 | 54 | if (themeId == 0) { 55 | // system theme 56 | qApp->setPalette(system_style->standardPalette()); 57 | qApp->setStyle(system_style); 58 | qApp->setStyleSheet(""); 59 | } else { 60 | if (themeId == 1 || themeId == 2 || themeId == 3) { 61 | // feiyangqingyun theme 62 | QString paletteColor = qss.mid(20, 7); 63 | qApp->setPalette(QPalette(paletteColor)); 64 | } else { 65 | // other theme 66 | qApp->setPalette(system_style->standardPalette()); 67 | } 68 | qApp->setStyleSheet(qss); 69 | } 70 | } else { 71 | // QStyleFactory 72 | const auto &_style = QStyleFactory::create(theme); 73 | if (_style != nullptr) { 74 | qApp->setPalette(_style->standardPalette()); 75 | qApp->setStyle(_style); 76 | qApp->setStyleSheet(""); 77 | } 78 | } 79 | 80 | current_theme = theme; 81 | }; 82 | internal(); 83 | 84 | auto nekoray_css = ReadFileText(":/neko/neko.css"); 85 | qApp->setStyleSheet(qApp->styleSheet().append("\n").append(nekoray_css)); 86 | } 87 | -------------------------------------------------------------------------------- /ui/ThemeManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class ThemeManager { 4 | public: 5 | QString system_style_name = ""; 6 | QString current_theme = "0"; // int: 0:system 1+:builtin string: QStyleFactory 7 | 8 | void ApplyTheme(const QString &theme); 9 | }; 10 | 11 | extern ThemeManager *themeManager; 12 | -------------------------------------------------------------------------------- /ui/dialog_basic_settings.h: -------------------------------------------------------------------------------- 1 | #ifndef DIALOG_BASIC_SETTINGS_H 2 | #define DIALOG_BASIC_SETTINGS_H 3 | 4 | #include 5 | #include 6 | 7 | namespace Ui { 8 | class DialogBasicSettings; 9 | } 10 | 11 | class DialogBasicSettings : public QDialog { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit DialogBasicSettings(QWidget *parent = nullptr); 16 | 17 | ~DialogBasicSettings(); 18 | 19 | public slots: 20 | 21 | void accept(); 22 | 23 | private: 24 | Ui::DialogBasicSettings *ui; 25 | 26 | struct { 27 | QJsonObject extraCore; 28 | QString custom_inbound; 29 | bool needRestart = false; 30 | } CACHE; 31 | 32 | private slots: 33 | 34 | void refresh_auth(); 35 | 36 | void on_set_custom_icon_clicked(); 37 | 38 | void on_inbound_auth_clicked(); 39 | 40 | void on_core_settings_clicked(); 41 | }; 42 | 43 | #endif // DIALOG_BASIC_SETTINGS_H 44 | -------------------------------------------------------------------------------- /ui/dialog_hotkey.cpp: -------------------------------------------------------------------------------- 1 | #include "dialog_hotkey.h" 2 | #include "ui_dialog_hotkey.h" 3 | 4 | #include "ui/mainwindow_interface.h" 5 | 6 | DialogHotkey::DialogHotkey(QWidget *parent) : QDialog(parent), ui(new Ui::DialogHotkey) { 7 | ui->setupUi(this); 8 | ui->show_mainwindow->setKeySequence(NekoGui::dataStore->hotkey_mainwindow); 9 | ui->show_groups->setKeySequence(NekoGui::dataStore->hotkey_group); 10 | ui->show_routes->setKeySequence(NekoGui::dataStore->hotkey_route); 11 | ui->system_proxy->setKeySequence(NekoGui::dataStore->hotkey_system_proxy_menu); 12 | GetMainWindow()->RegisterHotkey(true); 13 | } 14 | 15 | DialogHotkey::~DialogHotkey() { 16 | if (result() == QDialog::Accepted) { 17 | NekoGui::dataStore->hotkey_mainwindow = ui->show_mainwindow->keySequence().toString(); 18 | NekoGui::dataStore->hotkey_group = ui->show_groups->keySequence().toString(); 19 | NekoGui::dataStore->hotkey_route = ui->show_routes->keySequence().toString(); 20 | NekoGui::dataStore->hotkey_system_proxy_menu = ui->system_proxy->keySequence().toString(); 21 | NekoGui::dataStore->Save(); 22 | } 23 | GetMainWindow()->RegisterHotkey(false); 24 | delete ui; 25 | } 26 | -------------------------------------------------------------------------------- /ui/dialog_hotkey.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "main/NekoGui.hpp" 5 | 6 | QT_BEGIN_NAMESPACE 7 | namespace Ui { 8 | class DialogHotkey; 9 | } 10 | QT_END_NAMESPACE 11 | 12 | class DialogHotkey : public QDialog { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit DialogHotkey(QWidget *parent = nullptr); 17 | 18 | ~DialogHotkey() override; 19 | 20 | private: 21 | Ui::DialogHotkey *ui; 22 | }; 23 | -------------------------------------------------------------------------------- /ui/dialog_hotkey.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | DialogHotkey 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Hotkey 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Show routes 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Qt::StrongFocus 34 | 35 | 36 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Show groups 47 | 48 | 49 | 50 | 51 | 52 | 53 | Trigger main window 54 | 55 | 56 | 57 | 58 | 59 | 60 | System Proxy 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | QtExtKeySequenceEdit 72 | QKeySequenceEdit 73 |
3rdparty/QtExtKeySequenceEdit.h
74 |
75 |
76 | 77 | show_mainwindow 78 | show_groups 79 | show_routes 80 | system_proxy 81 | buttonBox 82 | 83 | 84 | 85 | 86 | buttonBox 87 | accepted() 88 | DialogHotkey 89 | accept() 90 | 91 | 92 | 258 93 | 255 94 | 95 | 96 | 199 97 | 149 98 | 99 | 100 | 101 | 102 | buttonBox 103 | rejected() 104 | DialogHotkey 105 | reject() 106 | 107 | 108 | 258 109 | 255 110 | 111 | 112 | 199 113 | 149 114 | 115 | 116 | 117 | 118 |
119 | -------------------------------------------------------------------------------- /ui/dialog_manage_groups.cpp: -------------------------------------------------------------------------------- 1 | #include "dialog_manage_groups.h" 2 | #include "ui_dialog_manage_groups.h" 3 | 4 | #include "db/Database.hpp" 5 | #include "sub/GroupUpdater.hpp" 6 | #include "main/GuiUtils.hpp" 7 | #include "ui/widget/GroupItem.h" 8 | #include "ui/edit/dialog_edit_group.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define AddGroupToListIfExist(_id) \ 15 | auto __ent = NekoGui::profileManager->GetGroup(_id); \ 16 | if (__ent != nullptr) { \ 17 | auto wI = new QListWidgetItem(); \ 18 | auto w = new GroupItem(this, __ent, wI); \ 19 | wI->setData(114514, _id); \ 20 | ui->listWidget->addItem(wI); \ 21 | ui->listWidget->setItemWidget(wI, w); \ 22 | } 23 | 24 | DialogManageGroups::DialogManageGroups(QWidget *parent) : QDialog(parent), ui(new Ui::DialogManageGroups) { 25 | ui->setupUi(this); 26 | 27 | for (auto id: NekoGui::profileManager->groupsTabOrder) { 28 | AddGroupToListIfExist(id) 29 | } 30 | 31 | connect(ui->listWidget, &QListWidget::itemDoubleClicked, this, [=](QListWidgetItem *wI) { 32 | auto w = dynamic_cast(ui->listWidget->itemWidget(wI)); 33 | emit w->edit_clicked(); 34 | }); 35 | } 36 | 37 | DialogManageGroups::~DialogManageGroups() { 38 | delete ui; 39 | } 40 | 41 | void DialogManageGroups::on_add_clicked() { 42 | auto ent = NekoGui::ProfileManager::NewGroup(); 43 | auto dialog = new DialogEditGroup(ent, this); 44 | int ret = dialog->exec(); 45 | dialog->deleteLater(); 46 | 47 | if (ret == QDialog::Accepted) { 48 | NekoGui::profileManager->AddGroup(ent); 49 | AddGroupToListIfExist(ent->id); 50 | MW_dialog_message(Dialog_DialogManageGroups, "refresh-1"); 51 | } 52 | } 53 | 54 | void DialogManageGroups::on_update_all_clicked() { 55 | if (QMessageBox::question(this, tr("Confirmation"), tr("Update all subscriptions?")) == QMessageBox::StandardButton::Yes) { 56 | UI_update_all_groups(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ui/dialog_manage_groups.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "db/Group.hpp" 9 | 10 | QT_BEGIN_NAMESPACE 11 | namespace Ui { 12 | class DialogManageGroups; 13 | } 14 | QT_END_NAMESPACE 15 | 16 | class DialogManageGroups : public QDialog { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit DialogManageGroups(QWidget *parent = nullptr); 21 | 22 | ~DialogManageGroups() override; 23 | 24 | private: 25 | Ui::DialogManageGroups *ui; 26 | 27 | private slots: 28 | 29 | void on_add_clicked(); 30 | 31 | void on_update_all_clicked(); 32 | }; 33 | -------------------------------------------------------------------------------- /ui/dialog_manage_groups.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | DialogManageGroups 4 | 5 | 6 | 7 | 0 8 | 0 9 | 640 10 | 480 11 | 12 | 13 | 14 | Qt::TabFocus 15 | 16 | 17 | Groups 18 | 19 | 20 | 21 | 22 | 23 | Qt::NoFocus 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Qt::NoFocus 33 | 34 | 35 | New group 36 | 37 | 38 | 39 | 40 | 41 | 42 | Qt::NoFocus 43 | 44 | 45 | Update all subscriptions 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /ui/dialog_manage_routes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "3rdparty/qv2ray/v2/ui/QvAutoCompleteTextEdit.hpp" 7 | #include "main/NekoGui.hpp" 8 | 9 | QT_BEGIN_NAMESPACE 10 | namespace Ui { 11 | class DialogManageRoutes; 12 | } 13 | QT_END_NAMESPACE 14 | 15 | class DialogManageRoutes : public QDialog { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit DialogManageRoutes(QWidget *parent = nullptr); 20 | 21 | ~DialogManageRoutes() override; 22 | 23 | private: 24 | Ui::DialogManageRoutes *ui; 25 | 26 | struct { 27 | QString custom_route; 28 | QString custom_route_global; 29 | } CACHE; 30 | 31 | QMenu *builtInSchemesMenu; 32 | Qv2ray::ui::widgets::AutoCompleteTextEdit *directDomainTxt; 33 | Qv2ray::ui::widgets::AutoCompleteTextEdit *proxyDomainTxt; 34 | Qv2ray::ui::widgets::AutoCompleteTextEdit *blockDomainTxt; 35 | // 36 | Qv2ray::ui::widgets::AutoCompleteTextEdit *directIPTxt; 37 | Qv2ray::ui::widgets::AutoCompleteTextEdit *blockIPTxt; 38 | Qv2ray::ui::widgets::AutoCompleteTextEdit *proxyIPTxt; 39 | // 40 | NekoGui::Routing routing_cn_lan = NekoGui::Routing(1); 41 | NekoGui::Routing routing_global = NekoGui::Routing(0); 42 | // 43 | QString title_base; 44 | QString active_routing; 45 | 46 | public slots: 47 | 48 | void accept() override; 49 | 50 | QList getBuiltInSchemes(); 51 | 52 | QAction *schemeToAction(const QString &name, const NekoGui::Routing &scheme); 53 | 54 | void UpdateDisplayRouting(NekoGui::Routing *conf, bool qv); 55 | 56 | void SaveDisplayRouting(NekoGui::Routing *conf); 57 | 58 | void on_load_save_clicked(); 59 | }; 60 | -------------------------------------------------------------------------------- /ui/dialog_vpn_settings.cpp: -------------------------------------------------------------------------------- 1 | #include "dialog_vpn_settings.h" 2 | #include "ui_dialog_vpn_settings.h" 3 | 4 | #include "main/GuiUtils.hpp" 5 | #include "main/NekoGui.hpp" 6 | #include "ui/mainwindow_interface.h" 7 | 8 | #include 9 | 10 | DialogVPNSettings::DialogVPNSettings(QWidget *parent) : QDialog(parent), ui(new Ui::DialogVPNSettings) { 11 | ui->setupUi(this); 12 | ADD_ASTERISK(this); 13 | 14 | ui->fake_dns->setChecked(NekoGui::dataStore->fake_dns); 15 | ui->vpn_implementation->setCurrentIndex(NekoGui::dataStore->vpn_implementation); 16 | ui->vpn_mtu->setCurrentText(Int2String(NekoGui::dataStore->vpn_mtu)); 17 | ui->vpn_ipv6->setChecked(NekoGui::dataStore->vpn_ipv6); 18 | ui->hide_console->setChecked(NekoGui::dataStore->vpn_hide_console); 19 | #ifndef Q_OS_WIN 20 | ui->hide_console->setVisible(false); 21 | #endif 22 | ui->strict_route->setChecked(NekoGui::dataStore->vpn_strict_route); 23 | ui->single_core->setChecked(NekoGui::dataStore->vpn_internal_tun); 24 | // 25 | D_LOAD_STRING_PLAIN(vpn_rule_cidr) 26 | D_LOAD_STRING_PLAIN(vpn_rule_process) 27 | // 28 | connect(ui->whitelist_mode, &QCheckBox::stateChanged, this, [=](int state) { 29 | if (state == Qt::Checked) { 30 | ui->gb_cidr->setTitle(tr("Proxy CIDR")); 31 | ui->gb_process_name->setTitle(tr("Proxy Process Name")); 32 | } else { 33 | ui->gb_cidr->setTitle(tr("Bypass CIDR")); 34 | ui->gb_process_name->setTitle(tr("Bypass Process Name")); 35 | } 36 | }); 37 | ui->whitelist_mode->setChecked(NekoGui::dataStore->vpn_rule_white); 38 | } 39 | 40 | DialogVPNSettings::~DialogVPNSettings() { 41 | delete ui; 42 | } 43 | 44 | void DialogVPNSettings::accept() { 45 | // 46 | auto mtu = ui->vpn_mtu->currentText().toInt(); 47 | if (mtu > 10000 || mtu < 1000) mtu = 9000; 48 | NekoGui::dataStore->vpn_implementation = ui->vpn_implementation->currentIndex(); 49 | NekoGui::dataStore->fake_dns = ui->fake_dns->isChecked(); 50 | NekoGui::dataStore->vpn_mtu = mtu; 51 | NekoGui::dataStore->vpn_ipv6 = ui->vpn_ipv6->isChecked(); 52 | NekoGui::dataStore->vpn_hide_console = ui->hide_console->isChecked(); 53 | NekoGui::dataStore->vpn_strict_route = ui->strict_route->isChecked(); 54 | NekoGui::dataStore->vpn_rule_white = ui->whitelist_mode->isChecked(); 55 | bool isInternalChanged = NekoGui::dataStore->vpn_internal_tun != ui->single_core->isChecked(); 56 | NekoGui::dataStore->vpn_internal_tun = ui->single_core->isChecked(); 57 | // 58 | D_SAVE_STRING_PLAIN(vpn_rule_cidr) 59 | D_SAVE_STRING_PLAIN(vpn_rule_process) 60 | // 61 | QStringList msg{"UpdateDataStore"}; 62 | if (isInternalChanged) { 63 | msg << "NeedRestart"; 64 | } else { 65 | msg << "VPNChanged"; 66 | } 67 | MW_dialog_message("", msg.join(",")); 68 | QDialog::accept(); 69 | } 70 | 71 | void DialogVPNSettings::on_troubleshooting_clicked() { 72 | auto r = QMessageBox::information(this, tr("Troubleshooting"), 73 | tr("If you have trouble starting VPN, you can force reset nekobox_core process here.\n\n" 74 | "If still not working, see documentation for more information.\n" 75 | "https://matsuridayo.github.io/n-configuration/#vpn-tun"), 76 | tr("Reset"), tr("Cancel"), "", 77 | 1, 1); 78 | if (r == 0) { 79 | GetMainWindow()->StopVPNProcess(true); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ui/dialog_vpn_settings.h: -------------------------------------------------------------------------------- 1 | #ifndef NEKORAY_DIALOG_VPN_SETTINGS_H 2 | #define NEKORAY_DIALOG_VPN_SETTINGS_H 3 | 4 | #include 5 | 6 | QT_BEGIN_NAMESPACE 7 | namespace Ui { 8 | class DialogVPNSettings; 9 | } 10 | QT_END_NAMESPACE 11 | 12 | class DialogVPNSettings : public QDialog { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit DialogVPNSettings(QWidget *parent = nullptr); 17 | 18 | ~DialogVPNSettings() override; 19 | 20 | private: 21 | Ui::DialogVPNSettings *ui; 22 | 23 | public slots: 24 | 25 | void accept() override; 26 | 27 | void on_troubleshooting_clicked(); 28 | }; 29 | 30 | #endif // NEKORAY_DIALOG_VPN_SETTINGS_H 31 | -------------------------------------------------------------------------------- /ui/edit/dialog_edit_group.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "db/Group.hpp" 5 | 6 | QT_BEGIN_NAMESPACE 7 | namespace Ui { 8 | class DialogEditGroup; 9 | } 10 | QT_END_NAMESPACE 11 | 12 | class DialogEditGroup : public QDialog { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit DialogEditGroup(const std::shared_ptr &ent, QWidget *parent = nullptr); 17 | 18 | ~DialogEditGroup() override; 19 | 20 | private: 21 | Ui::DialogEditGroup *ui; 22 | 23 | std::shared_ptr ent; 24 | 25 | struct { 26 | int front_proxy; 27 | } CACHE; 28 | 29 | void refresh_front_proxy(); 30 | 31 | private slots: 32 | 33 | void accept() override; 34 | 35 | void on_front_proxy_clicked(); 36 | }; 37 | -------------------------------------------------------------------------------- /ui/edit/dialog_edit_profile.h: -------------------------------------------------------------------------------- 1 | #ifndef DIALOG_EDIT_PROFILE_H 2 | #define DIALOG_EDIT_PROFILE_H 3 | 4 | #include 5 | #include "db/Database.hpp" 6 | #include "profile_editor.h" 7 | 8 | #include "ui/widget/FloatCheckBox.h" 9 | 10 | namespace Ui { 11 | class DialogEditProfile; 12 | } 13 | 14 | class DialogEditProfile : public QDialog { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit DialogEditProfile(const QString &_type, int profileOrGroupId, QWidget *parent = nullptr); 19 | 20 | ~DialogEditProfile() override; 21 | 22 | public slots: 23 | 24 | void accept() override; 25 | 26 | private slots: 27 | 28 | void on_custom_outbound_edit_clicked(); 29 | 30 | void on_custom_config_edit_clicked(); 31 | 32 | void on_certificate_edit_clicked(); 33 | 34 | void on_apply_to_group_clicked(); 35 | 36 | private: 37 | Ui::DialogEditProfile *ui; 38 | 39 | std::map apply_to_group_ui; 40 | 41 | QWidget *innerWidget{}; 42 | ProfileEditor *innerEditor{}; 43 | 44 | QString type; 45 | int groupId; 46 | bool newEnt = false; 47 | std::shared_ptr ent; 48 | 49 | QString network_title_base; 50 | 51 | struct { 52 | QString custom_outbound; 53 | QString custom_config; 54 | QString certificate; 55 | } CACHE; 56 | 57 | void typeSelected(const QString &newType); 58 | 59 | bool onEnd(); 60 | 61 | void editor_cache_updated_impl(); 62 | 63 | void do_apply_to_group(const std::shared_ptr &group, QWidget *key); 64 | }; 65 | 66 | #endif // DIALOG_EDIT_PROFILE_H 67 | -------------------------------------------------------------------------------- /ui/edit/edit_chain.cpp: -------------------------------------------------------------------------------- 1 | #include "edit_chain.h" 2 | #include "ui_edit_chain.h" 3 | 4 | #include "ui/mainwindow_interface.h" 5 | #include "ui/widget/ProxyItem.h" 6 | 7 | #include "db/Database.hpp" 8 | #include "fmt/ChainBean.hpp" 9 | 10 | EditChain::EditChain(QWidget *parent) : QWidget(parent), ui(new Ui::EditChain) { 11 | ui->setupUi(this); 12 | } 13 | 14 | EditChain::~EditChain() { 15 | delete ui; 16 | } 17 | 18 | void EditChain::onStart(std::shared_ptr _ent) { 19 | this->ent = _ent; 20 | auto bean = this->ent->ChainBean(); 21 | 22 | for (auto id: bean->list) { 23 | AddProfileToListIfExist(id); 24 | } 25 | } 26 | 27 | bool EditChain::onEnd() { 28 | if (get_edit_text_name().isEmpty()) { 29 | MessageBoxWarning(software_name, tr("Name cannot be empty.")); 30 | return false; 31 | } 32 | 33 | auto bean = this->ent->ChainBean(); 34 | 35 | QList idList; 36 | for (int i = 0; i < ui->listWidget->count(); i++) { 37 | idList << ui->listWidget->item(i)->data(114514).toInt(); 38 | } 39 | bean->list = idList; 40 | 41 | return true; 42 | } 43 | 44 | void EditChain::on_select_profile_clicked() { 45 | get_edit_dialog()->hide(); 46 | GetMainWindow()->start_select_mode(this, [=](int id) { 47 | get_edit_dialog()->show(); 48 | AddProfileToListIfExist(id); 49 | }); 50 | } 51 | 52 | void EditChain::AddProfileToListIfExist(int profileId) { 53 | auto _ent = NekoGui::profileManager->GetProfile(profileId); 54 | if (_ent != nullptr && _ent->type != "chain") { 55 | auto wI = new QListWidgetItem(); 56 | wI->setData(114514, profileId); 57 | auto w = new ProxyItem(this, _ent, wI); 58 | ui->listWidget->addItem(wI); 59 | ui->listWidget->setItemWidget(wI, w); 60 | // change button 61 | connect(w->get_change_button(), &QPushButton::clicked, w, [=] { 62 | get_edit_dialog()->hide(); 63 | GetMainWindow()->start_select_mode(w, [=](int newId) { 64 | get_edit_dialog()->show(); 65 | ReplaceProfile(w, newId); 66 | }); 67 | }); 68 | } 69 | } 70 | 71 | void EditChain::ReplaceProfile(ProxyItem *w, int profileId) { 72 | auto _ent = NekoGui::profileManager->GetProfile(profileId); 73 | if (_ent != nullptr && _ent->type != "chain") { 74 | w->item->setData(114514, profileId); 75 | w->ent = _ent; 76 | w->refresh_data(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /ui/edit/edit_chain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "profile_editor.h" 5 | 6 | QT_BEGIN_NAMESPACE 7 | namespace Ui { 8 | class EditChain; 9 | } 10 | QT_END_NAMESPACE 11 | 12 | class ProxyItem; 13 | 14 | class EditChain : public QWidget, public ProfileEditor { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit EditChain(QWidget *parent = nullptr); 19 | 20 | ~EditChain() override; 21 | 22 | void onStart(std::shared_ptr _ent) override; 23 | 24 | bool onEnd() override; 25 | 26 | private: 27 | Ui::EditChain *ui; 28 | std::shared_ptr ent; 29 | 30 | void AddProfileToListIfExist(int profileId); 31 | 32 | static void ReplaceProfile(ProxyItem *w, int profileId); 33 | 34 | private slots: 35 | 36 | void on_select_profile_clicked(); 37 | }; 38 | -------------------------------------------------------------------------------- /ui/edit/edit_chain.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | EditChain 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 400 11 | 12 | 13 | 14 | EditChain 15 | 16 | 17 | 18 | 19 | 20 | Traffic order is from top to bottom 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 0 29 | 300 30 | 31 | 32 | 33 | Qt::ActionsContextMenu 34 | 35 | 36 | QAbstractItemView::InternalMove 37 | 38 | 39 | Qt::MoveAction 40 | 41 | 42 | QListView::Free 43 | 44 | 45 | 46 | 47 | 48 | 49 | Select Profile 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /ui/edit/edit_custom.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "profile_editor.h" 5 | 6 | QT_BEGIN_NAMESPACE 7 | namespace Ui { 8 | class EditCustom; 9 | } 10 | QT_END_NAMESPACE 11 | 12 | class EditCustom : public QWidget, public ProfileEditor { 13 | Q_OBJECT 14 | 15 | public: 16 | QString preset_core; 17 | QString preset_command; 18 | QString preset_config; 19 | 20 | explicit EditCustom(QWidget *parent = nullptr); 21 | 22 | ~EditCustom() override; 23 | 24 | void onStart(std::shared_ptr _ent) override; 25 | 26 | bool onEnd() override; 27 | 28 | private: 29 | Ui::EditCustom *ui; 30 | std::shared_ptr ent; 31 | 32 | private slots: 33 | 34 | void on_as_json_clicked(); 35 | }; 36 | -------------------------------------------------------------------------------- /ui/edit/edit_naive.cpp: -------------------------------------------------------------------------------- 1 | #include "edit_naive.h" 2 | #include "ui_edit_naive.h" 3 | 4 | #include "fmt/NaiveBean.hpp" 5 | 6 | #include 7 | 8 | EditNaive::EditNaive(QWidget *parent) : QWidget(parent), ui(new Ui::EditNaive) { 9 | ui->setupUi(this); 10 | } 11 | 12 | EditNaive::~EditNaive() { 13 | delete ui; 14 | } 15 | 16 | void EditNaive::onStart(std::shared_ptr _ent) { 17 | this->ent = _ent; 18 | auto bean = this->ent->NaiveBean(); 19 | 20 | P_LOAD_STRING(username); 21 | P_LOAD_STRING(password); 22 | P_LOAD_COMBO_STRING(protocol); 23 | P_C_LOAD_STRING(extra_headers); 24 | P_LOAD_STRING(sni); 25 | P_C_LOAD_STRING(certificate); 26 | P_LOAD_INT(insecure_concurrency); 27 | P_LOAD_BOOL(disable_log); 28 | } 29 | 30 | bool EditNaive::onEnd() { 31 | auto bean = this->ent->NaiveBean(); 32 | 33 | P_SAVE_STRING(username); 34 | P_SAVE_STRING(password); 35 | P_SAVE_COMBO_STRING(protocol); 36 | P_C_SAVE_STRING(extra_headers); 37 | P_SAVE_STRING(sni); 38 | P_C_SAVE_STRING(certificate); 39 | P_SAVE_INT(insecure_concurrency); 40 | P_SAVE_BOOL(disable_log); 41 | 42 | return true; 43 | } 44 | 45 | QList> EditNaive::get_editor_cached() { 46 | return { 47 | {ui->certificate, CACHE.certificate}, 48 | {ui->extra_headers, CACHE.extra_headers}, 49 | }; 50 | } 51 | 52 | void EditNaive::on_certificate_clicked() { 53 | bool ok; 54 | auto txt = QInputDialog::getMultiLineText(this, tr("Certificate"), "", CACHE.certificate, &ok); 55 | if (ok) { 56 | CACHE.certificate = txt; 57 | editor_cache_updated(); 58 | } 59 | } 60 | 61 | void EditNaive::on_extra_headers_clicked() { 62 | bool ok; 63 | auto txt = QInputDialog::getMultiLineText(this, tr("Extra headers"), "", CACHE.extra_headers, &ok); 64 | if (ok) { 65 | CACHE.extra_headers = txt; 66 | editor_cache_updated(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ui/edit/edit_naive.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "profile_editor.h" 5 | 6 | QT_BEGIN_NAMESPACE 7 | namespace Ui { 8 | class EditNaive; 9 | } 10 | QT_END_NAMESPACE 11 | 12 | class EditNaive : public QWidget, public ProfileEditor { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit EditNaive(QWidget *parent = nullptr); 17 | 18 | ~EditNaive() override; 19 | 20 | void onStart(std::shared_ptr _ent) override; 21 | 22 | bool onEnd() override; 23 | 24 | QList> get_editor_cached() override; 25 | 26 | private: 27 | Ui::EditNaive *ui; 28 | std::shared_ptr ent; 29 | 30 | struct { 31 | QString certificate; 32 | QString extra_headers; 33 | } CACHE; 34 | 35 | private slots: 36 | 37 | void on_certificate_clicked(); 38 | 39 | void on_extra_headers_clicked(); 40 | }; 41 | -------------------------------------------------------------------------------- /ui/edit/edit_quic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "profile_editor.h" 7 | 8 | QT_BEGIN_NAMESPACE 9 | namespace Ui { 10 | class EditQUIC; 11 | } 12 | QT_END_NAMESPACE 13 | 14 | class EditQUIC : public QWidget, public ProfileEditor { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit EditQUIC(QWidget *parent = nullptr); 19 | 20 | ~EditQUIC() override; 21 | 22 | void onStart(std::shared_ptr _ent) override; 23 | 24 | bool onEnd() override; 25 | 26 | QList> get_editor_cached() override; 27 | 28 | private: 29 | Ui::EditQUIC *ui; 30 | std::shared_ptr ent; 31 | 32 | struct { 33 | QString caText; 34 | } CACHE; 35 | 36 | private slots: 37 | 38 | void on_certificate_clicked(); 39 | }; 40 | -------------------------------------------------------------------------------- /ui/edit/edit_shadowsocks.cpp: -------------------------------------------------------------------------------- 1 | #include "edit_shadowsocks.h" 2 | #include "ui_edit_shadowsocks.h" 3 | 4 | #include "fmt/ShadowSocksBean.hpp" 5 | #include "fmt/Preset.hpp" 6 | 7 | EditShadowSocks::EditShadowSocks(QWidget *parent) : QWidget(parent), 8 | ui(new Ui::EditShadowSocks) { 9 | ui->setupUi(this); 10 | ui->method->addItems(Preset::SingBox::ShadowsocksMethods); 11 | } 12 | 13 | EditShadowSocks::~EditShadowSocks() { 14 | delete ui; 15 | } 16 | 17 | void EditShadowSocks::onStart(std::shared_ptr _ent) { 18 | this->ent = _ent; 19 | auto bean = this->ent->ShadowSocksBean(); 20 | 21 | ui->method->setCurrentText(bean->method); 22 | ui->uot->setCurrentIndex(bean->uot); 23 | ui->password->setText(bean->password); 24 | auto ssPlugin = bean->plugin.split(";"); 25 | if (!ssPlugin.empty()) { 26 | ui->plugin->setCurrentText(ssPlugin[0]); 27 | ui->plugin_opts->setText(SubStrAfter(bean->plugin, ";")); 28 | } 29 | } 30 | 31 | bool EditShadowSocks::onEnd() { 32 | auto bean = this->ent->ShadowSocksBean(); 33 | 34 | bean->method = ui->method->currentText(); 35 | bean->password = ui->password->text(); 36 | bean->uot = ui->uot->currentIndex(); 37 | bean->plugin = ui->plugin->currentText(); 38 | if (!bean->plugin.isEmpty()) { 39 | bean->plugin += ";" + ui->plugin_opts->text(); 40 | } 41 | 42 | return true; 43 | } 44 | -------------------------------------------------------------------------------- /ui/edit/edit_shadowsocks.h: -------------------------------------------------------------------------------- 1 | #ifndef EDIT_SHADOWSOCKS_H 2 | #define EDIT_SHADOWSOCKS_H 3 | 4 | #include 5 | #include "profile_editor.h" 6 | 7 | namespace Ui { 8 | class EditShadowSocks; 9 | } 10 | 11 | class EditShadowSocks : public QWidget, public ProfileEditor { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit EditShadowSocks(QWidget *parent = nullptr); 16 | 17 | ~EditShadowSocks() override; 18 | 19 | void onStart(std::shared_ptr _ent) override; 20 | 21 | bool onEnd() override; 22 | 23 | private: 24 | Ui::EditShadowSocks *ui; 25 | std::shared_ptr ent; 26 | }; 27 | 28 | #endif // EDIT_SHADOWSOCKS_H 29 | -------------------------------------------------------------------------------- /ui/edit/edit_socks_http.cpp: -------------------------------------------------------------------------------- 1 | #include "edit_socks_http.h" 2 | #include "ui_edit_socks_http.h" 3 | 4 | #include "fmt/SocksHttpBean.hpp" 5 | 6 | EditSocksHttp::EditSocksHttp(QWidget *parent) : QWidget(parent), 7 | ui(new Ui::EditSocksHttp) { 8 | ui->setupUi(this); 9 | } 10 | 11 | EditSocksHttp::~EditSocksHttp() { 12 | delete ui; 13 | } 14 | 15 | void EditSocksHttp::onStart(std::shared_ptr _ent) { 16 | this->ent = _ent; 17 | auto bean = this->ent->SocksHTTPBean(); 18 | 19 | if (bean->socks_http_type == NekoGui_fmt::SocksHttpBean::type_Socks4) { 20 | ui->version->setCurrentIndex(1); 21 | } else { 22 | ui->version->setCurrentIndex(0); 23 | } 24 | if (bean->socks_http_type == NekoGui_fmt::SocksHttpBean::type_HTTP) { 25 | ui->version->setVisible(false); 26 | ui->version_l->setVisible(false); 27 | } 28 | 29 | ui->username->setText(bean->username); 30 | ui->password->setText(bean->password); 31 | } 32 | 33 | bool EditSocksHttp::onEnd() { 34 | auto bean = this->ent->SocksHTTPBean(); 35 | 36 | if (ui->version->isVisible()) { 37 | if (ui->version->currentIndex() == 1) { 38 | bean->socks_http_type = NekoGui_fmt::SocksHttpBean::type_Socks4; 39 | } else { 40 | bean->socks_http_type = NekoGui_fmt::SocksHttpBean::type_Socks5; 41 | } 42 | } 43 | 44 | bean->username = ui->username->text(); 45 | bean->password = ui->password->text(); 46 | 47 | return true; 48 | } 49 | -------------------------------------------------------------------------------- /ui/edit/edit_socks_http.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "profile_editor.h" 5 | 6 | namespace Ui { 7 | class EditSocksHttp; 8 | } 9 | 10 | class EditSocksHttp : public QWidget, public ProfileEditor { 11 | Q_OBJECT 12 | 13 | public: 14 | explicit EditSocksHttp(QWidget *parent = nullptr); 15 | 16 | ~EditSocksHttp() override; 17 | 18 | void onStart(std::shared_ptr _ent) override; 19 | 20 | bool onEnd() override; 21 | 22 | private: 23 | Ui::EditSocksHttp *ui; 24 | std::shared_ptr ent; 25 | }; 26 | -------------------------------------------------------------------------------- /ui/edit/edit_socks_http.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | EditSocksHttp 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 20 | Version 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Username 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 5 42 | 43 | 44 | 45 | 46 | 4 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Password 55 | 56 | 57 | 58 | 59 | 60 | 61 | version 62 | username 63 | password 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /ui/edit/edit_trojan_vless.cpp: -------------------------------------------------------------------------------- 1 | #include "edit_trojan_vless.h" 2 | #include "ui_edit_trojan_vless.h" 3 | 4 | #include "fmt/TrojanVLESSBean.hpp" 5 | #include "fmt/Preset.hpp" 6 | 7 | EditTrojanVLESS::EditTrojanVLESS(QWidget *parent) : QWidget(parent), ui(new Ui::EditTrojanVLESS) { 8 | ui->setupUi(this); 9 | } 10 | 11 | EditTrojanVLESS::~EditTrojanVLESS() { 12 | delete ui; 13 | } 14 | 15 | void EditTrojanVLESS::onStart(std::shared_ptr _ent) { 16 | this->ent = _ent; 17 | auto bean = this->ent->TrojanVLESSBean(); 18 | if (bean->proxy_type == NekoGui_fmt::TrojanVLESSBean::proxy_VLESS) { 19 | ui->label->setText("UUID"); 20 | } 21 | if (bean->proxy_type != NekoGui_fmt::TrojanVLESSBean::proxy_VLESS) { 22 | ui->flow->hide(); 23 | ui->flow_l->hide(); 24 | } 25 | ui->password->setText(bean->password); 26 | ui->flow->addItems(Preset::SingBox::Flows); 27 | ui->flow->setCurrentText(bean->flow); 28 | } 29 | 30 | bool EditTrojanVLESS::onEnd() { 31 | auto bean = this->ent->TrojanVLESSBean(); 32 | bean->password = ui->password->text(); 33 | bean->flow = ui->flow->currentText(); 34 | return true; 35 | } 36 | -------------------------------------------------------------------------------- /ui/edit/edit_trojan_vless.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "profile_editor.h" 5 | 6 | QT_BEGIN_NAMESPACE 7 | namespace Ui { 8 | class EditTrojanVLESS; 9 | } 10 | QT_END_NAMESPACE 11 | 12 | class EditTrojanVLESS : public QWidget, public ProfileEditor { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit EditTrojanVLESS(QWidget *parent = nullptr); 17 | 18 | ~EditTrojanVLESS() override; 19 | 20 | void onStart(std::shared_ptr _ent) override; 21 | 22 | bool onEnd() override; 23 | 24 | private: 25 | Ui::EditTrojanVLESS *ui; 26 | std::shared_ptr ent; 27 | }; 28 | -------------------------------------------------------------------------------- /ui/edit/edit_trojan_vless.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | EditTrojanVLESS 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Password 24 | 25 | 26 | 27 | 28 | 29 | 30 | Flow 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | MyLineEdit 48 | QLineEdit 49 |
ui/widget/MyLineEdit.h
50 |
51 |
52 | 53 | 54 |
55 | -------------------------------------------------------------------------------- /ui/edit/edit_vmess.cpp: -------------------------------------------------------------------------------- 1 | #include "edit_vmess.h" 2 | #include "ui_edit_vmess.h" 3 | 4 | #include "fmt/VMessBean.hpp" 5 | 6 | #include 7 | 8 | EditVMess::EditVMess(QWidget *parent) : QWidget(parent), ui(new Ui::EditVMess) { 9 | ui->setupUi(this); 10 | connect(ui->uuidgen, &QPushButton::clicked, this, [=] { ui->uuid->setText(QUuid::createUuid().toString().remove("{").remove("}")); }); 11 | } 12 | 13 | EditVMess::~EditVMess() { 14 | delete ui; 15 | } 16 | 17 | void EditVMess::onStart(std::shared_ptr _ent) { 18 | this->ent = _ent; 19 | auto bean = this->ent->VMessBean(); 20 | 21 | ui->uuid->setText(bean->uuid); 22 | ui->aid->setText(Int2String(bean->aid)); 23 | ui->security->setCurrentText(bean->security); 24 | } 25 | 26 | bool EditVMess::onEnd() { 27 | auto bean = this->ent->VMessBean(); 28 | 29 | bean->uuid = ui->uuid->text(); 30 | bean->aid = ui->aid->text().toInt(); 31 | bean->security = ui->security->currentText(); 32 | 33 | return true; 34 | } 35 | -------------------------------------------------------------------------------- /ui/edit/edit_vmess.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "profile_editor.h" 5 | 6 | QT_BEGIN_NAMESPACE 7 | namespace Ui { 8 | class EditVMess; 9 | } 10 | QT_END_NAMESPACE 11 | 12 | class EditVMess : public QWidget, public ProfileEditor { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit EditVMess(QWidget *parent = nullptr); 17 | 18 | ~EditVMess() override; 19 | 20 | void onStart(std::shared_ptr _ent) override; 21 | 22 | bool onEnd() override; 23 | 24 | private: 25 | Ui::EditVMess *ui; 26 | std::shared_ptr ent; 27 | }; 28 | -------------------------------------------------------------------------------- /ui/edit/profile_editor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "db/ProxyEntity.hpp" 6 | #include "main/GuiUtils.hpp" 7 | 8 | class ProfileEditor { 9 | public: 10 | virtual void onStart(std::shared_ptr ent) = 0; 11 | 12 | virtual bool onEnd() = 0; 13 | 14 | std::function get_edit_dialog; 15 | std::function get_edit_text_name; 16 | std::function get_edit_text_serverAddress; 17 | std::function get_edit_text_serverPort; 18 | 19 | // cached editor 20 | 21 | std::function editor_cache_updated; 22 | 23 | virtual QList> get_editor_cached() { return {}; }; 24 | }; 25 | -------------------------------------------------------------------------------- /ui/mainwindow_interface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MW_INTERFACE 4 | 5 | #include "mainwindow.h" 6 | -------------------------------------------------------------------------------- /ui/widget/FloatCheckBox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class FloatCheckBox : public QCheckBox { 7 | public: 8 | QWidget *parent; 9 | QWidget *window; 10 | 11 | void refresh() { 12 | setFixedSize(24, 24); 13 | auto pos = parent->rect().topRight(); 14 | pos = parent->mapTo(window, pos); 15 | pos.setX(pos.x() - 48); // ? 16 | move(pos); 17 | raise(); 18 | setVisible(parent->isVisible()); 19 | }; 20 | 21 | bool eventFilter(QObject *obj, QEvent *e) override { 22 | if (obj != window || e->type() != QEvent::Resize) return false; 23 | refresh(); 24 | return false; 25 | }; 26 | 27 | explicit FloatCheckBox(QWidget *parent, QWidget *window) : QCheckBox(window) { 28 | this->parent = parent; 29 | this->window = window; 30 | window->installEventFilter(this); 31 | refresh(); 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /ui/widget/GroupItem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "db/Database.hpp" 7 | 8 | QT_BEGIN_NAMESPACE 9 | namespace Ui { 10 | class GroupItem; 11 | } 12 | QT_END_NAMESPACE 13 | 14 | class GroupItem : public QWidget { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit GroupItem(QWidget *parent, const std::shared_ptr &ent, QListWidgetItem *item); 19 | 20 | ~GroupItem() override; 21 | 22 | void refresh_data(); 23 | 24 | std::shared_ptr ent; 25 | QListWidgetItem *item; 26 | 27 | private: 28 | Ui::GroupItem *ui; 29 | 30 | QWidget *parentWindow; 31 | 32 | signals: 33 | 34 | void edit_clicked(); 35 | 36 | private slots: 37 | 38 | void on_update_sub_clicked(); 39 | 40 | void on_edit_clicked(); 41 | 42 | void on_remove_clicked(); 43 | }; 44 | -------------------------------------------------------------------------------- /ui/widget/MessageBoxTimer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class MessageBoxTimer : public QTimer { 7 | public: 8 | QMessageBox *msgbox = nullptr; 9 | bool showed = false; 10 | 11 | explicit MessageBoxTimer(QObject *parent, QMessageBox *msgbox, int delayMs) : QTimer(parent) { 12 | connect(this, &QTimer::timeout, this, &MessageBoxTimer::timeoutFunc, Qt::ConnectionType::QueuedConnection); 13 | this->msgbox = msgbox; 14 | setSingleShot(true); 15 | setInterval(delayMs); 16 | start(); 17 | }; 18 | 19 | void cancel() { 20 | QTimer::stop(); 21 | if (msgbox != nullptr && showed) { 22 | msgbox->reject(); // return the timeoutFunc 23 | } 24 | }; 25 | 26 | private: 27 | void timeoutFunc() { 28 | if (msgbox == nullptr) return; 29 | showed = true; 30 | msgbox->exec(); 31 | msgbox = nullptr; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /ui/widget/MyLineEdit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class MyLineEdit : public QLineEdit { 6 | public slots: 7 | 8 | explicit MyLineEdit(QWidget *parent = nullptr) : QLineEdit(parent) { 9 | } 10 | 11 | void setText(const QString &s) { 12 | QLineEdit::setText(s); 13 | QLineEdit::home(false); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /ui/widget/ProxyItem.cpp: -------------------------------------------------------------------------------- 1 | #include "ProxyItem.h" 2 | #include "ui_ProxyItem.h" 3 | 4 | #include 5 | 6 | ProxyItem::ProxyItem(QWidget *parent, const std::shared_ptr &ent, QListWidgetItem *item) 7 | : QWidget(parent), ui(new Ui::ProxyItem) { 8 | ui->setupUi(this); 9 | this->setLayoutDirection(Qt::LeftToRight); 10 | 11 | this->item = item; 12 | this->ent = ent; 13 | if (ent == nullptr) return; 14 | 15 | refresh_data(); 16 | } 17 | 18 | ProxyItem::~ProxyItem() { 19 | delete ui; 20 | } 21 | 22 | void ProxyItem::refresh_data() { 23 | ui->type->setText(ent->bean->DisplayType()); 24 | ui->name->setText(ent->bean->DisplayName()); 25 | ui->address->setText(ent->bean->DisplayAddress()); 26 | ui->traffic->setText(ent->traffic_data->DisplayTraffic()); 27 | ui->test_result->setText(ent->DisplayLatency()); 28 | 29 | runOnUiThread( 30 | [=] { 31 | adjustSize(); 32 | item->setSizeHint(sizeHint()); 33 | dynamic_cast(parent())->adjustSize(); 34 | }, 35 | this); 36 | } 37 | 38 | void ProxyItem::on_remove_clicked() { 39 | if (!this->remove_confirm || 40 | QMessageBox::question(this, tr("Confirmation"), tr("Remove %1?").arg(ent->bean->DisplayName())) == QMessageBox::StandardButton::Yes) { 41 | // TODO do remove (or not) -> callback 42 | delete item; 43 | } 44 | } 45 | 46 | QPushButton *ProxyItem::get_change_button() { 47 | return ui->change; 48 | } 49 | -------------------------------------------------------------------------------- /ui/widget/ProxyItem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "db/ProxyEntity.hpp" 7 | 8 | QT_BEGIN_NAMESPACE 9 | namespace Ui { 10 | class ProxyItem; 11 | } 12 | QT_END_NAMESPACE 13 | 14 | class QPushButton; 15 | 16 | class ProxyItem : public QWidget { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit ProxyItem(QWidget *parent, const std::shared_ptr &ent, QListWidgetItem *item); 21 | 22 | ~ProxyItem() override; 23 | 24 | void refresh_data(); 25 | 26 | QPushButton *get_change_button(); 27 | 28 | std::shared_ptr ent; 29 | QListWidgetItem *item; 30 | bool remove_confirm = false; 31 | 32 | private: 33 | Ui::ProxyItem *ui; 34 | 35 | private slots: 36 | 37 | void on_remove_clicked(); 38 | }; 39 | --------------------------------------------------------------------------------