diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..80d3506 --- /dev/null +++ b/.clang-format @@ -0,0 +1,84 @@ +# Generated from CLion C/C++ Code Style settings +Language: Cpp +BasedOnStyle: Google +ColumnLimit: 100 +SortIncludes: true +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +CompactNamespaces: false +ContinuationIndentWidth: 4 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +DerivePointerAlignment: false +PointerAlignment: Left +ReflowComments: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +IncludeBlocks: Merge +TabWidth: 2 +UseTab: Never + +--- +Language: ObjC +BasedOnStyle: Google +ColumnLimit: 100 + +# Only sort headers in each include block +SortIncludes: true +IncludeBlocks: Preserve +DerivePointerAlignment: false +PointerAlignment: Left +AllowShortFunctionsOnASingleLine: None +BraceWrapping: + SplitEmptyFunction: true \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b058aa3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,94 @@ +cmake_minimum_required(VERSION 3.13) +project(inspectorTool) + +include(./third_party/vendor_tools/vendor.cmake) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +cmake_policy(SET CMP0063 NEW) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(TGFX_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tgfx) +set(INSPECTOR_COMMON_DIR ${TGFX_DIR}/src/debug) + +if (NOT CMAKE_PREFIX_PATH) + if (NOT EXISTS ${PROJECT_SOURCE_DIR}/QTCMAKE.cfg) + file(WRITE ${PROJECT_SOURCE_DIR}/QTCMAKE.cfg + "set(CMAKE_PREFIX_PATH /Users/[username]/Qt/6.6.1/macos/lib/cmake) #put your own QT path here") + endif () + include("QTCMAKE.cfg") +endif () + +string(REGEX MATCH "([0-9]+)\\.[0-9]+\\.[0-9]+" QT_VERSION ${CMAKE_PREFIX_PATH}) +if (QT_VERSION) + string(REGEX MATCH "^[0-9]+" QT_VERSION_MAJOR ${QT_VERSION}) + if (QT_VERSION_MAJOR GREATER_EQUAL 6 AND CMAKE_SIZEOF_VOID_P EQUAL 4) + message(FATAL_ERROR "QT has dropped support for 32-bit builds since version 6.0.0") + endif () +endif () + +set(TGFX_USE_QT ON) +set(TGFX_BUILD_INSPECTOR OFF) +set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) +add_subdirectory(${TGFX_DIR} tgfx EXCLUDE_FROM_ALL) + +find_package(Qt6 REQUIRED COMPONENTS Core Widgets OpenGL qml Quick WebSockets Network QuickControls2 Core5Compat) +list(APPEND TGFX_INSPECTOR_LIBS Qt6::Core Qt6::Widgets Qt6::WebSockets Qt6::OpenGL Qt6::Qml Qt6::Quick Qt6::Network + Qt6::QuickControls2 Qt6::Core5Compat) +qt6_add_resources(QT_RESOURCES res.qrc) + +if (APPLE) + find_library(APPLICATION_SERVICES_FRAMEWORK ApplicationServices REQUIRED) + list(APPEND TGFX_INSPECTOR_LIBS ${APPLICATION_SERVICES_FRAMEWORK}) + find_library(QUARTZ_CORE QuartzCore REQUIRED) + list(APPEND TGFX_INSPECTOR_LIBS ${QUARTZ_CORE}) + find_library(COCOA Cocoa REQUIRED) + list(APPEND TGFX_INSPECTOR_LIBS ${COCOA}) + find_library(FOUNDATION Foundation REQUIRED) + list(APPEND TGFX_INSPECTOR_LIBS ${FOUNDATION}) + find_library(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c) + list(APPEND TGFX_INSPECTOR_LIBS ${ICONV_LIBRARIES}) + find_library(VIDEOTOOLBOX VideoToolbox) + list(APPEND TGFX_INSPECTOR_LIBS ${VIDEOTOOLBOX}) + find_library(CORE_MEDIA CoreMedia) + list(APPEND TGFX_INSPECTOR_LIBS ${CORE_MEDIA}) + find_library(COMPRESSION_LIBRARIES NAMES compression) + list(APPEND TGFX_INSPECTOR_LIBS ${COMPRESSION_LIBRARIES}) +elseif (WIN32) + set(BUILD_USE_64BITS ON) + add_definitions(-DNOMINMAX -D_USE_MATH_DEFINES) + find_library(Bcrypt_LIB Bcrypt) + list(APPEND TGFX_INSPECTOR_LIBS ${Bcrypt_LIB}) + find_library(ws2_32_LIB ws2_32) + list(APPEND TGFX_INSPECTOR_LIBS ${ws2_32_LIB}) +endif () + +list(APPEND TGFX_INSPECTOR_SRC src src/tags src/socket/*.* src/layerInspector/*.*) +file(GLOB TGFX_INSPECTOR_FILES src/*.* src/tags/*.* src/socket/*.* src/layerInspector/*.*) +set(LZ4_DIR ${TGFX_DIR}/third_party/lz4/lib) + +list(APPEND LZ4_FILE ${LZ4_DIR}/lz4.c) +list(APPEND TGFX_INSPECTOR_COMMON_INCLUDE ${LZ4_DIR} ${INSPECTOR_COMMON_DIR}) + +file(GLOB TGFX_INSPECTOR_COMMON_FILES + ${LZ4_FILE} + ${INSPECTOR_COMMON_DIR}/Socket.cpp) + +list(APPEND INSPECTOR_STATIC_VENDORS KDDockWidgets) +add_vendor_target(inspector-vendor STATIC_VENDORS ${INSPECTOR_STATIC_VENDORS}) +find_vendor_libraries(inspector-vendor STATIC INSPECTOR_VENDOR_STATIC_LIBRARIES) +list(APPEND TGFX_INSPECTOR_LIBS ${INSPECTOR_VENDOR_STATIC_LIBRARIES}) +list(APPEND TGFX_INSPECTOR_DEFINE KDDW_FRONTEND_QTQUICK KDDW_FRONTEND_QT KDDW_QTGUI_TYPES) + +list(APPEND TGFX_INSPECTOR_INCLUDE ./third_party/KDDockWidgets/src/fwd_headers) + +list(APPEND TGFX_INSPECTOR_OPTIONS -fno-rtti) + +add_executable(inspectorTool ${RC_FILES} ${TGFX_INSPECTOR_FILES} ${TGFX_INSPECTOR_COMMON_FILES} ${QT_RESOURCES}) +target_include_directories(inspectorTool PUBLIC ${TGFX_INSPECTOR_SRC} ${TGFX_INSPECTOR_COMMON_INCLUDE}) +target_include_directories(inspectorTool SYSTEM PRIVATE ${TGFX_INSPECTOR_INCLUDE}) +target_compile_definitions(inspectorTool PUBLIC ${TGFX_INSPECTOR_DEFINE}) +target_compile_options(inspectorTool PUBLIC ${TGFX_INSPECTOR_OPTIONS}) +target_link_libraries(inspectorTool tgfx ${TGFX_INSPECTOR_LIBS}) \ No newline at end of file diff --git a/DEPS b/DEPS new file mode 100644 index 0000000..445956a --- /dev/null +++ b/DEPS @@ -0,0 +1,33 @@ +{ + "version": "1.3.12", + "vars": { + "PAG_GROUP": "https://github.com/libpag" + }, + "repos": { + "common": [ + { + "url": "${PAG_GROUP}/vendor_tools.git", + "commit": "76b266c30e3314678a0d8daeb5b40a9389ea5544", + "dir": "third_party/vendor_tools" + }, + { + "url": "https://github.com/KDAB/KDDockWidgets.git", + "commit": "bdf4e7bd4ee49c961cac8a0caff44c25ca2a06b4", + "dir": "third_party/KDDockWidgets" + }, + { + "url": "https://github.com/Tencent/tgfx.git", + "commit": "ff01c45b1b00b5c732e388358cb3065188b23763", + "dir": "tgfx" + } + ] + }, + "actions": { + "common": [ + { + "command": "depsync --clean", + "dir": "third_party" + } + ] + } +} diff --git a/codeformat.sh b/codeformat.sh new file mode 100755 index 0000000..266dfc0 --- /dev/null +++ b/codeformat.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +cd $(dirname $0) + +if [[ $(uname) == 'Darwin' ]]; then + MAC_REQUIRED_TOOLS="python3" + for TOOL in ${MAC_REQUIRED_TOOLS[@]}; do + if [ ! $(which $TOOL) ]; then + if [ ! $(which brew) ]; then + echo "Homebrew not found. Trying to install..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || + exit 1 + fi + echo "$TOOL not found. Trying to install..." + brew install $TOOL || exit 1 + fi + done + clangformat=`clang-format --version` + if [[ $clangformat =~ "14." ]] + then + echo "----$clangformat----" + else + echo "----install clang-format----" + pip3 install clang-format==14 + fi +fi + +echo "----begin to scan code format----" +find src -name "*.cpp" -print -o -name "*.c" -print -o -name "*.h" -print -o -name "*.mm" -print -o -name "*.m" -print | xargs clang-format -i + +git diff +result=`git diff` +if [[ $result =~ "diff" ]] +then + echo "----Failed to pass the code format check----" + exit 1 +else + echo "----Complete the code format check-----" +fi + + + diff --git a/icons/InspectorIcon.png b/icons/InspectorIcon.png new file mode 100644 index 0000000..b429eed Binary files /dev/null and b/icons/InspectorIcon.png differ diff --git a/icons/LayerTreeIcon.png b/icons/LayerTreeIcon.png new file mode 100644 index 0000000..fe67523 Binary files /dev/null and b/icons/LayerTreeIcon.png differ diff --git a/icons/arrow_icon.png b/icons/arrow_icon.png new file mode 100644 index 0000000..41db4b3 Binary files /dev/null and b/icons/arrow_icon.png differ diff --git a/icons/capture.png b/icons/capture.png new file mode 100644 index 0000000..4ec18f6 Binary files /dev/null and b/icons/capture.png differ diff --git a/icons/chooseOpenFile.png b/icons/chooseOpenFile.png new file mode 100644 index 0000000..2a3f26a Binary files /dev/null and b/icons/chooseOpenFile.png differ diff --git a/icons/close.png b/icons/close.png new file mode 100644 index 0000000..c46bf52 Binary files /dev/null and b/icons/close.png differ diff --git a/icons/disconnected.png b/icons/disconnected.png new file mode 100644 index 0000000..91ccac5 Binary files /dev/null and b/icons/disconnected.png differ diff --git a/icons/openfile.png b/icons/openfile.png new file mode 100644 index 0000000..2c8e38b Binary files /dev/null and b/icons/openfile.png differ diff --git a/icons/savefile.png b/icons/savefile.png new file mode 100644 index 0000000..afd4178 Binary files /dev/null and b/icons/savefile.png differ diff --git a/install_tools.sh b/install_tools.sh new file mode 100755 index 0000000..71161ba --- /dev/null +++ b/install_tools.sh @@ -0,0 +1,18 @@ +#!/bin/bash -e +cd $(dirname $0) +# install all the tools required for the autotest. + +if [[ `uname` == 'Darwin' ]]; then + MAC_REQUIRED_TOOLS="cmake ninja git-lfs" + for TOOL in ${MAC_REQUIRED_TOOLS[@]}; do + if [ ! $(which $TOOL) ]; then + if [ ! $(which brew) ]; then + echo "Homebrew not found. Trying to install..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + fi + echo "$TOOL not found. Trying to install..." + brew install $TOOL + fi + done +fi + diff --git a/qml/StartView.qml b/qml/StartView.qml new file mode 100644 index 0000000..848c1fd --- /dev/null +++ b/qml/StartView.qml @@ -0,0 +1,638 @@ +import QtQuick 2.6 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import QtQuick.Window 2.12 +import QtQuick.Dialogs +import com.kdab.dockwidgets 2.0 as KDDW +import Qt5Compat.GraphicalEffects + + +ApplicationWindow { + id: startView + width: 800 + height: 600 + minimumWidth: 400 + minimumHeight: 300 + maximumWidth: 1600 + maximumHeight: 1200 + visible: true + title: "Inspector-welcome" + color: "#383838" + property int selectedIndex: 0 + property int selectedFilePathIndex: -1 + property int selectedClientIndex: -1 + + FileDialog { + id: openFileDialog + title: qsTr("open File") + fileMode: FileDialog.OpenFile + nameFilters: [ "Inspector files (*.isp)" ] + onAccepted: { + startViewModel.openFile(openFileDialog.selectedFile) + close() + } + } + + Column { + anchors.fill: parent + spacing: 2 + clip: true + ///* open file area header*/// + Column { + spacing: 2 + width: parent.width + + Rectangle { + width: parent.width + height: 30 + color: "#535353" + + Row { + anchors.left: parent.left + anchors.leftMargin: 10 + spacing: 10 + Text { + anchors.verticalCenter: parent.verticalCenter + text: "Open File" + color: "#DDDDDD" + font.pixelSize: 14 + } + + Item { + height: 30 + width: 30 + Image { + id: openFile + source: "qrc:icons/chooseOpenFile.png" + width: 20 + height: 20 + anchors.centerIn: parent + + ColorOverlay{ + anchors.fill: parent + color: "#DDDDDD" + source: parent + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: parent.scale = 1.1 + onExited: parent.scale = 1.0 + onClicked: { + openFileDialog.open(); + } + } + + } + } + } + } + + ///* open file and drag open file area */// + Row { + width: parent.width + height: (parent.height - 110) * 0.45 + spacing: 2 + + Rectangle { + width: parent.width / 2 + height: parent.height + color: "#434343" + + Text { + id: recentFilesTitle + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.topMargin: 5 + text: "Recent File" + color: "#DDDDDD" + font.pixelSize: 14 + } + + ListView { + id: recentFilesList + anchors.top: recentFilesTitle.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 10 + spacing: 6 + clip: true + model: startViewModel ? startViewModel.fileItems : null + + delegate: Rectangle { + width: recentFilesList.width + height: 40 + color: { + if(mouseArea.containsMouse) { + return "#6b6b6b" + } + else if(index === selectedFilePathIndex) { + return "#6b6b6b" + } + else { + return "transparent" + } + } + radius: 3 + + Column { + anchors.fill: parent + anchors.leftMargin: 8 + anchors.rightMargin: 8 + anchors.verticalCenter: parent.verticalCenter + spacing: 2 + + Text { + text: modelData.filesName + color: "#DDDDDD" + font.pixelSize: 12 + width: parent.width + elide: Text.ElideMiddle + } + + Text { + text: modelData.filesPath + color: "#dddddd" + font.pixelSize: 10 + width: parent.width + elide: Text.ElideMiddle + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + selectedFilePathIndex = index + } + onDoubleClicked: { + if (startViewModel && selectedIndex === 0) { + startViewModel.openFile(modelData.filesPath) + } + else if(selectedIndex === 1) { + console.error("Error: Cannot open file by double-clicking in LayerTree mode") + } + } + } + } + } + } + + Rectangle { + width: parent.width / 2 + height: parent.height + color: "#434343" + + Text { + id: dragAreaTitle + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.topMargin: 5 + text: "Drag Open File" + color: "#DDDDDD" + font.pixelSize: 14 + } + + Rectangle { + id: dropContainer + anchors.top: dragAreaTitle.bottom + anchors.topMargin: 10 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 20 + color: dropArea.containsMouse ? "#2d2d2d" : "#434343" + radius: 5 + + Image { + anchors.centerIn: parent + source : "qrc:/icons/openfile.png" + width: 48 + height: 48 + opacity: 0.5 + } + + Text { + anchors.centerIn: parent + anchors.verticalCenterOffset: 50 + text: "Drag file to here" + color: "#44ffffff" + font.pixelSize: 14 + } + + DropArea { + id: dropArea + anchors.fill: parent + + onDropped: function(drop) { + if (drop.hasUrls) { + var fileUrl = drop.urls[0]; + var filePath; + if (Qt.platform.os === "windows") { + filePath = fileUrl.toString().replace(/^(file:\/{3})|^file:\//, ""); + } else { + filePath = fileUrl.toString().replace(/^(file:\/{2})|^file:\//, ""); + } + filePath = decodeURIComponent(filePath); + if (startViewModel) { + startViewModel.openFile(filePath); + } + } + } + + onEntered: { + dropContainer.color = "#3d3d3d" + } + + onExited: { + dropContainer.color = "#434343" + } + } + } + } + } + + ///* open client and select launcher header*/// + Column { + spacing: 2 + width: parent.width + + Rectangle { + width: parent.width + height: 30 + color: "#535353" + + Text { + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.verticalCenter: parent.verticalCenter + text: "Start Connection" + color: "#DDDDDD" + font.pixelSize: 14 + } + } + } + + ///* open client and select launcher area */// + Row { + width: parent.width + height: (parent.height - 110) * 0.55 + spacing: 2 + + Rectangle { + width: parent.width / 2 + height: parent.height + color: "#434343" + + Text { + id: clientsTitle + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.topMargin: 5 + text: "Clients" + color: "#DDDDDD" + font.pixelSize: 14 + } + + ListView { + id: frameCaptureclientsList + visible: selectedIndex === 0 + anchors.top: clientsTitle.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 10 + spacing: 6 + clip: true + model: startViewModel ? startViewModel.frameCaptureClientItems : null + + delegate: Rectangle { + width: frameCaptureclientsList.width + height: 40 + color: { + if(mouseArea.containsMouse) { + return "#6b6b6b" + } + else if(index === selectedClientIndex) { + return "#6b6b6b" + } + else { + return "transparent" + } + } + radius: 3 + + Column { + anchors.fill: parent + anchors.leftMargin: 8 + anchors.rightMargin: 8 + anchors.verticalCenter: parent.verticalCenter + spacing: 2 + + Text { + text: modelData.procName + color: modelData.connected ? "#888888" : "#DDDDDD" + font.pixelSize: 12 + width: parent.width + elide: Text.ElideMiddle + } + + Text { + text: modelData.address + ":" + modelData.port + color: modelData.connected ? "#777777" : "#AAAAAA" + font.pixelSize: 10 + width: parent.width + elide: Text.ElideMiddle + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + enabled: !modelData.connected + onClicked: { + selectedClientIndex = index + } + + onDoubleClicked: { + if (startViewModel) { + if(selectedIndex === 0){ + startViewModel.connectToClient(modelData) + }else if(selectedIndex === 1){ + startViewModel.connectToClientByLayerInspector(modelData) + } + } + startView.hide() + } + } + } + } + + ListView { + id: layerTreeClientsList + visible: selectedIndex === 1 + anchors.top: clientsTitle.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 10 + spacing: 6 + clip: true + model: startViewModel ? startViewModel.layerTreeClientItems : null + + delegate: Rectangle { + width: layerTreeClientsList.width + height: 40 + color: { + if(mouseArea.containsMouse) { + return "#6b6b6b" + } + else if(index === selectedClientIndex) { + return "#6b6b6b" + } + else { + return "transparent" + } + } + radius: 3 + + Column { + anchors.fill: parent + anchors.leftMargin: 8 + anchors.rightMargin: 8 + anchors.verticalCenter: parent.verticalCenter + spacing: 2 + + Text { + text: modelData.procName + color: modelData.connected ? "#888888" : "#DDDDDD" + font.pixelSize: 12 + width: parent.width + elide: Text.ElideMiddle + } + + Text { + text: modelData.address + ":" + modelData.port + color: modelData.connected ? "#777777" : "#AAAAAA" + font.pixelSize: 10 + width: parent.width + elide: Text.ElideMiddle + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + enabled: !modelData.connected + onClicked: { + selectedClientIndex = index + } + + onDoubleClicked: { + if (startViewModel) { + if(selectedIndex === 0){ + startViewModel.connectToClient(modelData) + }else if(selectedIndex === 1){ + startViewModel.connectToClientByLayerInspector(modelData) + } + } + startView.hide() + } + } + } + } + + Text { + anchors.centerIn: parent + text: { + if(selectedIndex === 0 && frameCaptureclientsList.count === 0){ + return "Waitting for project start..."; + }else if(selectedIndex === 1 && layerTreeClientsList.count === 0){ + return "Waitting for project start..."; + }else{ + return ""; + } + } + color: "#44ffffff" + font.pixelSize: 14 + } + } + + Rectangle { + width: parent.width / 2 + height: parent.height + color: "#434343" + + + Row { + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 10 + spacing: 20 + + + Rectangle { + width: 100 + height: 120 + color: selectedIndex === 0 ? "#6b6b6b" : "transparent" + radius: 5 + + Column { + anchors.centerIn: parent + spacing: 5 + Image { + source: "qrc:/icons/InspectorIcon.png" + width: 80 + height: 80 + fillMode: Image.PreserveAspectFit + anchors.horizontalCenter: parent.horizontalCenter + } + Text { + text: "FrameCapture" + color: "#DDDDDD" + font.pixelSize: 14 + anchors.horizontalCenter: parent.horizontalCenter + } + } + + MouseArea { + anchors.fill: parent + onClicked: selectedIndex = 0 + } + } + + Rectangle { + width: 100 + height: 120 + color: selectedIndex === 1 ? "#6b6b6b" : "transparent" + radius: 5 + + Column { + anchors.centerIn: parent + spacing: 5 + Image { + source: "qrc:/icons/LayerTreeIcon.png" + width: 80 + height: 80 + fillMode: Image.PreserveAspectFit + } + Text { + text: "LayerTree" + color: "#DDDDDD" + font.pixelSize: 14 + anchors.horizontalCenter: parent.horizontalCenter + } + } + + MouseArea { + anchors.fill: parent + onClicked: selectedIndex = 1 + } + } + } + } + } + + ///* cancel and launch button */// + Rectangle { + width: parent.width + height: 40 + color: "#535353" + + Row { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 20 + spacing: 10 + + + Rectangle { + id: cancelBtn + width: 100 + height: 30 + radius: 3 + color: cancelArea.containsMouse ? "#444444" : "#535353" + border.color: "#383838" + border.width: 1 + + Text { + anchors.centerIn: parent + text: "Cancel" + color: "#DDDDDD" + font.pixelSize: 14 + } + + MouseArea { + id: cancelArea + anchors.fill: parent + hoverEnabled: true + onClicked: Qt.quit() + } + } + + + Rectangle { + id: launchBtn + width: 100 + height: 30 + radius: 3 + property bool canLaunch: + (selectedFilePathIndex !== -1 && selectedIndex === 0) || + (selectedClientIndex !== -1) + color: { + if(!canLaunch){ + return "#666666" + } + else if(launchArea.containsMouse){ + return "#444444" + } + else{ + return "#535353" + } + } + border.color: canLaunch ? "#383838" : "#666666" + border.width: 1 + opacity: canLaunch ? 1.0 : 0.6 + + Text { + anchors.centerIn: parent + text: "Launch" + color: "#DDDDDD" + font.pixelSize: 14 + } + + MouseArea { + id: launchArea + anchors.fill: parent + hoverEnabled: true + enabled: launchBtn.canLaunch + onClicked: { + if(selectedIndex === 0 && selectedFilePathIndex !== -1){ + var selectedFilePath = startViewModel.fileItems[selectedFilePathIndex].filesPath + startViewModel.openFile(selectedFilePath) + } + else if(selectedClientIndex !== -1){ + var selectedClient; + if(selectedIndex === 0){ + selectedClient = startViewModel.frameCaptureClientItems[selectedClientIndex] + startViewModel.connectToClient(selectedClient) + } else if(selectedIndex === 1){ + selectedClient = startViewModel.layerTreeClientsList[selectedClientIndex] + startViewModel.connectToClientByLayerInspector(selectedClient) + } + } + startView.hide(); + } + } + } + } + } + } +} diff --git a/res.qrc b/res.qrc new file mode 100644 index 0000000..3f92c37 --- /dev/null +++ b/res.qrc @@ -0,0 +1,13 @@ + + + qml/StartView.qml + icons/InspectorIcon.png + icons/LayerTreeIcon.png + icons/openfile.png + icons/chooseOpenFile.png + icons/capture.png + icons/arrow_icon.png + icons/savefile.png + icons/disconnected.png + + diff --git a/src/ResolvService.cpp b/src/ResolvService.cpp new file mode 100644 index 0000000..34feac0 --- /dev/null +++ b/src/ResolvService.cpp @@ -0,0 +1,74 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// +#include "ResolvService.h" +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#else +#include +#include +#include +#endif + +namespace inspector { +ResolvService::ResolvService(uint16_t port) : port(port) { + thread = std::make_unique([this] { worker(); }); +} + +ResolvService::~ResolvService() { + exit.store(true, std::memory_order_relaxed); + conditionVariable.notify_one(); + if (thread) { + thread->join(); + thread.reset(); + } +} + +void ResolvService::query(uint32_t ip, const std::function& callback) { + std::lock_guard lock(mutex); + queue.emplace_back(QueueItem{ip, callback}); + conditionVariable.notify_one(); +} + +void ResolvService::worker() { + struct sockaddr_in addr = {}; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + + char buf[128] = {}; + + for (;;) { + std::unique_lock lock(mutex); + conditionVariable.wait( + lock, [this] { return !queue.empty() || exit.load(std::memory_order_relaxed); }); + if (exit.load(std::memory_order_relaxed)) { + return; + } + auto query = queue.back(); + queue.pop_back(); + lock.unlock(); + + addr.sin_addr.s_addr = query.ip; + if (getnameinfo((const struct sockaddr*)&addr, sizeof(sockaddr_in), buf, 128, nullptr, 0, + NI_NOFQDN) != 0) { + inet_ntop(AF_INET, &query.ip, buf, 17); + } + query.callback(buf); + } +} +} // namespace inspector \ No newline at end of file diff --git a/src/ResolvService.h b/src/ResolvService.h new file mode 100644 index 0000000..7456e59 --- /dev/null +++ b/src/ResolvService.h @@ -0,0 +1,54 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace inspector { + +class ResolvService { + struct QueueItem { + uint32_t ip = 0; + std::function callback = nullptr; + }; + + public: + explicit ResolvService(uint16_t port); + + ~ResolvService(); + + void query(uint32_t ip, const std::function& callback); + + private: + void worker(); + + std::atomic exit = false; + std::mutex mutex = {}; + std::condition_variable conditionVariable = {}; + std::vector queue = {}; + uint16_t port = 0; + std::unique_ptr thread = nullptr; +}; +} // namespace inspector diff --git a/src/StartView.cpp b/src/StartView.cpp new file mode 100644 index 0000000..b474c55 --- /dev/null +++ b/src/StartView.cpp @@ -0,0 +1,295 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "StartView.h" +#include +#include +#include +#include +#include +#include "Protocol.h" +#include "Socket.h" +#include "tgfx/core/Clock.h" + +namespace inspector { +ClientData::ClientData(Data data) : data(std::move(data)) { +} + +StartView::StartView(QObject* parent) : QObject(parent) { + resolv = std::make_unique(port); + loadRecentFiles(); + + broadcastTimer = new QTimer(this); + connect(broadcastTimer, &QTimer::timeout, this, &StartView::updateBroadcastClients); + broadcastTimer->start(1000); +} + +StartView::~StartView() { + saveRecentFiles(); + qDeleteAll(fileItems); + if (broadcastTimer) { + broadcastTimer->stop(); + delete broadcastTimer; + } + for (auto& it : clients) { + delete it.second; + } + clients.clear(); + broadcastListen.reset(); + Q_EMIT quitStartView(); +} + +QList StartView::getFileItems() const { + QList items = {}; + items.reserve(fileItems.size()); + for (const auto& fileItem : fileItems) { + items.append(fileItem); + } + return items; +} + +void StartView::openFile(const QString& fPath) { + if (fPath.isEmpty() || !QFileInfo::exists(fPath)) { + return; + } + addRecentFile(fPath); + // TODO Open file +} + +void StartView::openFile(const QUrl& filePath) { + openFile(filePath.path()); +} + +void StartView::addRecentFile(const QString& fPath) { + if (fPath.isEmpty()) { + return; + } + recentFiles.removeAll(fPath); + recentFiles.prepend(fPath); + if (lastOpenFile != fPath) { + lastOpenFile = fPath; + Q_EMIT lastOpenFileChanged(); + } + while (recentFiles.size() >= 15) { + recentFiles.removeLast(); + } + qDeleteAll(fileItems); + fileItems.clear(); + for (const QString& file : recentFiles) { + fileItems.append(new FileItem(file, QFileInfo(file).fileName(), this)); + } + Q_EMIT fileItemsChanged(); + saveRecentFiles(); +} + +void StartView::clearRecentFiles() { + recentFiles.clear(); + qDeleteAll(fileItems); + fileItems.clear(); + + Q_EMIT fileItemsChanged(); + + saveRecentFiles(); +} + +QVector StartView::getFrameCaptureClientItems() const { + QVector clientDatas = {}; + clientDatas.reserve(static_cast(clients.size())); + for (auto& client : clients) { + if (client.second->data.type == static_cast(tgfx::debug::ToolType::FrameCapture)) { + clientDatas.push_back(client.second); + } + } + return clientDatas; +} + +QVector StartView::getLayerTreeClientItems() const { + QVector clientDatas = {}; + clientDatas.reserve(static_cast(clients.size())); + for (auto& client : clients) { + if (client.second->data.type == static_cast(tgfx::debug::ToolType::LayerTree)) { + clientDatas.push_back(client.second); + } + } + return clientDatas; +} + +void StartView::connectToClient(QObject* object) { + auto client = static_cast(object); + if (client) { + // TODO Connect to Insepector + } +} + +void StartView::connectToClientByLayerInspector(QObject* object) { + auto client = static_cast(object); + if (client) { + // TODO Connect to Layer Inspector + } +} + +void StartView::showStartView() { + if (!qmlEngine) { + qmlEngine = new QQmlApplicationEngine(this); + qmlEngine->rootContext()->setContextProperty("startViewModel", this); + qmlEngine->load(QUrl(QStringLiteral("qrc:/qml/StartView.qml"))); + + if (!qmlEngine->rootObjects().isEmpty()) { + auto startWindow = static_cast(qmlEngine->rootObjects().first()); + startWindow->setFlags(Qt::Window); + startWindow->setTitle("Inspector - Start"); + startWindow->resize(1000, 600); + startWindow->show(); + + connect(startWindow, &QQuickWindow::closing, this, &StartView::onCloseAllView); + connect(this, &StartView::quitStartView, QApplication::instance(), &QApplication::quit); + } else { + qWarning() << "无法加载StartView.qml"; + } + } + + if (!qmlEngine->rootObjects().isEmpty()) { + auto startWindow = static_cast(qmlEngine->rootObjects().first()); + startWindow->show(); + } +} + +void StartView::loadRecentFiles() { + QSettings settings("TGFX", "Inspector"); + recentFiles = settings.value(QStringLiteral("recentFiles")).toStringList(); + + QStringList validFiles; + for (const QString& file : recentFiles) { + if (QFileInfo::exists(file)) { + validFiles.append(file); + } + } + + recentFiles = validFiles; + qDeleteAll(fileItems); + fileItems.clear(); + + for (const QString& file : recentFiles) { + fileItems.append(new FileItem(file, QFileInfo(file).fileName(), this)); + } + + Q_EMIT fileItemsChanged(); +} + +void StartView::saveRecentFiles() { + QSettings settings("TGFXInspector", "Inspector"); + settings.setValue(QStringLiteral("recentFiles"), recentFiles); + settings.sync(); +} + +void StartView::updateBroadcastClients() { + const auto time = tgfx::Clock::Now(); + if (!broadcastListen) { + bool isListen = false; + broadcastListen = std::make_unique(); + for (uint16_t i = 0; i < tgfx::debug::BroadcastNum; i++) { + isListen = broadcastListen->listenSock(port + i); + if (isListen) { + break; + } + } + if (!isListen) { + broadcastListen.reset(); + } + } else { + tgfx::debug::IpAddress addr; + size_t len = 0; + for (;;) { + auto broadcastMessage = broadcastListen->readData(len, addr, 0); + if (!broadcastMessage) { + break; + } + if (len > sizeof(tgfx::debug::BroadcastMessage)) { + continue; + } + tgfx::debug::BroadcastMessage bm = {}; + memcpy(&bm, broadcastMessage, len); + auto protoVer = bm.protocolVersion; + char procname[tgfx::debug::WelcomeMessageProgramNameSize] = ""; + strcpy(procname, bm.programName); + auto activeTime = bm.activeTime; + auto listenPort = bm.listenPort; + auto pid = bm.pid; + auto type = bm.type; + + auto address = addr.getText(); + const auto ipNumerical = addr.getNumber(); + const auto clientId = uint64_t(ipNumerical) | (uint64_t(listenPort) << 32); + auto it = clients.find(clientId); + if (activeTime >= 0) { + if (it == clients.end()) { + std::string ip(address); + resolvLock.lock(); + if (resolvMap.find(ip) == resolvMap.end()) { + resolvMap.emplace(ip, ip); + resolv->query(ipNumerical, [&, ip](const char* name) { + std::lock_guard lock(resolvLock); + auto iter = resolvMap.find(ip); + assert(iter != resolvMap.end()); + iter->second = name; + }); + } + resolvLock.unlock(); + auto client = new ClientData( + {time, protoVer, activeTime, listenPort, pid, procname, std::move(ip), type}); + clients.emplace(clientId, client); + Q_EMIT clientItemsChanged(); + } else { + auto client = it->second; + client->data.time = time; + client->data.activeTime = activeTime; + client->data.port = listenPort; + client->data.pid = pid; + client->data.protocolVersion = protoVer; + if (strcmp(client->data.procName.c_str(), procname) != 0) { + client->data.procName = procname; + } + client->data.type = type; + } + } else if (it != clients.end()) { + clients.erase(it); + Q_EMIT clientItemsChanged(); + } + } + + auto it = clients.begin(); + while (it != clients.end()) { + const auto diff = time - it->second->data.time; + if (diff > 4000) { + it = clients.erase(it); + Q_EMIT clientItemsChanged(); + } else { + ++it; + } + } + } +} + +void StartView::onCloseView(QObject* view) { + view->deleteLater(); +} + +void StartView::onCloseAllView() { + this->deleteLater(); +} +} // namespace inspector diff --git a/src/StartView.h b/src/StartView.h new file mode 100644 index 0000000..1a3e40e --- /dev/null +++ b/src/StartView.h @@ -0,0 +1,185 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ResolvService.h" +#include "Socket.h" + +namespace inspector { + +class ClientData : public QObject { + Q_OBJECT + Q_PROPERTY(bool connected READ getConnected NOTIFY connectStateChange) + Q_PROPERTY(uint16_t port READ getPort CONSTANT) + Q_PROPERTY(QString procName READ getProcName CONSTANT) + Q_PROPERTY(QString address READ getAddress CONSTANT) + Q_PROPERTY(uint8_t type READ getType CONSTANT) + public: + struct Data { + int64_t time = 0; + uint32_t protocolVersion = 0; + int32_t activeTime = 0; + uint16_t port = 0; + uint64_t pid = 0; + std::string procName = {}; + std::string address = {}; + uint8_t type = 0; + }; + + explicit ClientData(Data data); + + QString getProcName() const { + return QString::fromStdString(data.procName); + } + + QString getAddress() const { + return QString::fromStdString(data.address); + } + + uint16_t getPort() const { + return data.port; + } + + bool getConnected() const { + return connected; + } + + uint8_t getType() const { + return data.type; + } + + void setConnected(bool isConnect) { + connected = isConnect; + Q_EMIT connectStateChange(); + } + + Q_SIGNAL void connectStateChange(); + + bool connected = false; + Data data = {}; +}; + +class FileItem : public QObject { + Q_OBJECT + Q_PROPERTY(QString filesPath READ getFilePath CONSTANT) + Q_PROPERTY(QString filesName READ getFileName CONSTANT) + Q_PROPERTY(QDateTime lastOpened READ getLastOpened CONSTANT) + + public: + explicit FileItem(const QString& fsPath, const QString& fsName, QObject* parent) + : QObject(parent), filesPath(fsPath), filesName(fsName), + lastOpened(QDateTime::currentDateTime()) { + } + + QString getFilePath() const { + return filesPath; + } + QString getFileName() const { + return QFileInfo(filesPath).fileName(); + } + QDateTime getLastOpened() const { + return lastOpened; + } + + private: + QString filesPath = ""; + QString filesName = ""; + QDateTime lastOpened = {}; +}; + +class StartView : public QObject { + Q_OBJECT + Q_PROPERTY(QList fileItems READ getFileItems NOTIFY fileItemsChanged) + Q_PROPERTY(QVector frameCaptureClientItems READ getFrameCaptureClientItems NOTIFY + clientItemsChanged) + Q_PROPERTY( + QVector layerTreeClientItems READ getLayerTreeClientItems NOTIFY clientItemsChanged) + Q_PROPERTY(QString lastOpenFile READ getLastOpenFile NOTIFY lastOpenFileChanged) + + public: + explicit StartView(QObject* parent = nullptr); + ~StartView() override; + + QStringList getRecentFiles() const { + return recentFiles; + } + QString getLastOpenFile() const { + return lastOpenFile; + } + + ///* file items */// + Q_INVOKABLE QList getFileItems() const; + Q_INVOKABLE void openFile(const QString& fPath); + Q_INVOKABLE void openFile(const QUrl& fPath); + Q_INVOKABLE void addRecentFile(const QString& fPath); + Q_INVOKABLE void clearRecentFiles(); + Q_INVOKABLE QString getFileNameFromPath(const QString& fPath) { + return QFileInfo(fPath).fileName(); + } + Q_INVOKABLE QString getDirectoryFromPath(const QString& fPath) { + return QFileInfo(fPath).path(); + } + ///* client items */// + Q_INVOKABLE QVector getFrameCaptureClientItems() const; + Q_INVOKABLE QVector getLayerTreeClientItems() const; + Q_INVOKABLE void connectToClient(QObject* object); + Q_INVOKABLE void connectToClientByLayerInspector(QObject* object); + Q_INVOKABLE void showStartView(); + + Q_SIGNAL void recentFilesChanged(); + Q_SIGNAL void fileItemsChanged(); + Q_SIGNAL void lastOpenFileChanged(); + Q_SIGNAL void openStatView(const QString& fPath); + + Q_SIGNAL void clientItemsChanged(); + Q_SIGNAL void openConnectView(const QString& address, uint16_t port); + Q_SIGNAL void quitStartView(); + + Q_SLOT void onCloseView(QObject* view); + Q_SLOT void onCloseAllView(); + + protected: + void loadRecentFiles(); + void saveRecentFiles(); + + void updateBroadcastClients(); + + private: + QString lastOpenFile = ""; + QStringList recentFiles = {}; + QList fileItems = {}; + std::mutex resolvLock = {}; + uint16_t port = 8086; + std::unique_ptr resolv = nullptr; + std::unique_ptr broadcastListen = nullptr; + std::unordered_map clients = {}; + std::unordered_map resolvMap = {}; + + QTimer* broadcastTimer = nullptr; + QQmlApplicationEngine* qmlEngine = nullptr; +}; +} // namespace inspector diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d9eef47 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,66 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include "StartView.h" + +int main(int argc, char* argv[]) { + QApplication::setApplicationName("Inspector"); + QApplication::setOrganizationName("org.tgfx"); + QSurfaceFormat defaultFormat = QSurfaceFormat(); + defaultFormat.setRenderableType(QSurfaceFormat::RenderableType::OpenGL); + defaultFormat.setVersion(3, 2); + defaultFormat.setProfile(QSurfaceFormat::CoreProfile); + QSurfaceFormat::setDefaultFormat(defaultFormat); + qputenv("QT_LOGGING_RULES", "qt.qpa.*=false"); + QQuickStyle::setStyle("Basic"); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); + +#else + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif + + QApplication app(argc, argv); + + static bool initialized = false; + if (!initialized) { + initFrontend(KDDockWidgets::FrontendType::QtQuick); + initialized = true; + } + + auto& config = KDDockWidgets::Config::self(); + config.setSeparatorThickness(2); + auto flags = config.flags() | KDDockWidgets::Config::Flag_TitleBarIsFocusable | + KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible; + + config.setFlags(flags); + + // 创建并显示StartView + auto startView = new inspector::StartView(&app); + startView->showStartView(); + + return app.exec(); +} diff --git a/sync_deps.sh b/sync_deps.sh new file mode 100755 index 0000000..fea1df9 --- /dev/null +++ b/sync_deps.sh @@ -0,0 +1,13 @@ +#!/bin/bash +cd $(dirname $0) + +./install_tools.sh + +if [ ! $(which depsync) ]; then + echo "depsync not found. Trying to install..." + npm install -g depsync > /dev/null +else + npm update -g depsync > /dev/null +fi + +depsync || exit 1 \ No newline at end of file diff --git a/vendor.json b/vendor.json new file mode 100644 index 0000000..caa64de --- /dev/null +++ b/vendor.json @@ -0,0 +1,32 @@ +{ + "source": "third_party", + "out": "third_party/out", + "vendors": [ + { + "name": "KDDockWidgets", + "cmake": { + "targets": [ + "kddockwidgets" + ], + "arguments": [ + "-DKDDockWidgets_QT6=ON", + "-DKDDockWidgets_STATIC=ON", + "-DKDDW_FRONTEND_QTQUICK=ON", + "-DCMAKE_PREFIX_PATH=\"/Users/[username]/Qt/[QtVersion]/macos/lib/cmake\"" + ] + } + }, + { + "name": "flatbuffers", + "cmake": { + "targets": [ + "flatbuffers" + ], + "arguments": [ + "-DFLATBUFFERS_BUILD_FLATLIB=ON", + "-DFLATBUFFERS_BUILD_CPP17=ON" + ] + } + } + ] +} \ No newline at end of file