From 9a81f58a76feab36d70d4b989ccc304820c930ad Mon Sep 17 00:00:00 2001
From: Benedict Gaster <benedict.gaster@gmail.com>
Date: Sun, 29 Nov 2020 22:37:32 +0000
Subject: [PATCH] added app class

---
 README.md            |   3 +-
 include/app.hpp      |  82 ++++++++++++++++++++++++++
 include/context.hpp  | 122 +++++++++++++++++++++++++++++++++++++++
 include/events.hpp   |  71 +++++++++++++++++++++++
 include/graphics.hpp |  60 +++++++++++++++++++
 main.cpp             |  70 +++++++++++++++++++++--
 src/app.cpp          |  85 +++++++++++++++++++++++++++
 src/context.cpp      |  56 ++++++++++++++++++
 src/graphics.cpp     | 133 -------------------------------------------
 9 files changed, 541 insertions(+), 141 deletions(-)
 create mode 100644 include/app.hpp
 create mode 100644 include/context.hpp
 create mode 100644 include/events.hpp
 create mode 100644 src/app.cpp
 create mode 100644 src/context.cpp
 delete mode 100644 src/graphics.cpp

diff --git a/README.md b/README.md
index a71a8c3..22abbbe 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
 # OSX
 
-
-clang++ -L./osx/lib/ -lSDL2 -I./osx/include/ main.cpp -framework OpenGL
+clang++ -std=c++17 -L./osx/lib/ -lSDL2 -I./osx/include/ -I./include src/context.cpp src/app.cpp main.cpp -framework OpenGL
 
 
 # Windows
diff --git a/include/app.hpp b/include/app.hpp
new file mode 100644
index 0000000..4e56f3c
--- /dev/null
+++ b/include/app.hpp
@@ -0,0 +1,82 @@
+/**
+ * @file 
+ * @author Benedict R. Gaster
+ */
+#pragma once
+
+#include <events.hpp>
+#include <context.hpp>
+
+namespace uwe {
+
+    class App {
+    private:
+        Context context_;
+
+        bool output_framerate_;
+    public:
+        App();
+        ~App();
+
+        void init(int width, int height);
+        void run();
+
+        /// toggles output framerate to console
+        void toggle_framerate() {
+            output_framerate_ = !output_framerate_;
+        }
+
+        virtual void begin() = 0;
+        virtual void update() = 0;
+        virtual void draw() = 0;
+
+        virtual void key_pressed(Scancode scancode, bool is_repeat) {
+
+        };
+
+        virtual void key_released(Scancode scancode) {
+            
+        };
+
+        virtual void mouse_pressed() {
+
+        };
+
+        virtual void mouse_released() {
+            
+        };
+
+        virtual void mouse_moved() {
+            
+        };
+
+        // API
+        int get_framebuffer_width() const {
+            return context_.get_tex_width();
+        }
+
+        int get_framebuffer_height() const {
+            return context_.get_tex_height();
+        }
+
+        uint8_t& framebuffer(size_t offset) {
+            return context_.pixel(offset);
+        }
+
+        void set_framebuffer(size_t offset, Colour colour) {
+            context_.set_pixel(offset, colour);
+        }
+
+        void clear(Colour colour) {
+            SDL_SetRenderDrawColor( 
+                context_.get_renderer(), 
+                colour.blue_,
+                colour.green_,
+                colour.red_,
+                colour.alpha_ );
+            SDL_RenderClear( context_.get_renderer() );
+        }
+    };
+
+    void run();
+} // namespace uwe
\ No newline at end of file
diff --git a/include/context.hpp b/include/context.hpp
new file mode 100644
index 0000000..68afe6e
--- /dev/null
+++ b/include/context.hpp
@@ -0,0 +1,122 @@
+/**
+ * @file 
+ * @author Benedict R. Gaster
+ */
+#pragma once
+
+#include <vector>
+
+#include <SDL2/SDL.h>
+
+#include <events.hpp>
+
+namespace uwe {
+    class Colour {
+    public:
+        uint8_t blue_;
+        uint8_t green_;
+        uint8_t red_;
+        uint8_t alpha_;
+
+        Colour(uint8_t red, uint8_t green,  uint8_t  blue, uint8_t alpha):
+            red_{red},
+            green_{green},
+            blue_{blue},
+            alpha_{alpha} {
+        }
+
+        static Colour red() {
+            return Colour{ 255, 0, 0, SDL_ALPHA_OPAQUE };
+        }
+
+        static Colour green() {
+            return Colour{ 0, 255, 0, SDL_ALPHA_OPAQUE };
+        }
+
+        static Colour blue() {
+            return Colour{ 0, 0, 255, SDL_ALPHA_OPAQUE };
+        }
+
+        static Colour white() {
+            return Colour{ 255, 255, 255, SDL_ALPHA_OPAQUE };
+        }
+
+        static Colour black() {
+            return Colour{ 0, 0, 0, SDL_ALPHA_OPAQUE };
+        }
+    };
+
+    class Context {
+    public:
+        /** @brief Create a context
+         * @param width an integer setting the window width
+         * @param height an integer setting the window height
+         */
+        Context();
+
+        /// Destory context
+        ~Context();
+
+        void init(int width, int height);
+
+        /// Output details of GPU backend and texture formats
+        void dump_renderer_info();
+
+        /// run the control loop, returns only when user exits
+        //void run(App* app);
+
+        int get_height() const {
+            return height_;
+        }
+
+        int get_width() const {
+            return width_;
+        }
+
+        SDL_Window* get_window() const {
+            return window_;
+        }
+
+        SDL_Renderer* get_renderer() const {
+            return renderer_;
+        }
+
+        SDL_Texture* get_texture() const {
+            return texture_;
+        }
+
+        std::vector< uint8_t >& get_pixels() {
+            return pixels_;
+        }
+
+        uint8_t& pixel(size_t offset) {
+            return pixels_[offset];
+        }
+
+        void set_pixel(size_t offset, Colour colour) {
+            pixels_[offset + 0] = colour.blue_;
+            pixels_[offset + 1] = colour.green_;
+            pixels_[offset + 2] = colour.red_;
+            pixels_[offset + 3] = colour.alpha_;
+        }
+
+        int get_tex_width() const {
+            return tex_width_;
+        }
+
+        int get_tex_height() const {
+            return tex_height_;
+        }
+    private:
+        int height_;                ///< window height
+        int width_;                 ///< window width
+        int tex_width_;             ///< texture width
+        int tex_height_;            ///< texture height
+        SDL_Window* window_;        ///< window handle
+        SDL_Renderer* renderer_;    ///< graphics backend handle
+        SDL_Texture* texture_;      ///< pixel buffer texture handle
+
+        std::vector< uint8_t > pixels_; ///< pixels representing framebuffer
+    };
+}
+
diff --git a/include/events.hpp b/include/events.hpp
new file mode 100644
index 0000000..328beeb
--- /dev/null
+++ b/include/events.hpp
@@ -0,0 +1,71 @@
+/**
+ * @file 
+ * @author Benedict R. Gaster
+ */
+#pragma once
+
+#include <vector>
+
+#include <SDL2/SDL.h>
+
+namespace uwe {
+
+    // event types
+    enum class Event {
+        EVENT_QUIT = SDL_QUIT,
+        EVENT_KEYDOWN = SDL_KEYDOWN,
+    };
+
+    // scancodes
+    enum class Scancode {
+        D_0 = SDL_SCANCODE_0,
+        D_1 = SDL_SCANCODE_1,
+        D_2 = SDL_SCANCODE_2,
+        D_3 = SDL_SCANCODE_3,
+        D_4 = SDL_SCANCODE_4,
+        D_5 = SDL_SCANCODE_5,
+        D_6 = SDL_SCANCODE_6,
+        D_7 = SDL_SCANCODE_7,
+        D_8 = SDL_SCANCODE_8,
+        D_9 = SDL_SCANCODE_9,
+
+        A = SDL_SCANCODE_A,
+        B = SDL_SCANCODE_B,
+        C = SDL_SCANCODE_C,
+        D = SDL_SCANCODE_D,
+        E = SDL_SCANCODE_E,
+        F = SDL_SCANCODE_F,
+        G = SDL_SCANCODE_G,
+        H = SDL_SCANCODE_H,
+        I = SDL_SCANCODE_I,
+        J = SDL_SCANCODE_J,
+        K = SDL_SCANCODE_K,
+        L = SDL_SCANCODE_L,
+        M = SDL_SCANCODE_M,
+        N = SDL_SCANCODE_N,
+        O = SDL_SCANCODE_O,
+        P = SDL_SCANCODE_P,
+        Q = SDL_SCANCODE_Q,
+        R = SDL_SCANCODE_R,
+        S = SDL_SCANCODE_S,
+        T = SDL_SCANCODE_T,
+        U = SDL_SCANCODE_U,
+        V = SDL_SCANCODE_V,
+        W = SDL_SCANCODE_W,
+        X = SDL_SCANCODE_X,
+        Y = SDL_SCANCODE_Y,
+        Z = SDL_SCANCODE_Z,    
+
+        LSHIFT = SDL_SCANCODE_LSHIFT,
+        RSHIFT = SDL_SCANCODE_RSHIFT,
+        RETURN = SDL_SCANCODE_RETURN,
+        BACKSPACE = SDL_SCANCODE_BACKSPACE,
+        SPACE = SDL_SCANCODE_SPACE,
+
+        UP = SDL_SCANCODE_UP,
+        DOWN = SDL_SCANCODE_DOWN,
+        LEFT = SDL_SCANCODE_LEFT,
+        RIGHT = SDL_SCANCODE_RIGHT,
+    };
+
+} // namespace uwe
\ No newline at end of file
diff --git a/include/graphics.hpp b/include/graphics.hpp
index f630567..9607504 100644
--- a/include/graphics.hpp
+++ b/include/graphics.hpp
@@ -8,7 +8,67 @@
 
 #include <SDL2/SDL.h>
 
+#include <drawcontext.hpp>
+
 namespace uwe {
+    // event types
+    enum class Event {
+        EVENT_QUIT = SDL_QUIT,
+        EVENT_KEYDOWN = SDL_KEYDOWN,
+    };
+
+    // scancodes
+    enum class Scancode {
+        SCANCODE_0 = SDL_SCANCODE_0,
+        SCANCODE_1 = SDL_SCANCODE_1,
+        SCANCODE_2 = SDL_SCANCODE_2,
+        SCANCODE_3 = SDL_SCANCODE_3,
+        SCANCODE_4 = SDL_SCANCODE_4,
+        SCANCODE_5 = SDL_SCANCODE_5,
+        SCANCODE_6 = SDL_SCANCODE_6,
+        SCANCODE_7 = SDL_SCANCODE_7,
+        SCANCODE_8 = SDL_SCANCODE_8,
+        SCANCODE_9 = SDL_SCANCODE_9,
+
+        SCANCODE_A = SDL_SCANCODE_A,
+        SCANCODE_B = SDL_SCANCODE_B,
+        SCANCODE_C = SDL_SCANCODE_C,
+        SCANCODE_D = SDL_SCANCODE_D,
+        SCANCODE_E = SDL_SCANCODE_E,
+        SCANCODE_F = SDL_SCANCODE_F,
+        SCANCODE_G = SDL_SCANCODE_G,
+        SCANCODE_H = SDL_SCANCODE_H,
+        SCANCODE_I = SDL_SCANCODE_I,
+        SCANCODE_J = SDL_SCANCODE_J,
+        SCANCODE_K = SDL_SCANCODE_K,
+        SCANCODE_L = SDL_SCANCODE_L,
+        SCANCODE_M = SDL_SCANCODE_M,
+        SCANCODE_N = SDL_SCANCODE_N,
+        SCANCODE_O = SDL_SCANCODE_O,
+        SCANCODE_P = SDL_SCANCODE_P,
+        SCANCODE_Q = SDL_SCANCODE_Q,
+        SCANCODE_R = SDL_SCANCODE_R,
+        SCANCODE_S = SDL_SCANCODE_S,
+        SCANCODE_T = SDL_SCANCODE_T,
+        SCANCODE_U = SDL_SCANCODE_U,
+        SCANCODE_V = SDL_SCANCODE_V,
+        SCANCODE_W = SDL_SCANCODE_W,
+        SCANCODE_X = SDL_SCANCODE_X,
+        SCANCODE_Y = SDL_SCANCODE_Y,
+        SCANCODE_Z = SDL_SCANCODE_Z,    
+
+        SCANCODE_LSHIFT = SDL_SCANCODE_LSHIFT,
+        SCANCODE_RSHIFT = SDL_SCANCODE_RSHIFT,
+        SCANCODE_RETURN = SDL_SCANCODE_RETURN,
+        SCANCODE_BACKSPACE = SDL_SCANCODE_BACKSPACE,
+        SCANCODE_SPACE = SDL_SCANCODE_SPACE,
+
+        SCANCODE_UP = SDL_SCANCODE_UP,
+        SCANCODE_DOWN = SDL_SCANCODE_DOWN,
+        SCANCODE_LEFT = SDL_SCANCODE_LEFT,
+        SCANCODE_RIGHT = SDL_SCANCODE_RIGHT,
+    };
+
     class Context {
     public:
         /** @brief Create a context
diff --git a/main.cpp b/main.cpp
index b448c16..738467b 100644
--- a/main.cpp
+++ b/main.cpp
@@ -3,18 +3,76 @@
 #include <vector>
 #include <cstring>
 
-#include <graphics.hpp>
+#include <context.hpp>
+#include <app.hpp>
 
 const int width = 640;
 const int height = 480;
 
-int main( int argc, char** argv )
-{
-    uwe::Context context{width, height};
+class MyApp: public uwe::App {
+private:
+    int width_;
+    int height_;
+public:
+    MyApp(int width, int height);
+    ~MyApp();
 
-    context.dump_renderer_info();
+    void begin() override;
+    void update() override;
+    void draw() override;
 
-    context.run();
+    void key_pressed(uwe::Scancode scancode, bool repeat) override;
+};
+
+MyApp::MyApp(int width, int height): 
+    width_{width},
+    height_{height} {
+        init(width, height);
+}
+
+MyApp::~MyApp() {
+
+}
+
+void MyApp::begin() {
+
+}
+
+void MyApp::update() {
+
+}
+
+void MyApp::key_pressed(uwe::Scancode scancode, bool repeat) {
+    if( scancode == uwe::Scancode::L) {
+        toggle_framerate();
+    }
+}
+
+void MyApp::draw() {
+    clear(uwe::Colour::black());
+
+    for( unsigned int i = 0; i < 1000; i++ ) {
+        const unsigned int x = rand() % get_framebuffer_width();
+        const unsigned int y = rand() % get_framebuffer_height();
+
+        const unsigned int offset = ( get_framebuffer_width() * 4 * y ) + x * 4;
+
+        set_framebuffer(
+            offset, 
+            uwe::Colour{
+                static_cast<uint8_t>(rand() % 256), 
+                static_cast<uint8_t>(rand() % 256), 
+                static_cast<uint8_t>(rand() % 256), 
+                SDL_ALPHA_OPAQUE});
+    }
+}
+
+
+int main( int argc, char** argv ) {
+    uwe::App* app = new MyApp{width, height};
+
+    //app->dump_renderer_info();
+    app->run();
 
     return 0;
 }
\ No newline at end of file
diff --git a/src/app.cpp b/src/app.cpp
new file mode 100644
index 0000000..b9efeb8
--- /dev/null
+++ b/src/app.cpp
@@ -0,0 +1,85 @@
+/**
+ * @file 
+ * @author Benedict R. Gaster
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <cstring>
+
+#include <app.hpp>
+
+namespace uwe {
+
+    App::App() : 
+        output_framerate_{false} {
+    }
+
+    App::~App() {
+
+    }
+
+    void App::init(int width, int height) {
+        context_.init(width, height);
+    }
+
+    void App::run() {
+        SDL_Event event;
+        bool running = true;
+        
+        unsigned int frames = 0;
+        Uint64 start = SDL_GetPerformanceCounter();
+
+        begin();
+
+        while( running )
+        {
+            while( SDL_PollEvent( &event ) ) {
+                if( ( SDL_QUIT == event.type ) ||
+                    ( SDL_KEYDOWN == event.type && SDL_SCANCODE_ESCAPE == event.key.keysym.scancode ) ) {
+                    running = false;
+                    break;
+                }
+                else if (event.type == SDL_KEYDOWN) {
+                    key_pressed(static_cast<Scancode>(event.key.keysym.scancode), event.key.repeat != 0);
+                }
+                else if (event.type == SDL_KEYUP) {
+                    key_released(static_cast<Scancode>(event.key.keysym.scancode));
+                }
+            }
+
+            update();
+            
+            draw();
+
+            // copy framebuffer pixels to GU texture for rendering
+            SDL_UpdateTexture(
+                context_.get_texture(),
+                NULL,
+                context_.get_pixels().data(),
+                context_.get_tex_width() * 4);
+
+            SDL_RenderCopy( context_.get_renderer(), context_.get_texture(), NULL, NULL );
+            SDL_RenderPresent( context_.get_renderer() );
+            
+            frames++;
+            if (output_framerate_) {
+                const uint64_t end = SDL_GetPerformanceCounter();
+                const static uint64_t freq = SDL_GetPerformanceFrequency();
+                const double seconds = ( end - start ) / static_cast< double >( freq );
+                if( seconds > 2.0 )
+                {
+                    std::cout
+                        << frames << " frames in "
+                        << std::setprecision(1) << std::fixed << seconds << " seconds = "
+                        << std::setprecision(1) << std::fixed << frames / seconds << " FPS ("
+                        << std::setprecision(3) << std::fixed << ( seconds * 1000.0 ) / frames << " ms/frame)"
+                        << std::endl;
+                    start = end;
+                    frames = 0;
+                }
+            }
+        }
+
+    }
+} // namespace uwe
\ No newline at end of file
diff --git a/src/context.cpp b/src/context.cpp
new file mode 100644
index 0000000..035143c
--- /dev/null
+++ b/src/context.cpp
@@ -0,0 +1,56 @@
+#include <iostream>
+
+#include <context.hpp>
+#include <iomanip>
+#include <cstring>
+
+namespace uwe {
+    Context::Context() {
+    } 
+
+    void Context::init(int width, int height) {
+        width_ =  width; 
+        height_ = height;
+        tex_width_ = width;
+        tex_height_ = height;
+        pixels_ = std::vector<uint8_t>(tex_width_ * tex_height_ *4),
+        SDL_Init( SDL_INIT_EVERYTHING );
+
+        window_ = SDL_CreateWindow(
+            "SDL2",
+            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+            width_, height_,
+            SDL_WINDOW_SHOWN);
+
+        renderer_ = SDL_CreateRenderer(
+            window_,
+            -1,
+            SDL_RENDERER_ACCELERATED);
+
+        texture_ = SDL_CreateTexture(
+            renderer_,
+            SDL_PIXELFORMAT_ARGB8888,
+            SDL_TEXTUREACCESS_STREAMING,
+            tex_width_, 
+            tex_height_);
+    }
+
+    Context::~Context() {
+        SDL_DestroyRenderer( renderer_ );
+        SDL_DestroyWindow( window_ );
+        SDL_Quit();
+    }
+
+    // void Context::run() {
+    
+
+    void Context::dump_renderer_info() {
+        SDL_RendererInfo info;
+        SDL_GetRendererInfo( renderer_, &info );
+        std::cout << "Renderer name: " << info.name << std::endl;
+        std::cout << "Texture formats: " << std::endl;
+        for( uint32_t i = 0; i < info.num_texture_formats; i++ ) {
+            std::cout << SDL_GetPixelFormatName( info.texture_formats[i] ) << std::endl;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/graphics.cpp b/src/graphics.cpp
deleted file mode 100644
index e4501d1..0000000
--- a/src/graphics.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-#include <iostream>
-
-#include <graphics.hpp>
-#include <iomanip>
-#include <cstring>
-
-namespace uwe {
-
-    Context::Context(int width, int height) : 
-        width_{width}, 
-        height_{height},
-        tex_width_{width},
-        tex_height_{height},
-        pixels_(tex_width_ * tex_height_ * 4, 0) {
-        SDL_Init( SDL_INIT_EVERYTHING );
-
-        window_ = SDL_CreateWindow(
-            "SDL2",
-            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
-            width_, height_,
-            SDL_WINDOW_SHOWN);
-
-        renderer_ = SDL_CreateRenderer(
-            window_,
-            -1,
-            SDL_RENDERER_ACCELERATED);
-
-        texture_ = SDL_CreateTexture(
-            renderer_,
-            SDL_PIXELFORMAT_ARGB8888,
-            SDL_TEXTUREACCESS_STREAMING,
-            tex_width_, 
-            tex_height_);
-    }
-
-    Context::~Context() {
-        SDL_DestroyRenderer( renderer_ );
-        SDL_DestroyWindow( window_ );
-        SDL_Quit();
-    }
-
-    void Context::run() {
-        SDL_Event event;
-        bool running = true;
-        bool useLocktexture = false;
-        
-        unsigned int frames = 0;
-        Uint64 start = SDL_GetPerformanceCounter();
-
-        while( running )
-        {
-
-            SDL_SetRenderDrawColor( renderer_, 0, 0, 0, SDL_ALPHA_OPAQUE );
-            SDL_RenderClear( renderer_ );
-
-            while( SDL_PollEvent( &event ) ) {
-                if( ( SDL_QUIT == event.type ) ||
-                    ( SDL_KEYDOWN == event.type && SDL_SCANCODE_ESCAPE == event.key.keysym.scancode ) )
-                {
-                    running = false;
-                    break;
-                }
-                if( SDL_KEYDOWN == event.type && SDL_SCANCODE_L == event.key.keysym.scancode )
-                {
-                    useLocktexture = !useLocktexture;
-                    std::cout << "Using " << ( useLocktexture ? "SDL_LockTexture() + memcpy()" : "SDL_UpdateTexture()" ) << std::endl;
-                }
-            }
-            
-            // splat down some random pixels
-            for( unsigned int i = 0; i < 1000; i++ ) {
-                const unsigned int x = rand() % tex_width_;
-                const unsigned int y = rand() % tex_height_;
-
-                const unsigned int offset = ( tex_width_ * 4 * y ) + x * 4;
-                pixels_[ offset + 0 ] = rand() % 256;        // b
-                pixels_[ offset + 1 ] = rand() % 256;        // g
-                pixels_[ offset + 2 ] = rand() % 256;        // r
-                pixels_[ offset + 3 ] = SDL_ALPHA_OPAQUE;    // a
-            }
-
-            if( useLocktexture ) {
-                unsigned char* lockedPixels = nullptr;
-                int pitch = 0;
-                SDL_LockTexture(
-                    texture_,
-                    NULL,
-                    reinterpret_cast< void** >( &lockedPixels ),
-                    &pitch
-                    );
-                std::memcpy( lockedPixels, pixels_.data(), pixels_.size() );
-                SDL_UnlockTexture( texture_ );
-            }
-            else {
-                SDL_UpdateTexture(
-                    texture_,
-                    NULL,
-                    pixels_.data(),
-                    tex_width_ * 4);
-            }
-
-            SDL_RenderCopy( renderer_, texture_, NULL, NULL );
-            SDL_RenderPresent( renderer_ );
-            
-            frames++;
-            const uint64_t end = SDL_GetPerformanceCounter();
-            const static uint64_t freq = SDL_GetPerformanceFrequency();
-            const double seconds = ( end - start ) / static_cast< double >( freq );
-            if( seconds > 2.0 )
-            {
-                std::cout
-                    << frames << " frames in "
-                    << std::setprecision(1) << std::fixed << seconds << " seconds = "
-                    << std::setprecision(1) << std::fixed << frames / seconds << " FPS ("
-                    << std::setprecision(3) << std::fixed << ( seconds * 1000.0 ) / frames << " ms/frame)"
-                    << std::endl;
-                start = end;
-                frames = 0;
-            }
-        }
-
-    }
-
-    void Context::dump_renderer_info() {
-        SDL_RendererInfo info;
-        SDL_GetRendererInfo( renderer_, &info );
-        std::cout << "Renderer name: " << info.name << std::endl;
-        std::cout << "Texture formats: " << std::endl;
-        for( uint32_t i = 0; i < info.num_texture_formats; i++ ) {
-            std::cout << SDL_GetPixelFormatName( info.texture_formats[i] ) << std::endl;
-        }
-    }
-}
\ No newline at end of file
-- 
GitLab