diff --git a/.onedev-buildspec.yml b/.onedev-buildspec.yml index 64e3c85..3d2b2c0 100644 --- a/.onedev-buildspec.yml +++ b/.onedev-buildspec.yml @@ -16,7 +16,7 @@ jobs: commands: | mkdir build cd build - cmake -S .. -B . -DTESSESFRAMEWORK_FETCHCONTENT=OFF + cmake -S .. -B . -DTESSESFRAMEWORK_FETCHCONTENT=OFF -DTESSESFRAMEWORK_ENABLE_SDL2=ON make -j12 make install DESTDIR=out useTTY: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 50732d4..f0060c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,10 +24,15 @@ src/Streams/Stream.cpp src/Streams/BufferedStream.cpp src/Streams/ByteReader.cpp src/Streams/ByteWriter.cpp +src/Streams/PtyStream.cpp src/TextStreams/StreamReader.cpp src/TextStreams/StreamWriter.cpp src/TextStreams/TextReader.cpp src/TextStreams/TextWriter.cpp +src/TextStreams/StdIOReader.cpp +src/TextStreams/StdIOWriter.cpp +src/TextStreams/StringReader.cpp +src/TextStreams/StringWriter.cpp src/Threading/Thread.cpp src/Threading/Mutex.cpp src/Filesystem/VFS.cpp @@ -42,6 +47,16 @@ src/TF_Init.cpp src/wrapper.cpp src/HiddenField.cpp src/SDL2/FontCache.cpp +src/SDL2/Stream.cpp +src/SDL2/GUI.cpp +src/SDL2/GUIWindow.cpp +src/SDL2/View.cpp +src/SDL2/Views/ButtonView.cpp +src/SDL2/Views/AbsoluteView.cpp +src/SDL2/Views/LabelView.cpp +src/SDL2/Views/TextListView.cpp +src/SDL2/Views/ProgressView.cpp +src/SDL2/Views/CheckView.cpp ) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) @@ -69,21 +84,31 @@ endif() file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include) +include(cmake/bin2h.cmake) if(TESSESFRAMEWORK_ENABLE_SDL2) find_package(SDL2 REQUIRED) find_package(SDL2_ttf REQUIRED) find_package(SDL2_image REQUIRED) -endif() +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/TanoheSans-Regular.h" "#pragma once\n") +bin2h(SOURCE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/font/NonMono/TanoheSans-Regular.ttf" HEADER_FILE "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/TanoheSans-Regular.h" VARIABLE_NAME TanoheSansRegular APPEND NULL_TERMINATE) +file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/TanoheSans-Regular.h" "\n") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/aardvark-fixed-regular.h" "#pragma once\n") +bin2h(SOURCE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/font/Mono/aardvark-fixed-regular.ttf" HEADER_FILE "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/aardvark-fixed-regular.h" VARIABLE_NAME AardvarkFixedRegular APPEND NULL_TERMINATE) +file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/aardvark-fixed-regular.h" "\n") + + +endif() if(TESSESFRAMEWORK_ENABLE_MBED) if(TESSESFRAMEWORK_EMBED_CERT_BUNDLE) -include(cmake/bin2h.cmake) -file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/CertificateChain.h "#pragma once\n") + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/CertificateChain.h" "#pragma once\n") #target_compile_definitions(TessesFramework PUBLIC TESSESFRAMEWORK_EMBED_CERT_BUNDLE) -bin2h(SOURCE_FILE ${TESSESFRAMEWORK_CERT_BUNDLE_FILE} HEADER_FILE ${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/CertificateChain.h VARIABLE_NAME CertificateChain APPEND NULL_TERMINATE) -file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/CertificateChain.h "\n") +bin2h(SOURCE_FILE "${TESSESFRAMEWORK_CERT_BUNDLE_FILE}" HEADER_FILE "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/CertificateChain.h" VARIABLE_NAME CertificateChain APPEND NULL_TERMINATE) +file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/include/TessesFramework/CertificateChain.h" "\n") else() target_compile_definitions(TessesFramework PUBLIC TESSESFRAMEWORK_CERT_BUNDLE_FILE=${TESSESFRAMEWORK_CERT_BUNDLE_FILE}) @@ -164,7 +189,7 @@ target_link_libraries(${TessesFramework_TARGET} PUBLIC ws2_32) endif() -if(NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube")) +if(NOT("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" OR PLATFORM_PS2 OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube")) if(TESSESFRAMEWORK_ENABLE_THREADING) set(THREADS_PREFER_PTHREAD_FLAG ON) @@ -176,6 +201,10 @@ if(TESSESFRAMEWORK_ENABLE_SETDATE) target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_ENABLE_SETDATE) endif() target_compile_definitions(${TessesFramework_TARGET} PUBLIC _FILE_OFFSET_BITS=64) + +if(PLATFORM_PS2) +target_link_libraries(${TessesFramework_TARGET} PUBLIC -ldebug) +endif() endfunction() include(GNUInstallDirs) @@ -281,6 +310,14 @@ if(TESSESFRAMEWORK_ENABLE_EXAMPLES) endif() if(TESSESFRAMEWORK_ENABLE_APPS) +if(TESSESFRAMEWORK_ENABLE_SDL2) +add_executable(tguilayouttester apps/guilayouttester.cpp) +target_link_libraries(tguilayouttester PUBLIC tessesframework) +install(TARGETS tguilayouttester DESTINATION bin) +endif() +add_executable(tanonydrop apps/tanonydrop.cpp) +target_link_libraries(tanonydrop PUBLIC tessesframework) +install(TARGETS tanonydrop DESTINATION bin) add_executable(tfetch apps/tfetch.cpp) target_link_libraries(tfetch PUBLIC tessesframework) install(TARGETS tfetch DESTINATION bin) diff --git a/README.md b/README.md index ac8c7bc..3b53e04 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,8 @@ sudo make install - [mbedtls](https://github.com/Mbed-TLS/mbedtls) ## Code used from other peoples projects -- [Multipart parser (rewriten in C++ was C# in dejuric/simplehttp)](https://github.com/dajuric/simple-http) \ No newline at end of file +- [Multipart parser (rewriten in C++ was C# in dejuric/simplehttp)](https://github.com/dajuric/simple-http) + +## Resources used from others +- [ca-certificate.crt (from ubuntu)](ca-certificate.crt) +- [TanoheSans-Regular.ttf (license OFL)](font/License-OFL.txt) \ No newline at end of file diff --git a/apps/guilayouttester.cpp b/apps/guilayouttester.cpp new file mode 100644 index 0000000..64bebff --- /dev/null +++ b/apps/guilayouttester.cpp @@ -0,0 +1,75 @@ + +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "TessesFramework/SDL2/GUI.hpp" +#include "TessesFramework/SDL2/Views/ButtonView.hpp" +#include "TessesFramework/SDL2/Views/AbsoluteView.hpp" +#include "TessesFramework/SDL2/Views/LabelView.hpp" +#include "TessesFramework/SDL2/Views/TextListView.hpp" +#include "TessesFramework/Filesystem/LocalFS.hpp" +#include "TessesFramework/SDL2/Views/ProgressView.hpp" +#include "TessesFramework/SDL2/Views/CheckView.hpp" +#include "TessesFramework/TextStreams/StreamReader.hpp" +#include "TessesFramework/Streams/FileStream.hpp" +#include +using namespace Tesses::Framework; +using namespace Tesses::Framework::SDL2; + +using namespace Tesses::Framework::Streams; +using namespace Tesses::Framework::TextStreams; +using namespace Tesses::Framework::Serialization::Json; + +void Load(GUIWindow& window, std::string jsonFile) +{ + FILE* f = fopen(jsonFile.c_str(),"rb"); + if(f != NULL) + { + FileStream strm(f,true,"rb"); + StreamReader r(&strm,false); + auto text = r.ReadToEnd(); + try { + auto obj = Json::Decode(text); + window.SetView(obj); + } catch(...) + { + + } + } +} +#endif + + + + + +int main(int argc,char** argv) +{ + #if defined(TESSESFRAMEWORK_ENABLE_SDL2) + if(argc < 2) + { + std::cout << "USAGE: " << argv[0] << " layout.json" << std::endl; + return 0; + } + std::string jsonFile = argv[1]; + TF_Init(); + + + GUIPalette pal0(true,(SDL_Color){.r=255,.g=0,.b=0,.a=255},20); + GUIWindow window("Press ALT+R to Reload",640,480,SDL_WINDOW_RESIZABLE,pal0); + + window.SDLEvent += std::make_shared>([&window,jsonFile](View* sender, GUISDLEventEventArgs& e)->void{ + if(e.event.type == SDL_KEYUP && (uint64_t)(e.event.key.keysym.mod & SDL_Keymod::KMOD_ALT) != 0 && e.event.key.keysym.sym == SDLK_r) + { + Load(window,jsonFile); + } + }); + + Load(window,jsonFile); + + + + TF_RunEventLoop(); + + TF_Quit(); + #endif + return 0; +} \ No newline at end of file diff --git a/apps/tanonydrop.cpp b/apps/tanonydrop.cpp new file mode 100644 index 0000000..b7e7409 --- /dev/null +++ b/apps/tanonydrop.cpp @@ -0,0 +1,94 @@ +#include "TessesFramework/TessesFramework.hpp" +#include +using namespace Tesses::Framework; +using namespace Tesses::Framework::Streams; +using namespace Tesses::Framework::Http; +using namespace Tesses::Framework::Filesystem; + +VFS* vfs; + +int main(int argc, char** argv) +{ + + + TF_InitWithConsole(); + vfs = new SubdirFilesystem(&LocalFS,Tesses::Framework::Filesystem::VFSPath::GetAbsoluteCurrentDirectory(),false); + + CallbackServer cb([](ServerContext& ctx)->bool{ + if(ctx.path == "/") + { + ctx.WithMimeType("text/html") + .SendText( + "" + "" + "AnonyDump" + "" + "

AnonyDump

" + "Files" + "
" + "" + "" + "
" + "" + "" + ); + + return true; + } + else if(ctx.path == "/upload") + { + if(ctx.NeedToParseFormData()) + { + ctx.ParseFormData([](std::string mime, std::string filename,std::string name)->Stream* { + if(name != "file") return nullptr; + VFSPath path("/"+filename); + auto strm = vfs->OpenFile(path,"wb"); + + return strm; + }); + ctx.WithMimeType("text/html") + .SendText( + "" + "" + "AnonyDump - Uploaded successfully" + "" + "

Uploaded successfully

" + "Back" + + "" + "" + "" + ); + return true; + } + else { + ctx.statusCode= Tesses::Framework::Http::BadRequest; + ctx.WithMimeType("text/html") + .SendText( + "" + "" + "AnonyDump - Error: Must contain multipart and POST" + "" + "

Error: Must contain multipart and POST

" + "Back" + + "" + "" + "" + ); + } + } + return false; + }); + + Tesses::Framework::Http::MountableServer mountable(cb); + mountable.Mount("/files/",new FileServer(vfs,true,true,false),true); + + HttpServer srv(4985,mountable); + srv.StartAccepting(); + + TF_RunEventLoop(); + + TF_Quit(); + return 0; +} \ No newline at end of file diff --git a/examples/sdl2.cpp b/examples/sdl2.cpp index 737972d..c2a8ca2 100644 --- a/examples/sdl2.cpp +++ b/examples/sdl2.cpp @@ -1,28 +1,101 @@ #if defined(TESSESFRAMEWORK_ENABLE_SDL2) -#include +#include "TessesFramework/SDL2/GUI.hpp" +#include "TessesFramework/SDL2/Views/ButtonView.hpp" +#include "TessesFramework/SDL2/Views/AbsoluteView.hpp" +#include "TessesFramework/SDL2/Views/LabelView.hpp" +#include "TessesFramework/SDL2/Views/TextListView.hpp" +#include "TessesFramework/Filesystem/LocalFS.hpp" +#include "TessesFramework/SDL2/Views/ProgressView.hpp" +#include "TessesFramework/SDL2/Views/CheckView.hpp" +#include +using namespace Tesses::Framework; +using namespace Tesses::Framework::SDL2; #endif + + + int main(int argc,char** argv) { #if defined(TESSESFRAMEWORK_ENABLE_SDL2) - SDL_Init(SDL_INIT_EVERYTHING); + TF_Init(); - SDL_Window* window = SDL_CreateWindow("My SDL Window",0,0,320,240,0); + std::vector> colors={ + std::pair({.r=255,.g=0,.b=128,.a=255},"Magenta"), + std::pair({.r=255,.g=0,.b=0,.a=255}, "Red"), + std::pair({.r=0,.g=255,.b=0,.a=255}, "Green"), + std::pair({.r=0,.g=0,.b=255,.a=255}, "Blue"), + std::pair({.r=255,.g=255,.b=0,.a=255}, "Yellow"), + std::pair({.r=143, .g=188, .b=143,.a=255},"Dark sea Green"), + std::pair({.r=63, .g=253, .b=170,.a=255},"Aqua") + }; + + bool darkMode=true; + size_t color_index=0; - SDL_Renderer* renderer=SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED); - while(true) + GUIPalette pal0(darkMode,colors[color_index % colors.size()].first,20); + GUIWindow window("My Window Title",640,480,SDL_WINDOW_RESIZABLE,pal0); + + + Views::LabelView lbl("A random label\nThat spans lines."); + Views::ButtonView btn("Dark Mode"); + Views::ButtonView btn2(colors[0].second); + Views::ProgressView progress(42.42); + + Views::CheckView cv(false,"Checkbox"); + Views::CheckView cv2(false,"Another Checkbox"); + + + + Views::TextListView list; + for(auto item : Tesses::Framework::Filesystem::LocalFS.EnumeratePaths((std::string)"/usr/bin")) { - SDL_Event event; - if(SDL_PollEvent(&event) && event.type == SDL_QUIT) break; - SDL_SetRenderDrawColor(renderer,255,0,0,255); - SDL_RenderClear(renderer); - SDL_RenderPresent(renderer); + list.items.push_back(item.GetFileName()); } - SDL_DestroyRenderer(renderer); + - SDL_DestroyWindow(window); + + //Views::LabelView labelView("My Sweet Label"); + + Views::AbsoluteView abs; + abs.Add({.x=0,.y=0,.w=400,.h=64},&lbl,false); + abs.Add({.x=32,.y=64,.w=200,.h=50},&btn,false); + abs.Add({.x=248,.y=64,.w=200,.h=50},&cv,false); + + abs.Add({.x=32,.y=134,.w=200,.h=50},&btn2,false); + + abs.Add({.x=248,.y=134,.w=200,.h=50},&cv2,false); + + + abs.Add({.x=32,.y=190,.w=200,.h=30},&progress,false); + + abs.Add({.x=32,.y=230,.w=320,.h=240},&list,false); + + + window.SetView(&abs,false); + + btn.Click += std::make_shared>([&window,&darkMode,&color_index,&colors,&btn](View* sender, GUIEventArgs& e)->void{ + darkMode = !darkMode; + btn.SetText(darkMode ? "Light Mode" : "Dark Mode"); + GUIPalette palette(darkMode,colors[color_index % colors.size()].first,20); + window.SetPalette(palette); + }); + + btn2.Click += std::make_shared>([&window,&darkMode,&color_index,&colors,&btn2](View* sender, GUIEventArgs& e)->void{ + color_index++; + btn2.SetText(colors[color_index % colors.size()].second); + + GUIPalette palette(darkMode,colors[color_index % colors.size()].first,20); + window.SetPalette(palette); + }); + + + + TF_RunEventLoop(); + + TF_Quit(); #endif return 0; } \ No newline at end of file diff --git a/font/Mono/README.md b/font/Mono/README.md new file mode 100644 index 0000000..464e033 --- /dev/null +++ b/font/Mono/README.md @@ -0,0 +1,8 @@ +Font Attribution +================ + +- [Licensed under OFL (SIL Open Font License)](http://scripts.sil.org/OFL) +- [Get the font](https://fontlibrary.org/en/font/aardvark-sans) +- [Made by Belleve Invis](https://fontlibrary.org/en/font/aardvark-sans) + +I didn't see a LICENSE file in the font ZIP diff --git a/font/Mono/aardvark-fixed-regular.ttf b/font/Mono/aardvark-fixed-regular.ttf new file mode 100644 index 0000000..14781d7 Binary files /dev/null and b/font/Mono/aardvark-fixed-regular.ttf differ diff --git a/font/NonMono/License-OFL.txt b/font/NonMono/License-OFL.txt new file mode 100644 index 0000000..d281db8 --- /dev/null +++ b/font/NonMono/License-OFL.txt @@ -0,0 +1,95 @@ +Copyright (c) 2014, Cooper Hewitt Smithsonian Design Museum +(cooperhewitt.org), with Reserved Font Name COOPER HEWITT. +Copyright (c) 2019-2020 by Cristiano Sobral (cssobral2013@gmail.com). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/font/NonMono/TanoheSans-Regular.ttf b/font/NonMono/TanoheSans-Regular.ttf new file mode 100644 index 0000000..dba51e1 Binary files /dev/null and b/font/NonMono/TanoheSans-Regular.ttf differ diff --git a/include/TessesFramework/Common.hpp b/include/TessesFramework/Common.hpp index e19a84a..1c75c38 100644 --- a/include/TessesFramework/Common.hpp +++ b/include/TessesFramework/Common.hpp @@ -87,6 +87,17 @@ namespace Tesses::Framework } mtx.Unlock(); } + void Remove(std::function>)> cb) + { + for(auto index = this->items.begin(); index != this->items.end(); index++) + { + if(cb(*index)) + { + this->items.erase(index); + index--; + } + } + } }; extern EventList OnItteraton; diff --git a/include/TessesFramework/Http/HttpUtils.hpp b/include/TessesFramework/Http/HttpUtils.hpp index ba64ec7..5ac1be6 100644 --- a/include/TessesFramework/Http/HttpUtils.hpp +++ b/include/TessesFramework/Http/HttpUtils.hpp @@ -147,6 +147,7 @@ struct CaseInsensitiveLess { static std::string UrlPathDecode(std::string v); static std::string UrlPathEncode(std::string v, bool ignoreSpace=false); static std::string HtmlEncode(std::string v); + static std::string HtmlDecodeOnlyEntityNumber(std::string v); static std::vector SplitString(std::string text, std::string delimiter,std::size_t maxCnt = std::string::npos); static std::string Replace(std::string str, std::string find, std::string replace); static std::string StatusCodeString(StatusCode code); diff --git a/include/TessesFramework/SDL2/FontCache.hpp b/include/TessesFramework/SDL2/FontCache.hpp index 52e7d53..c7cb7e6 100644 --- a/include/TessesFramework/SDL2/FontCache.hpp +++ b/include/TessesFramework/SDL2/FontCache.hpp @@ -1,4 +1,5 @@ #pragma once + #if defined(TESSESFRAMEWORK_ENABLE_SDL2) #include #include @@ -11,17 +12,20 @@ class FontCache { std::array font_chrs; int mw,mh,ps; - void Load(SDL_Renderer* renderer,TTF_Font* font,const SDL_Color& color); + void Load(SDL_Renderer* renderer,TTF_Font* font); public: - FontCache(SDL_Renderer* renderer,TTF_Font* font,const SDL_Color& color); - FontCache(SDL_Renderer* renderer,std::string font,int sz,const SDL_Color& color); - FontCache(SDL_Renderer* renderer,const uint8_t* mem,size_t cnt,int sz,const SDL_Color& color); - FontCache(SDL_Renderer* renderer,const std::vector& v,int sz,const SDL_Color& color); - + FontCache(SDL_Renderer* renderer,TTF_Font* font); + FontCache(SDL_Renderer* renderer,std::string font,int sz); + FontCache(SDL_Renderer* renderer,const uint8_t* mem,size_t cnt,int sz); + FontCache(SDL_Renderer* renderer,const std::vector& v,int sz); + FontCache(SDL_Renderer* renderer,int sz); SDL_Texture* operator[](char c); + SDL_Texture* GetCharOfColor(char c, const SDL_Color& color); int MaxWidth(); int MaxHeight(); int PointSize(); + void CalculateSize(std::string text, int& x,int& y); + void Render(SDL_Renderer* renderer,int x,int y, std::string text, const SDL_Color& color); ~FontCache(); }; } diff --git a/include/TessesFramework/SDL2/GUI.hpp b/include/TessesFramework/SDL2/GUI.hpp new file mode 100644 index 0000000..dd1344e --- /dev/null +++ b/include/TessesFramework/SDL2/GUI.hpp @@ -0,0 +1,244 @@ +#pragma once +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include +#include "FontCache.hpp" +#include "../Filesystem/VFSFix.hpp" +#include "../Filesystem/VFS.hpp" +#include "../Common.hpp" +#include "../Serialization/Json.hpp" + +namespace Tesses::Framework::SDL2 +{ + class GUIPalette { + public: + GUIPalette(); + GUIPalette(bool isDarkMode, SDL_Color accent,int fontSize=24); + GUIPalette(SDL_Color accent, SDL_Color background, SDL_Color border_color, SDL_Color border_hover, SDL_Color border_active, SDL_Color border_hover_active, int fontSize=24); + SDL_Color accent; //color is used for font when not over accent background + SDL_Color background; + SDL_Color border_color; //color is used for font when over accent background + SDL_Color border_hover; + SDL_Color border_active; + SDL_Color border_hover_active; + int fontSize; + + SDL_Color& GetBorderColor(bool isHovering, bool isActive, bool isMouseDown); + }; + + class GUIEventArgs + { + public: + virtual std::string Type(); + virtual ~GUIEventArgs(); + }; + class View; + class GUIMouseButtonEventArgs : public GUIEventArgs + { + public: + Uint32 which; + int x; + int y; + Uint8 button; + std::string Type(); + }; + class GUIJsonViewNotFoundEventArgs : public GUIEventArgs + { + public: + std::string Type(); + View* destView; + Tesses::Framework::Serialization::Json::JObject jsonObject; + std::string typeString; + }; + class GUISDLEventEventArgs : public GUIEventArgs + { + public: + std::string Type(); + SDL_Event event; + }; + constexpr uint64_t VIEWFLAG_HOVER_STATE=(uint64_t)1 << 0; + constexpr uint64_t VIEWFLAG_MOUSEDOWN_STATE =(uint64_t)1<<1; + constexpr uint64_t VIEWFLAG_ISACTIVE=(uint64_t)1<<2; + constexpr uint64_t VIEWFLAG_TABSTOP=(uint64_t)1<<3; + constexpr uint64_t VIEWFLAG_INTERCEPT_TAB=(uint64_t)1<<4; + constexpr uint64_t VIEWFLAG_CHECKED=(uint64_t)1<<5; + class GUIPopup; + class GUIWindow; + class ContainerView; + class View { + protected: + std::string text; + std::string id; + uint64_t flags; + protected: + + View(); + View(std::string text); + virtual void OnDraw(SDL_Renderer* renderer, SDL_Rect& r); + virtual void OnDragDropFile(Tesses::Framework::Filesystem::VFSPath filePath,SDL_Rect myRect, SDL_Point dropLoc); + virtual void OnDragDropText(std::string text,SDL_Rect myRect, SDL_Point dropLoc); + virtual void OnEnter(GUIEventArgs& evt); + virtual void OnLeave(GUIEventArgs& evt); + virtual void OnMouseDown(GUIMouseButtonEventArgs& evt); + virtual void OnMouseUp(GUIMouseButtonEventArgs& evt); + virtual void OnClick(GUIEventArgs& evt); + virtual bool OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds); + virtual void OnSetParent(View* v); + + void CallOnDraw(View* view, SDL_Renderer* renderer, SDL_Rect& myRect) + { + view->OnDraw(renderer,myRect); + } + bool CallOnEvent(View* view,SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds) + { + return view->OnEvent(event,myBounds,visibleBounds); + } + + + ContainerView* parent; + + public: + bool GetViewFlag(uint64_t flag) + { + return (this->flags & flag) != 0; + } + void SetViewFlag(uint64_t flag, bool value) + { + if(value) + this->flags |= flag; + else + this->flags &= ~flag; + } + + EventList MouseUp; + EventList MouseDown; + EventList Click; + EventList Enter; + EventList Leave; + EventList SDLEvent; + virtual ~View(); + friend class GUIWindow; + virtual GUIWindow* GetWindow(); + virtual std::string GetText(); + virtual void SetText(std::string text); + virtual void SetId(std::string id); + virtual std::string GetId(); + virtual View* FindViewById(std::string id); + friend class ContainerView; + }; + class ContainerView : public View { + public: + virtual size_t ViewCount()=0; + virtual View* GetViewAt(size_t index)=0; + virtual View* FindViewById(std::string id); + protected: + ContainerView(); + ContainerView(std::string text); + + + + + void AssignChildParentToThis(View* view) + { + if(view != nullptr) + { + view->parent = this; + view->OnSetParent(this); + + } + } + }; + enum class TabNextResult { + KeepGoing, + TabNext, + Done + }; + class GUIPopup : public ContainerView { + View* child; + bool ownsChild; + protected: + void OnDraw(SDL_Renderer* renderer, SDL_Rect& myRect); + bool OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds); + public: + + size_t ViewCount(); + View* GetViewAt(size_t index); + GUIPopup(); + GUIPopup(SDL_Rect bounds); + GUIPopup(int x, int y,int w, int h); + SDL_Rect bounds; + + void SetView(View* view, bool owns=true); + ~GUIPopup(); + + friend class GUIWindow; + }; + class GUIWindow : public ContainerView + { + std::vector popups; + View* child; + bool ownsChild; + SDL_Window* window; + SDL_Renderer* renderer; + void Event(SDL_Event& event); + void Draw(); + void DeactivateAll(View* view); + void TabNext(View* view,TabNextResult& nr); + + protected: + void OnDraw(SDL_Renderer* renderer, SDL_Rect& myRect); + bool OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds); + + public: + EventList JsonViewNotFound; + size_t ViewCount(); + View* GetViewAt(size_t index); + FontCache* normal_font; + FontCache* monospaced_font; + GUIPalette palette; + GUIWindow(std::string title, int w, int h, Uint32 flags, const GUIPalette& palette); + void SetPalette(const GUIPalette& palette); + void SetView(View* view,bool owns=true); + + void ShowPopup(GUIPopup* popup); + void ShowPopup(GUIPopup& popup); + + void MakeActive(View* view); + void TabNext(); + GUIWindow* GetWindow(); + + ~GUIWindow(); + + friend class GUI; + friend class GUIPopup; + + void SetText(std::string text); + + void SetView(Tesses::Framework::Serialization::Json::JToken json); + + View* CreateViewFromJson(Tesses::Framework::Serialization::Json::JObject json); + }; + + + class GUI { + std::vector windows; + public: + void Update(); + friend class GUIWindow; + }; + extern GUI gui; + + class Clipper { + SDL_Rect theRect; + SDL_Renderer* renderer; + bool isClipped; + public: + Clipper(SDL_Renderer* renderer, SDL_Rect& myRect); + bool Clip(SDL_Rect rect); + ~Clipper(); + static void ClipRect(SDL_Rect& child, SDL_Rect& parent); + }; + + + +} +#endif \ No newline at end of file diff --git a/include/TessesFramework/SDL2/ParseColor.hpp b/include/TessesFramework/SDL2/ParseColor.hpp new file mode 100644 index 0000000..326a534 --- /dev/null +++ b/include/TessesFramework/SDL2/ParseColor.hpp @@ -0,0 +1,12 @@ +#pragma once + +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include +#include + +namespace Tesses::Framework::SDL2 +{ + bool TryParseSDLColor(std::string str, SDL_Color& col); +} + +#endif \ No newline at end of file diff --git a/include/TessesFramework/SDL2/Stream.hpp b/include/TessesFramework/SDL2/Stream.hpp new file mode 100644 index 0000000..903ac86 --- /dev/null +++ b/include/TessesFramework/SDL2/Stream.hpp @@ -0,0 +1,9 @@ +#pragma once +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "../Streams/Stream.hpp" +#include +namespace Tesses::Framework::SDL2 +{ + SDL_RWops* RwopsFromStream(Tesses::Framework::Streams::Stream* strm, bool owns=true); +} +#endif \ No newline at end of file diff --git a/include/TessesFramework/SDL2/Views/AbsoluteView.hpp b/include/TessesFramework/SDL2/Views/AbsoluteView.hpp new file mode 100644 index 0000000..128ddb1 --- /dev/null +++ b/include/TessesFramework/SDL2/Views/AbsoluteView.hpp @@ -0,0 +1,33 @@ +#pragma once +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "../GUI.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + class AbsoluteView : public ContainerView { + std::vector,SDL_Rect>> views; + protected: + void OnDraw(SDL_Renderer* renderer, SDL_Rect& myRect); + bool OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds); + public: + size_t ViewCount(); + View* GetViewAt(size_t index); + AbsoluteView(); + void Add(SDL_Rect rect, View* view, bool owns=true); + void Set(View* view, SDL_Rect rect); + void Remove(View* view); + ~AbsoluteView(); + + }; + + /* class AbsoluteContainer : public View { + std::vector,SDL_Rect>> views; + public: + void Add(SDL_Rect rect, View* view, bool owns=true); + void Set(View* view, SDL_Rect rect); + void Remove(View* view); + + ~AbsoluteContainer(); + };*/ +} +#endif \ No newline at end of file diff --git a/include/TessesFramework/SDL2/Views/ButtonView.hpp b/include/TessesFramework/SDL2/Views/ButtonView.hpp new file mode 100644 index 0000000..a18914c --- /dev/null +++ b/include/TessesFramework/SDL2/Views/ButtonView.hpp @@ -0,0 +1,18 @@ +#pragma once +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "../GUI.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + class ButtonView : public View { + protected: + virtual void OnDraw(SDL_Renderer* renderer, SDL_Rect& r); + virtual bool OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds); + + public: + ButtonView(); + ButtonView(std::string text); + + }; +} +#endif \ No newline at end of file diff --git a/include/TessesFramework/SDL2/Views/CheckView.hpp b/include/TessesFramework/SDL2/Views/CheckView.hpp new file mode 100644 index 0000000..abbd872 --- /dev/null +++ b/include/TessesFramework/SDL2/Views/CheckView.hpp @@ -0,0 +1,25 @@ +#pragma once +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "../GUI.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + class CheckView : public View { + protected: + virtual void OnCheckChanged(GUIEventArgs& event); + virtual void OnDraw(SDL_Renderer* renderer, SDL_Rect& r); + virtual bool OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds); + + public: + CheckView(bool checked, std::string label); + CheckView(); + + virtual bool GetChecked(); + virtual void SetChecked(bool value); + + + + EventList CheckChanged; + }; +} +#endif \ No newline at end of file diff --git a/include/TessesFramework/SDL2/Views/LabelView.hpp b/include/TessesFramework/SDL2/Views/LabelView.hpp new file mode 100644 index 0000000..112baa9 --- /dev/null +++ b/include/TessesFramework/SDL2/Views/LabelView.hpp @@ -0,0 +1,15 @@ +#pragma once +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "../GUI.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + class LabelView : public View { + protected: + virtual void OnDraw(SDL_Renderer* renderer, SDL_Rect& r); + public: + LabelView(); + LabelView(std::string text); + }; +} +#endif \ No newline at end of file diff --git a/include/TessesFramework/SDL2/Views/ProgressView.hpp b/include/TessesFramework/SDL2/Views/ProgressView.hpp new file mode 100644 index 0000000..f371eda --- /dev/null +++ b/include/TessesFramework/SDL2/Views/ProgressView.hpp @@ -0,0 +1,17 @@ +#pragma once +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "../GUI.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + class ProgressView : public View + { + protected: + virtual void OnDraw(SDL_Renderer* renderer, SDL_Rect& r); + public: + ProgressView(); + ProgressView(double value); + double value; + }; +}; +#endif \ No newline at end of file diff --git a/include/TessesFramework/SDL2/Views/TextListView.hpp b/include/TessesFramework/SDL2/Views/TextListView.hpp new file mode 100644 index 0000000..9b62fae --- /dev/null +++ b/include/TessesFramework/SDL2/Views/TextListView.hpp @@ -0,0 +1,20 @@ +#pragma once +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "../GUI.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + class TextListView : public View { + + protected: + virtual void OnDraw(SDL_Renderer* renderer, SDL_Rect& r); + virtual bool OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds); + public: + TextListView(); + size_t firstIndex; + int selected; + std::vector items; + + }; +} +#endif \ No newline at end of file diff --git a/include/TessesFramework/Serialization/Json.hpp b/include/TessesFramework/Serialization/Json.hpp index 5a95c46..dedcde6 100644 --- a/include/TessesFramework/Serialization/Json.hpp +++ b/include/TessesFramework/Serialization/Json.hpp @@ -17,19 +17,7 @@ namespace Tesses::Framework::Serialization::Json class JOItem; - class JObject { - std::map map; - public: - JObject(); - JObject(std::initializer_list items); - void SetValue(std::string key, JToken item); - JToken GetValue(std::string key); - void Remove(std::string key); - std::map& GetMap(); - std::map::iterator begin(); - std::map::iterator end(); - }; - class JArray + class JArray { std::vector items; public: @@ -46,15 +34,36 @@ namespace Tesses::Framework::Serialization::Json std::vector::iterator begin(); std::vector::iterator end(); }; - class JOItem { + + + + + + class JObject { + std::map map; public: - JOItem(); - JOItem(std::string key, JToken value); - - std::string key; - JToken value; + JObject(); + JObject(std::initializer_list items); + void SetValue(std::string key, JToken item); + template + bool TryGetValueAsType(std::string key, T& value) + { + auto obj = GetValue(key); + if(std::holds_alternative(obj)) + { + value = std::get(obj); + return true; + } + return false; + } + + JToken GetValue(std::string key); + void Remove(std::string key); + std::map& GetMap(); + std::map::iterator begin(); + std::map::iterator end(); }; - template + template bool TryGetJToken(JToken json, T& item) { if(std::holds_alternative(json)) @@ -64,6 +73,16 @@ namespace Tesses::Framework::Serialization::Json } return false; } + class JOItem { + public: + JOItem(); + JOItem(std::string key, JToken value); + + std::string key; + JToken value; + }; + + class Json { diff --git a/include/TessesFramework/Streams/NetworkStream.hpp b/include/TessesFramework/Streams/NetworkStream.hpp index 85ce07c..5e67609 100644 --- a/include/TessesFramework/Streams/NetworkStream.hpp +++ b/include/TessesFramework/Streams/NetworkStream.hpp @@ -21,6 +21,13 @@ namespace Tesses::Framework::Streams bool IsValid(); void Close(); }; + enum class SocketType { + ST_IPv4_TCP, + ST_IPv4_UDP, + ST_IPv6_TCP, + ST_IPv6_UDP, + ST_UNIX + }; class NetworkStream : public Stream { int32_t sock; bool owns; @@ -30,7 +37,7 @@ namespace Tesses::Framework::Streams bool EndOfStream(); bool CanRead(); bool CanWrite(); - NetworkStream(bool ipV6,bool datagram); + NetworkStream(SocketType type); NetworkStream(std::string unixPath,bool isServer); NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram,bool broadcast,bool supportIPv6); NetworkStream(int32_t sock, bool owns); diff --git a/include/TessesFramework/Streams/PtyStream.hpp b/include/TessesFramework/Streams/PtyStream.hpp new file mode 100644 index 0000000..c8bb2b6 --- /dev/null +++ b/include/TessesFramework/Streams/PtyStream.hpp @@ -0,0 +1,29 @@ +#pragma once +#include "Stream.hpp" +namespace Tesses::Framework::Streams +{ + struct WindowSize { + uint16_t Width; + uint16_t Height; + uint16_t Columns; + uint16_t Rows; + }; + class PtyStream : public Stream + { + int socket; + int64_t pid; + bool eos; + WindowSize wS; + + public: + PtyStream(WindowSize sz,std::string filename, std::vector args,std::vector env); + bool EndOfStream(); + bool CanRead(); + bool CanWrite(); + size_t Read(uint8_t* buff, size_t sz); + size_t Write(const uint8_t* buff, size_t sz); + void Resize(WindowSize sz); + WindowSize GetWindowSize(); + ~PtyStream(); + }; +} \ No newline at end of file diff --git a/include/TessesFramework/TessesFramework.hpp b/include/TessesFramework/TessesFramework.hpp index 463c1fc..c1c665e 100644 --- a/include/TessesFramework/TessesFramework.hpp +++ b/include/TessesFramework/TessesFramework.hpp @@ -14,6 +14,10 @@ #include "Streams/ByteWriter.hpp" #include "TextStreams/StreamReader.hpp" #include "TextStreams/StreamWriter.hpp" +#include "TextStreams/StdIOReader.hpp" +#include "TextStreams/StdIOWriter.hpp" +#include "TextStreams/StringReader.hpp" +#include "TextStreams/StringWriter.hpp" #include "Threading/Thread.hpp" #include "Threading/Mutex.hpp" #include "Threading/ThreadPool.hpp" @@ -28,4 +32,6 @@ #include "Mail/Smtp.hpp" #include "HiddenField.hpp" #include "Serialization/Json.hpp" -#include "SDL2/FontCache.hpp" \ No newline at end of file +#include "SDL2/FontCache.hpp" +#include "SDL2/Stream.hpp" +#include "SDL2/GUI.hpp" \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StdIOReader.hpp b/include/TessesFramework/TextStreams/StdIOReader.hpp new file mode 100644 index 0000000..abf6883 --- /dev/null +++ b/include/TessesFramework/TextStreams/StdIOReader.hpp @@ -0,0 +1,13 @@ +#include "TextReader.hpp" + + +namespace Tesses::Framework::TextStreams +{ + class ConsoleReader : public TextReader { + public: + ConsoleReader(); + bool ReadBlock(std::string& str,size_t sz); + }; + + ConsoleReader StdIn(); +} \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StdIOWriter.hpp b/include/TessesFramework/TextStreams/StdIOWriter.hpp new file mode 100644 index 0000000..3b4c293 --- /dev/null +++ b/include/TessesFramework/TextStreams/StdIOWriter.hpp @@ -0,0 +1,15 @@ +#include "TextWriter.hpp" + + +namespace Tesses::Framework::TextStreams +{ + class ConsoleWriter : public TextWriter { + bool isError; + public: + ConsoleWriter(bool isError=false); + void WriteData(const char* text, size_t len); + }; + + ConsoleWriter StdOut(); + ConsoleWriter StdErr(); +} \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StreamReader.hpp b/include/TessesFramework/TextStreams/StreamReader.hpp index bc97ee6..651d8c2 100644 --- a/include/TessesFramework/TextStreams/StreamReader.hpp +++ b/include/TessesFramework/TextStreams/StreamReader.hpp @@ -14,6 +14,7 @@ namespace Tesses::Framework::TextStreams StreamReader(Tesses::Framework::Streams::Stream* strm, bool owns); StreamReader(std::filesystem::path filename); bool ReadBlock(std::string& str,size_t sz); + bool Rewind(); ~StreamReader(); }; } \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StringReader.hpp b/include/TessesFramework/TextStreams/StringReader.hpp new file mode 100644 index 0000000..8bd8f7c --- /dev/null +++ b/include/TessesFramework/TextStreams/StringReader.hpp @@ -0,0 +1,15 @@ +#include "TextReader.hpp" + +namespace Tesses::Framework::TextStreams { + class StringReader : public TextReader { + std::string str; + size_t offset; + public: + StringReader(); + StringReader(std::string str); + size_t& GetOffset(); + std::string& GetString(); + bool Rewind(); + bool ReadBlock(std::string& str,size_t sz); + }; +} \ No newline at end of file diff --git a/include/TessesFramework/TextStreams/StringWriter.hpp b/include/TessesFramework/TextStreams/StringWriter.hpp new file mode 100644 index 0000000..b6c002d --- /dev/null +++ b/include/TessesFramework/TextStreams/StringWriter.hpp @@ -0,0 +1,15 @@ +#pragma once +#include "TextWriter.hpp" + +namespace Tesses::Framework::TextStreams +{ + class StringWriter : public TextWriter { + private: + std::string text; + public: + std::string& GetString(); + StringWriter(); + StringWriter(std::string str); + void WriteData(const char* text, size_t len); + }; +} diff --git a/include/TessesFramework/TextStreams/TextReader.hpp b/include/TessesFramework/TextStreams/TextReader.hpp index 521cbfd..96da392 100644 --- a/include/TessesFramework/TextStreams/TextReader.hpp +++ b/include/TessesFramework/TextStreams/TextReader.hpp @@ -6,6 +6,7 @@ namespace Tesses::Framework::TextStreams class TextReader { public: + virtual bool Rewind(); virtual bool ReadBlock(std::string& str,size_t sz)=0; int32_t ReadChar(); std::string ReadLine(); diff --git a/include/TessesFramework/TextStreams/TextWriter.hpp b/include/TessesFramework/TextStreams/TextWriter.hpp index 50bdc82..5c75351 100644 --- a/include/TessesFramework/TextStreams/TextWriter.hpp +++ b/include/TessesFramework/TextStreams/TextWriter.hpp @@ -3,14 +3,73 @@ namespace Tesses::Framework::TextStreams { + class NewLine {}; //dummy class class TextWriter { public: TextWriter(); std::string newline; virtual void WriteData(const char* text, size_t len)=0; - void Write(std::string txt); + void Write(int64_t n); + void Write(uint64_t n); + void Write(const void* ptr); + void Write(const char* ptr); + void Write(char c); + void Write(double d); + void Write(std::string text); + inline TextWriter& operator<<(int64_t n) + { + Write(n); + return *this; + } + inline TextWriter& operator<<(uint64_t n) + { + Write(n); + return *this; + } + inline TextWriter& operator<<(const void* n) + { + Write(n); + return *this; + } + inline TextWriter& operator<<(const char* n) + { + Write(n); + return *this; + } + inline TextWriter& operator<<(char n) + { + Write(n); + return *this; + } + inline TextWriter& operator<<(double n) + { + Write(n); + return *this; + } + inline TextWriter& operator<<(std::string n) + { + Write(n); + return *this; + } + + inline TextWriter& operator<<(NewLine nl) + { + WriteLine(); + return *this; + } + void WriteLine(std::string txt); + void WriteLine(int64_t n); + void WriteLine(uint64_t n); + void WriteLine(const void* ptr); + void WriteLine(const char* ptr); + void WriteLine(char c); + void WriteLine(double d); + void WriteLine(); + virtual ~TextWriter(); }; + + } \ No newline at end of file diff --git a/src/Date/Date.cpp b/src/Date/Date.cpp index 94aa52b..5d6ecb4 100644 --- a/src/Date/Date.cpp +++ b/src/Date/Date.cpp @@ -10,7 +10,7 @@ namespace Tesses::Framework::Date { int GetTimeZone() { - #if defined(__SWITCH__) || defined(_WIN32) || defined(GEKKO) + #if defined(__SWITCH__) || defined(_WIN32) || defined(GEKKO) || defined(__PS2__) return (int)(-_timezone); #else return (int)(-timezone); @@ -18,7 +18,7 @@ namespace Tesses::Framework::Date } bool TimeZoneSupportDST() { - #if defined(__SWITCH__) || defined(_WIN32) || defined(GEKKO) + #if defined(__SWITCH__) || defined(_WIN32) || defined(GEKKO) || defined(__PS2__) return _daylight == 1; #else return daylight == 1; @@ -56,9 +56,9 @@ namespace Tesses::Framework::Date secs /= 60; hour = secs % 24; - day = (int)(uint32_t)ymd.day(); - month = (int)(uint32_t)ymd.month(); - year = (int32_t)ymd.year(); + day = (int)(unsigned)ymd.day(); + month = (int)(unsigned)ymd.month(); + year = (int)ymd.year(); } DateTime::DateTime(int64_t epoch) { @@ -141,7 +141,7 @@ namespace Tesses::Framework::Date date::year_month_day ymd(epoch); - auto month = (uint32_t)ymd.month(); + auto month = (unsigned)ymd.month(); if(month > 3 && month < 11) { @@ -149,7 +149,7 @@ namespace Tesses::Framework::Date } else if(month == 3) { - auto day = (uint32_t)ymd.day(); + auto day = (unsigned)ymd.day(); if(day > 14) isDST=true; else if(day >= 8 && day <= 14) { @@ -172,7 +172,7 @@ namespace Tesses::Framework::Date } else if(month == 11) { - auto day = (uint32_t)ymd.day(); + auto day = (unsigned)ymd.day(); if(day >= 1 && day <= 7) { date::year_month_weekday ymw(epoch); @@ -220,7 +220,7 @@ namespace Tesses::Framework::Date date::year_month_day ymd(epoch); - auto month = (uint32_t)ymd.month(); + auto month = (unsigned)ymd.month(); if(month > 3 && month < 11) { @@ -228,7 +228,7 @@ namespace Tesses::Framework::Date } else if(month == 3) { - auto day = (uint32_t)ymd.day(); + auto day = (unsigned)ymd.day(); if(day > 14) isDST=true; else if(day >= 8 && day <= 14) { @@ -251,7 +251,7 @@ namespace Tesses::Framework::Date } else if(month == 11) { - auto day = (uint32_t)ymd.day(); + auto day = (unsigned)ymd.day(); if(day >= 1 && day <= 7) { date::year_month_weekday ymw(epoch); @@ -357,7 +357,7 @@ namespace Tesses::Framework::Date date::year_month_day ymd(epoch); - auto month = (uint32_t)ymd.month(); + auto month = (unsigned)ymd.month(); if(month > 3 && month < 11) { @@ -365,7 +365,7 @@ namespace Tesses::Framework::Date } else if(month == 3) { - auto day = (uint32_t)ymd.day(); + auto day = (unsigned)ymd.day(); if(day > 14) isDST=true; else if(day >= 8 && day <= 14) { @@ -388,7 +388,7 @@ namespace Tesses::Framework::Date } else if(month == 11) { - auto day = (uint32_t)ymd.day(); + auto day = (unsigned)ymd.day(); if(day >= 1 && day <= 7) { date::year_month_weekday ymw(epoch); diff --git a/src/Http/ContentDisposition.cpp b/src/Http/ContentDisposition.cpp index f70f547..b8a3c44 100644 --- a/src/Http/ContentDisposition.cpp +++ b/src/Http/ContentDisposition.cpp @@ -36,7 +36,11 @@ namespace Tesses::Framework::Http p = p.substr(1, p.size()-2); } + p = HttpUtils::UrlPathDecode(p); + + + cd.filename = p; } else if(res2[0] == "name") diff --git a/src/Http/FileServer.cpp b/src/Http/FileServer.cpp index c23a299..5553109 100644 --- a/src/Http/FileServer.cpp +++ b/src/Http/FileServer.cpp @@ -75,7 +75,7 @@ namespace Tesses::Framework::Http if(this->allowListing) { std::string p = HttpUtils::HtmlEncode(ctx.originalPath); - std::string html = "Index of "; + std::string html = "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>Index of "; html.append(p); html.append("

Index of "); html.append(p); diff --git a/src/Http/HttpServer.cpp b/src/Http/HttpServer.cpp index 85ff563..9de22d2 100644 --- a/src/Http/HttpServer.cpp +++ b/src/Http/HttpServer.cpp @@ -9,19 +9,18 @@ #include "TessesFramework/Crypto/MbedHelpers.hpp" #include "TessesFramework/Threading/Mutex.hpp" #include "TessesFramework/Common.hpp" +#include "TessesFramework/TextStreams/StdIOWriter.hpp" #include using FileStream = Tesses::Framework::Streams::FileStream; using Stream = Tesses::Framework::Streams::Stream; using SeekOrigin = Tesses::Framework::Streams::SeekOrigin; using MemoryStream = Tesses::Framework::Streams::MemoryStream; -using StreamReader = Tesses::Framework::TextStreams::StreamReader; -using StreamWriter = Tesses::Framework::TextStreams::StreamWriter; using TcpServer = Tesses::Framework::Streams::TcpServer; using NetworkStream = Tesses::Framework::Streams::NetworkStream; using BufferedStream = Tesses::Framework::Streams::BufferedStream; - +using namespace Tesses::Framework::TextStreams; namespace Tesses::Framework::Http { @@ -503,6 +502,7 @@ namespace Tesses::Framework::Http else { auto strm = cb(ct, cd1.filename, cd1.fieldName); + if(strm == nullptr) strm = new Stream(); bool retVal = parseUntillBoundaryEnd(&ctx->GetStream(),strm,boundary); delete strm; return retVal; @@ -635,13 +635,13 @@ namespace Tesses::Framework::Http { TF_LOG("Before printing interfaces"); - std::cout << "\x1B[34mInterfaces:\n"; + StdOut() << "\x1B[34mInterfaces:" << NewLine(); for(auto _ip : NetworkStream::GetIPs()) { - std::cout << "\x1B[32m"; - std::cout << _ip.first << ": "; - std::cout << "\x1B[35mhttp://"; - std::cout << _ip.second << ":" << std::to_string(this->GetPort()) << "/\n"; + StdOut() << "\x1B[32m" + << _ip.first << ": " + << "\x1B[35mhttp://" + << _ip.second << ":" << (uint64_t)this->GetPort() << "/" << NewLine(); } @@ -649,7 +649,7 @@ namespace Tesses::Framework::Http if(this->showARTL) { if(!svr->IsValid()) std::cout << "\x1B[31mError, we failed to bind or something\x1B[39m\n" << std::endl; - std::cout << "\x1B[31mAlmost Ready to Listen\x1B[39m\n"; + StdOut() << "\x1B[31mAlmost Ready to Listen\x1B[39m" << NewLine(); } TF_LOG("After printing interfaces"); diff --git a/src/Http/HttpUtils.cpp b/src/Http/HttpUtils.cpp index 8a0e3b1..11caaa7 100644 --- a/src/Http/HttpUtils.cpp +++ b/src/Http/HttpUtils.cpp @@ -516,6 +516,68 @@ namespace Tesses::Framework::Http { } return strs; } + std::string HttpUtils::HtmlDecodeOnlyEntityNumber(std::string v) + { + std::string buff={}; + int state = 0; + uint64_t n=0; + for(auto item : v) + { + switch(state) + { + case 0: + if(item == '&') state=1; + else buff.push_back(item); + break; + case 1: + if(item == '#') {state = 2; n=0;} + else {state=0; buff.push_back('&'); buff.push_back(item); } + break; + case 2: + if(item == ';') { + state = 0; + if(n <= 0x7F) + { + buff.push_back((char)n); + } + else if(n >= 0x80 && n <= 0x7FF) + { + uint8_t high = 0b11000000 | ((uint8_t)(n >> 6) & 0b00011111); + uint8_t low = 0b10000000 | ((uint8_t)(n) & 0b00111111); + buff.push_back((char)high); + buff.push_back((char)low); + } + else if(n >= 0x800 && n <= 0xFFFF) + { + uint8_t high = 0b11100000 | ((uint8_t)(n >> 12) & 0b00001111); + uint8_t low = 0b10000000 | ((uint8_t)(n >> 6) & 0b00111111); + uint8_t lowest = 0b10000000 | ((uint8_t)(n) & 0b00111111); + buff.push_back((char)high); + buff.push_back((char)low); + buff.push_back((char)lowest); + } + else if(n >= 0x010000 && n <= 0x10FFFF) + { + uint8_t highest = 0b11110000 | ((uint8_t)(n >> 18) & 0b00000111); + uint8_t high = 0b10000000 | ((uint8_t)(n >> 12) & 0b00111111); + uint8_t low = 0b10000000 | ((uint8_t)(n >> 6) & 0b00111111); + uint8_t lowest = 0b10000000 | ((uint8_t)(n) & 0b00111111); + buff.push_back((char)highest); + + buff.push_back((char)high); + buff.push_back((char)low); + buff.push_back((char)lowest); + } + } + else if(item >= '0' && item <= '9') + { + n *= 10; + n += item - '0'; + } + } + } + return buff; + } std::string HttpUtils::HtmlEncode(std::string html) { std::string myHtml = {}; diff --git a/src/SDL2/FontCache.cpp b/src/SDL2/FontCache.cpp index 69c3d56..46d1ede 100644 --- a/src/SDL2/FontCache.cpp +++ b/src/SDL2/FontCache.cpp @@ -1,7 +1,10 @@ #if defined(TESSESFRAMEWORK_ENABLE_SDL2) #include "TessesFramework/SDL2/FontCache.hpp" +#include "TessesFramework/Http/HttpUtils.hpp" +#include "TessesFramework/TanoheSans-Regular.h" +using HU= Tesses::Framework::Http::HttpUtils; namespace Tesses::Framework::SDL2 { -void FontCache::Load(SDL_Renderer* renderer,TTF_Font* font,const SDL_Color& color) +void FontCache::Load(SDL_Renderer* renderer,TTF_Font* font) { this->mw=0; this->mh=0; @@ -9,7 +12,7 @@ void FontCache::Load(SDL_Renderer* renderer,TTF_Font* font,const SDL_Color& colo for(size_t i = 0; i < this->font_chrs.size();i++) { - SDL_Surface* surf = TTF_RenderGlyph_Blended(font,(Uint16)(i+32),color); + SDL_Surface* surf = TTF_RenderGlyph_Blended(font,(Uint16)(i+32),{.r=255,.g=255,.b=255,.a=255}); if(surf->w > this->mw) mw = surf->w; if(surf->h > this->mh) mh = surf->h; this->font_chrs[i] = SDL_CreateTextureFromSurface(renderer,surf); @@ -17,35 +20,123 @@ void FontCache::Load(SDL_Renderer* renderer,TTF_Font* font,const SDL_Color& colo } } -FontCache::FontCache(SDL_Renderer* renderer,std::string font,int sz,const SDL_Color& color) +FontCache::FontCache(SDL_Renderer* renderer,std::string font,int sz) { TTF_Font* f = TTF_OpenFont(font.c_str(),sz); - Load(renderer,f,color); + Load(renderer,f); TTF_CloseFont(f); } -FontCache::FontCache(SDL_Renderer* renderer,const uint8_t* mem,size_t cnt,int sz,const SDL_Color& color) +FontCache::FontCache(SDL_Renderer* renderer,const uint8_t* mem,size_t cnt,int sz) { TTF_Font* f = TTF_OpenFontRW(SDL_RWFromConstMem(mem,cnt),1,sz); - Load(renderer,f,color); + Load(renderer,f); TTF_CloseFont(f); } -FontCache::FontCache(SDL_Renderer* renderer,const std::vector& v,int sz,const SDL_Color& color) : FontCache(renderer,v.data(),v.size(),sz,color) +FontCache::FontCache(SDL_Renderer* renderer,const std::vector& v,int sz) : FontCache(renderer,v.data(),v.size(),sz) { } -FontCache::FontCache(SDL_Renderer* renderer,TTF_Font* font,const SDL_Color& color) +FontCache::FontCache(SDL_Renderer* renderer,TTF_Font* font) { - this->Load(renderer,font,color); + this->Load(renderer,font); } +FontCache::FontCache(SDL_Renderer* renderer,int sz) : FontCache(renderer,TANOHESANSREGULAR,TANOHESANSREGULAR_SIZE,sz) +{ + +} +void FontCache::CalculateSize(std::string text, int& x,int& y) +{ + int myX = 0; + x=0; + y=0; + int maxH = MaxHeight(); + y=maxH; + for(auto c : text) + { + switch(c) + { + case '\n': + { + y += maxH; + if(myX > x) x = myX; + myX = 0; + } + break; + case '\t': + { + auto tex = operator[](' '); + int wi; + SDL_QueryTexture(tex,NULL,NULL,&wi,NULL); + + myX += wi * 4; + } + break; + default: + { + auto tex = operator[](c); + int wi; + int he; + SDL_QueryTexture(tex,NULL,NULL,&wi,&he); + + myX += wi; + } + break; + } + } + if(myX > x) x = myX; +} +void FontCache::Render(SDL_Renderer* renderer,int x,int y, std::string text,const SDL_Color& color) +{ + int myX = x; + int maxH = MaxHeight(); + for(auto c : text) + { + switch(c) + { + case '\n': + { + y += maxH; + myX = x; + } + break; + case '\t': + { + auto tex = GetCharOfColor(' ',color); + int wi; + SDL_QueryTexture(tex,NULL,NULL,&wi,NULL); + + myX += wi * 4; + } + break; + default: + { + auto tex = GetCharOfColor(c,color); + int wi; + int he; + SDL_QueryTexture(tex,NULL,NULL,&wi,&he); + SDL_Rect src={.x=0,.y=0,.w=wi,.h=he}; + + SDL_Rect dest={.x=myX,.y=y,.w=wi,.h=he}; + + myX += wi; + SDL_RenderCopy(renderer,tex,&src,&dest); + } + break; + + } + } +} +SDL_Texture* FontCache::GetCharOfColor(char c, const SDL_Color& color) +{ + auto res = (c >= 32 && c <= 126) ? this->font_chrs[c-32] : this->font_chrs[95]; + SDL_SetTextureColorMod(res,color.r,color.g,color.b); + return res; +} SDL_Texture* FontCache::operator[](char c) { - if(c >= 32 && c <= 126) - { - return this->font_chrs[c-32]; - } - return this->font_chrs[95]; + return GetCharOfColor(c,{.r=255,.g=255,.b=255,.a=255}); } FontCache::~FontCache() { @@ -65,5 +156,121 @@ int FontCache::PointSize() { return this->ps; } + +bool TryParseSDLColor(std::string str, SDL_Color& col) +{ + str = HU::Replace(str," ",""); + //rgba(255,255,255,1.0) + //rgb(197, 30, 30) + //#rgb + //#rgba + //#rrggbb + //#rrggbbaa + + if(str.size() >= 4 && str[0] == '#') + { + if(str.size() == 4) + { + auto r = HU::HexToNibble(str[1]); + r |= r << 4; + auto g = HU::HexToNibble(str[2]); + g |= g << 4; + auto b = HU::HexToNibble(str[3]); + b |= b << 4; + col.r = r; + col.g = g; + col.b = b; + col.a = 255; + return true; + } + else if(str.size() == 5) + { + auto r = HU::HexToNibble(str[1]); + r |= r << 4; + auto g = HU::HexToNibble(str[2]); + g |= g << 4; + auto b = HU::HexToNibble(str[3]); + b |= b << 4; + auto a = HU::HexToNibble(str[4]); + a |= a << 4; + col.r = r; + col.g = g; + col.b = b; + col.a = a; + return true; + } + else if(str.size() == 7) { + auto r = HU::HexToNibble(str[1]); + r |= HU::HexToNibble(str[2]) << 4; + auto g = HU::HexToNibble(str[3]); + g |= HU::HexToNibble(str[4]) << 4; + auto b = HU::HexToNibble(str[5]); + b |= HU::HexToNibble(str[6]) << 4; + + col.r = r; + col.g = g; + col.b = b; + col.a = 255; + return true; + } + else if(str.size() == 9) + { + auto r = HU::HexToNibble(str[1]); + r |= HU::HexToNibble(str[2]) << 4; + auto g = HU::HexToNibble(str[3]); + g |= HU::HexToNibble(str[4]) << 4; + auto b = HU::HexToNibble(str[5]); + b |= HU::HexToNibble(str[6]) << 4; + auto a = HU::HexToNibble(str[7]); + a |= HU::HexToNibble(str[8]) << 4; + + col.r = r; + col.g = g; + col.b = b; + col.a = a; + return true; + } + } + else if(str.size() > 4 && strncmp(str.c_str(),"rgb(",4) == 0 && str[str.size()-1] == ')') + { + str = HU::Replace(str.substr(4),")",""); + auto parts = HU::SplitString(str,","); + if(parts.size() != 3) return false; + try{ + auto r = std::stoul(parts[0]); + auto g = std::stoul(parts[1]); + auto b = std::stoul(parts[2]); + col.r = (Uint8)r; + col.g = (Uint8)g; + col.b = (Uint8)b; + col.a = 255; + } catch(...) { + return false; + } + + return true; + } + else if(str.size() > 5 && strncmp(str.c_str(),"rgba(",5) == 0 && str[str.size()-1] == ')') + { + str = HU::Replace(str.substr(5),")",""); + auto parts = HU::SplitString(str,","); + if(parts.size() != 4) return false; + try{ + auto r = std::stoul(parts[0]); + auto g = std::stoul(parts[1]); + auto b = std::stoul(parts[2]); + double a= std::stod(parts[3]); + col.r = (Uint8)r; + col.g = (Uint8)g; + col.b = (Uint8)b; + col.a = (Uint8)(a * 255); + } catch(...) { + return false; + } + return true; + } + return false; +} + } #endif \ No newline at end of file diff --git a/src/SDL2/GUI.cpp b/src/SDL2/GUI.cpp new file mode 100644 index 0000000..aa119ef --- /dev/null +++ b/src/SDL2/GUI.cpp @@ -0,0 +1,231 @@ +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "TessesFramework/SDL2/GUI.hpp" +#include +namespace Tesses::Framework::SDL2 +{ + GUI gui; + void GUI::Update() + { + if(this->windows.empty()) return; + for(auto index = this->windows.begin(); index != this->windows.end(); index++) + { + if(*index == nullptr) + { + this->windows.erase(index); + index--; + } + } + if(this->windows.empty()) return; + + + SDL_Event event; + while(SDL_PollEvent(&event)) + { + + for(auto win : this->windows) + { + if(win == nullptr) continue; + auto id = SDL_GetWindowID(win->window); + switch(event.type) + { + case SDL_EventType::SDL_WINDOWEVENT: + if(event.window.windowID == id) + { + win->Event(event); + } + break; + case SDL_EventType::SDL_MOUSEBUTTONDOWN: + case SDL_EventType::SDL_MOUSEBUTTONUP: + if(event.button.windowID == id) + { + win->Event(event); + } + break; + case SDL_EventType::SDL_MOUSEMOTION: + if(event.motion.windowID == id) + { + win->Event(event); + } + break; + case SDL_EventType::SDL_MOUSEWHEEL: + if(event.wheel.windowID == id) + { + win->Event(event); + } + break; + case SDL_EventType::SDL_KEYUP: + case SDL_EventType::SDL_KEYDOWN: + if(event.key.windowID == id) + { + win->Event(event); + } + break; + case SDL_EventType::SDL_TEXTEDITING: + if(event.edit.windowID == id) + { + win->Event(event); + } + break; + case SDL_EventType::SDL_TEXTEDITING_EXT: + if(event.editExt.windowID == id) + { + win->Event(event); + } + break; + case SDL_EventType::SDL_TEXTINPUT: + if(event.text.windowID == id) + { + win->Event(event); + } + break; + case SDL_EventType::SDL_DROPBEGIN: + case SDL_EventType::SDL_DROPCOMPLETE: + case SDL_EventType::SDL_DROPFILE: + case SDL_EventType::SDL_DROPTEXT: + if(event.drop.windowID == id) + { + win->Event(event); + } + break; + default: + win->Event(event); + break; + } + + } + + if(event.type == SDL_QUIT) + { + TF_SetIsRunning(false); + break; + } + } + for(auto item : this->windows) + { + if(item != nullptr) + item->Draw(); + } + } + + + Clipper::Clipper(SDL_Renderer* renderer, SDL_Rect& myRect) + { + this->isClipped=SDL_RenderIsClipEnabled(renderer); + SDL_RenderGetClipRect(renderer,&this->theRect); + if(!this->isClipped) this->theRect = myRect; + this->renderer = renderer; + } + void Clipper::ClipRect(SDL_Rect& child, SDL_Rect& parent) + { + if(child.x < parent.x) + { + int rem = parent.x- child.x; + child.x += rem; + child.w -= rem; + } + if(child.y < parent.y) + { + int rem = parent.y- child.y; + child.y += rem; + child.h -= rem; + } + + if((child.x + child.w) > (parent.x + parent.w)) + child.w = (parent.x + parent.w) - child.x; + + if((child.y + child.h) > (parent.y + parent.h)) + child.h = (parent.y + parent.h) - child.y; + + } + bool Clipper::Clip(SDL_Rect rect) + { + ClipRect(rect,this->theRect); + if(rect.w <= 0 || rect.h <= 0) return false; + + SDL_RenderSetClipRect(renderer, &rect); + return true; + } + Clipper::~Clipper() + { + if(isClipped) SDL_RenderSetClipRect(renderer, &this->theRect); + else SDL_RenderSetClipRect(renderer, nullptr); + } + + GUIPalette::GUIPalette() + { + this->fontSize=24; + } + SDL_Color& GUIPalette::GetBorderColor(bool isHovering, bool isActive, bool isMouseDown) + { + bool isHovering2=isHovering ^ isMouseDown; + if(isHovering2 && isActive) + return this->border_hover_active; + if(isHovering2) + return this->border_hover; + if(isActive) + return this->border_active; + return this->border_color; + } + + GUIPalette::GUIPalette(SDL_Color accent, SDL_Color background, SDL_Color border_color, SDL_Color border_hover, SDL_Color border_active, SDL_Color border_hover_active, int fontSize) + { + this->accent=accent; + this->background = background; + this->border_color=border_color; + this->border_hover = border_hover; + this->border_active = border_active; + this->border_hover_active=border_hover_active; + this->fontSize = fontSize; + } + + std::string GUIEventArgs::Type() + { + return "Base"; + } + GUIEventArgs::~GUIEventArgs() + { + + } + std::string GUIMouseButtonEventArgs::Type() + { + return "MouseButton"; + } + std::string GUIJsonViewNotFoundEventArgs::Type() + { + return "JsonViewNotFound"; + } + std::string GUISDLEventEventArgs::Type() + { + return "SDLEvent"; + } + + GUIPalette::GUIPalette(bool isDarkMode, SDL_Color accent,int fontSize) + { + this->accent = accent; + this->fontSize = fontSize; + + if(isDarkMode) + { + this->background = {.r = 42,.g=42,.b=42,.a=255}; + this->border_color = {.r=0,.g=0,.b=0,.a=255}; + this->border_hover = {.r=92,.g=92,.b=92,.a=255}; + + this->border_active = {.r=200,.g=200,.b=200,.a=255}; + this->border_hover_active = {.r=(uint8_t)(255-accent.r),.g=(uint8_t)(255-accent.g),.b=(uint8_t)(255-accent.b),.a=255}; + + } + else + { + this->background = {.r=239,.g=239,.b=239,.a=255}; + this->border_color = {.r=0,.g=0,.b=0,.a=255}; + + this->border_active = {.r=92,.g=92,.b=92,.a=255}; + + this->border_hover = {.r=200,.g=200,.b=200,.a=255}; + + this->border_hover_active = {.r=(uint8_t)(255-accent.r),.g=(uint8_t)(255-accent.g),.b=(uint8_t)(255-accent.b),.a=255}; + } + } + +} +#endif \ No newline at end of file diff --git a/src/SDL2/GUIWindow.cpp b/src/SDL2/GUIWindow.cpp new file mode 100644 index 0000000..d80fbdf --- /dev/null +++ b/src/SDL2/GUIWindow.cpp @@ -0,0 +1,367 @@ +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "TessesFramework/SDL2/GUI.hpp" +#include "TessesFramework/aardvark-fixed-regular.h" +#include "TessesFramework/SDL2/Views/ButtonView.hpp" +#include "TessesFramework/SDL2/Views/LabelView.hpp" +#include "TessesFramework/SDL2/Views/CheckView.hpp" +#include "TessesFramework/SDL2/Views/ProgressView.hpp" +#include "TessesFramework/SDL2/Views/TextListView.hpp" +#include "TessesFramework/SDL2/Views/AbsoluteView.hpp" +#include "TessesFramework/SDL2/ParseColor.hpp" +namespace Tesses::Framework::SDL2 +{ + void GUIWindow::MakeActive(View* view) + { + if(!view->GetViewFlag(VIEWFLAG_TABSTOP)) return; + if(view->GetViewFlag(VIEWFLAG_ISACTIVE)) return; + DeactivateAll(this); + view->SetViewFlag(VIEWFLAG_ISACTIVE,true); + } + void GUIWindow::DeactivateAll(View* view) + { + view->SetViewFlag(VIEWFLAG_ISACTIVE,false); + auto cv = dynamic_cast(view); + if(cv != nullptr) + { + for(size_t i = 0; i < cv->ViewCount();i++) + { + DeactivateAll(cv->GetViewAt(i)); + } + } + } + void GUIWindow::TabNext(View* view,TabNextResult& nr) + { + if(nr == TabNextResult::Done) + return; + auto cv = dynamic_cast(view); + if(cv != nullptr) + { + for(size_t i = 0; i < cv->ViewCount();i++) + { + TabNext(cv->GetViewAt(i),nr); + if(nr == TabNextResult::Done) + return; + } + } + else + { + if(view->GetViewFlag(VIEWFLAG_ISACTIVE)) + { + if(view->GetViewFlag(VIEWFLAG_INTERCEPT_TAB)) + { + SDL_Rect r; + r.x=0; + r.y=0; + r.w=0; + r.h=0; + SDL_Event event; + event.type = SDL_KEYDOWN; + event.key.keysym.mod = SDL_Keymod::KMOD_NONE; + event.key.keysym.scancode = SDL_SCANCODE_TAB; + event.key.keysym.sym = SDL_KeyCode::SDLK_TAB; + view->CallOnEvent(view,event,r,r); + nr = TabNextResult::Done; + } + view->SetViewFlag(VIEWFLAG_ISACTIVE,false); + nr= TabNextResult::TabNext; + } + else if(view->GetViewFlag(VIEWFLAG_TABSTOP) && nr == TabNextResult::TabNext) + { + view->SetViewFlag(VIEWFLAG_ISACTIVE,true); + nr = TabNextResult::Done; + } + } + } + void GUIWindow::TabNext() + { + TabNextResult nr=TabNextResult::KeepGoing; + TabNext(this,nr); + if(nr != TabNextResult::Done) + { + nr = TabNextResult::TabNext; + TabNext(this,nr); + } + } + + void GUIWindow::SetView(View* view,bool owns) + { + if(this->ownsChild && this->child != view) + delete this->child; + this->child = view; + this->ownsChild = owns; + if(view != nullptr) + view->parent = this; + } + GUIWindow* GUIWindow::GetWindow() + { + return this; + } + size_t GUIWindow::ViewCount() + { + return this->child != nullptr ? 1 : 0; + } + View* GUIWindow::GetViewAt(size_t index) + { + if(index > 0) return nullptr; + return this->child; + } + void GUIWindow::Draw() + { + int w,h; + SDL_GetWindowSize(window,&w,&h); + SDL_Rect r={.x=0,.y=0,.w=w,.h=h}; + OnDraw(renderer,r); + } + void GUIWindow::Event(SDL_Event& event) + { + int w,h; + SDL_GetWindowSize(window,&w,&h); + SDL_Rect r={.x=0,.y=0,.w=w,.h=h}; + OnEvent(event,r,r); + } + void GUIWindow::OnDraw(SDL_Renderer* renderer, SDL_Rect& r) + { + SDL_SetRenderDrawColor(renderer,this->palette.background.r,this->palette.background.g,this->palette.background.b,this->palette.background.a); + SDL_RenderClear(renderer); + if(this->child != nullptr) + this->child->OnDraw(renderer,r); + SDL_RenderPresent(renderer); + } + bool GUIWindow::OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& myVisibleBounds) + { + + if(event.type == SDL_EventType::SDL_KEYDOWN && event.key.keysym.sym ==SDL_KeyCode::SDLK_TAB) + { + GUISDLEventEventArgs sdle; + sdle.event = event; + this->SDLEvent.Invoke(this,sdle); + TabNext(); + return true; + } + if(this->child != nullptr) { GUISDLEventEventArgs sdle; + sdle.event = event; this->SDLEvent.Invoke(this,sdle); return this->child->OnEvent(event,myBounds,myVisibleBounds);} + return View::OnEvent(event,myBounds,myVisibleBounds); + } + GUIWindow::GUIWindow(std::string title, int w, int h, Uint32 flags, const GUIPalette& palette) : ContainerView(title) + { + this->window = SDL_CreateWindow(title.c_str(),SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,w,h,flags); + this->renderer = SDL_CreateRenderer(this->window,-1,SDL_RENDERER_ACCELERATED); + SDL_SetRenderDrawBlendMode(renderer,SDL_BlendMode::SDL_BLENDMODE_BLEND); + this->child=nullptr; + this->parent=nullptr; + this->ownsChild=false; + this->normal_font=nullptr; + this->monospaced_font=nullptr; + gui.windows.push_back(this); + this->SetPalette(palette); + } + void GUIWindow::SetText(std::string text) + { + this->text = text; + SDL_SetWindowTitle(this->window,text.c_str()); + } + + void GUIWindow::SetPalette(const GUIPalette& palette) + { + this->palette = palette; + if(this->normal_font != nullptr) + delete this->normal_font; + if(this->monospaced_font != nullptr) + delete this->monospaced_font; + this->normal_font = new FontCache(this->renderer,this->palette.fontSize); + this->monospaced_font = new FontCache(this->renderer,AARDVARKFIXEDREGULAR,AARDVARKFIXEDREGULAR_SIZE,this->palette.fontSize); + } + + GUIWindow::~GUIWindow() + { + if(this->ownsChild) + delete this->child; + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + + for(auto index = gui.windows.begin(); index < gui.windows.end(); index++) + { + if(*index == this) + { + *index = nullptr; + break; + } + } + } + void GUIWindow::SetView(Tesses::Framework::Serialization::Json::JToken item) + { + Tesses::Framework::Serialization::Json::JObject dict; + if(Tesses::Framework::Serialization::Json::TryGetJToken(item,dict)) + { + Tesses::Framework::Serialization::Json::JObject o; + Tesses::Framework::Serialization::Json::JObject sz; + Tesses::Framework::Serialization::Json::JObject pal0; + std::string title; + if(dict.TryGetValueAsType("Child",o)) + { + this->SetView(CreateViewFromJson(o)); + } + if(dict.TryGetValueAsType("Size",sz)) + { + int w0,h0; + + + SDL_GetWindowSize(this->window,&w0,&h0); + int64_t w=w0; + int64_t h=h0; + + sz.TryGetValueAsType("Width",w); + sz.TryGetValueAsType("Height",h); + + SDL_SetWindowSize(this->window,(int)w,(int)h); + } + if(dict.TryGetValueAsType("Palette",pal0)) + { + bool darkMode=true; + SDL_Color accent={.r=255,.g=0,.b=0,.a=255}; + std::string _str; + int64_t fontSize=20; + + pal0.TryGetValueAsType("IsDarkMode",darkMode); + pal0.TryGetValueAsType("FontSize",fontSize); + if(pal0.TryGetValueAsType("Accent",_str)) + TryParseSDLColor(_str,accent); + + GUIPalette pal(darkMode,accent,fontSize); + if(pal0.TryGetValueAsType("Background",_str)) + TryParseSDLColor(_str,pal.background); + if(pal0.TryGetValueAsType("Border",_str)) + TryParseSDLColor(_str,pal.border_color); + if(pal0.TryGetValueAsType("BorderActive",_str)) + TryParseSDLColor(_str,pal.border_active); + if(pal0.TryGetValueAsType("BorderHover",_str)) + TryParseSDLColor(_str,pal.border_hover); + if(pal0.TryGetValueAsType("BorderHoverActive",_str)) + TryParseSDLColor(_str,pal.border_hover_active); + this->SetPalette(pal); + } + if(dict.TryGetValueAsType("Title",title) || dict.TryGetValueAsType("Text",title)) + { + this->SetText(title); + } + } + } + + View* GUIWindow::CreateViewFromJson(Tesses::Framework::Serialization::Json::JObject json) + { + std::string type; + if(json.TryGetValueAsType("Type",type)) + { + std::string id={}; + std::string text={}; + bool active=false; + json.TryGetValueAsType("Id",id); + json.TryGetValueAsType("Text",text); + json.TryGetValueAsType("Active",active); + if(type == "ButtonView") + { + auto btn = new Views::ButtonView(text); + btn->SetId(id); + btn->SetViewFlag(VIEWFLAG_ISACTIVE,active); + return btn; + } + else if(type == "CheckView") + { + bool checked; + json.TryGetValueAsType("Checked",checked); + auto cv = new Views::CheckView(checked,text); + cv->SetId(id); + + cv->SetViewFlag(VIEWFLAG_ISACTIVE,active); + return cv; + } + else if(type == "LabelView") + { + auto lv = new Views::LabelView(text); + lv->SetId(id); + return lv; + } + else if(type == "ProgressView") + { + double v=0; + json.TryGetValueAsType("Value",v); + auto pv = new Views::ProgressView(v); + pv->SetId(id); + return pv; + } + else if(type == "TextListView") + { + std::vector items; + Tesses::Framework::Serialization::Json::JArray arr; + if(json.TryGetValueAsType("Items",arr)) + { + std::string str; + for(auto item : arr) + { + if(Tesses::Framework::Serialization::Json::TryGetJToken(item,str)) items.push_back(str); + } + } + int64_t index=-1; + int64_t first=0; + json.TryGetValueAsType("SelectedIndex",index); + json.TryGetValueAsType("FirstIndex",first); + + auto tlv = new Views::TextListView(); + + tlv->SetViewFlag(VIEWFLAG_ISACTIVE,active); + tlv->SetId(id); + tlv->items = items; + tlv->firstIndex = (size_t)first; + tlv->selected = (int)index; + return tlv; + } + else if(type == "AbsoluteView") + { + auto av = new Views::AbsoluteView(); + av->SetId(id); + Tesses::Framework::Serialization::Json::JArray arr; + if(json.TryGetValueAsType("Items",arr)) + { + for(auto item : arr) + { + Tesses::Framework::Serialization::Json::JObject dict; + if(Tesses::Framework::Serialization::Json::TryGetJToken(item,dict)) + { + Tesses::Framework::Serialization::Json::JObject boundsDict; + SDL_Rect r={.x=0,.y=0,.w=200,.h=200}; + + if(dict.TryGetValueAsType("Bounds",boundsDict)) + { + int64_t n; + if(boundsDict.TryGetValueAsType("X",n)) r.x = (int)n; + if(boundsDict.TryGetValueAsType("Y",n)) r.y = (int)n; + if(boundsDict.TryGetValueAsType("Width",n)) r.w = (int)n; + if(boundsDict.TryGetValueAsType("Height",n)) r.h = (int)n; + } + + auto myO = CreateViewFromJson(dict); + if(myO != nullptr) + { + av->Add(r,myO); + } + } + } + } + return av; + } + else { + GUIJsonViewNotFoundEventArgs e; + e.destView == nullptr; + e.jsonObject = json; + e.typeString = type; + + JsonViewNotFound.Invoke(this,e); + + return e.destView; + } + } + return nullptr; + } + +} +#endif \ No newline at end of file diff --git a/src/SDL2/Stream.cpp b/src/SDL2/Stream.cpp new file mode 100644 index 0000000..373de9d --- /dev/null +++ b/src/SDL2/Stream.cpp @@ -0,0 +1,77 @@ + +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) + +#include "TessesFramework/SDL2/Stream.hpp" +namespace Tesses::Framework::SDL2 { + +static SDLCALL size_t rwops_stream_read(struct SDL_RWops * context, void *ptr, + size_t size, size_t maxnum) +{ + if(context->hidden.unknown.data1 == nullptr) return 0; + auto ctx = static_cast(context->hidden.unknown.data1); + if(ctx->CanRead()) + return ctx->Read((uint8_t*)ptr,size*maxnum) / size; + return 0; +} +static SDLCALL size_t rwops_stream_write(struct SDL_RWops * context, const void *ptr, + size_t size, size_t maxnum) +{ + if(context->hidden.unknown.data1 == nullptr) return 0; + auto ctx = static_cast(context->hidden.unknown.data1); + if(ctx->CanWrite()) + return ctx->Write((const uint8_t*)ptr,size*maxnum) / size; + return 0; +} +static SDLCALL Sint64 rwops_stream_seek(struct SDL_RWops * context, Sint64 offset, + int whence) +{ + + if(context->hidden.unknown.data1 == nullptr) return -1; + auto ctx = static_cast(context->hidden.unknown.data1); + if(!ctx->CanSeek()) return -1; + + switch(whence) + { + case RW_SEEK_SET: + ctx->Seek(offset,Tesses::Framework::Streams::SeekOrigin::Begin); + break; + case RW_SEEK_CUR: + ctx->Seek(offset,Tesses::Framework::Streams::SeekOrigin::Current); + break; + case RW_SEEK_END: + ctx->Seek(offset,Tesses::Framework::Streams::SeekOrigin::End); + break; + } + + + return ctx->GetPosition(); +} +static SDLCALL Sint64 rwops_stream_size(struct SDL_RWops * context) +{ + if(context->hidden.unknown.data1 == nullptr) return -1; + auto ctx = static_cast(context->hidden.unknown.data1); + return ctx->GetLength(); +} +static SDLCALL int rwops_stream_close(struct SDL_RWops* context) +{ + if(context->hidden.unknown.data2 == nullptr) return 0; + delete static_cast(context->hidden.unknown.data1); + return 0; +} +SDL_RWops* RwopsFromStream(Tesses::Framework::Streams::Stream* strm, bool owns) +{ + auto rw=SDL_AllocRW(); + rw->type = SDL_RWOPS_UNKNOWN; + rw->hidden.unknown.data1 = static_cast(strm); + rw->hidden.unknown.data2 = (void*)(uintptr_t)(owns?1:0); + rw->read = rwops_stream_read; + rw->write = rwops_stream_write; + rw->seek = rwops_stream_seek; + rw->size = rwops_stream_size; + rw->close = rwops_stream_close; + + + return rw; +} +} +#endif \ No newline at end of file diff --git a/src/SDL2/View.cpp b/src/SDL2/View.cpp new file mode 100644 index 0000000..d1e7d5d --- /dev/null +++ b/src/SDL2/View.cpp @@ -0,0 +1,173 @@ +#include "TessesFramework/SDL2/GUI.hpp" +using namespace Tesses::Framework::Serialization::Json; +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "TessesFramework/SDL2/Views/ButtonView.hpp" +#include "TessesFramework/SDL2/Views/CheckView.hpp" +#include "TessesFramework/SDL2/Views/LabelView.hpp" +namespace Tesses::Framework::SDL2 +{ + ContainerView::ContainerView() : View() + { + this->flags = 0; + } + ContainerView::ContainerView(std::string text) : View(text) + { + this->flags = 0; + } + View::View() : View((std::string){}) + { + + } + View::View(std::string text) + { + this->text = text; + this->flags=VIEWFLAG_TABSTOP; + } + void View::SetText(std::string text) + { + this->text=text; + } + std::string View::GetText() + { + return this->text; + } + void View::OnMouseUp(GUIMouseButtonEventArgs& click) + { + + } + void View::OnMouseDown(GUIMouseButtonEventArgs& click) + { + + } + void View::OnClick(GUIEventArgs& click) + { + + } + GUIWindow* View::GetWindow() + { + if(this->parent ==nullptr) return nullptr; + return this->parent->GetWindow(); + } + void View::OnDragDropFile(Tesses::Framework::Filesystem::VFSPath filePath,SDL_Rect myRect, SDL_Point dropLoc) + { + + } + void View::OnDragDropText(std::string text,SDL_Rect myRect, SDL_Point dropLoc) + { + + } + + void View::OnEnter(GUIEventArgs& evt) + { + + } + void View::OnLeave(GUIEventArgs& evt) + { + + } + + + void View::OnDraw(SDL_Renderer* renderer, SDL_Rect& myRect) + { + + } + void View::OnSetParent(View* v) + { + + } + + bool View::OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& myVisibleBounds) + { + GUISDLEventEventArgs sdle; + sdle.event = event; + this->SDLEvent.Invoke(this,sdle); + if(event.type == SDL_MOUSEBUTTONDOWN) + { + if(event.button.x >= myVisibleBounds.x && event.button.x < myVisibleBounds.x+myVisibleBounds.w && event.button.y >= myVisibleBounds.y && event.button.y < myVisibleBounds.y+myVisibleBounds.h) + { + GUIMouseButtonEventArgs cea; + cea.button = (int)event.button.button; + cea.x = event.button.x - myVisibleBounds.x; + cea.y = event.button.y - myVisibleBounds.y; + cea.which = event.button.which; + this->SetViewFlag(VIEWFLAG_MOUSEDOWN_STATE,true); + OnMouseDown(cea); + this->MouseDown.Invoke(this,cea); + } + } + if(event.type == SDL_MOUSEBUTTONUP) + { + if(this->GetViewFlag(VIEWFLAG_MOUSEDOWN_STATE)) + { + GUIMouseButtonEventArgs cea; + cea.button = (int)event.button.button; + cea.x = event.button.x - myVisibleBounds.x; + cea.y = event.button.y - myVisibleBounds.y; + cea.which = event.button.which; + this->SetViewFlag(VIEWFLAG_MOUSEDOWN_STATE,false); + OnMouseUp(cea); + this->MouseUp.Invoke(this,cea); + GUIEventArgs cea2; + OnClick(cea2); + this->Click.Invoke(this,cea); + return true; + } + + } + else if(event.type == SDL_MOUSEMOTION) + { + bool inside = event.motion.x >= myVisibleBounds.x && event.motion.x < myVisibleBounds.x+myVisibleBounds.w && event.motion.y >= myVisibleBounds.y && event.motion.y < myVisibleBounds.y+myVisibleBounds.h; + bool hoverFlag = this->GetViewFlag(VIEWFLAG_HOVER_STATE); + if(inside && !hoverFlag) + { + this->SetViewFlag(VIEWFLAG_HOVER_STATE,true); + GUIEventArgs e; + this->OnEnter(e); + this->Enter.Invoke(this,e); + } + else if(!inside && hoverFlag) + { + this->SetViewFlag(VIEWFLAG_HOVER_STATE,false); + GUIEventArgs e; + this->OnLeave(e); + this->Leave.Invoke(this,e); + } + } + return false; + } + + + std::string View::GetId() + { + return this->id; + } + void View::SetId(std::string id) + { + this->id = id; + } + View* View::FindViewById(std::string id) + { + if(this->GetId() == id) return this; + return nullptr; + } + + View* ContainerView::FindViewById(std::string id) + { + if(this->GetId() == id) return this; + for(size_t i = 0; i ViewCount(); i++) + { + auto v = this->GetViewAt(i); + auto v2 = v->FindViewById(id); + if(v2 != nullptr) return v2; + } + return nullptr; + } + + + View::~View() + { + + } + +} +#endif \ No newline at end of file diff --git a/src/SDL2/Views/AbsoluteView.cpp b/src/SDL2/Views/AbsoluteView.cpp new file mode 100644 index 0000000..2ed50c0 --- /dev/null +++ b/src/SDL2/Views/AbsoluteView.cpp @@ -0,0 +1,109 @@ +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "TessesFramework/SDL2/Views/AbsoluteView.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + void AbsoluteView::OnDraw(SDL_Renderer* renderer, SDL_Rect& myRect) + { + Clipper clipper(renderer,myRect); + SDL_Rect oldClip; + bool isClipped=SDL_RenderIsClipEnabled(renderer); + SDL_RenderGetClipRect(renderer,&oldClip); + if(!isClipped) oldClip = myRect; + + for(auto& ctrl : this->views) + { + SDL_Rect childRect = ctrl.second; + childRect.x += oldClip.x; + childRect.y += oldClip.y; + + + if(clipper.Clip(childRect)) + CallOnDraw(ctrl.first.first,renderer,childRect); + } + + } + bool AbsoluteView::OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds) + { + bool handled=false; + + for(auto& item : this->views) + { + SDL_Rect theirBounds = item.second; + theirBounds.x += myBounds.x; + theirBounds.y += myBounds.y; + + SDL_Rect theirVisibleBounds = item.second; + theirVisibleBounds.x += visibleBounds.x; + theirVisibleBounds.y += visibleBounds.y; + Clipper::ClipRect(theirVisibleBounds, visibleBounds); + + + if(CallOnEvent(item.first.first,event,theirBounds, theirVisibleBounds)) + handled=true; + if(handled && event.type != SDL_MOUSEBUTTONUP && event.type != SDL_MOUSEMOTION) + { + GUISDLEventEventArgs sdle; + sdle.event = event; this->SDLEvent.Invoke(this,sdle); + return true; + } + } + + + if(handled) {GUISDLEventEventArgs sdle; + sdle.event = event; this->SDLEvent.Invoke(this,sdle);return true;} + return View::OnEvent(event,myBounds,visibleBounds); + } + size_t AbsoluteView::ViewCount() + { + return this->views.size(); + } + View* AbsoluteView::GetViewAt(size_t index) + { + return this->views.at(index).first.first; + } + AbsoluteView::AbsoluteView() : ContainerView() + { + + } + void AbsoluteView::Add(SDL_Rect rect, View* view, bool owns) + { + for(auto& item : this->views) + { + if(item.first.first == view) return; + } + this->AssignChildParentToThis(view); + this->views.push_back(std::pair,SDL_Rect>(std::pair(view,owns),rect)); + } + void AbsoluteView::Set(View* view, SDL_Rect rect) + { + for(auto& item : this->views) + { + if(item.first.first == view) + { + item.second = rect; + return; + } + } + } + void AbsoluteView::Remove(View* view) + { + for(auto index = this->views.begin(); index != this->views.end(); index++) + { + if(index->first.first == view) + { + if(index->first.second) delete index->first.first; + this->views.erase(index); + return; + } + } + } + AbsoluteView::~AbsoluteView() + { + for(auto& item : this->views) + { + if(item.first.second) delete item.first.first; + } + } +} +#endif \ No newline at end of file diff --git a/src/SDL2/Views/ButtonView.cpp b/src/SDL2/Views/ButtonView.cpp new file mode 100644 index 0000000..5212f2d --- /dev/null +++ b/src/SDL2/Views/ButtonView.cpp @@ -0,0 +1,77 @@ +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "TessesFramework/SDL2/Views/ButtonView.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + ButtonView::ButtonView() : View() + { + + } + ButtonView::ButtonView(std::string text) : View(text) + { + + } + bool ButtonView::OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& myVisibleBounds) + { + if(this->GetViewFlag(VIEWFLAG_ISACTIVE)) + { + if(event.type == SDL_KEYUP) + { + switch(event.key.keysym.sym) + { + case SDL_KeyCode::SDLK_SPACE: + { + GUIEventArgs e; + this->OnClick(e); + this->Click.Invoke(this,e); + } break; + } + } + } + if(event.type == SDL_MOUSEBUTTONUP) + { + if(event.button.x >= myVisibleBounds.x && event.button.x < myVisibleBounds.x+myVisibleBounds.w && event.button.y >= myVisibleBounds.y && event.button.y < myVisibleBounds.y+myVisibleBounds.h) + { + this->GetWindow()->MakeActive(this); + } + } + return View::OnEvent(event,myBounds,myVisibleBounds); + } + void ButtonView::OnDraw(SDL_Renderer* renderer, SDL_Rect& r) + { + auto win = this->GetWindow(); + SDL_SetRenderDrawColor(renderer,win->palette.accent.r,win->palette.accent.g,win->palette.accent.b,win->palette.accent.a); + SDL_RenderFillRect(renderer,&r); + int textW; + int textH; + win->normal_font->CalculateSize(this->text,textW,textH); + + int x=(r.w/2)-(textW/2); + int y=(r.h/2)-(textH/2); + x+=r.x; + y+=r.y; + + auto isHovering = this->GetViewFlag(VIEWFLAG_HOVER_STATE); + auto isActive = this->GetViewFlag(VIEWFLAG_ISACTIVE); + auto isMouseDown = this->GetViewFlag(VIEWFLAG_MOUSEDOWN_STATE); + + SDL_Color& color = win->palette.GetBorderColor(isHovering,isActive,isMouseDown); + + win->normal_font->Render(renderer,x,y,this->text,color); + SDL_SetRenderDrawColor(renderer,color.r,color.g,color.b,color.a); + + SDL_Rect r2={.x=r.x,.y=r.y,.w=r.w,.h=r.h}; + for(size_t i=0;i < 4; i++) + { + SDL_RenderDrawRect(renderer,&r2); + r2.x++; + r2.y++; + r2.w-=2; + r2.h-=2; + } + + + } + +} +#endif \ No newline at end of file diff --git a/src/SDL2/Views/CheckView.cpp b/src/SDL2/Views/CheckView.cpp new file mode 100644 index 0000000..e241527 --- /dev/null +++ b/src/SDL2/Views/CheckView.cpp @@ -0,0 +1,122 @@ +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "TessesFramework/SDL2/Views/CheckView.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + void CheckView::OnCheckChanged(GUIEventArgs& event) + { + + } + void CheckView::OnDraw(SDL_Renderer* renderer, SDL_Rect& r) + { + auto win = this->GetWindow(); + + const int checkSz=32; + + SDL_Rect checkBoxRect={.x=r.x,.y=r.y+((r.h/2)-(checkSz/2)),.w=checkSz,.h=checkSz}; + + int textW; + int textH; + win->normal_font->CalculateSize(this->text,textW,textH); + + //we only need the y + + int x = r.x+checkSz+8; + int y = r.y+((r.h/2)-(textH/2)); + + win->normal_font->Render(renderer,x,y,text,win->palette.accent); + + + + if(this->GetViewFlag(VIEWFLAG_CHECKED)) + { + SDL_SetRenderDrawColor(renderer,win->palette.accent.r,win->palette.accent.g,win->palette.accent.b,win->palette.accent.a); + + //x=0,y=0.5, x=0.5, y=1 + + int x1=checkBoxRect.x+4; + int y1=checkBoxRect.y+4+((checkSz-8)/2); + int x2=checkBoxRect.x+4+((checkSz-8)/2); + int y2=checkBoxRect.y+4+(checkSz-8); + + int x3=checkBoxRect.x+4+(checkSz-8); + int y3=checkBoxRect.y+4; + + for(int i = 0; i < 4; i++) + { + SDL_RenderDrawLine(renderer,x1,y1-i,x2,y2-i); + SDL_RenderDrawLine(renderer,x2-i,y2,x3-i,y3); + } + } + + auto isHovering = this->GetViewFlag(VIEWFLAG_HOVER_STATE); + auto isActive = this->GetViewFlag(VIEWFLAG_ISACTIVE); + auto isMouseDown = this->GetViewFlag(VIEWFLAG_MOUSEDOWN_STATE); + + SDL_Color& color = win->palette.GetBorderColor(isHovering,isActive,isMouseDown); + + SDL_SetRenderDrawColor(renderer,color.r,color.g,color.b,color.a); + + for(int i = 0; i < 4; i++) + { + SDL_RenderDrawRect(renderer,&checkBoxRect); + checkBoxRect.x++; + checkBoxRect.y++; + checkBoxRect.w-=2; + checkBoxRect.h-=2; + } + } + bool CheckView::OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds) + { + if(this->GetViewFlag(VIEWFLAG_ISACTIVE)) + { + if(event.type == SDL_KEYUP) + { + switch(event.key.keysym.sym) + { + case SDL_KeyCode::SDLK_SPACE: + { + GUIEventArgs e; + + this->SetViewFlag(VIEWFLAG_CHECKED,!this->GetViewFlag(VIEWFLAG_CHECKED)); + this->OnClick(e); + this->Click.Invoke(this,e); + this->OnCheckChanged(e); + this->CheckChanged.Invoke(this,e); + } break; + } + } + } + if(event.type == SDL_MOUSEBUTTONUP) + { + if(event.button.x >= visibleBounds.x && event.button.x < visibleBounds.x+visibleBounds.w && event.button.y >= visibleBounds.y && event.button.y < visibleBounds.y+visibleBounds.h) + { + this->GetWindow()->MakeActive(this); + this->SetViewFlag(VIEWFLAG_CHECKED,!this->GetViewFlag(VIEWFLAG_CHECKED)); + GUIEventArgs e; + this->OnCheckChanged(e); + this->CheckChanged.Invoke(this,e); + } + } + return View::OnEvent(event,myBounds,visibleBounds); + } + + CheckView::CheckView(bool checked, std::string label) : View(label) + { + this->SetViewFlag(VIEWFLAG_CHECKED,checked); + } + CheckView::CheckView() : View() + { + this->SetViewFlag(VIEWFLAG_CHECKED,false); + } + + bool CheckView::GetChecked() + { + return this->GetViewFlag(VIEWFLAG_CHECKED); + } + void CheckView::SetChecked(bool value) + { + this->SetViewFlag(VIEWFLAG_CHECKED,true); + } +} +#endif \ No newline at end of file diff --git a/src/SDL2/Views/LabelView.cpp b/src/SDL2/Views/LabelView.cpp new file mode 100644 index 0000000..f4a760f --- /dev/null +++ b/src/SDL2/Views/LabelView.cpp @@ -0,0 +1,20 @@ +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) +#include "TessesFramework/SDL2/Views/LabelView.hpp" +namespace Tesses::Framework::SDL2::Views +{ + void LabelView::OnDraw(SDL_Renderer* renderer, SDL_Rect& r) + { + auto win =this->GetWindow(); + win->normal_font->Render(renderer,r.x+4,r.y+4,text,win->palette.accent); + } + + LabelView::LabelView() : View() + { + this->flags &= ~VIEWFLAG_TABSTOP; + } + LabelView::LabelView(std::string text) : View(text) + { + this->flags &= ~VIEWFLAG_TABSTOP; + } +} +#endif \ No newline at end of file diff --git a/src/SDL2/Views/ProgressView.cpp b/src/SDL2/Views/ProgressView.cpp new file mode 100644 index 0000000..472f2f4 --- /dev/null +++ b/src/SDL2/Views/ProgressView.cpp @@ -0,0 +1,39 @@ +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) + +#include "TessesFramework/SDL2/Views/ProgressView.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + ProgressView::ProgressView() : ProgressView(0.0) + { + + } + ProgressView::ProgressView(double v) : View() + { + this->value = v; + this->SetViewFlag(VIEWFLAG_TABSTOP,false); + } + void ProgressView::OnDraw(SDL_Renderer* renderer, SDL_Rect& rect) + { + auto win = this->GetWindow(); + + + SDL_SetRenderDrawColor(renderer,win->palette.border_color.r,win->palette.border_color.g,win->palette.border_color.b,win->palette.border_color.a); + + SDL_Rect r2={.x=rect.x,.y=rect.y,.w=rect.w,.h=rect.h}; + for(size_t i=0;i < 4; i++) + { + SDL_RenderDrawRect(renderer,&r2); + r2.x++; + r2.y++; + r2.w-=2; + r2.h-=2; + } + + auto res = (int)((rect.w-8)*(this->value/100.0)); + r2={.x=rect.x+4,.y=rect.y+4,.w=res,.h=rect.h-8}; + SDL_SetRenderDrawColor(renderer,win->palette.accent.r,win->palette.accent.g,win->palette.accent.b,win->palette.accent.a); + SDL_RenderFillRect(renderer,&r2); + } +} +#endif \ No newline at end of file diff --git a/src/SDL2/Views/TextListView.cpp b/src/SDL2/Views/TextListView.cpp new file mode 100644 index 0000000..cd2e961 --- /dev/null +++ b/src/SDL2/Views/TextListView.cpp @@ -0,0 +1,140 @@ +#if defined(TESSESFRAMEWORK_ENABLE_SDL2) + +#include "TessesFramework/SDL2/Views/TextListView.hpp" + +namespace Tesses::Framework::SDL2::Views +{ + TextListView::TextListView() : View() + { + this->firstIndex=0; + this->selected=-1; + } + bool TextListView::OnEvent(SDL_Event& event, SDL_Rect& myBounds, SDL_Rect& visibleBounds) + { + auto win = this->GetWindow(); + auto item_height = win->normal_font->MaxHeight()+8; + auto no_items = (myBounds.h-8) / item_height; + + if(this->items.size() > no_items) + { + + if(this->items.size() - this->firstIndex < no_items) + { + this->firstIndex = this->items.size() - no_items; + } + + } + else { + this->firstIndex=0; + + no_items = (int)this->items.size(); + } + if(event.type == SDL_KEYDOWN) + { + if(this->GetViewFlag(VIEWFLAG_ISACTIVE)) + { + switch(event.key.keysym.sym) + { + case SDLK_HOME: + this->selected=0; + this->firstIndex=0; + break; + case SDLK_END: + this->selected = (int)(this->items.size()-1); + this->firstIndex = (this->selected/no_items) * no_items; + + break; + case SDLK_DOWN: + this->selected++; + if(this->selected < -1 || this->selected >= this->items.size()) + { + this->selected=0; + } + this->firstIndex = (this->selected/no_items) * no_items; + break; + case SDLK_UP: + this->selected--; + if(this->selected < 0 || this->selected >= this->items.size()) + { + this->selected = (int)(this->items.size()-1); + } + + this->firstIndex = (this->selected/no_items) * no_items; + + break; + } + } + } + if(event.type == SDL_MOUSEBUTTONUP && event.button.x >= (visibleBounds.x+4) && event.button.x < (visibleBounds.x+visibleBounds.w)-8 && event.button.y >= (visibleBounds.y+4) && event.button.y < (visibleBounds.y+visibleBounds.h)-8) + { + win->MakeActive(this); + auto myRealY=event.button.y - (myBounds.y+4); + auto yThing = myRealY / item_height; + + if(yThing < no_items) + { + auto high= yThing+this->firstIndex; + this->selected = (int)high; + } + + } + return View::OnEvent(event,myBounds,visibleBounds); + } + + void TextListView::OnDraw(SDL_Renderer* renderer,SDL_Rect& rect) + { + auto win = this->GetWindow(); + auto item_height = win->normal_font->MaxHeight()+8; + auto no_items = (rect.h-8) / item_height; + auto isHovering = this->GetViewFlag(VIEWFLAG_HOVER_STATE); + auto isActive = this->GetViewFlag(VIEWFLAG_ISACTIVE); + auto isMouseDown = this->GetViewFlag(VIEWFLAG_MOUSEDOWN_STATE); + + SDL_Color& color = win->palette.GetBorderColor(isHovering,isActive,isMouseDown); + + SDL_SetRenderDrawColor(renderer,color.r,color.g,color.b,color.a); + + + + + + if(this->items.size() > no_items) + { + + if(this->items.size() - this->firstIndex < no_items) + { + this->firstIndex = this->items.size() - no_items; + } + } + else { + this->firstIndex=0; + no_items = (int)this->items.size(); + } + + SDL_Rect r2={.x=rect.x,.y=rect.y,.w=rect.w,.h=rect.h}; + for(size_t i=0;i < 4; i++) + { + SDL_RenderDrawRect(renderer,&r2); + r2.x++; + r2.y++; + r2.w-=2; + r2.h-=2; + } + + for(int i = 0; i < no_items; i++) + { + int realI = i+(int)firstIndex; + if(realI == this->selected) + { + SDL_SetRenderDrawColor(renderer,win->palette.accent.r,win->palette.accent.g,win->palette.accent.b,win->palette.accent.a); + SDL_Rect r2={.x=rect.x+4,.y=rect.y+4+(item_height*i),.w=rect.w-8,.h=item_height}; + SDL_RenderFillRect(renderer,&r2); + win->normal_font->Render(renderer,rect.x+12,(rect.y+12)+(item_height*i),this->items[realI],color); + } + else { + win->normal_font->Render(renderer,rect.x+12,(rect.y+12)+(item_height*i),this->items[realI],win->palette.accent); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Streams/NetworkStream.cpp b/src/Streams/NetworkStream.cpp index 01424f1..17c16b7 100644 --- a/src/Streams/NetworkStream.cpp +++ b/src/Streams/NetworkStream.cpp @@ -43,7 +43,7 @@ extern "C" { #if defined(GEKKO) extern "C" uint32_t if_config( char *local_ip, char *netmask, char *gateway,bool use_dhcp, int max_retries); -#elif !defined(_WIN32) && !defined(__ANDROID__) && !defined(__SWITCH__) +#elif !defined(_WIN32) && !defined(__ANDROID__) && !defined(__SWITCH__) && !defined(__PS2__) #include #endif @@ -117,7 +117,7 @@ namespace Tesses::Framework::Streams { free(addresses); - #elif defined(__ANDROID__) || defined(__SWITCH__) + #elif defined(__ANDROID__) || defined(__SWITCH__) || defined(__PS2__) #else struct ifaddrs *ifAddrStruct = NULL; @@ -181,7 +181,12 @@ namespace Tesses::Framework::Streams { { memset(addr,0,sizeof(struct sockaddr_storage)); uint8_t ip[16]; + + #if defined(__PS2__) + if(inet_aton(str.c_str(),ip) == 1) + #else if(inet_pton(AF_INET,str.c_str(),ip)==1) + #endif { struct sockaddr_in* inAddr = (struct sockaddr_in*)addr; inAddr->sin_family = AF_INET; @@ -521,25 +526,46 @@ namespace Tesses::Framework::Streams { { return this->success; } - NetworkStream::NetworkStream(bool ipV6,bool datagram) + NetworkStream::NetworkStream(SocketType type) { this->endOfStream=false; this->owns = true; this->success=false; - if(ipV6) + switch(type) { - #if defined(AF_INET6) - this->sock = NETWORK_SOCKET(AF_INET6, datagram ? SOCK_DGRAM : SOCK_STREAM, 0); - if(this->sock >= 0) this->success=true; - #endif - } - else - { - #if defined(AF_INET) - this->sock = NETWORK_SOCKET(AF_INET, datagram ? SOCK_DGRAM : SOCK_STREAM, 0); - if(this->sock >= 0) this->success=true; + case SocketType::ST_IPv4_TCP: + #if defined(AF_INET) + this->sock = NETWORK_SOCKET(AF_INET,SOCK_STREAM, 0); + + #endif + break; + case SocketType::ST_IPv4_UDP: + #if defined(AF_INET) + this->sock = NETWORK_SOCKET(AF_INET,SOCK_DGRAM, 0); + + #endif + break; + + case SocketType::ST_IPv6_TCP: + #if defined(AF_INET6) + this->sock = NETWORK_SOCKET(AF_INET6,SOCK_STREAM, 0); + + #endif + break; + case SocketType::ST_IPv6_UDP: + #if defined(AF_INET6) + this->sock = NETWORK_SOCKET(AF_INET6,SOCK_DGRAM, 0); + + #endif + break; + case SocketType::ST_UNIX: + #if defined(AF_UNIX) + this->sock = NETWORK_SOCKET(AF_UNIX,SOCK_DGRAM, 0); + #endif + break; } + if(this->sock >= 0) this->success=true; } NetworkStream::NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram, bool broadcast, bool supportIPv6) { @@ -672,7 +698,7 @@ namespace Tesses::Framework::Streams { ip = StringifyIP((struct sockaddr*)&storage); port = getPort((struct sockaddr*)&storage); - return new NetworkStream(s,true); + return new NetworkStream((int32_t)s,(bool)true); } size_t NetworkStream::Read(uint8_t* buff, size_t sz) { @@ -797,7 +823,7 @@ bool NetworkStream::CanWrite() { return false; } -NetworkStream::NetworkStream(bool ipV6,bool datagram) +NetworkStream::NetworkStream(SocketType type) { } diff --git a/src/Streams/PtyStream.cpp b/src/Streams/PtyStream.cpp new file mode 100644 index 0000000..35dfbc0 --- /dev/null +++ b/src/Streams/PtyStream.cpp @@ -0,0 +1,132 @@ +#include "TessesFramework/Streams/PtyStream.hpp" +#if !defined(GEKKO) && !defined(__PS2__) && !defined(_WIN32) +#include +#include +#include +#include + + +#endif +namespace Tesses::Framework::Streams { + PtyStream::PtyStream(WindowSize windowSize,std::string filename, std::vector args, std::vector env) + { + #if !defined(GEKKO) && !defined(__PS2__) && !defined(_WIN32) + this->wS = windowSize; + this->eos=false; + winsize sz; + sz.ws_col =(unsigned short)windowSize.Columns; + sz.ws_row = (unsigned short)windowSize.Rows; + sz.ws_xpixel = (unsigned short)windowSize.Width; + sz.ws_ypixel = (unsigned short)windowSize.Height; + termios ios; + cfmakeraw(&ios); + + pid= forkpty(&this->socket,NULL,&ios,&sz); + if(pid == -1) + { + this->eos=true; + } + if(pid == 0) + { + char** argv = new char*[args.size()+1]; + argv[args.size()]=NULL; + char** envp = new char*[env.size()+1]; + envp[env.size()]=NULL; + + for(size_t i = 0; i < args.size();i++) + { + argv[i] = (char*)args[i].c_str(); + } + for(size_t i = 0; i < env.size();i++) + { + envp[i] = (char*)env[i].c_str(); + } + + if(execve(filename.c_str(),argv,envp) == -1) + { + perror("execve returned -1"); + exit(1); + } + } + int flags = fcntl(this->socket, F_GETFL, 0); + if(flags == -1) { + perror("fcntl F_GETFL"); + this->eos=true; + return; + } + flags |= O_NONBLOCK; + + flags=fcntl(this->socket,F_SETFL,flags); + if(flags == -1) { + perror("fcntl F_SETFL"); + + this->eos=true; + return; + } + #endif + } + bool PtyStream::EndOfStream() + { + return this->eos; + } + bool PtyStream::CanRead() + { + return true; + } + bool PtyStream::CanWrite() + { + return true; + } + size_t PtyStream::Read(uint8_t* buff, size_t sz) + { + if(this->eos) return 0; + #if !defined(GEKKO) && !defined(__PS2__) && !defined(_WIN32) + auto res = read(this->socket, buff,sz); + + if(res == -1) + { + if(errno != EAGAIN && errno != EWOULDBLOCK) + this->eos=true; + return 0; + } + return (size_t)res; + #else + return 0; + #endif + } + size_t PtyStream::Write(const uint8_t* buff, size_t sz) + { + #if !defined(GEKKO) && !defined(__PS2__) && !defined(_WIN32) + auto res = write(this->socket, buff,sz); + return res; + #else + return 0; + #endif + } + WindowSize PtyStream::GetWindowSize() + { + return this->wS; + } + void PtyStream::Resize(WindowSize windowSize) + { + #if !defined(GEKKO) && !defined(__PS2__) && !defined(_WIN32) + this->wS = windowSize; + winsize sz; + sz.ws_col =(unsigned short)windowSize.Columns; + sz.ws_row = (unsigned short)windowSize.Rows; + sz.ws_xpixel = (unsigned short)windowSize.Width; + sz.ws_ypixel = (unsigned short)windowSize.Height; + + ioctl(this->socket,TIOCSWINSZ,&sz); + #endif + } + PtyStream::~PtyStream() + { + this->eos=true; + #if !defined(GEKKO) && !defined(__PS2__) && !defined(_WIN32) + close(this->socket); + + kill((pid_t)this->pid,SIGHUP); + #endif + } +} \ No newline at end of file diff --git a/src/TF_Init.cpp b/src/TF_Init.cpp index 25a4bc9..bf6bc45 100644 --- a/src/TF_Init.cpp +++ b/src/TF_Init.cpp @@ -2,6 +2,7 @@ #include "TessesFramework/Streams/NetworkStream.hpp" #include #include +#include #if defined(_WIN32) #include #undef min @@ -35,6 +36,9 @@ static GXRModeObj *rmode = NULL; #endif #if defined(TESSESFRAMEWORK_ENABLE_SDL2) #include +#include +#include +#include "TessesFramework/SDL2/GUI.hpp" #endif namespace Tesses::Framework @@ -87,6 +91,7 @@ namespace Tesses::Framework #if defined(TESSESFRAMEWORK_ENABLE_THREADING) && (defined(GEKKO) || defined(__SWITCH__)) Tesses::Framework::Threading::LookForFinishedThreads(); #endif + if(!isRunningSig) isRunning=false; #if defined(GEKKO) @@ -115,6 +120,9 @@ namespace Tesses::Framework } #endif + #if defined(TESSESFRAMEWORK_ENABLE_SDL2) + Tesses::Framework::SDL2::gui.Update(); + #endif } void TF_SetIsRunning(bool _isRunning) { @@ -135,6 +143,14 @@ namespace Tesses::Framework #if defined(TESSESFRAMEWORK_ENABLE_SDL2) //SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS,"1"); SDL_Init(SDL_INIT_EVERYTHING); + TTF_Init(); + int r = IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF | IMG_INIT_WEBP | IMG_INIT_JXL |IMG_INIT_AVIF; + if(IMG_Init( + r + ) != r) + { + std::cout << "IMG_Init: " << IMG_GetError() << std::endl; + } #endif tzset(); diff --git a/src/TextStreams/StdIOReader.cpp b/src/TextStreams/StdIOReader.cpp new file mode 100644 index 0000000..d210862 --- /dev/null +++ b/src/TextStreams/StdIOReader.cpp @@ -0,0 +1,36 @@ +#include "TessesFramework/TextStreams/StdIOReader.hpp" + + +namespace Tesses::Framework::TextStreams +{ + ConsoleReader::ConsoleReader() + { + + } + bool ConsoleReader::ReadBlock(std::string& str,size_t len) + { + #if defined(_WIN32) + uint8_t* buff = new uint8_t[len]; + #else + uint8_t buff[len]; + #endif + size_t read=0; + size_t readTotal=0; + uint8_t* buffOff=buff; + do { + read=fread(buffOff,1,len,stdin); + if(read != 0) {readTotal+= read;len-=read; buffOff+=read;} + } while(read != 0); + if(readTotal == 0) return false; + str.append((const char*)buff, readTotal); + #if defined(_WIN32) + delete buff; + #endif + return true; + } + + ConsoleReader StdIn() + { + return ConsoleReader(); + } +} \ No newline at end of file diff --git a/src/TextStreams/StdIOWriter.cpp b/src/TextStreams/StdIOWriter.cpp new file mode 100644 index 0000000..12bfcf4 --- /dev/null +++ b/src/TextStreams/StdIOWriter.cpp @@ -0,0 +1,42 @@ +#include "TessesFramework/TextStreams/StdIOWriter.hpp" +#if defined(__PS2__) +#include +#else +#include +#endif +namespace Tesses::Framework::TextStreams +{ + ConsoleWriter::ConsoleWriter(bool isError) : TextWriter() + { + this->isError=isError; + } + + void ConsoleWriter::WriteData(const char* text, size_t len) + { + #if defined(__PS2__) + char lenThing[10];//%.2048s + while(len > 0) { + int b = std::min((int)2047,(int)len); + snprintf(lenThing,18,"%%.%is",b); + + scr_printf(lenThing,text); + + len -= b; + text += b; + } + #else + if(isError) + fwrite(text,1,len,stderr); + else + fwrite(text,1,len,stdout); + #endif + } + ConsoleWriter StdOut() + { + return ConsoleWriter(false); + } + ConsoleWriter StdErr() + { + return ConsoleWriter(true); + } +} \ No newline at end of file diff --git a/src/TextStreams/StreamReader.cpp b/src/TextStreams/StreamReader.cpp index 544eaf7..ee1da18 100644 --- a/src/TextStreams/StreamReader.cpp +++ b/src/TextStreams/StreamReader.cpp @@ -11,6 +11,15 @@ namespace Tesses::Framework::TextStreams { StreamReader::StreamReader(std::filesystem::path path) : StreamReader(new FileStream(path,"rb"),true) { + } + bool StreamReader::Rewind() + { + if(this->strm->CanSeek()) + { + this->strm->Seek((int64_t)0,Tesses::Framework::Streams::SeekOrigin::Begin); + return true; + } + return false; } StreamReader::StreamReader(Stream* strm, bool owns) : TextReader() { diff --git a/src/TextStreams/StringReader.cpp b/src/TextStreams/StringReader.cpp new file mode 100644 index 0000000..5f466e9 --- /dev/null +++ b/src/TextStreams/StringReader.cpp @@ -0,0 +1,38 @@ +#include "TessesFramework/TextStreams/StringReader.hpp" + +namespace Tesses::Framework::TextStreams { + StringReader::StringReader() + { + this->offset=0; + this->str=""; + } + StringReader::StringReader(std::string str) + { + this->offset=0; + this->str=str; + } + size_t& StringReader::GetOffset() + { + return this->offset; + } + std::string& StringReader::GetString() + { + return this->str; + } + bool StringReader::Rewind() + { + this->offset=0; + return true; + } + bool StringReader::ReadBlock(std::string& str,size_t sz) + { + if(this->offset < this->str.size()) + { + size_t len = std::min(sz,this->str.size()-this->offset); + str.insert(str.size(),this->str.data()+this->offset,len); + offset+=len; + return true; + } + return false; + } +} \ No newline at end of file diff --git a/src/TextStreams/StringWriter.cpp b/src/TextStreams/StringWriter.cpp new file mode 100644 index 0000000..ddf30fe --- /dev/null +++ b/src/TextStreams/StringWriter.cpp @@ -0,0 +1,21 @@ +#include "TessesFramework/TextStreams/StringWriter.hpp" + +namespace Tesses::Framework::TextStreams +{ + std::string& StringWriter::GetString() + { + return this->text; + } + StringWriter::StringWriter() : TextWriter() + { + + } + StringWriter::StringWriter(std::string str) : TextWriter() + { + this->text = str; + } + void StringWriter::WriteData(const char* text, size_t len) + { + this->text.append(text,len); + } +} \ No newline at end of file diff --git a/src/TextStreams/TextReader.cpp b/src/TextStreams/TextReader.cpp index 8546023..d9cf42e 100644 --- a/src/TextStreams/TextReader.cpp +++ b/src/TextStreams/TextReader.cpp @@ -2,6 +2,10 @@ namespace Tesses::Framework::TextStreams { + bool TextReader::Rewind() + { + return false; + } int32_t TextReader::ReadChar() { std::string txt; diff --git a/src/TextStreams/TextWriter.cpp b/src/TextStreams/TextWriter.cpp index d27b335..5431280 100644 --- a/src/TextStreams/TextWriter.cpp +++ b/src/TextStreams/TextWriter.cpp @@ -1,7 +1,91 @@ #include "TessesFramework/TextStreams/TextWriter.hpp" +#include "TessesFramework/Http/HttpUtils.hpp" namespace Tesses::Framework::TextStreams { + void TextWriter::Write(int64_t n) + { + std::string text = std::to_string(n); + WriteData(text.c_str(),text.size()); + } + void TextWriter::Write(uint64_t n) + { + std::string text = std::to_string(n); + WriteData(text.c_str(),text.size()); + } + + void TextWriter::Write(const void* ptr) + { + std::string text = "0x"; + uintptr_t ptr2 = (uintptr_t)ptr; + + for(size_t i = 1; i <= sizeof(ptr); i++) + { + uint8_t v = (uint8_t)(ptr2 >> (int)((sizeof(ptr) - i) * 8)); + text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v >> 4)); + text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v)); + } + WriteData(text.c_str(),text.size()); + } + void TextWriter::Write(const char* ptr) + { + WriteData(ptr,strlen(ptr)); + } + void TextWriter::Write(char c) + { + WriteData(&c,1); + } + void TextWriter::Write(double d) + { + std::string text = std::to_string(d); + WriteData(text.c_str(),text.size()); + } + + + void TextWriter::WriteLine(int64_t n) + { + std::string text = std::to_string(n); + text.append(newline); + WriteData(text.c_str(),text.size()); + } + void TextWriter::WriteLine(uint64_t n) + { + std::string text = std::to_string(n); + text.append(newline); + WriteData(text.c_str(),text.size()); + } + void TextWriter::WriteLine(const void* ptr) + { + std::string text = "0x"; + uintptr_t ptr2 = (uintptr_t)ptr; + + for(size_t i = 1; i <= sizeof(ptr); i++) + { + uint8_t v = (uint8_t)(ptr2 >> (int)((sizeof(ptr) - i) * 8)); + text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v >> 4)); + text.push_back(Tesses::Framework::Http::HttpUtils::NibbleToHex(v)); + } + text.append(newline); + WriteData(text.c_str(),text.size()); + } + void TextWriter::WriteLine(const char* ptr) + { + std::string text = ptr; + text.append(newline); + WriteData(text.c_str(),text.size()); + } + void TextWriter::WriteLine(char c) + { + std::string text = {c}; + text.append(newline); + WriteData(text.c_str(),text.size()); + } + void TextWriter::WriteLine(double d) + { + std::string text = std::to_string(d); + text.append(newline); + WriteData(text.c_str(),text.size()); + } TextWriter::TextWriter() { #if defined(WIN32) || defined(_WIN32)