diff --git a/CMakeLists.txt b/CMakeLists.txt index f0e6bc0884..348e0d9797 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,6 +187,7 @@ if (WITH_GUI) add_subdirectory(imgui) add_subdirectory(wpigui) add_subdirectory(glass) + add_subdirectory(outlineviewer) endif() if (WITH_CSCORE) diff --git a/glass/src/libnt/native/cpp/NetworkTables.cpp b/glass/src/libnt/native/cpp/NetworkTables.cpp index 7a1c6b7ed1..cea8be538f 100644 --- a/glass/src/libnt/native/cpp/NetworkTables.cpp +++ b/glass/src/libnt/native/cpp/NetworkTables.cpp @@ -741,30 +741,20 @@ void glass::DisplayNetworkTables(NetworkTablesModel* model, ImGui::Columns(); } -void NetworkTablesView::Display() { - auto& storage = GetStorage(); - auto pTreeView = - storage.GetBoolRef("tree", m_defaultFlags & NetworkTablesFlags_TreeView); - auto pShowConnections = storage.GetBoolRef( - "connections", m_defaultFlags & NetworkTablesFlags_ShowConnections); - auto pShowFlags = storage.GetBoolRef( - "flags", m_defaultFlags & NetworkTablesFlags_ShowFlags); - auto pShowTimestamp = storage.GetBoolRef( - "timestamp", m_defaultFlags & NetworkTablesFlags_ShowTimestamp); - auto pCreateNoncanonicalKeys = storage.GetBoolRef( - "createNonCanonical", - m_defaultFlags & NetworkTablesFlags_CreateNoncanonicalKeys); - - if (ImGui::BeginPopupContextItem()) { - ImGui::MenuItem("Tree View", "", pTreeView); - ImGui::MenuItem("Show Connections", "", pShowConnections); - ImGui::MenuItem("Show Flags", "", pShowFlags); - ImGui::MenuItem("Show Timestamp", "", pShowTimestamp); - ImGui::Separator(); - ImGui::MenuItem("Allow creation of non-canonical keys", "", - pCreateNoncanonicalKeys); - - ImGui::EndPopup(); +void NetworkTablesFlagsSettings::Update() { + if (!m_pTreeView) { + auto& storage = GetStorage(); + m_pTreeView = storage.GetBoolRef( + "tree", m_defaultFlags & NetworkTablesFlags_TreeView); + m_pShowConnections = storage.GetBoolRef( + "connections", m_defaultFlags & NetworkTablesFlags_ShowConnections); + m_pShowFlags = storage.GetBoolRef( + "flags", m_defaultFlags & NetworkTablesFlags_ShowFlags); + m_pShowTimestamp = storage.GetBoolRef( + "timestamp", m_defaultFlags & NetworkTablesFlags_ShowTimestamp); + m_pCreateNoncanonicalKeys = storage.GetBoolRef( + "createNonCanonical", + m_defaultFlags & NetworkTablesFlags_CreateNoncanonicalKeys); } m_flags &= @@ -772,11 +762,32 @@ void NetworkTablesView::Display() { NetworkTablesFlags_ShowFlags | NetworkTablesFlags_ShowTimestamp | NetworkTablesFlags_CreateNoncanonicalKeys); m_flags |= - (*pTreeView ? NetworkTablesFlags_TreeView : 0) | - (*pShowConnections ? NetworkTablesFlags_ShowConnections : 0) | - (*pShowFlags ? NetworkTablesFlags_ShowFlags : 0) | - (*pShowTimestamp ? NetworkTablesFlags_ShowTimestamp : 0) | - (*pCreateNoncanonicalKeys ? NetworkTablesFlags_CreateNoncanonicalKeys - : 0); - DisplayNetworkTables(m_model, m_flags); + (*m_pTreeView ? NetworkTablesFlags_TreeView : 0) | + (*m_pShowConnections ? NetworkTablesFlags_ShowConnections : 0) | + (*m_pShowFlags ? NetworkTablesFlags_ShowFlags : 0) | + (*m_pShowTimestamp ? NetworkTablesFlags_ShowTimestamp : 0) | + (*m_pCreateNoncanonicalKeys ? NetworkTablesFlags_CreateNoncanonicalKeys + : 0); +} + +void NetworkTablesFlagsSettings::DisplayMenu() { + if (!m_pTreeView) { + return; + } + ImGui::MenuItem("Tree View", "", m_pTreeView); + ImGui::MenuItem("Show Connections", "", m_pShowConnections); + ImGui::MenuItem("Show Flags", "", m_pShowFlags); + ImGui::MenuItem("Show Timestamp", "", m_pShowTimestamp); + ImGui::Separator(); + ImGui::MenuItem("Allow creation of non-canonical keys", "", + m_pCreateNoncanonicalKeys); +} + +void NetworkTablesView::Display() { + m_flags.Update(); + if (ImGui::BeginPopupContextItem()) { + m_flags.DisplayMenu(); + ImGui::EndPopup(); + } + DisplayNetworkTables(m_model, m_flags.GetFlags()); } diff --git a/glass/src/libnt/native/include/glass/networktables/NetworkTables.h b/glass/src/libnt/native/include/glass/networktables/NetworkTables.h index f515764f42..e7dbbece83 100644 --- a/glass/src/libnt/native/include/glass/networktables/NetworkTables.h +++ b/glass/src/libnt/native/include/glass/networktables/NetworkTables.h @@ -104,19 +104,39 @@ void DisplayNetworkTables( NetworkTablesModel* model, NetworkTablesFlags flags = NetworkTablesFlags_Default); +class NetworkTablesFlagsSettings { + public: + explicit NetworkTablesFlagsSettings( + NetworkTablesFlags defaultFlags = NetworkTablesFlags_Default) + : m_defaultFlags{defaultFlags}, m_flags{defaultFlags} {} + + void Update(); + void DisplayMenu(); + + NetworkTablesFlags GetFlags() const { return m_flags; } + + private: + bool* m_pTreeView = nullptr; + bool* m_pShowConnections = nullptr; + bool* m_pShowFlags = nullptr; + bool* m_pShowTimestamp = nullptr; + bool* m_pCreateNoncanonicalKeys = nullptr; + NetworkTablesFlags m_defaultFlags; // NOLINT + NetworkTablesFlags m_flags; // NOLINT +}; + class NetworkTablesView : public View { public: explicit NetworkTablesView( NetworkTablesModel* model, NetworkTablesFlags defaultFlags = NetworkTablesFlags_Default) - : m_model{model}, m_defaultFlags{defaultFlags}, m_flags{defaultFlags} {} + : m_model{model}, m_flags{defaultFlags} {} void Display() override; private: NetworkTablesModel* m_model; - NetworkTablesFlags m_defaultFlags; - NetworkTablesFlags m_flags; + NetworkTablesFlagsSettings m_flags; }; } // namespace glass diff --git a/outlineviewer/.styleguide b/outlineviewer/.styleguide new file mode 100644 index 0000000000..c8e055a4ca --- /dev/null +++ b/outlineviewer/.styleguide @@ -0,0 +1,27 @@ +cppHeaderFileInclude { + \.h$ + \.inc$ + \.inl$ +} + +cppSrcFileInclude { + \.cpp$ +} + +generatedFileExclude { + src/main/native/resources/ + src/main/native/win/outlineviewer.ico + src/main/native/mac/ov.icns +} + +repoRootNameOverride { + outlineviewer +} + +includeOtherLibs { + ^GLFW + ^imgui + ^ntcore + ^wpi/ + ^wpigui +} diff --git a/outlineviewer/CMakeLists.txt b/outlineviewer/CMakeLists.txt new file mode 100644 index 0000000000..537134d3eb --- /dev/null +++ b/outlineviewer/CMakeLists.txt @@ -0,0 +1,28 @@ +project(outlineviewer) + +include(CompileWarnings) +include(GenResources) +include(LinkMacOSGUI) + +configure_file(src/main/generate/WPILibVersion.cpp.in WPILibVersion.cpp) +GENERATE_RESOURCES(src/main/native/resources generated/main/cpp OV ov outlineviewer_resources_src) + +file(GLOB outlineviewer_src src/main/native/cpp/*.cpp ${CMAKE_CURRENT_BINARY_DIR}/WPILibVersion.cpp) + +if (WIN32) + set(outlineviewer_rc src/main/native/win/outlineviewer.rc) +elseif(APPLE) + set(MACOSX_BUNDLE_ICON_FILE ov.icns) + set(APP_ICON_MACOSX src/main/native/mac/ov.icns) + set_source_files_properties(${APP_ICON_MACOSX} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") +endif() + +add_executable(outlineviewer ${outlineviewer_src} ${outlineviewer_resources_src} ${outlineviewer_rc} ${APP_ICON_MACOSX}) +wpilib_link_macos_gui(outlineviewer) +target_link_libraries(outlineviewer libglassnt libglass) + +if (WIN32) + set_target_properties(outlineviewer PROPERTIES WIN32_EXECUTABLE YES) +elseif(APPLE) + set_target_properties(outlineviewer PROPERTIES MACOSX_BUNDLE YES OUTPUT_NAME "OutlineViewer") +endif() diff --git a/outlineviewer/Info.plist b/outlineviewer/Info.plist new file mode 100644 index 0000000000..a3e8a85d91 --- /dev/null +++ b/outlineviewer/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleName + OutlineViewer + CFBundleExecutable + outlineviewer + CFBundleDisplayName + OutlineViewer + CFBundleIdentifier + edu.wpi.first.tools.OutlineViewer + CFBundleIconFile + ov.icns + CFBundlePackageType + APPL + CFBundleSupportedPlatforms + + MacOSX + + CFBundleInfoDictionaryVersion + 6.0 + CFBundleShortVersionString + 2021 + CFBundleVersion + 2021 + LSMinimumSystemVersion + 10.11 + NSHighResolutionCapable + + + diff --git a/outlineviewer/build.gradle b/outlineviewer/build.gradle new file mode 100644 index 0000000000..5b4ee6ed40 --- /dev/null +++ b/outlineviewer/build.gradle @@ -0,0 +1,119 @@ +import org.gradle.internal.os.OperatingSystem + +if (!project.hasProperty('onlylinuxathena') && !project.hasProperty('onlylinuxraspbian') && !project.hasProperty('onlylinuxaarch64bionic')) { + + description = "NetworkTables Viewer" + + apply plugin: 'cpp' + apply plugin: 'c' + apply plugin: 'google-test-test-suite' + apply plugin: 'visual-studio' + apply plugin: 'edu.wpi.first.NativeUtils' + + if (OperatingSystem.current().isWindows()) { + apply plugin: 'windows-resources' + } + + ext { + nativeName = 'outlineviewer' + } + + apply from: "${rootDir}/shared/resources.gradle" + apply from: "${rootDir}/shared/config.gradle" + + def wpilibVersionFileInput = file("src/main/generate/WPILibVersion.cpp.in") + def wpilibVersionFileOutput = file("$buildDir/generated/main/cpp/WPILibVersion.cpp") + + task generateCppVersion() { + description = 'Generates the wpilib version class' + group = 'WPILib' + + outputs.file wpilibVersionFileOutput + inputs.file wpilibVersionFileInput + + if (wpilibVersioning.releaseMode) { + outputs.upToDateWhen { false } + } + + // We follow a simple set of checks to determine whether we should generate a new version file: + // 1. If the release type is not development, we generate a new version file + // 2. If there is no generated version number, we generate a new version file + // 3. If there is a generated build number, and the release type is development, then we will + // only generate if the publish task is run. + doLast { + def version = wpilibVersioning.version.get() + println "Writing version ${version} to $wpilibVersionFileOutput" + + if (wpilibVersionFileOutput.exists()) { + wpilibVersionFileOutput.delete() + } + def read = wpilibVersionFileInput.text.replace('${wpilib_version}', version) + wpilibVersionFileOutput.write(read) + } + } + + gradle.taskGraph.addTaskExecutionGraphListener { graph -> + def willPublish = graph.hasTask(publish) + if (willPublish) { + generateCppVersion.outputs.upToDateWhen { false } + } + } + + def generateTask = createGenerateResourcesTask('main', 'OV', 'ov', project) + + project(':').libraryBuild.dependsOn build + tasks.withType(CppCompile) { + dependsOn generateTask + dependsOn generateCppVersion + } + + model { + components { + // By default, a development executable will be generated. This is to help the case of + // testing specific functionality of the library. + "${nativeName}"(NativeExecutableSpec) { + baseName = 'outlineviewer' + sources { + cpp { + source { + srcDirs 'src/main/native/cpp', "$buildDir/generated/main/cpp" + include '**/*.cpp' + } + exportedHeaders { + srcDirs 'src/main/native/include' + } + } + if (OperatingSystem.current().isWindows()) { + rc { + source { + srcDirs 'src/main/native/win' + include '*.rc' + } + } + } + } + binaries.all { + if (it.targetPlatform.name == nativeUtils.wpi.platforms.roborio || it.targetPlatform.name == nativeUtils.wpi.platforms.raspbian || it.targetPlatform.name == nativeUtils.wpi.platforms.aarch64bionic) { + it.buildable = false + return + } + lib project: ':glass', library: 'glassnt', linkage: 'static' + lib project: ':glass', library: 'glass', linkage: 'static' + lib project: ':ntcore', library: 'ntcore', linkage: 'static' + lib project: ':wpiutil', library: 'wpiutil', linkage: 'static' + lib project: ':wpigui', library: 'wpigui', linkage: 'static' + nativeUtils.useRequiredLibrary(it, 'imgui_static') + if (it.targetPlatform.operatingSystem.isWindows()) { + it.linker.args << 'Gdi32.lib' << 'Shell32.lib' << 'd3d11.lib' << 'd3dcompiler.lib' + } else if (it.targetPlatform.operatingSystem.isMacOsX()) { + it.linker.args << '-framework' << 'Metal' << '-framework' << 'MetalKit' << '-framework' << 'Cocoa' << '-framework' << 'IOKit' << '-framework' << 'CoreFoundation' << '-framework' << 'CoreVideo' << '-framework' << 'QuartzCore' + } else { + it.linker.args << '-lX11' + } + } + } + } + } + + apply from: 'publish.gradle' +} diff --git a/outlineviewer/publish.gradle b/outlineviewer/publish.gradle new file mode 100644 index 0000000000..85605c47e3 --- /dev/null +++ b/outlineviewer/publish.gradle @@ -0,0 +1,96 @@ +apply plugin: 'maven-publish' + +def baseArtifactId = 'OutlineViewer' +def artifactGroupId = 'edu.wpi.first.tools' +def zipBaseName = '_GROUP_edu_wpi_first_tools_ID_OutlineViewer_CLS' + +def outputsFolder = file("$project.buildDir/outputs") + +model { + publishing { + def outlineViewerTaskList = [] + $.components.each { component -> + component.binaries.each { binary -> + if (binary in NativeExecutableBinarySpec && binary.component.name.contains("outlineviewer")) { + if (binary.buildable && binary.name.contains("Release")) { + // We are now in the binary that we want. + // This is the default application path for the ZIP task. + def applicationPath = binary.executable.file + def icon = file("$project.projectDir/src/main/native/mac/ov.icns") + + // Create the macOS bundle. + def bundleTask = project.tasks.create("bundleOutlineViewerOsxApp", Copy) { + description("Creates a macOS application bundle for OutlineViewer") + from(file("$project.projectDir/Info.plist")) + into(file("$project.buildDir/outputs/bundles/OutlineViewer.app/Contents")) + into("MacOS") { with copySpec { from binary.executable.file } } + into("Resources") { with copySpec { from icon } } + + doLast { + if (project.hasProperty("developerID")) { + // Get path to binary. + exec { + workingDir rootDir + def args = [ + "sh", + "-c", + "codesign --force --strict --deep " + + "--timestamp --options=runtime " + + "--verbose -s ${project.findProperty("developerID")} " + + "$project.buildDir/outputs/bundles/OutlineViewer.app/" + ] + commandLine args + } + } + } + } + + // Reset the application path if we are creating a bundle. + if (binary.targetPlatform.operatingSystem.isMacOsX()) { + applicationPath = file("$project.buildDir/outputs/bundles") + project.build.dependsOn bundleTask + } + + // Create the ZIP. + def task = project.tasks.create("copyOutlineViewerExecutable", Zip) { + description("Copies the OutlineViewer executable to the outputs directory.") + destinationDirectory = outputsFolder + + archiveBaseName = '_M_' + zipBaseName + duplicatesStrategy = 'exclude' + classifier = nativeUtils.getPublishClassifier(binary) + + from(licenseFile) { + into '/' + } + + from(applicationPath) + into(nativeUtils.getPlatformPath(binary)) + } + + if (binary.targetPlatform.operatingSystem.isMacOsX()) { + bundleTask.dependsOn binary.tasks.link + task.dependsOn(bundleTask) + } + + task.dependsOn binary.tasks.link + outlineViewerTaskList.add(task) + project.build.dependsOn task + project.artifacts { task } + addTaskToCopyAllOutputs(task) + } + } + } + } + + publications { + outlineViewer(MavenPublication) { + outlineViewerTaskList.each { artifact it } + + artifactId = baseArtifactId + groupId = artifactGroupId + version wpilibVersioning.version.get() + } + } + } +} diff --git a/outlineviewer/src/main/generate/WPILibVersion.cpp.in b/outlineviewer/src/main/generate/WPILibVersion.cpp.in new file mode 100644 index 0000000000..b0a4490520 --- /dev/null +++ b/outlineviewer/src/main/generate/WPILibVersion.cpp.in @@ -0,0 +1,7 @@ +/* + * Autogenerated file! Do not manually edit this file. This version is regenerated + * any time the publish task is run, or when this file is deleted. + */ +const char* GetWPILibVersion() { + return "${wpilib_version}"; +} diff --git a/outlineviewer/src/main/native/cpp/main.cpp b/outlineviewer/src/main/native/cpp/main.cpp new file mode 100644 index 0000000000..0bfd91614e --- /dev/null +++ b/outlineviewer/src/main/native/cpp/main.cpp @@ -0,0 +1,229 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include + +#include +#include +#include +#include +#include + +#include "glass/Context.h" +#include "glass/Model.h" +#include "glass/networktables/NetworkTables.h" +#include "glass/networktables/NetworkTablesSettings.h" +#include "glass/other/Log.h" + +namespace gui = wpi::gui; + +const char* GetWPILibVersion(); + +namespace ov { +wpi::StringRef GetResource_ov_16_png(); +wpi::StringRef GetResource_ov_32_png(); +wpi::StringRef GetResource_ov_48_png(); +wpi::StringRef GetResource_ov_64_png(); +wpi::StringRef GetResource_ov_128_png(); +wpi::StringRef GetResource_ov_256_png(); +wpi::StringRef GetResource_ov_512_png(); +} // namespace ov + +static std::unique_ptr gModel; +static std::unique_ptr gSettings; +static glass::LogData gLog; +static glass::NetworkTablesFlagsSettings gFlagsSettings; + +static void NtInitialize() { + // update window title when connection status changes + auto inst = nt::GetDefaultInstance(); + auto poller = nt::CreateConnectionListenerPoller(inst); + nt::AddPolledConnectionListener(poller, true); + gui::AddEarlyExecute([inst, poller] { + auto win = gui::GetSystemWindow(); + if (!win) { + return; + } + bool timedOut; + for (auto&& event : nt::PollConnectionListener(poller, 0, &timedOut)) { + if ((nt::GetNetworkMode(inst) & NT_NET_MODE_SERVER) != 0) { + // for server mode, just print number of clients connected + wpi::SmallString<64> title; + glfwSetWindowTitle(win, ("OutlineViewer - " + + wpi::Twine{nt::GetConnections(inst).size()} + + " Clients Connected") + .toNullTerminatedStringRef(title) + .data()); + } else if (event.connected) { + wpi::SmallString<64> title; + title = "OutlineViewer - Connected ("; + title += event.conn.remote_ip; + title += ')'; + glfwSetWindowTitle(win, title.c_str()); + } else { + glfwSetWindowTitle(win, "OutlineViewer - DISCONNECTED"); + } + } + }); + + // handle NetworkTables log messages + auto logPoller = nt::CreateLoggerPoller(inst); + nt::AddPolledLogger(logPoller, NT_LOG_INFO, 100); + gui::AddEarlyExecute([logPoller] { + bool timedOut; + for (auto&& msg : nt::PollLogger(logPoller, 0, &timedOut)) { + const char* level = ""; + if (msg.level >= NT_LOG_CRITICAL) { + level = "CRITICAL: "; + } else if (msg.level >= NT_LOG_ERROR) { + level = "ERROR: "; + } else if (msg.level >= NT_LOG_WARNING) { + level = "WARNING: "; + } + gLog.Append(wpi::Twine{level} + msg.message + wpi::Twine{" ("} + + msg.filename + wpi::Twine{':'} + wpi::Twine{msg.line} + + wpi::Twine{")\n"}); + } + }); + + // NetworkTables table window + gModel = std::make_unique(); + gui::AddEarlyExecute([] { gModel->Update(); }); + + // NetworkTables settings window + gSettings = std::make_unique(); + gui::AddEarlyExecute([] { gSettings->Update(); }); +} + +static void DisplayGui() { + ImGui::GetStyle().WindowRounding = 0; + + // fill entire OS window with this window + ImGui::SetNextWindowPos(ImVec2(0, 0)); + int width, height; + glfwGetWindowSize(gui::GetSystemWindow(), &width, &height); + ImGui::SetNextWindowSize(ImVec2(width, height)); + + ImGui::Begin("Entries", nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_MenuBar | + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoCollapse); + + gFlagsSettings.Update(); + + // can't create popups from within menu, so use flags + bool settings = false; + bool log = false; + bool about = false; + + // main menu + ImGui::BeginMenuBar(); + gui::EmitViewMenu(); + if (ImGui::BeginMenu("View")) { + gFlagsSettings.DisplayMenu(); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Options")) { + if (ImGui::MenuItem("Settings")) { + settings = true; + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Info")) { + if (ImGui::MenuItem("Log")) { + log = true; + } + ImGui::Separator(); + if (ImGui::MenuItem("About")) { + about = true; + } + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + + // settings popup + if (settings) { + ImGui::OpenPopup("Settings"); + } + if (ImGui::BeginPopupModal("Settings", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + if (gSettings->Display()) { + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + // log popup + if (log) { + ImGui::OpenPopup("Log"); + } + if (ImGui::BeginPopupModal("Log", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + if (ImGui::Button("Close")) { + ImGui::CloseCurrentPopup(); + } + ImGui::BeginChild("Lines", ImVec2(width * 0.75f, height * 0.75f)); + glass::DisplayLog(&gLog, true); + ImGui::EndChild(); + ImGui::EndPopup(); + } + + // about popup + if (about) { + ImGui::OpenPopup("About"); + } + if (ImGui::BeginPopupModal("About", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("OutlineViewer"); + ImGui::Separator(); + ImGui::Text("v%s", GetWPILibVersion()); + if (ImGui::Button("Close")) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + // display table view + glass::DisplayNetworkTables(gModel.get(), gFlagsSettings.GetFlags()); + + ImGui::End(); +} + +#ifdef _WIN32 +int __stdcall WinMain(void* hInstance, void* hPrevInstance, char* pCmdLine, + int nCmdShow) { +#else +int main() { +#endif + gui::CreateContext(); + glass::CreateContext(); + + gui::AddIcon(ov::GetResource_ov_16_png()); + gui::AddIcon(ov::GetResource_ov_32_png()); + gui::AddIcon(ov::GetResource_ov_48_png()); + gui::AddIcon(ov::GetResource_ov_64_png()); + gui::AddIcon(ov::GetResource_ov_128_png()); + gui::AddIcon(ov::GetResource_ov_256_png()); + gui::AddIcon(ov::GetResource_ov_512_png()); + + gui::ConfigurePlatformSaveFile("outlineviewer.ini"); + gui::AddInit(NtInitialize); + + gui::AddLateExecute(DisplayGui); + + gui::Initialize("OutlineViewer - DISCONNECTED", 1024, 768); + gui::Main(); + + gModel.reset(); + gSettings.reset(); + + glass::DestroyContext(); + gui::DestroyContext(); +} diff --git a/outlineviewer/src/main/native/mac/ov.icns b/outlineviewer/src/main/native/mac/ov.icns new file mode 100644 index 0000000000..b96e85928b Binary files /dev/null and b/outlineviewer/src/main/native/mac/ov.icns differ diff --git a/outlineviewer/src/main/native/resources/ov-128.png b/outlineviewer/src/main/native/resources/ov-128.png new file mode 100644 index 0000000000..950d0ca2db Binary files /dev/null and b/outlineviewer/src/main/native/resources/ov-128.png differ diff --git a/outlineviewer/src/main/native/resources/ov-16.png b/outlineviewer/src/main/native/resources/ov-16.png new file mode 100644 index 0000000000..db84366bd9 Binary files /dev/null and b/outlineviewer/src/main/native/resources/ov-16.png differ diff --git a/outlineviewer/src/main/native/resources/ov-256.png b/outlineviewer/src/main/native/resources/ov-256.png new file mode 100644 index 0000000000..7b3b2b72f6 Binary files /dev/null and b/outlineviewer/src/main/native/resources/ov-256.png differ diff --git a/outlineviewer/src/main/native/resources/ov-32.png b/outlineviewer/src/main/native/resources/ov-32.png new file mode 100644 index 0000000000..4db8e8b589 Binary files /dev/null and b/outlineviewer/src/main/native/resources/ov-32.png differ diff --git a/outlineviewer/src/main/native/resources/ov-48.png b/outlineviewer/src/main/native/resources/ov-48.png new file mode 100644 index 0000000000..026491c286 Binary files /dev/null and b/outlineviewer/src/main/native/resources/ov-48.png differ diff --git a/outlineviewer/src/main/native/resources/ov-512.png b/outlineviewer/src/main/native/resources/ov-512.png new file mode 100644 index 0000000000..d4fd60ce6b Binary files /dev/null and b/outlineviewer/src/main/native/resources/ov-512.png differ diff --git a/outlineviewer/src/main/native/resources/ov-64.png b/outlineviewer/src/main/native/resources/ov-64.png new file mode 100644 index 0000000000..9cd5d968a0 Binary files /dev/null and b/outlineviewer/src/main/native/resources/ov-64.png differ diff --git a/outlineviewer/src/main/native/win/outlineviewer.ico b/outlineviewer/src/main/native/win/outlineviewer.ico new file mode 100644 index 0000000000..7156af8d6a Binary files /dev/null and b/outlineviewer/src/main/native/win/outlineviewer.ico differ diff --git a/outlineviewer/src/main/native/win/outlineviewer.rc b/outlineviewer/src/main/native/win/outlineviewer.rc new file mode 100644 index 0000000000..85bdcae2ef --- /dev/null +++ b/outlineviewer/src/main/native/win/outlineviewer.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON "outlineviewer.ico" diff --git a/settings.gradle b/settings.gradle index 89cba97761..0cd962f700 100644 --- a/settings.gradle +++ b/settings.gradle @@ -29,6 +29,7 @@ include 'wpilibjExamples' include 'wpilibjIntegrationTests' include 'wpilibj' include 'glass' +include 'outlineviewer' include 'simulation:gz_msgs' include 'simulation:frc_gazebo_plugins' include 'simulation:halsim_gazebo'