Initial commit (next)

This commit is contained in:
Joel16 2020-08-03 18:10:06 -04:00
commit c0a6c47af9
43 changed files with 44413 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

13
.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
*~
*.exe
*.o
test
tahoma12.c
tahoma24.c
*.zip
build
*.elf
*.nso
*.pfs0
*.nacp
*.nro

17
.travis.yml Normal file
View File

@ -0,0 +1,17 @@
language: c
sudo: true
services: docker
install:
- docker pull devkitpro/devkita64
script:
- docker run -e ENABLE_COMPATIBILITY_REPORTING -v $TRAVIS_BUILD_DIR:/NX-Shell
ubuntu:18.04 /bin/bash -ex /NX-Shell/.travis/.build.sh
deploy:
provider: pages
skip_cleanup: true
github_token: $GITHUB_TOKEN # Set in travis-ci.org dashboard
on:
branch: master

19
.travis/.build.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash -ex
apt-get update -y && apt-get -y install sudo
sudo apt-get -y install build-essential sudo wget libxml2
# install dkp deps
touch /trustdb.gpg
wget https://github.com/devkitPro/pacman/releases/download/devkitpro-pacman-1.0.1/devkitpro-pacman.deb
sudo dpkg -i devkitpro-pacman.deb
sudo dkp-pacman --noconfirm -Syu switch-dev switch-sdl2 switch-sdl2_gfx switch-sdl2_image switch-sdl2_ttf switch-opusfile switch-liblzma switch-libvorbisidec switch-mpg123 switch-flac
source /etc/profile.d/devkit-env.sh
export DEVKITA64=/opt/devkitpro/devkitA64
cd /NX-Shell && make
rm -rf .travis build include libs romfs source
rm .gitattributes .gitignore icon.jpg LICENSE Makefile NX-Shell.elf NX-Shell.nacp NX-Shell.nso NX-Shell.pfs0 README.md .travis.yml

12
.travis/.travis-deps.sh Normal file
View File

@ -0,0 +1,12 @@
#!/bin/sh
set -e
set -x
# Get latest libnx and overwrite bundled one
mkdir libnx-update && cd libnx-update
git clone https://github.com/switchbrew/libnx.git
cd libnx/
make
sudo make install
cd ../../
rm -rf libnx-update/

231
Makefile Normal file
View File

@ -0,0 +1,231 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated.
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
# ICON is the filename of the icon (.jpg), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := libs/source libs/source/imgui source
DATA := data
INCLUDES := libs/include libs/include/imgui include
ROMFS := romfs
VERSION_MAJOR := 3
VERSION_MINOR := 0
VERSION_MICRO := 1
APP_TITLE := NX-Shell
APP_AUTHOR := Joel16
APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO}
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O3 -ffunction-sections -Wno-format-truncation \
-DVERSION_MAJOR=$(VERSION_MAJOR) -DVERSION_MINOR=$(VERSION_MINOR) -DVERSION_MICRO=$(VERSION_MICRO) \
$(ARCH) $(DEFINES)
CFLAGS += -D__SWITCH__ $(INCLUDE) `sdl2-config --cflags` -DDEBUG
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := `sdl2-config --libs` -lglad -lEGL -lglapi -ldrm_nouveau -ljansson -lnx -lm
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
else
ifneq (,$(findstring icon.jpg,$(icons)))
export APP_ICON := $(TOPDIR)/icon.jpg
endif
endif
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
ifeq ($(strip $(NO_ICON)),)
export NROFLAGS += --icon=$(APP_ICON)
endif
ifeq ($(strip $(NO_NACP)),)
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
endif
ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID)
endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

18
README.md Normal file
View File

@ -0,0 +1,18 @@
# NX-Shell (Next)
NX Shell is a multi-purpose file manager for the Nintendo Switch that aims towards handling media files. NX Shell leans towards more of the file management features such as opening and managing various files types and includes the basic necessity of a standard file manager.
# Features:
- Delete files and folders.
- Copy/Move files and folders.
- Rename files and folders (standard switch keyboard).
- Create files and folders (standard switch keyboard).
- Displays file properties such as size, time created, modified and accessed.
- Sorting options (Sort by name, date, size).
# Credits:
- **Preetisketch** for the banner.
- **imgui developers and contributors** for the GUI library.
- **devkitPro maintainers and contributors**.

BIN
icon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

20
include/config.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef NX_SHELL_CONFIG_H
#define NX_SHELL_CONFIG_H
#include <switch.h>
typedef struct {
int sort = 0;
bool dark_theme = false;
char cwd[FS_MAX_PATH];
} config_t;
extern config_t config;
namespace Config {
int Save(config_t config);
int Load(void);
}
#endif

41
include/fs.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef NX_SHELL_FS_H
#define NX_SHELL_FS_H
#include <string>
#include <switch.h>
extern FsFileSystem *fs;
extern FsFileSystem devices[4];
typedef enum FileType {
FileTypeNone,
FileTypeArchive,
FileTypeAudio,
FileTypeBinary,
FileTypeImage,
FileTypeText
} FileType;
namespace FS {
bool FileExists(const char path[FS_MAX_PATH]);
bool DirExists(const char path[FS_MAX_PATH]);
std::string GetFileExt(const std::string &filename);
FileType GetFileType(const std::string &filename);
s64 GetDirList(char path[FS_MAX_PATH], FsDirectoryEntry **entriesp);
void FreeDirEntries(FsDirectoryEntry **entries, s64 entry_count);
s64 RefreshEntries(FsDirectoryEntry **entries, s64 entry_count);
s64 ChangeDirNext(const char path[FS_MAX_PATH], FsDirectoryEntry **entries, s64 entry_count);
s64 ChangeDirPrev(FsDirectoryEntry **entries, s64 entry_count);
int ConstructPath(FsDirectoryEntry *entry, char path[FS_MAX_PATH], const char filename[FS_MAX_PATH]);
Result GetTimeStamp(FsDirectoryEntry *entry, FsTimeStampRaw *timestamp);
Result Rename(FsDirectoryEntry *entry, const char filename[FS_MAX_PATH]);
Result Delete(FsDirectoryEntry *entry);
Result SetArchiveBit(FsDirectoryEntry *entry);
void Copy(FsDirectoryEntry *entry);
Result Paste(void);
Result Move(void);
Result GetFileSize(const char path[FS_MAX_PATH], s64 *size);
Result WriteFile(const char path[FS_MAX_PATH], const void *buf, u64 size);
}
#endif

10
include/gui.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef NX_SHELL_GUI_H
#define NX_SHELL_GUI_H
#include <SDL.h>
namespace GUI {
int RenderMainMenu(SDL_Window *window);
}
#endif

10
include/keyboard.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef NX_SHELL_KEYBOARD_H
#define NX_SHELL_KEYBOARD_H
#include <string>
namespace Keyboard {
std::string GetText(const std::string &guide_text, const std::string &initial_text);
}
#endif

22
include/textures.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef NX_SHELL_TEXTURES_H
#define NX_SHELL_TEXTURES_H
#include <glad/glad.h>
#include <string>
typedef struct {
GLuint id = 0;
int width = 0;
int height = 0;
} Tex;
extern Tex folder_texture, file_texture, archive_texture, audio_texture, image_texture, check_texture, uncheck_texture;
namespace Textures {
bool LoadImageFile(const std::string &path, Tex *texture);
void Free(Tex *texture);
void Init(void);
void Exit(void);
}
#endif

8
include/utils.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef NX_SHELL_UTILS_H
#define NX_SHELL_UTILS_H
namespace Utils {
void GetSizeString(char *string, double size);
}
#endif

View File

@ -0,0 +1,108 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h)
// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h"
// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include
// the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
//---- Define assertion handler. Defaults to calling assert().
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//---- Disable all of Dear ImGui or don't implement standard windows.
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
//#define IMGUI_DISABLE_METRICS_WINDOW // Disable debug/metrics window: ShowMetricsWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use 32-bit for ImWchar (default is 16-bit) to support full unicode code points.
//#define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of imgui cpp files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//---- Unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined, use the much faster STB sprintf library implementation of vsnprintf instead of the one from the default C library.
// Note that stb_sprintf.h is meant to be provided by the user and available in the include path at compile time. Also, the compatibility checks of the arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf.
// #define IMGUI_USE_STB_SPRINTF
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
#define IM_VEC2_CLASS_EXTRA \
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
operator MyVec2() const { return MyVec2(x,y); }
#define IM_VEC4_CLASS_EXTRA \
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
// Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bit indices).
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
//#define ImDrawIdx unsigned int
//---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly)
//struct ImDrawList;
//struct ImDrawCmd;
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(),
// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.)
// This adds a small runtime cost which is why it is not enabled by default.
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
}
*/

2392
libs/include/imgui/imgui.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,84 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// About Desktop OpenGL function loaders:
// Modern Desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
// About GLSL version:
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// Specific OpenGL ES versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
// Attempt to auto-detect the default Desktop GL loader based on available header files.
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash in ImGui_ImplOpenGL3_Init().
// You can explicitly select a loader by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
&& !defined(IMGUI_IMPL_OPENGL_ES3) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
// Try to detect GLES on matching platforms
#if defined(__APPLE__)
#include "TargetConditionals.h"
#endif
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
#elif defined(__EMSCRIPTEN__)
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
// Otherwise try to detect supported Desktop OpenGL loaders..
#elif defined(__has_include)
#if __has_include(<GL/glew.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
#elif __has_include(<glad/glad.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
#elif __has_include(<GL/gl3w.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#elif __has_include(<glbinding/glbinding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
#elif __has_include(<glbinding/Binding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
#else
#error "Cannot detect OpenGL loader!"
#endif
#else
#define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository
#endif
#endif

View File

@ -0,0 +1,29 @@
// dear imgui: Platform Binding for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
struct SDL_Window;
typedef union SDL_Event SDL_Event;
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window);
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,639 @@
// [DEAR IMGUI]
// This is a slightly modified version of stb_rect_pack.h 1.00.
// Those changes would need to be pushed into nothings/stb:
// - Added STBRP__CDECL
// Grep for [DEAR IMGUI] to find the changes.
// stb_rect_pack.h - v1.00 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
// Does not do rotation.
//
// Not necessarily the awesomest packing method, but better than
// the totally naive one in stb_truetype (which is primarily what
// this is meant to replace).
//
// Has only had a few tests run, may have issues.
//
// More docs to come.
//
// No memory allocations; uses qsort() and assert() from stdlib.
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
//
// This library currently uses the Skyline Bottom-Left algorithm.
//
// Please note: better rectangle packers are welcome! Please
// implement them to the same API, but with a different init
// function.
//
// Credits
//
// Library
// Sean Barrett
// Minor features
// Martins Mozeiko
// github:IntellectualKitty
//
// Bugfixes / warning fixes
// Jeremy Jaussaud
// Fabian Giesen
//
// Version history:
//
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
// 0.99 (2019-02-07) warning fixes
// 0.11 (2017-03-03) return packing success/fail result
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
// 0.09 (2016-08-27) fix compiler warnings
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
// 0.05: added STBRP_ASSERT to allow replacing assert
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
// 0.01: initial release
//
// LICENSE
//
// See end of file for license information.
//////////////////////////////////////////////////////////////////////////////
//
// INCLUDE SECTION
//
#ifndef STB_INCLUDE_STB_RECT_PACK_H
#define STB_INCLUDE_STB_RECT_PACK_H
#define STB_RECT_PACK_VERSION 1
#ifdef STBRP_STATIC
#define STBRP_DEF static
#else
#define STBRP_DEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stbrp_context stbrp_context;
typedef struct stbrp_node stbrp_node;
typedef struct stbrp_rect stbrp_rect;
#ifdef STBRP_LARGE_RECTS
typedef int stbrp_coord;
#else
typedef unsigned short stbrp_coord;
#endif
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
// Assign packed locations to rectangles. The rectangles are of type
// 'stbrp_rect' defined below, stored in the array 'rects', and there
// are 'num_rects' many of them.
//
// Rectangles which are successfully packed have the 'was_packed' flag
// set to a non-zero value and 'x' and 'y' store the minimum location
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
// if you imagine y increasing downwards). Rectangles which do not fit
// have the 'was_packed' flag set to 0.
//
// You should not try to access the 'rects' array from another thread
// while this function is running, as the function temporarily reorders
// the array while it executes.
//
// To pack into another rectangle, you need to call stbrp_init_target
// again. To continue packing into the same rectangle, you can call
// this function again. Calling this multiple times with multiple rect
// arrays will probably produce worse packing results than calling it
// a single time with the full rectangle array, but the option is
// available.
//
// The function returns 1 if all of the rectangles were successfully
// packed and 0 otherwise.
struct stbrp_rect
{
// reserved for your use:
int id;
// input:
stbrp_coord w, h;
// output:
stbrp_coord x, y;
int was_packed; // non-zero if valid packing
}; // 16 bytes, nominally
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
// Initialize a rectangle packer to:
// pack a rectangle that is 'width' by 'height' in dimensions
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
//
// You must call this function every time you start packing into a new target.
//
// There is no "shutdown" function. The 'nodes' memory must stay valid for
// the following stbrp_pack_rects() call (or calls), but can be freed after
// the call (or calls) finish.
//
// Note: to guarantee best results, either:
// 1. make sure 'num_nodes' >= 'width'
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
//
// If you don't do either of the above things, widths will be quantized to multiples
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
//
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
// may run out of temporary storage and be unable to pack some rectangles.
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
// Optionally call this function after init but before doing any packing to
// change the handling of the out-of-temp-memory scenario, described above.
// If you call init again, this will be reset to the default (false).
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
// Optionally select which packing heuristic the library should use. Different
// heuristics will produce better/worse results for different data sets.
// If you call init again, this will be reset to the default.
enum
{
STBRP_HEURISTIC_Skyline_default=0,
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
STBRP_HEURISTIC_Skyline_BF_sortHeight
};
//////////////////////////////////////////////////////////////////////////////
//
// the details of the following structures don't matter to you, but they must
// be visible so you can handle the memory allocations for them
struct stbrp_node
{
stbrp_coord x,y;
stbrp_node *next;
};
struct stbrp_context
{
int width;
int height;
int align;
int init_mode;
int heuristic;
int num_nodes;
stbrp_node *active_head;
stbrp_node *free_head;
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
};
#ifdef __cplusplus
}
#endif
#endif
//////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION SECTION
//
#ifdef STB_RECT_PACK_IMPLEMENTATION
#ifndef STBRP_SORT
#include <stdlib.h>
#define STBRP_SORT qsort
#endif
#ifndef STBRP_ASSERT
#include <assert.h>
#define STBRP_ASSERT assert
#endif
// [DEAR IMGUI] Added STBRP__CDECL
#ifdef _MSC_VER
#define STBRP__NOTUSED(v) (void)(v)
#define STBRP__CDECL __cdecl
#else
#define STBRP__NOTUSED(v) (void)sizeof(v)
#define STBRP__CDECL
#endif
enum
{
STBRP__INIT_skyline = 1
};
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
{
switch (context->init_mode) {
case STBRP__INIT_skyline:
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
context->heuristic = heuristic;
break;
default:
STBRP_ASSERT(0);
}
}
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
{
if (allow_out_of_mem)
// if it's ok to run out of memory, then don't bother aligning them;
// this gives better packing, but may fail due to OOM (even though
// the rectangles easily fit). @TODO a smarter approach would be to only
// quantize once we've hit OOM, then we could get rid of this parameter.
context->align = 1;
else {
// if it's not ok to run out of memory, then quantize the widths
// so that num_nodes is always enough nodes.
//
// I.e. num_nodes * align >= width
// align >= width / num_nodes
// align = ceil(width/num_nodes)
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
}
}
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
{
int i;
#ifndef STBRP_LARGE_RECTS
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
#endif
for (i=0; i < num_nodes-1; ++i)
nodes[i].next = &nodes[i+1];
nodes[i].next = NULL;
context->init_mode = STBRP__INIT_skyline;
context->heuristic = STBRP_HEURISTIC_Skyline_default;
context->free_head = &nodes[0];
context->active_head = &context->extra[0];
context->width = width;
context->height = height;
context->num_nodes = num_nodes;
stbrp_setup_allow_out_of_mem(context, 0);
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
context->extra[0].x = 0;
context->extra[0].y = 0;
context->extra[0].next = &context->extra[1];
context->extra[1].x = (stbrp_coord) width;
#ifdef STBRP_LARGE_RECTS
context->extra[1].y = (1<<30);
#else
context->extra[1].y = 65535;
#endif
context->extra[1].next = NULL;
}
// find minimum y position if it starts at x1
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
{
stbrp_node *node = first;
int x1 = x0 + width;
int min_y, visited_width, waste_area;
STBRP__NOTUSED(c);
STBRP_ASSERT(first->x <= x0);
#if 0
// skip in case we're past the node
while (node->next->x <= x0)
++node;
#else
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
#endif
STBRP_ASSERT(node->x <= x0);
min_y = 0;
waste_area = 0;
visited_width = 0;
while (node->x < x1) {
if (node->y > min_y) {
// raise min_y higher.
// we've accounted for all waste up to min_y,
// but we'll now add more waste for everything we've visted
waste_area += visited_width * (node->y - min_y);
min_y = node->y;
// the first time through, visited_width might be reduced
if (node->x < x0)
visited_width += node->next->x - x0;
else
visited_width += node->next->x - node->x;
} else {
// add waste area
int under_width = node->next->x - node->x;
if (under_width + visited_width > width)
under_width = width - visited_width;
waste_area += under_width * (min_y - node->y);
visited_width += under_width;
}
node = node->next;
}
*pwaste = waste_area;
return min_y;
}
typedef struct
{
int x,y;
stbrp_node **prev_link;
} stbrp__findresult;
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
{
int best_waste = (1<<30), best_x, best_y = (1 << 30);
stbrp__findresult fr;
stbrp_node **prev, *node, *tail, **best = NULL;
// align to multiple of c->align
width = (width + c->align - 1);
width -= width % c->align;
STBRP_ASSERT(width % c->align == 0);
// if it can't possibly fit, bail immediately
if (width > c->width || height > c->height) {
fr.prev_link = NULL;
fr.x = fr.y = 0;
return fr;
}
node = c->active_head;
prev = &c->active_head;
while (node->x + width <= c->width) {
int y,waste;
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
// bottom left
if (y < best_y) {
best_y = y;
best = prev;
}
} else {
// best-fit
if (y + height <= c->height) {
// can only use it if it first vertically
if (y < best_y || (y == best_y && waste < best_waste)) {
best_y = y;
best_waste = waste;
best = prev;
}
}
}
prev = &node->next;
node = node->next;
}
best_x = (best == NULL) ? 0 : (*best)->x;
// if doing best-fit (BF), we also have to try aligning right edge to each node position
//
// e.g, if fitting
//
// ____________________
// |____________________|
//
// into
//
// | |
// | ____________|
// |____________|
//
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
//
// This makes BF take about 2x the time
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
tail = c->active_head;
node = c->active_head;
prev = &c->active_head;
// find first node that's admissible
while (tail->x < width)
tail = tail->next;
while (tail) {
int xpos = tail->x - width;
int y,waste;
STBRP_ASSERT(xpos >= 0);
// find the left position that matches this
while (node->next->x <= xpos) {
prev = &node->next;
node = node->next;
}
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
if (y + height <= c->height) {
if (y <= best_y) {
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
best_x = xpos;
STBRP_ASSERT(y <= best_y);
best_y = y;
best_waste = waste;
best = prev;
}
}
}
tail = tail->next;
}
}
fr.prev_link = best;
fr.x = best_x;
fr.y = best_y;
return fr;
}
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
{
// find best position according to heuristic
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
stbrp_node *node, *cur;
// bail if:
// 1. it failed
// 2. the best node doesn't fit (we don't always check this)
// 3. we're out of memory
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
res.prev_link = NULL;
return res;
}
// on success, create new node
node = context->free_head;
node->x = (stbrp_coord) res.x;
node->y = (stbrp_coord) (res.y + height);
context->free_head = node->next;
// insert the new node into the right starting point, and
// let 'cur' point to the remaining nodes needing to be
// stiched back in
cur = *res.prev_link;
if (cur->x < res.x) {
// preserve the existing one, so start testing with the next one
stbrp_node *next = cur->next;
cur->next = node;
cur = next;
} else {
*res.prev_link = node;
}
// from here, traverse cur and free the nodes, until we get to one
// that shouldn't be freed
while (cur->next && cur->next->x <= res.x + width) {
stbrp_node *next = cur->next;
// move the current node to the free list
cur->next = context->free_head;
context->free_head = cur;
cur = next;
}
// stitch the list back in
node->next = cur;
if (cur->x < res.x + width)
cur->x = (stbrp_coord) (res.x + width);
#ifdef _DEBUG
cur = context->active_head;
while (cur->x < context->width) {
STBRP_ASSERT(cur->x < cur->next->x);
cur = cur->next;
}
STBRP_ASSERT(cur->next == NULL);
{
int count=0;
cur = context->active_head;
while (cur) {
cur = cur->next;
++count;
}
cur = context->free_head;
while (cur) {
cur = cur->next;
++count;
}
STBRP_ASSERT(count == context->num_nodes+2);
}
#endif
return res;
}
// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
if (p->h > q->h)
return -1;
if (p->h < q->h)
return 1;
return (p->w > q->w) ? -1 : (p->w < q->w);
}
// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
}
#ifdef STBRP_LARGE_RECTS
#define STBRP__MAXVAL 0xffffffff
#else
#define STBRP__MAXVAL 0xffff
#endif
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
{
int i, all_rects_packed = 1;
// we use the 'was_packed' field internally to allow sorting/unsorting
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = i;
}
// sort according to heuristic
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
for (i=0; i < num_rects; ++i) {
if (rects[i].w == 0 || rects[i].h == 0) {
rects[i].x = rects[i].y = 0; // empty rect needs no space
} else {
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
if (fr.prev_link) {
rects[i].x = (stbrp_coord) fr.x;
rects[i].y = (stbrp_coord) fr.y;
} else {
rects[i].x = rects[i].y = STBRP__MAXVAL;
}
}
}
// unsort
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
// set was_packed flags and all_rects_packed status
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
if (!rects[i].was_packed)
all_rects_packed = 0;
}
// return the all_rects_packed status
return all_rects_packed;
}
#endif
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

10625
libs/source/imgui/imgui.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,677 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
// 2019-03-15: OpenGL: Added a dummy GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
//----------------------------------------
// OpenGL GLSL GLSL
// version version string
//----------------------------------------
// 2.0 110 "#version 110"
// 2.1 120 "#version 120"
// 3.0 130 "#version 130"
// 3.1 140 "#version 140"
// 3.2 150 "#version 150"
// 3.3 330 "#version 330 core"
// 4.0 400 "#version 400 core"
// 4.1 410 "#version 410 core"
// 4.2 420 "#version 410 core"
// 4.3 430 "#version 430 core"
// ES 2.0 100 "#version 100" = WebGL 1.0
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
#include <stdint.h> // intptr_t
#endif
// GL includes
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <GLES2/gl2.h>
#elif defined(IMGUI_IMPL_OPENGL_ES3)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
#else
#include <GLES3/gl3.h> // Use GL ES 3
#endif
#else
// About Desktop OpenGL function loaders:
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
#include <GL/gl3w.h> // Needs to be initialized with gl3wInit() in user's code
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code.
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code.
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
#ifndef GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
#endif
#include <glbinding/Binding.h> // Needs to be initialized with glbinding::Binding::initialize() in user's code.
#include <glbinding/gl/gl.h>
using namespace gl;
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
#ifndef GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
#endif
#include <glbinding/glbinding.h>// Needs to be initialized with glbinding::initialize() in user's code.
#include <glbinding/gl/gl.h>
using namespace gl;
#else
#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#endif
#endif
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3) || !defined(GL_VERSION_3_2)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 0
#else
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 1
#endif
// OpenGL Data
static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings.
static GLuint g_FontTexture = 0;
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
// Query for GL version (e.g. 320 for GL 3.2)
#if !defined(IMGUI_IMPL_OPENGL_ES2)
GLint major, minor;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
g_GlVersion = (GLuint)(major * 100 + minor * 10);
#else
g_GlVersion = 200; // GLES 2
#endif
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 320)
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
#if defined(IMGUI_IMPL_OPENGL_ES2)
if (glsl_version == NULL)
glsl_version = "#version 100";
#elif defined(IMGUI_IMPL_OPENGL_ES3)
if (glsl_version == NULL)
glsl_version = "#version 300 es";
#elif defined(__APPLE__)
if (glsl_version == NULL)
glsl_version = "#version 150";
#else
if (glsl_version == NULL)
glsl_version = "#version 130";
#endif
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n");
// Dummy construct to make it easily visible in the IDE and debugger which GL loader has been selected.
// The code actually never uses the 'gl_loader' variable! It is only here so you can read it!
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash below.
// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
const char* gl_loader = "Unknown";
IM_UNUSED(gl_loader);
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
gl_loader = "GL3W";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
gl_loader = "GLEW";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
gl_loader = "GLAD";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
gl_loader = "glbinding2";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
gl_loader = "glbinding3";
#elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
gl_loader = "custom";
#else
gl_loader = "none";
#endif
// Make a dummy GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
return true;
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
void ImGui_ImplOpenGL3_NewFrame()
{
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
bool clip_origin_lower_left = true;
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&current_clip_origin);
if (current_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
#endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif
(void)vertex_array_object;
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(vertex_array_object);
#endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
}
// OpenGL3 Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program);
GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture);
#ifdef GL_SAMPLER_BINDING
GLuint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler);
#endif
GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
#endif
#ifdef GL_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
#ifndef IMGUI_IMPL_OPENGL_ES2
glGenVertexArrays(1, &vertex_array_object);
#endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec4 clip_rect;
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 320)
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
else
#endif
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
}
}
}
}
// Destroy the temporary VAO
#ifndef IMGUI_IMPL_OPENGL_ES2
glDeleteVertexArrays(1, &vertex_array_object);
#endif
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, last_sampler);
#endif
glActiveTexture(last_active_texture);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array_object);
#endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
#endif
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_UNPACK_ROW_LENGTH
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO& io = ImGui::GetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts->TexID = 0;
g_FontTexture = 0;
}
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#endif
// Parse GLSL version string
int glsl_version = 130;
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n"
"attribute vec2 Position;\n"
"attribute vec2 UV;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_300_es =
"precision mediump float;\n"
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader_glsl_120 =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D Texture;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"uniform sampler2D Texture;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
// Select shaders matching our GLSL versions
const GLchar* vertex_shader = NULL;
const GLchar* fragment_shader = NULL;
if (glsl_version < 130)
{
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
}
else if (glsl_version >= 410)
{
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
}
else if (glsl_version == 300)
{
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
}
else
{
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
glCompileShader(g_VertHandle);
CheckShader(g_VertHandle, "vertex shader");
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
glCompileShader(g_FragHandle);
CheckShader(g_FragHandle, "fragment shader");
g_ShaderHandle = glCreateProgram();
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
CheckProgram(g_ShaderHandle, "shader program");
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(g_ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array);
#endif
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; }
if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; }
if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); }
if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); }
if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; }
if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; }
if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture();
}

View File

@ -0,0 +1,370 @@
// dear imgui: Platform Binding for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// (Requires: SDL 2.0. Prefer SDL 2.0.4+ for full feature support.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends.
// 2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2).
// 2019-12-17: Inputs: On Wayland, use SDL_GetMouseState (because there is no global mouse state).
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
// 2019-04-23: Inputs: Added support for SDL_GameController (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
// 2018-12-21: Inputs: Workaround for Android/iOS which don't seem to handle focus related calls.
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'.
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples.
// 2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter.
// 2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText).
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS).
// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes.
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS.
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
#include "imgui_impl_sdl.h"
// SDL
#include <SDL.h>
#include <SDL_syswm.h>
#if defined(__APPLE__)
#include "TargetConditionals.h"
#endif
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4)
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
// Data
static SDL_Window* g_Window = NULL;
static Uint64 g_Time = 0;
static bool g_MousePressed[3] = { false, false, false };
static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {};
static char* g_ClipboardTextData = NULL;
static bool g_MouseCanUseGlobalState = true;
static const char* ImGui_ImplSDL2_GetClipboardText(void*)
{
if (g_ClipboardTextData)
SDL_free(g_ClipboardTextData);
g_ClipboardTextData = SDL_GetClipboardText();
return g_ClipboardTextData;
}
static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
{
SDL_SetClipboardText(text);
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
{
ImGuiIO& io = ImGui::GetIO();
switch (event->type)
{
case SDL_MOUSEWHEEL:
{
if (event->wheel.x > 0) io.MouseWheelH += 1;
if (event->wheel.x < 0) io.MouseWheelH -= 1;
if (event->wheel.y > 0) io.MouseWheel += 1;
if (event->wheel.y < 0) io.MouseWheel -= 1;
return true;
}
case SDL_MOUSEBUTTONDOWN:
{
if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true;
if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true;
if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true;
return true;
}
case SDL_TEXTINPUT:
{
io.AddInputCharactersUTF8(event->text.text);
return true;
}
case SDL_KEYDOWN:
case SDL_KEYUP:
{
int key = event->key.keysym.scancode;
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
#ifdef _WIN32
io.KeySuper = false;
#else
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
#endif
return true;
}
}
return false;
}
static bool ImGui_ImplSDL2_Init(SDL_Window* window)
{
g_Window = window;
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_sdl";
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT;
io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE;
io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE;
io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE;
io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN;
io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE;
io.KeyMap[ImGuiKey_KeyPadEnter] = SDL_SCANCODE_KP_ENTER;
io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A;
io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C;
io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V;
io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X;
io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y;
io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z;
io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
io.ClipboardUserData = NULL;
// Load mouse cursors
g_MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
g_MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
g_MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
g_MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
g_MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
// Check and store if we are on Wayland
g_MouseCanUseGlobalState = strncmp(SDL_GetCurrentVideoDriver(), "wayland", 7) != 0;
#ifdef _WIN32
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(window, &wmInfo);
io.ImeWindowHandle = wmInfo.info.win.window;
#else
(void)window;
#endif
return true;
}
bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
{
(void)sdl_gl_context; // Viewport branch will need this.
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
{
#if !SDL_HAS_VULKAN
IM_ASSERT(0 && "Unsupported");
#endif
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
{
#if !defined(_WIN32)
IM_ASSERT(0 && "Unsupported");
#endif
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
{
return ImGui_ImplSDL2_Init(window);
}
void ImGui_ImplSDL2_Shutdown()
{
g_Window = NULL;
// Destroy last known clipboard data
if (g_ClipboardTextData)
SDL_free(g_ClipboardTextData);
g_ClipboardTextData = NULL;
// Destroy SDL mouse cursors
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
SDL_FreeCursor(g_MouseCursors[cursor_n]);
memset(g_MouseCursors, 0, sizeof(g_MouseCursors));
}
static void ImGui_ImplSDL2_UpdateMousePosAndButtons()
{
ImGuiIO& io = ImGui::GetIO();
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
SDL_WarpMouseInWindow(g_Window, (int)io.MousePos.x, (int)io.MousePos.y);
else
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
int mx, my;
Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my);
io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false;
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS)
SDL_Window* focused_window = SDL_GetKeyboardFocus();
if (g_Window == focused_window)
{
if (g_MouseCanUseGlobalState)
{
// SDL_GetMouseState() gives mouse position seemingly based on the last window entered/focused(?)
// The creation of a new windows at runtime and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally.
// Won't use this workaround when on Wayland, as there is no global mouse position.
int wx, wy;
SDL_GetWindowPosition(focused_window, &wx, &wy);
SDL_GetGlobalMouseState(&mx, &my);
mx -= wx;
my -= wy;
}
io.MousePos = ImVec2((float)mx, (float)my);
}
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger the OS window resize cursor.
// The function is only supported from SDL 2.0.4 (released Jan 2016)
bool any_mouse_button_down = ImGui::IsAnyMouseDown();
SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE);
#else
if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS)
io.MousePos = ImVec2((float)mx, (float)my);
#endif
}
static void ImGui_ImplSDL2_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
SDL_ShowCursor(SDL_FALSE);
}
else
{
// Show OS mouse cursor
SDL_SetCursor(g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]);
SDL_ShowCursor(SDL_TRUE);
}
}
static void ImGui_ImplSDL2_UpdateGamepads()
{
ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
// Get gamepad
SDL_GameController* game_controller = SDL_GameControllerOpen(0);
if (!game_controller)
{
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
return;
}
// Update gamepad inputs
#define MAP_BUTTON(NAV_NO, BUTTON_NO) { io.NavInputs[NAV_NO] = (SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0) ? 1.0f : 0.0f; }
#define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value.
MAP_BUTTON(ImGuiNavInput_Activate, SDL_CONTROLLER_BUTTON_B); // Cross / A
//MAP_BUTTON(ImGuiNavInput_Cancel, SDL_CONTROLLER_BUTTON_A); // Circle / B
//MAP_BUTTON(ImGuiNavInput_Menu, SDL_CONTROLLER_BUTTON_X); // Square / X
MAP_BUTTON(ImGuiNavInput_Input, SDL_CONTROLLER_BUTTON_Y); // Triangle / Y
MAP_BUTTON(ImGuiNavInput_DpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT); // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP); // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN); // D-Pad Down
MAP_BUTTON(ImGuiNavInput_FocusPrev, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_FocusNext, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB
MAP_BUTTON(ImGuiNavInput_TweakSlow, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_TweakFast, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB
MAP_ANALOG(ImGuiNavInput_LStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768);
MAP_ANALOG(ImGuiNavInput_LStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767);
MAP_ANALOG(ImGuiNavInput_LStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32767);
MAP_ANALOG(ImGuiNavInput_LStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767);
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
#undef MAP_BUTTON
#undef MAP_ANALOG
}
void ImGui_ImplSDL2_NewFrame(SDL_Window* window)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
// Setup display size (every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
SDL_GetWindowSize(window, &w, &h);
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
w = h = 0;
SDL_GL_GetDrawableSize(window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
static Uint64 frequency = SDL_GetPerformanceFrequency();
Uint64 current_time = SDL_GetPerformanceCounter();
io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f);
g_Time = current_time;
ImGui_ImplSDL2_UpdateMousePosAndButtons();
ImGui_ImplSDL2_UpdateMouseCursor();
// Update game controllers (if enabled and available)
ImGui_ImplSDL2_UpdateGamepads();
}

File diff suppressed because it is too large Load Diff

BIN
romfs/NotoSans-Regular.ttf Normal file

Binary file not shown.

BIN
romfs/archive.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

BIN
romfs/audio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
romfs/check.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
romfs/file.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

BIN
romfs/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

BIN
romfs/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

BIN
romfs/uncheck.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

106
source/config.cpp Normal file
View File

@ -0,0 +1,106 @@
#include <cstdio>
#include <cstring>
#include <jansson.h>
#include "config.h"
#include "fs.h"
#define CONFIG_VERSION 0
config_t config;
namespace Config {
static const char *config_file = "{\n\t\"config_ver\": %d,\n\t\"sort\": %d,\n\t\"dark_theme\": %d,\n\t\"last_dir\": \"%s\"\n}";
static int config_version_holder = 0;
static const int buf_size = 128 + FS_MAX_PATH;
int Save(config_t config) {
Result ret = 0;
char *buf = new char[buf_size];
u64 len = snprintf(buf, buf_size, config_file, CONFIG_VERSION, config.sort, config.dark_theme, config.cwd);
if (R_FAILED(ret = FS::WriteFile("/switch/NX-Shell/config.json", buf, len))) {
delete[] buf;
return ret;
}
delete[] buf;
return 0;
}
static void SetDefault(config_t *config) {
config->sort = 0;
config->dark_theme = false;
std::strcpy(config->cwd, "/");
}
int Load(void) {
Result ret = 0;
if (!FS::DirExists("/switch/"))
fsFsCreateDirectory(fs, "/switch");
if (!FS::DirExists("/switch/NX-Shell/"))
fsFsCreateDirectory(fs, "/switch/NX-Shell");
if (!FS::FileExists("/switch/NX-Shell/config.json")) {
Config::SetDefault(&config);
return Config::Save(config);
}
FsFile file;
if (R_FAILED(ret = fsFsOpenFile(fs, "/switch/NX-Shell/config.json", FsOpenMode_Read, &file))) {
fsFileClose(&file);
return ret;
}
s64 size = 0;
if (R_FAILED(ret = fsFileGetSize(&file, &size))) {
fsFileClose(&file);
return ret;
}
char *buf = new char[size + 1];
if (R_FAILED(ret = fsFileRead(&file, 0, buf, (u64)size + 1, FsReadOption_None, nullptr))) {
delete[] buf;
fsFileClose(&file);
return ret;
}
fsFileClose(&file);
json_t *root;
json_error_t error;
root = json_loads(buf, 0, &error);
delete[] buf;
if (!root) {
printf("error: on line %d: %s\n", error.line, error.text);
return -1;
}
json_t *config_ver = json_object_get(root, "config_ver");
config_version_holder = json_integer_value(config_ver);
json_t *sort = json_object_get(root, "sort");
config.sort = json_integer_value(sort);
json_t *dark_theme = json_object_get(root, "dark_theme");
config.dark_theme = json_integer_value(dark_theme);
json_t *last_dir = json_object_get(root, "last_dir");
std::strcpy(config.cwd, json_string_value(last_dir));
if (!FS::DirExists(config.cwd))
std::strcpy(config.cwd, "/");
// Delete config file if config file is updated. This will rarely happen.
if (config_version_holder < CONFIG_VERSION) {
fsFsDeleteFile(fs, "/switch/NX-Shell/config.json");
Config::SetDefault(&config);
return Config::Save(config);
}
json_decref(root);
return 0;
}
}

453
source/fs.cpp Normal file
View File

@ -0,0 +1,453 @@
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <filesystem>
#include "config.h"
#include "fs.h"
// Global vars
FsFileSystem *fs;
FsFileSystem devices[4];
namespace FS {
static int PREVIOUS_BROWSE_STATE = 0;
typedef struct {
char copy_path[FS_MAX_PATH];
char copy_filename[FS_MAX_PATH];
bool is_dir = false;
} FS_Copy_Struct;
FS_Copy_Struct fs_copy_struct;
bool FileExists(const char path[FS_MAX_PATH]) {
FsFile file;
if (R_SUCCEEDED(fsFsOpenFile(fs, path, FsOpenMode_Read, &file))) {
fsFileClose(&file);
return true;
}
return false;
}
bool DirExists(const char path[FS_MAX_PATH]) {
FsDir dir;
if (R_SUCCEEDED(fsFsOpenDirectory(fs, path, FsDirOpenMode_ReadDirs, &dir))) {
fsDirClose(&dir);
return true;
}
return false;
}
std::string GetFileExt(const std::string &filename) {
std::string ext = std::filesystem::path(filename).extension();
std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper);
return ext;
}
FileType GetFileType(const std::string &filename) {
std::string ext = FS::GetFileExt(filename);
if (ext == ".ZIP" || ext == ".RAR" || ext == ".7Z")
return FileTypeArchive;
else if (ext == ".FLAC" || ext == ".IT" || ext == ".MOD" || ext == ".MP3" || ext == ".OGG" || ext == ".OPUS"
|| ext == ".S3M" || ext == ".WAV" || ext == ".XM")
return FileTypeAudio;
else if (ext == ".NRO" || ext == ".BIN")
return FileTypeBinary;
else if (ext == ".BMP" || ext == ".GIF" || ext == ".JPG" || ext == ".JPEG" || ext == ".PGM" || ext == ".PPM"
|| ext == ".PNG" || ext == ".PSD" || ext == ".TGA")
return FileTypeImage;
return FileTypeNone;
}
static int Sort(const void *p1, const void *p2) {
FsDirectoryEntry *entryA = (FsDirectoryEntry *)p1;
FsDirectoryEntry *entryB = (FsDirectoryEntry *)p2;
if ((entryA->type == FsDirEntryType_Dir) && !(entryB->type == FsDirEntryType_Dir))
return -1;
else if (!(entryA->type == FsDirEntryType_Dir) && (entryB->type == FsDirEntryType_Dir))
return 1;
else {
switch(config.sort) {
case 0: // Sort alphabetically (ascending - A to Z)
return strcasecmp(entryA->name, entryB->name);
break;
case 1: // Sort alphabetically (descending - Z to A)
return strcasecmp(entryB->name, entryA->name);
break;
case 2: // Sort by file size (largest first)
return entryA->file_size > entryB->file_size? -1 : entryA->file_size < entryB->file_size? 1 : 0;
break;
case 3: // Sort by file size (smallest first)
return entryB->file_size > entryA->file_size? -1 : entryB->file_size < entryA->file_size? 1 : 0;
break;
}
}
return 0;
}
s64 GetDirList(char path[FS_MAX_PATH], FsDirectoryEntry **entriesp) {
FsDir dir;
Result ret = 0;
if (R_FAILED(ret = fsFsOpenDirectory(fs, path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, &dir)))
return ret;
s64 entry_count = 0;
if (R_FAILED(ret = fsDirGetEntryCount(&dir, &entry_count)))
return ret;
FsDirectoryEntry *entries = new FsDirectoryEntry[entry_count * sizeof(*entries)];
if (R_FAILED(ret = fsDirRead(&dir, nullptr, (size_t)entry_count, entries))) {
delete[] entries;
return ret;
}
qsort(entries, entry_count, sizeof(FsDirectoryEntry), FS::Sort);
fsDirClose(&dir);
*entriesp = entries;
return entry_count;
}
void FreeDirEntries(FsDirectoryEntry **entries, s64 entry_count) {
if (entry_count > 0)
delete[] (*entries);
*entries = nullptr;
}
s64 RefreshEntries(FsDirectoryEntry **entries, s64 entry_count) {
FS::FreeDirEntries(entries, entry_count);
return FS::GetDirList(config.cwd, entries);
}
static s64 ChangeDir(char path[FS_MAX_PATH], FsDirectoryEntry **entries, s64 entry_count) {
FsDirectoryEntry *new_entries;
s64 num_entries = FS::GetDirList(path, &new_entries);
if (num_entries < 0)
return -1;
// Apply cd after successfully listing new directory
FS::FreeDirEntries(entries, entry_count);
std::strncpy(config.cwd, path, FS_MAX_PATH);
Config::Save(config);
*entries = new_entries;
return num_entries;
}
static int GetPrevPath(char path[FS_MAX_PATH]) {
if (std::strlen(config.cwd) <= 1 && config.cwd[0] == '/')
return -1;
// Remove upmost directory
bool copy = false;
int len = 0;
for (ssize_t i = (ssize_t)std::strlen(config.cwd); i >= 0; i--) {
if (config.cwd[i] == '/')
copy = true;
if (copy) {
path[i] = config.cwd[i];
len++;
}
}
// remove trailing slash
if (len > 1 && path[len - 1] == '/')
len--;
path[len] = '\0';
return 0;
}
s64 ChangeDirNext(const char path[FS_MAX_PATH], FsDirectoryEntry **entries, s64 entry_count) {
char new_cwd[FS_MAX_PATH];
const char *sep = !std::strncmp(config.cwd, "/", 2) ? "" : "/"; // Don't append / if at /
std::snprintf(new_cwd, FS_MAX_PATH, "%s%s%s", config.cwd, sep, path);
return FS::ChangeDir(new_cwd, entries, entry_count);
}
s64 ChangeDirPrev(FsDirectoryEntry **entries, s64 entry_count) {
char new_cwd[FS_MAX_PATH];
if (FS::GetPrevPath(new_cwd) < 0)
return -1;
return FS::ChangeDir(new_cwd, entries, entry_count);
}
int ConstructPath(FsDirectoryEntry *entry, char path[FS_MAX_PATH], const char filename[FS_MAX_PATH]) {
int length = 0;
if (entry)
length = std::snprintf(path, FS_MAX_PATH, "%s%s%s", config.cwd, !std::strncmp(config.cwd, "/", 2) ? "" : "/", entry? entry->name : "");
else
length = std::snprintf(path, FS_MAX_PATH, "%s%s%s", config.cwd, !std::strncmp(config.cwd, "/", 2) ? "" : "/", filename);
return length;
}
Result GetTimeStamp(FsDirectoryEntry *entry, FsTimeStampRaw *timestamp) {
Result ret = 0;
char path[FS_MAX_PATH];
if (FS::ConstructPath(entry, path, nullptr) <= 0)
return -1;
if (R_FAILED(ret = fsFsGetFileTimeStampRaw(fs, path, timestamp)))
return ret;
return 0;
}
Result Rename(FsDirectoryEntry *entry, const char filename[FS_MAX_PATH]) {
Result ret = 0;
char path[FS_MAX_PATH];
if (FS::ConstructPath(entry, path, nullptr) <= 0)
return -1;
char new_path[FS_MAX_PATH];
if (FS::ConstructPath(nullptr, new_path, filename) <= 0)
return -1;
if (entry->type == FsDirEntryType_Dir) {
if (R_FAILED(ret = fsFsRenameDirectory(fs, path, new_path)))
return ret;
}
else {
if (R_FAILED(ret = fsFsRenameFile(fs, path, new_path)))
return ret;
}
return 0;
}
Result Delete(FsDirectoryEntry *entry) {
Result ret = 0;
char path[FS_MAX_PATH];
if (FS::ConstructPath(entry, path, nullptr) <= 0)
return -1;
if (entry->type == FsDirEntryType_Dir) {
if (R_FAILED(ret = fsFsDeleteDirectoryRecursively(fs, path)))
return ret;
}
else {
if (R_FAILED(ret = fsFsDeleteFile(fs, path)))
return ret;
}
return 0;
}
Result SetArchiveBit(FsDirectoryEntry *entry) {
Result ret = 0;
char path[FS_MAX_PATH];
if (FS::ConstructPath(entry, path, nullptr) <= 0)
return -1;
if (R_FAILED(ret = fsFsSetConcatenationFileAttribute(fs, path)))
return ret;
return 0;
}
static Result CopyFile(const char src_path[FS_MAX_PATH], const char dest_path[FS_MAX_PATH]) {
Result ret = 0;
FsFile src_handle, dest_handle;
if (R_FAILED(ret = fsFsOpenFile(&devices[PREVIOUS_BROWSE_STATE], src_path, FsOpenMode_Read, &src_handle))) {
fsFileClose(&src_handle);
return ret;
}
s64 size = 0;
if (R_FAILED(ret = fsFileGetSize(&src_handle, &size)))
return ret;
if (!FS::FileExists(dest_path))
fsFsCreateFile(fs, dest_path, size, 0);
if (R_FAILED(ret = fsFsOpenFile(fs, dest_path, FsOpenMode_Write, &dest_handle))) {
fsFileClose(&src_handle);
fsFileClose(&dest_handle);
return ret;
}
u64 bytes_read = 0;
const u64 buf_size = 0x10000;
s64 offset = 0;
unsigned char *buf = new unsigned char[buf_size];
do {
std::memset(buf, 0, buf_size);
if (R_FAILED(ret = fsFileRead(&src_handle, offset, buf, buf_size, FsReadOption_None, &bytes_read))) {
delete[] buf;
fsFileClose(&src_handle);
fsFileClose(&dest_handle);
return ret;
}
if (R_FAILED(ret = fsFileWrite(&dest_handle, offset, buf, bytes_read, FsWriteOption_Flush))) {
delete[] buf;
fsFileClose(&src_handle);
fsFileClose(&dest_handle);
return ret;
}
offset += bytes_read;
} while(offset < size);
delete[] buf;
fsFileClose(&src_handle);
fsFileClose(&dest_handle);
return 0;
}
static Result CopyDir(const char src_path[FS_MAX_PATH], const char dest_path[FS_MAX_PATH]) {
Result ret = 0;
FsDir dir;
if (R_FAILED(ret = fsFsOpenDirectory(&devices[PREVIOUS_BROWSE_STATE], src_path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, &dir)))
return ret;
// This may fail or not, but we don't care -> make the dir if it doesn't exist otherwise it fails.
fsFsCreateDirectory(fs, dest_path);
s64 entry_count = 0;
if (R_FAILED(ret = fsDirGetEntryCount(&dir, &entry_count)))
return ret;
FsDirectoryEntry *entries = new FsDirectoryEntry[entry_count * sizeof(*entries)];
if (R_FAILED(ret = fsDirRead(&dir, nullptr, (size_t)entry_count, entries))) {
delete[] entries;
return ret;
}
for (s64 i = 0; i < entry_count; i++) {
std::string filename = entries[i].name;
if (!filename.empty()) {
if ((filename == ".") || (filename == ".."))
continue;
std::string src = src_path;
src.append("/");
src.append(filename);
std::string dest = dest_path;
dest.append("/");
dest.append(filename);
if (entries[i].type == FsDirEntryType_Dir)
FS::CopyDir(src.c_str(), dest.c_str()); // Copy Folder (via recursion)
else
FS::CopyFile(src.c_str(), dest.c_str()); // Copy File
}
}
delete[] entries;
fsDirClose(&dir);
return 0;
}
void Copy(FsDirectoryEntry *entry) {
char path[FS_MAX_PATH];
if (FS::ConstructPath(entry, path, nullptr) <= 0)
return;
std::strcpy(fs_copy_struct.copy_path, path);
std::strcpy(fs_copy_struct.copy_filename, entry->name);
if (entry->type == FsDirEntryType_Dir)
fs_copy_struct.is_dir = true;
}
Result Paste(void) {
Result ret = 0;
char path[FS_MAX_PATH];
FS::ConstructPath(nullptr, path, fs_copy_struct.copy_filename);
if (fs_copy_struct.is_dir) // Copy folder recursively
ret = FS::CopyDir(fs_copy_struct.copy_path, path);
else // Copy file
ret = FS::CopyFile(fs_copy_struct.copy_path, path);
std::memset(fs_copy_struct.copy_path, 0, FS_MAX_PATH);
std::memset(fs_copy_struct.copy_filename, 0, FS_MAX_PATH);
fs_copy_struct.is_dir = false;
return ret;
}
Result Move(void) {
Result ret = 0;
char path[FS_MAX_PATH];
FS::ConstructPath(nullptr, path, fs_copy_struct.copy_filename);
if (fs_copy_struct.is_dir) {
if (R_FAILED(ret = fsFsRenameDirectory(fs, fs_copy_struct.copy_path, path)))
return ret;
}
else {
if (R_FAILED(ret = fsFsRenameFile(fs, fs_copy_struct.copy_path, path)))
return ret;
}
std::memset(fs_copy_struct.copy_path, 0, FS_MAX_PATH);
std::memset(fs_copy_struct.copy_filename, 0, FS_MAX_PATH);
fs_copy_struct.is_dir = false;
return 0;
}
Result GetFileSize(const char path[FS_MAX_PATH], s64 *size) {
Result ret = 0;
FsFile file;
if (R_FAILED(ret = fsFsOpenFile(fs, path, FsOpenMode_Read, &file))) {
fsFileClose(&file);
return ret;
}
if (R_FAILED(ret = fsFileGetSize(&file, size))) {
fsFileClose(&file);
return ret;
}
fsFileClose(&file);
return 0;
}
Result WriteFile(const char path[FS_MAX_PATH], const void *buf, u64 size) {
Result ret = 0;
// Delete and re-create the file, we don't care about the return value here.
fsFsDeleteFile(fs, path);
fsFsCreateFile(fs, path, size, 0);
FsFile file;
if (R_FAILED(ret = fsFsOpenFile(fs, path, FsOpenMode_Write, &file))) {
fsFileClose(&file);
return ret;
}
if (R_FAILED(ret = fsFileWrite(&file, 0, buf, size, FsWriteOption_Flush))) {
fsFileClose(&file);
return ret;
}
fsFileClose(&file);
return 0;
}
}

521
source/gui.cpp Normal file
View File

@ -0,0 +1,521 @@
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
#include "config.h"
#include "fs.h"
#include "gui.h"
#include "imgui.h"
#include "imgui_impl_sdl.h"
#include "imgui_impl_opengl3.h"
#include "imgui_internal.h"
#include "keyboard.h"
#include "textures.h"
#include "utils.h"
namespace GUI {
enum MENU_STATES {
MENU_STATE_HOME = 0,
MENU_STATE_OPTIONS = 1,
MENU_STATE_DELETE = 2,
MENU_STATE_PROPERTIES = 3,
MENU_STATE_SETTINGS = 4,
MENU_STATE_IMAGEVIEWER = 5
};
typedef struct {
int state = 0;
bool copy = false;
bool move = false;
int selected = 0;
int file_count = 0;
std::string selected_filename;
FsDirectoryEntry *entries = nullptr;
std::vector<bool> checked;
std::string checked_cwd;
int checked_count = 0;
} MenuItem;
static void ResetCheckbox(MenuItem *item) {
item->checked.resize(item->file_count);
item->checked.assign(item->checked.size(), false);
item->checked_cwd.clear();
}
static void RenderOptionsMenu(MenuItem *item) {
ImGui::OpenPopup("Options");
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(15, 15));
if (ImGui::BeginPopupModal("Options", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
if (ImGui::Button("Properties", ImVec2(200, 50))) {
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_PROPERTIES;
}
ImGui::SameLine(0.0f, 15.0f);
if (ImGui::Button("Rename", ImVec2(200, 50))) {
std::string path = Keyboard::GetText("Enter name", item->entries[item->selected].name);
if (R_SUCCEEDED(FS::Rename(&item->entries[item->selected], path.c_str())))
item->file_count = FS::RefreshEntries(&item->entries, item->file_count);
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_HOME;
}
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::Button("New Folder", ImVec2(200, 50))) {
std::string path = config.cwd;
path.append("/");
std::string name = Keyboard::GetText("Enter folder name", "New folder");
path.append(name);
if (R_SUCCEEDED(fsFsCreateDirectory(fs, path.c_str()))) {
item->file_count = FS::RefreshEntries(&item->entries, item->file_count);
GUI::ResetCheckbox(item);
}
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_HOME;
}
ImGui::SameLine(0.0f, 15.0f);
if (ImGui::Button("New File", ImVec2(200, 50))) {
std::string path = config.cwd;
path.append("/");
std::string name = Keyboard::GetText("Enter file name", "New file");
path.append(name);
if (R_SUCCEEDED(fsFsCreateFile(fs, path.c_str(), 0, 0))) {
item->file_count = FS::RefreshEntries(&item->entries, item->file_count);
GUI::ResetCheckbox(item);
}
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_HOME;
}
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::Button(!item->copy? "Copy" : "Paste", ImVec2(200, 50))) {
if (!item->copy)
FS::Copy(&item->entries[item->selected]);
else {
if (R_SUCCEEDED(FS::Paste())) {
item->file_count = FS::RefreshEntries(&item->entries, item->file_count);
GUI::ResetCheckbox(item);
}
}
item->copy = !item->copy;
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_HOME;
}
ImGui::SameLine(0.0f, 15.0f);
if (ImGui::Button(!item->move? "Move" : "Paste", ImVec2(200, 50))) {
if (!item->move)
FS::Copy(&item->entries[item->selected]);
else {
if (R_SUCCEEDED(FS::Move())) {
item->file_count = FS::RefreshEntries(&item->entries, item->file_count);
GUI::ResetCheckbox(item);
}
}
item->move = !item->move;
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_HOME;
}
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::Button("Delete", ImVec2(200, 50))) {
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_DELETE;
}
ImGui::SameLine(0.0f, 15.0f);
if (ImGui::Button("Set archive bit", ImVec2(200, 50))) {
if (R_SUCCEEDED(FS::SetArchiveBit(&item->entries[item->selected]))) {
item->file_count = FS::RefreshEntries(&item->entries, item->file_count);
GUI::ResetCheckbox(item);
}
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_HOME;
}
ImGui::EndPopup();
}
ImGui::PopStyleVar();
}
static char *FormatDate(char *string, time_t timestamp) {
strftime(string, 36, "%Y/%m/%d %H:%M:%S", localtime(&timestamp));
return string;
}
static void RenderPropertiesMenu(MenuItem *item) {
ImGui::OpenPopup("Properties");
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20, 20));
if (ImGui::BeginPopupModal("Properties", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
std::string name_text = "Name: " + item->selected_filename;
ImGui::Text(name_text.c_str());
if (item->entries[item->selected].type == FsDirEntryType_File) {
char size[16];
Utils::GetSizeString(size, item->entries[item->selected].file_size);
std::string size_text = "Size: ";
size_text.append(size);
ImGui::Text(size_text.c_str());
}
FsTimeStampRaw timestamp;
if (R_SUCCEEDED(FS::GetTimeStamp(&item->entries[item->selected], &timestamp))) {
if (timestamp.is_valid == 1) { // Confirm valid timestamp
char date[3][36];
std::string created_time = "Created: ";
created_time.append(GUI::FormatDate(date[0], timestamp.created));
ImGui::Text(created_time.c_str());
std::string modified_time = "Modified: ";
modified_time.append(GUI::FormatDate(date[1], timestamp.modified));
ImGui::Text(modified_time.c_str());
std::string accessed_time = "Accessed: ";
accessed_time.append(GUI::FormatDate(date[2], timestamp.accessed));
ImGui::Text(accessed_time.c_str());
}
}
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::Button("OK", ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_OPTIONS;
}
ImGui::EndPopup();
}
ImGui::PopStyleVar();
}
static void RenderDeleteMenu(MenuItem *item) {
ImGui::OpenPopup("Delete");
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(15, 15));
if (ImGui::BeginPopupModal("Delete", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("This action cannot be undone");
if ((item->checked_count > 1) && (!item->checked_cwd.compare(config.cwd))) {
ImGui::Text("Do you wish to delete the following:");
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
ImGui::BeginChild("Scrolling", ImVec2(0, 100));
for (long unsigned int i = 0; i < item->checked.size(); i++) {
if (item->checked.at(i))
ImGui::Text(item->entries[i].name);
}
ImGui::EndChild();
}
else {
std::string text = "Do you wish to delete " + item->selected_filename + "?";
ImGui::Text(text.c_str());
}
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::Button("OK", ImVec2(120, 0))) {
Result ret = 0;
if ((item->checked_count > 1) && (!item->checked_cwd.compare(config.cwd))) {
for (long unsigned int i = 0; i < item->checked.size(); i++) {
if (item->checked.at(i)) {
if (R_FAILED(ret = FS::Delete(&item->entries[i]))) {
item->file_count = FS::RefreshEntries(&item->entries, item->file_count);
GUI::ResetCheckbox(item);
break;
}
}
}
}
else
ret = FS::Delete(&item->entries[item->selected]);
if (R_SUCCEEDED(ret)) {
item->file_count = FS::RefreshEntries(&item->entries, item->file_count);
GUI::ResetCheckbox(item);
}
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_HOME;
}
ImGui::SetItemDefaultFocus();
ImGui::SameLine(0.0f, 15.0f);
if (ImGui::Button("Cancel", ImVec2(120, 0))) {
ImGui::CloseCurrentPopup();
item->state = MENU_STATE_OPTIONS;
}
ImGui::EndPopup();
}
ImGui::PopStyleVar();
}
static void RenderSettingsMenu(MenuItem *item) {
ImGui::SetNextWindowPos({0.0f, 0.0f}, ImGuiCond_Once);
ImGui::SetNextWindowSize({1280.0f, 720.0f}, ImGuiCond_Once);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
if (ImGui::Begin("Settings", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) {
if (ImGui::TreeNode("Sort Settings")) {
ImGui::RadioButton(" By name (ascending)", &config.sort, 0);
ImGui::RadioButton(" By name (descending)", &config.sort, 1);
ImGui::Dummy(ImVec2(0.0f, 10.0f)); // Spacing
ImGui::RadioButton(" By size (largest first)", &config.sort, 2);
ImGui::Dummy(ImVec2(0.0f, 10.0f)); // Spacing
ImGui::RadioButton(" By size (smallest first)", &config.sort, 3);
ImGui::TreePop();
}
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::TreeNode("About")) {
ImGui::Text("NX-Shell Version: v%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
ImGui::Text("ImGui Version: %s", ImGui::GetVersion());
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
ImGui::Text("Author: Joel16");
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
ImGui::Text("Banner: Preetisketch");
ImGui::TreePop();
}
}
ImGui::End();
ImGui::PopStyleVar();
Config::Save(config);
item->file_count = FS::RefreshEntries(&item->entries, item->file_count);
}
static void RenderImageViewer(MenuItem *item, Tex *texture) {
ImGui::SetNextWindowPos({0.0f, 0.0f}, ImGuiCond_Once);
ImGui::SetNextWindowSize({1280.0f, 720.0f}, ImGuiCond_Once);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
if (ImGui::Begin(item->selected_filename.c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse))
ImGui::Image((void *)(intptr_t)texture->id, ImVec2(texture->width, texture->height));
ImGui::End();
ImGui::PopStyleVar();
}
int RenderMainMenu(SDL_Window *window) {
ImGuiIO& io = ImGui::GetIO(); (void)io;
// Used for displaying images
Tex texture;
MenuItem item;
item.state = MENU_STATE_HOME;
item.selected = 0;
item.file_count = FS::GetDirList(config.cwd, &item.entries);
if (item.file_count < 0)
return -1;
item.checked.resize(item.file_count);
// Main loop
bool done = false, set_focus = false, first = true;
while (!done) {
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplSDL2_NewFrame(window);
ImGui::NewFrame();
ImGui::SetNextWindowPos({0.0f, 0.0f}, ImGuiCond_Once);
ImGui::SetNextWindowSize({1280.0f, 720.0f}, ImGuiCond_Once);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
if (ImGui::Begin("NX-Shell", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) {
// Initially set default focus to next window (FS::DirList)
if (!set_focus) {
ImGui::SetNextWindowFocus();
set_focus = true;
}
// Display current working directory
ImGui::TextColored(ImVec4(1.00f, 1.00f, 1.00f, 1.00f), config.cwd);
ImGui::BeginChild("##FS::DirList");
if (item.file_count != 0) {
for (s64 i = 0; i < item.file_count; i++) {
std::string filename = item.entries[i].name;
if ((item.checked.at(i)) && (!item.checked_cwd.compare(config.cwd)))
ImGui::Image((void *)(intptr_t)check_texture.id, ImVec2(check_texture.width, check_texture.height));
else
ImGui::Image((void *)(intptr_t)uncheck_texture.id, ImVec2(uncheck_texture.width, uncheck_texture.height));
ImGui::SameLine();
if (item.entries[i].type == FsDirEntryType_Dir)
ImGui::Image((void *)(intptr_t)folder_texture.id, ImVec2(folder_texture.width, folder_texture.height));
else {
FileType file_type = FS::GetFileType(filename);
if (file_type == FileTypeArchive)
ImGui::Image((void *)(intptr_t)archive_texture.id, ImVec2(archive_texture.width, archive_texture.height));
else if (file_type == FileTypeAudio)
ImGui::Image((void *)(intptr_t)audio_texture.id, ImVec2(audio_texture.width, audio_texture.height));
else if (file_type == FileTypeImage)
ImGui::Image((void *)(intptr_t)image_texture.id, ImVec2(image_texture.width, image_texture.height));
else
ImGui::Image((void *)(intptr_t)file_texture.id, ImVec2(file_texture.width, file_texture.height));
}
ImGui::SameLine();
ImGui::Selectable(filename.c_str());
if (first) {
ImGui::SetFocusID(ImGui::GetID((item.entries[0].name)), ImGui::GetCurrentWindow());
ImGuiContext& g = *ImGui::GetCurrentContext();
g.NavDisableHighlight = false;
first = false;
}
if (!ImGui::IsAnyItemFocused())
GImGui->NavId = GImGui->CurrentWindow->DC.LastItemId;
if (ImGui::IsItemHovered()) {
item.selected = i;
item.selected_filename = item.entries[item.selected].name;
}
}
}
else
ImGui::Text("No file entries");
ImGui::EndChild();
switch (item.state) {
case MENU_STATE_OPTIONS:
GUI::RenderOptionsMenu(&item);
break;
case MENU_STATE_PROPERTIES:
GUI::RenderPropertiesMenu(&item);
break;
case MENU_STATE_DELETE:
GUI::RenderDeleteMenu(&item);
break;
case MENU_STATE_SETTINGS:
GUI::RenderSettingsMenu(&item);
break;
case MENU_STATE_IMAGEVIEWER:
GUI::RenderImageViewer(&item, &texture);
break;
default:
break;
}
}
SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_JOYBUTTONDOWN) {
if (event.jbutton.which == 0) {
if (event.jbutton.button == 0) {
if (item.state == MENU_STATE_HOME) {
if (item.entries[item.selected].type == FsDirEntryType_Dir) {
if (item.file_count != 0) {
s64 value = FS::ChangeDirNext(item.entries[item.selected].name, &item.entries, item.file_count);
if (value >= 0) {
item.file_count = value;
item.checked.resize(item.file_count);
GImGui->NavId = 0;
}
}
}
else {
FileType file_type = FS::GetFileType(item.selected_filename);
if (file_type == FileTypeImage) {
std::string path = std::string();
int length = FS::ConstructPath(&item.entries[item.selected], path.data(), nullptr);
printf("FileTypeImage: %s %d\n", path.c_str(), length);
bool image_ret = Textures::LoadImageFile(path, &texture);
IM_ASSERT(image_ret);
item.state = MENU_STATE_IMAGEVIEWER;
}
}
}
}
else if (event.jbutton.button == 1) {
if (item.state == MENU_STATE_HOME) {
s64 value = FS::ChangeDirPrev(&item.entries, item.file_count);
if (value >= 0) {
item.file_count = value;
item.checked.resize(item.file_count);
GImGui->NavId = 0;
}
}
else if ((item.state == MENU_STATE_PROPERTIES) || (item.state == MENU_STATE_DELETE))
item.state = MENU_STATE_OPTIONS;
else if (item.state == MENU_STATE_IMAGEVIEWER) {
Textures::Free(&texture);
item.state = MENU_STATE_HOME;
}
else
item.state = MENU_STATE_HOME;
}
else if (event.jbutton.button == 2)
item.state = MENU_STATE_OPTIONS;
else if (event.jbutton.button == 3) {
if (item.state == MENU_STATE_HOME) {
item.checked_cwd = config.cwd;
item.checked.at(item.selected) = !item.checked.at(item.selected);
item.checked_count = std::count(item.checked.begin(), item.checked.end(), true);
}
}
else if (event.jbutton.button == 11)
item.state = MENU_STATE_SETTINGS;
else if (event.jbutton.button == 10)
done = true;
}
}
if (event.type == SDL_QUIT)
done = true;
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
done = true;
}
ImGui::End();
ImGui::PopStyleVar();
// Rendering
ImGui::Render();
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
glClearColor(0.00f, 0.00f, 0.00f, 1.00f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
SDL_GL_SwapWindow(window);
}
return 0;
}
}

43
source/keyboard.cpp Normal file
View File

@ -0,0 +1,43 @@
#include <cstring>
#include <switch.h>
#include "keyboard.h"
namespace Keyboard {
// Empty strings are invalid.
SwkbdTextCheckResult ValidateText(char *string, size_t size) {
if (std::strcmp(string, "") == 0) {
std::strncpy(string, "The name cannot be empty.", size);
return SwkbdTextCheckResult_Bad;
}
return SwkbdTextCheckResult_OK;
}
std::string GetText(const std::string &guide_text, const std::string &initial_text) {
Result ret = 0;
SwkbdConfig swkbd;
static char input_string[256];
if (R_FAILED(ret = swkbdCreate(&swkbd, 0))) {
swkbdClose(&swkbd);
return std::string();
}
swkbdConfigMakePresetDefault(&swkbd);
if (!guide_text.empty())
swkbdConfigSetGuideText(&swkbd, guide_text.c_str());
if (!initial_text.empty())
swkbdConfigSetInitialText(&swkbd, initial_text.c_str());
swkbdConfigSetTextCheckCallback(&swkbd, Keyboard::ValidateText);
if (R_FAILED(ret = swkbdShow(&swkbd, input_string, sizeof(input_string)))) {
swkbdClose(&swkbd);
return std::string();
}
swkbdClose(&swkbd);
return input_string;
}
}

192
source/main.cpp Normal file
View File

@ -0,0 +1,192 @@
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
#ifdef DEBUG
#include <sys/socket.h>
#endif
#include <switch.h>
#include "config.h"
#include "fs.h"
#include "gui.h"
#include "imgui.h"
#include "imgui_impl_sdl.h"
#include "imgui_impl_opengl3.h"
#include "textures.h"
SDL_Window *window = nullptr;
SDL_GLContext gl_context;
namespace Services {
void SetDefaultTheme(void) {
ImGui::GetStyle().FrameRounding = 4.0f;
ImGui::GetStyle().GrabRounding = 4.0f;
ImVec4 *colors = ImGui::GetStyle().Colors;
colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.36f, 0.42f, 0.47f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_ChildBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
colors[ImGuiCol_Border] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.12f, 0.20f, 0.28f, 1.00f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.09f, 0.12f, 0.14f, 1.00f);
colors[ImGuiCol_TitleBg] = ImVec4(0.09f, 0.12f, 0.14f, 0.65f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.39f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.18f, 0.22f, 0.25f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.09f, 0.21f, 0.31f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.37f, 0.61f, 1.00f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.00f, 0.50f, 0.50f, 1.0f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.00f, 0.50f, 0.50f, 1.0f);
colors[ImGuiCol_Header] = ImVec4(0.20f, 0.25f, 0.29f, 0.55f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.00f, 0.50f, 0.50f, 1.0f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.00f, 0.50f, 0.50f, 1.0f);
colors[ImGuiCol_Separator] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.78f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.10f, 0.40f, 0.75f, 1.00f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_Tab] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_TabHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
colors[ImGuiCol_TabActive] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.00f, 0.50f, 0.50f, 1.0f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
}
int InitImGui(void) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) {
SDL_Log("Error: %s\n", SDL_GetError());
return -1;
}
for (int i = 0; i < 2; i++) {
if (!SDL_JoystickOpen(i)) {
SDL_Log("SDL_JoystickOpen: %s\n", SDL_GetError());
SDL_Quit();
return -1;
}
}
// GL 3.0 + GLSL 130
const char *glsl_version = "#version 130";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
// Create window with graphics context
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
window = SDL_CreateWindow("NX-Shell", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);
gl_context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, gl_context);
SDL_GL_SetSwapInterval(1); // Enable vsync
// Initialize OpenGL loader
bool err = gladLoadGL() == 0;
if (err) {
SDL_Log("Failed to initialize OpenGL loader!\n");
return -1;
}
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen;
// Setup Dear ImGui style
ImGui::StyleColorsDark();
// Setup Platform/Renderer bindings
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
ImGui_ImplOpenGL3_Init(glsl_version);
ImFont *font = io.Fonts->AddFontFromFileTTF("romfs:/NotoSans-Regular.ttf", 35.0f);
IM_ASSERT(font != nullptr);
Services::SetDefaultTheme();
Textures::Init();
return 0;
}
void ExitImGui(void) {
Textures::Exit();
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_GL_DeleteContext(gl_context);
SDL_DestroyWindow(window);
SDL_Quit();
}
int Init(void) {
Result ret = 0;
if (R_FAILED(ret = romfsInit())) {
printf("romfsInit() failed: 0x%x\n", ret);
return ret;
}
// FS
devices[0] = *fsdevGetDeviceFileSystem("sdmc");
fs = &devices[0];
#ifdef DEBUG
socketInitializeDefault();
nxlinkStdio();
#endif
Config::Load();
return 0;
}
void Exit(void) {
#ifdef DEBUG
socketExit();
#endif
romfsExit();
}
}
int main(int argc, char *argv[]) {
Result ret = 0;
if (R_FAILED(ret = Services::Init()))
return ret;
if (R_FAILED(ret = Services::InitImGui()))
return ret;
if (R_FAILED(ret = GUI::RenderMainMenu(window)))
return ret;
Services::ExitImGui();
Services::Exit();
return 0;
}

90
source/textures.cpp Normal file
View File

@ -0,0 +1,90 @@
#include <cstring>
// STB
#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_HDR
#define STBI_NO_PIC
#define STBI_ONLY_BMP
#define STBI_ONLY_GIF
#define STBI_ONLY_JPEG
#define STBI_ONLY_PNG
#define STBI_ONLY_PNM
#define STBI_ONLY_PSD
#define STBI_ONLY_TGA
#include <stb_image.h>
#include <switch.h>
#include "fs.h"
#include "imgui.h"
#include "textures.h"
Tex folder_texture, file_texture, archive_texture, audio_texture, image_texture, check_texture, uncheck_texture;
namespace Textures {
static bool LoadImage(unsigned char *data, int *width, int *height, GLint format, Tex *texture, void (*free_func)(void *)) {
// Create a OpenGL texture identifier
glGenTextures(1, &texture->id);
glBindTexture(GL_TEXTURE_2D, texture->id);
// Setup filtering parameters for display
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Upload pixels into texture
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexImage2D(GL_TEXTURE_2D, 0, format, *width, *height, 0, format, GL_UNSIGNED_BYTE, data);
if (*free_func)
free_func(data);
texture->width = *width;
texture->height = *height;
return true;
}
bool LoadImageFile(const std::string &path, Tex *texture) {
int width = 0, height = 0;
unsigned char *data = stbi_load(path.c_str(), &width, &height, NULL, 4);
if (!data)
return false;
bool ret = Textures::LoadImage(data, &width, &height, GL_RGBA, texture, stbi_image_free);
return ret;
}
void Init(void) {
bool image_ret = Textures::LoadImageFile("romfs:/archive.png", &archive_texture);
IM_ASSERT(image_ret);
image_ret = Textures::LoadImageFile("romfs:/audio.png", &audio_texture);
IM_ASSERT(image_ret);
image_ret = Textures::LoadImageFile("romfs:/file.png", &file_texture);
IM_ASSERT(image_ret);
image_ret = Textures::LoadImageFile("romfs:/folder.png", &folder_texture);
IM_ASSERT(image_ret);
image_ret = Textures::LoadImageFile("romfs:/image.png", &image_texture);
IM_ASSERT(image_ret);
image_ret = Textures::LoadImageFile("romfs:/check.png", &check_texture);
IM_ASSERT(image_ret);
image_ret = Textures::LoadImageFile("romfs:/uncheck.png", &uncheck_texture);
IM_ASSERT(image_ret);
}
void Free(Tex *texture) {
glDeleteTextures(1, &texture->id);
}
void Exit(void) {
glDeleteTextures(1, &image_texture.id);
glDeleteTextures(1, &folder_texture.id);
glDeleteTextures(1, &file_texture.id);
glDeleteTextures(1, &audio_texture.id);
glDeleteTextures(1, &archive_texture.id);
}
}

16
source/utils.cpp Normal file
View File

@ -0,0 +1,16 @@
#include <stdio.h>
#include "utils.h"
namespace Utils {
void GetSizeString(char *string, double size) {
int i = 0;
const char *units[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
while (size >= 1024.0f) {
size /= 1024.0f;
i++;
}
sprintf(string, "%.*f %s", (i == 0) ? 0 : 2, size, units[i]);
}
}