# HG changeset patch # User David Demelier # Date 1460117807 -7200 # Node ID 56cc058200b5e954686eaf7b96bc521cbcef4a0b # Parent dc47ce56ce360171cb8d9614a5cf7ff89075d336 Client: add basic mlk-client code with an example, #472 diff -r dc47ce56ce36 -r 56cc058200b5 client/CMakeLists.txt --- a/client/CMakeLists.txt Wed Apr 06 13:33:30 2016 +0200 +++ b/client/CMakeLists.txt Fri Apr 08 14:16:47 2016 +0200 @@ -23,37 +23,6 @@ main.cpp ) -add_executable(client ${FILES}) - -target_include_directories( - client - PRIVATE - ${client_SOURCE_DIR} -) - -target_link_libraries( - client - libcommon - libclient -) - -add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/resources/images/mokodemo.png - DEPENDS ${client_SOURCE_DIR}/resources/images/mokodemo.png - COMMAND - ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/resources/images - COMMAND - ${CMAKE_COMMAND} -E copy ${client_SOURCE_DIR}/resources/images/mokodemo.png ${CMAKE_BINARY_DIR}/resources/images/mokodemo.png -) - -add_custom_target( - client-resources ALL - DEPENDS ${CMAKE_BINARY_DIR}/resources/images/mokodemo.png - SOURCES ${client_SOURCE_DIR}/resources/images/mokodemo.png -) - -add_dependencies(client-resources client) - -if (APPLE) - target_compile_options(libcommon PRIVATE "-Wno-deprecated-declarations") -endif () +add_executable(mlk-client ${FILES}) +target_include_directories(mlk-client PRIVATE ${client_SOURCE_DIR}) +target_link_libraries(mlk-client libcommon libclient) diff -r dc47ce56ce36 -r 56cc058200b5 client/main.cpp --- a/client/main.cpp Wed Apr 06 13:33:30 2016 +0200 +++ b/client/main.cpp Fri Apr 08 14:16:47 2016 +0200 @@ -1,7 +1,7 @@ /* - * main.cpp -- main client files + * main.cpp -- main client file * - * Copyright (c) 2014 David Demelier + * Copyright (c) 2013-2016 David Demelier * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,225 +16,178 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#if 0 - +#include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + +#include +#include -using namespace std::literals::chrono_literals; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -// TODO delete this... just for fun -bool goRight = true; -bool goDown = true; -const int mokoSize = 300; +using namespace malikania; + +namespace { + +int usage() +{ + std::cerr << "usage: mlk-client directory\n"; + + return 1; +} -void bounce(malikania::Window& window, int &x, int &y) { - malikania::Size resolution = window.getWindowResolution(); - int width = resolution.width; - int height = resolution.height; - if (y < 10) { - goDown = true; - y += 1; - } - if (goDown && y < height - mokoSize) { - // Moko falls - y += 0.2 * y; +duk::Context init() +{ + duk::Context ctx; + + /* TODO: Put Malikania global somewhere else */ + duk::putGlobal(ctx, "Malikania", duk::Object()); + + loadMalikaniaAnimation(ctx); + loadMalikaniaAnimator(ctx); + loadMalikaniaColor(ctx); + loadMalikaniaFont(ctx); + loadMalikaniaImage(ctx); + loadMalikaniaLine(ctx); + loadMalikaniaPoint(ctx); + loadMalikaniaRectangle(ctx); + loadMalikaniaSize(ctx); + loadMalikaniaSprite(ctx); + loadMalikaniaWindow(ctx); + + return ctx; +} + +void start(duk::Context &ctx) +{ + duk::getGlobal(ctx, "start"); + + if (duk::is(ctx, -1)) { + duk::getGlobal(ctx, "\xff""\xff""window"); + duk::pcall(ctx, 1); + duk::pop(ctx); } else { - // Moko will bounce!!! - goDown = false; - } - if (!goDown && y > 0) { - y -= 0.1 * y; - } else { - goDown = true; - } - - if (goRight && x < width - mokoSize) { - x += 4; - } else { - goRight = false; - } - if (!goRight && x > 0) { - x -= 4; - } else { - goRight = true; + duk::pop(ctx); } } -// End TODO +void update(duk::Context &ctx) +{ + duk::getGlobal(ctx, "update"); -int main(void) -{ - malikania::Window mainWindow; + if (duk::is(ctx, -1)) { + duk::pcall(ctx, 0); + duk::pop(ctx); + } else { + duk::pop(ctx); + } +} - bool isBouncing = false; +void draw(duk::Context &ctx) +{ + duk::getGlobal(ctx, "draw"); - int mokoPositionX = 0; - int mokoPositionY = 0; + if (duk::is(ctx, -1)) { + duk::getGlobal(ctx, "\xff""\xff""window"); + duk::pcall(ctx, 1); + duk::pop(ctx); + } else { + duk::pop(ctx); + } +} - std::map keyPressed = { {SDLK_UP, false}, {SDLK_DOWN, false}, {SDLK_RIGHT, false}, {SDLK_LEFT, false} }; +int run(duk::Context &ctx) +{ + bool running = true; - mainWindow.setOnKeyDown([&mainWindow, &mokoPositionX, &mokoPositionY, &isBouncing, &keyPressed](int sdlKey) { - switch (sdlKey) { - case SDLK_ESCAPE: - mainWindow.close(); - break; - case SDLK_UP: - keyPressed[SDLK_UP] = true; - break; - case SDLK_DOWN: - keyPressed[SDLK_DOWN] = true; - break; - case SDLK_RIGHT: - keyPressed[SDLK_RIGHT] = true; - break; - case SDLK_LEFT: - keyPressed[SDLK_LEFT] = true; - break; - case SDLK_m: - isBouncing = !isBouncing; - break; + /* js-window use duk::Pointer at the moment so store it from there temporarily */ + duk::putGlobal(ctx, "\xff""\xff""window", duk::Pointer{new Window}); + + Window *window = duk::getGlobal>(ctx, "\xff""\xff""window"); + + window->setOnQuit([&] () { + running = false; + }); + window->setOnKeyDown([&] (unsigned key) { + duk::getGlobal(ctx, "keyDown"); + + if (duk::is(ctx, -1)) { + duk::push(ctx, static_cast(key)); + duk::pcall(ctx, 1); + duk::pop(ctx); + } else { + duk::pop(ctx); + } + }); + window->setOnKeyDown([&] (unsigned key) { + duk::getGlobal(ctx, "keyUp"); + + if (duk::is(ctx, -1)) { + duk::push(ctx, static_cast(key)); + duk::pcall(ctx, 1); + duk::pop(ctx); + } else { + duk::pop(ctx); } }); - mainWindow.setOnKeyUp([&keyPressed](int sdlKey) { - switch (sdlKey) { - case SDLK_UP: - keyPressed[SDLK_UP] = false; - break; - case SDLK_DOWN: - keyPressed[SDLK_DOWN] = false; - break; - case SDLK_RIGHT: - keyPressed[SDLK_RIGHT] = false; - break; - case SDLK_LEFT: - keyPressed[SDLK_LEFT] = false; - break; - } - }); + start(ctx); - int animationStep = 1; - mainWindow.setOnRefresh([&mainWindow, &keyPressed, &animationStep](){ - if (keyPressed[SDLK_LEFT]) { - std::string animationState = "left" + std::to_string(animationStep > 4 ? 4 : animationStep++); - } else if (keyPressed[SDLK_RIGHT]) { - std::string animationState = "right" + std::to_string(animationStep > 4 ? 4 : animationStep++); - } else if (keyPressed[SDLK_DOWN]) { - std::string animationState = "down" + std::to_string(animationStep > 4 ? 4 : animationStep++); - } else { - animationStep = 1; - } - }); - - malikania::Sprite testSprite = malikania::Sprite::fromJson(mainWindow, malikania::JsonDocument( - "{\"image\": \"resources/images/mokodemo.png\", \"alias\": \"testSprite\", \"cell\": [300, 300], \"size\": [1200, 900]}" - ).toObject()); - - std::shared_ptr font = std::make_shared("resources/fonts/DejaVuSans.ttf", 48); - malikania::Label testLabel("Malikania !!! Youpi !", font, {0, 0, 100, 50}); - - std::shared_ptr testAnimation = std::make_shared(malikania::Animation::fromJson(mainWindow, malikania::JsonDocument( - std::string("{\"sprite\": \"no-working-yet.json\", \"alias\": \"testAnimation\", \"frames\": [") - + "{ \"delay\": 200, \"cell\": 0 }, { \"delay\": 10, \"cell\": 1 }," - + "{ \"delay\": 10, \"cell\": 2 }, { \"delay\": 200, \"cell\": 3 }," - + "{ \"delay\": 10, \"cell\": 1 }, { \"delay\": 10, \"cell\": 1 }," - + "{ \"delay\": 200, \"cell\": 4 }, { \"delay\": 10, \"cell\": 5 }," - + "{ \"delay\": 10, \"cell\": 6 }, { \"delay\": 200, \"cell\": 7 }," - + "{ \"delay\": 10, \"cell\": 6 }, { \"delay\": 10, \"cell\": 5 }," - + "{ \"delay\": 200, \"cell\": 8 }, { \"delay\": 10, \"cell\": 9 }," - + "{ \"delay\": 10, \"cell\": 10 }, { \"delay\": 200, \"cell\": 11 }," - + "{ \"delay\": 10, \"cell\": 10 }, { \"delay\": 10, \"cell\": 9 }" - + "]}" - ).toObject())); - - std::shared_ptr testAnimation2 = std::make_shared(malikania::Animation::fromJson(mainWindow, malikania::JsonDocument( - std::string("{\"sprite\": \"no-working-yet.json\", \"alias\": \"testAnimation\", \"frames\": [") - + "{ \"delay\": 2000, \"cell\": 0 }, { \"delay\": 10, \"cell\": 1 }," - + "{ \"delay\": 10, \"cell\": 2 }, { \"delay\": 2000, \"cell\": 3 }," - + "{ \"delay\": 10, \"cell\": 1 }, { \"delay\": 10, \"cell\": 1 }" - + "]}" - ).toObject())); + while (running) { + window->poll(); - malikania::Animator testAnimator1 = malikania::Animator(testAnimation); - malikania::Animator testAnimator2 = malikania::Animator(std::move(testAnimation)); - malikania::Animator testAnimator3 = malikania::Animator(std::move(testAnimation2)); - - while (mainWindow.isOpen()) { - - // TODO delete this, just for fun... - if (isBouncing) { - bounce(mainWindow, mokoPositionX, mokoPositionY); - } - - mainWindow.processEvent(); - mainWindow.setDrawingColor({255, 255, 255, 255}); - mainWindow.clear(); - mainWindow.update(); - - testSprite.draw(mainWindow, 0, {0, 0, 300, 300}); - testSprite.draw(mainWindow, 1, {200, 200, 300, 300}); - testSprite.draw(mainWindow, 2, {400, 400, 300, 300}); - testSprite.draw(mainWindow, 11, {600, 400, 300, 300}); - - malikania::Color c{255, 50, 40, 255}; - mainWindow.setDrawingColor(c); - mainWindow.drawLine({0, 0, 300, 300}); - - std::vector points{{20, 20}, {30, 50}, {100, 200}, {30, 60}, {20, 300}, {100, 20}}; - mainWindow.drawLines(points); + update(ctx); + draw(ctx); - mainWindow.setDrawingColor({200, 50, 200, 255}); - mainWindow.drawPoint({400, 400}); - mainWindow.drawPoint({400, 402}); - mainWindow.drawPoint({400, 405}); - mainWindow.drawPoint({400, 407}); - mainWindow.drawPoint({400, 410}); - - mainWindow.setDrawingColor({0, 0, 0, 255}); - mainWindow.drawPoints(points); - - mainWindow.setDrawingColor({30, 30, 30, 255}); - mainWindow.drawRectangle({500, 500, 200, 100}); - - mainWindow.setDrawingColor({130, 30, 30, 255}); - mainWindow.drawRectangles({{800, 800, 200, 100}, {700, 700, 200, 100}, {750, 750, 200, 100}}); - - mainWindow.drawRectangle({600, 200, 200, 100}, true, {0, 255, 0, 255}); - - mainWindow.drawRectangles( - {{800, 400, 200, 100}, {700, 450, 200, 100}, {750, 500, 200, 100}}, - true, - {{255,0,0,255},{0,255,0,255},{0,0,255,255}} - ); - - testLabel.draw(mainWindow, {300, 300, 200, 50}); - - testAnimator1.draw(mainWindow, {1000, 0, 300, 300}); - testAnimator2.draw(mainWindow, {100, 600, 300, 300}); - testAnimator3.draw(mainWindow, {400, 600, 300, 300}); - - mainWindow.present(); - - std::this_thread::sleep_for(5ms); + // TODO: remove this with an appropriate FPS calculation. + std::this_thread::sleep_for(std::chrono::milliseconds(50)); } - //malikania::Window::quit(); - return 0; } -#endif +int boot(const std::string &directory) +{ + std::string path = directory + "/client.js"; + duk::Context ctx = init(); + + /* Store the loader */ + ResourcesLocatorDirectory locator(directory); + ClientResourcesLoader loader(locator); + + duk::putGlobal(ctx, "\xff""\xff""loader", duk::RawPointer{&loader}); + + if (duk::pevalFile(ctx, path) != 0) { + duk::ErrorInfo info = duk::error(ctx, -1); + + std::cerr << info.fileName << ":" << info.lineNumber << ": " << info.stack << std::endl; -int main() { return 0; } + return 1; + } + + return run(ctx); +} + +} // !namespace + +int main(int argc, char **argv) +{ + -- argc; + ++ argv; + + if (argc < 1) { + return usage(); + } + + return boot(argv[0]); +} diff -r dc47ce56ce36 -r 56cc058200b5 examples/01-bouncing/client.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/01-bouncing/client.js Fri Apr 08 14:16:47 2016 +0200 @@ -0,0 +1,41 @@ +var x = 0; +var y = 0; + +var dx = 1; +var dy = 1; + +var font; +var clip; + +function start(window) +{ + font = new Malikania.Font("fonts/DejaVuSans.ttf", 10); + clip = font.clip("Malikania"); + + x = (640 / 2) - (clip.width / 2); + y = (480 / 2) - (clip.height / 2); +} + +function update() +{ + x += dx; + y += dy; + + if (x >= 640 - clip.width) + dx = -1; + else if (x <= 0) + dx = 1; + if (y >= 480 - clip.height) + dy = -1; + else if (y <= 0) + dy = 1; +} + +function draw(window) +{ + window.setDrawingColor('lightskyblue'); + window.clear(); + window.setDrawingColor('white'); + window.drawText('Malikania', font, { x: x, y: y }); + window.present(); +} \ No newline at end of file diff -r dc47ce56ce36 -r 56cc058200b5 examples/01-bouncing/fonts/DejaVuSans.ttf Binary file examples/01-bouncing/fonts/DejaVuSans.ttf has changed diff -r dc47ce56ce36 -r 56cc058200b5 libclient/malikania/backend/sdl/window-backend.cpp --- a/libclient/malikania/backend/sdl/window-backend.cpp Wed Apr 06 13:33:30 2016 +0200 +++ b/libclient/malikania/backend/sdl/window-backend.cpp Fri Apr 08 14:16:47 2016 +0200 @@ -62,25 +62,20 @@ } } -#if 0 - -// TODO: later */ - -void Window::Backend::processEvents(Window &window) +void Window::Backend::poll(Window &self) { SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_KEYUP: - window.onKeyUp(event.key.keysym.sym); + self.onKeyUp(event.key.keysym.sym); break; - // TODO continue implemanting all event possible case SDL_KEYDOWN: - window.onKeyDown(event.key.keysym.sym); + self.onKeyDown(event.key.keysym.sym); break; case SDL_QUIT: - window.close(); + self.onQuit(); break; default: break; @@ -88,8 +83,6 @@ } } -#endif - void Window::Backend::clear() { SDL_RenderClear(m_renderer.get()); diff -r dc47ce56ce36 -r 56cc058200b5 libclient/malikania/backend/sdl/window-backend.h --- a/libclient/malikania/backend/sdl/window-backend.h Wed Apr 06 13:33:30 2016 +0200 +++ b/libclient/malikania/backend/sdl/window-backend.h Fri Apr 08 14:16:47 2016 +0200 @@ -44,6 +44,8 @@ return m_renderer.get(); } + void poll(Window &self); + void close(); void clear(); diff -r dc47ce56ce36 -r 56cc058200b5 libclient/malikania/js-window.h --- a/libclient/malikania/js-window.h Wed Apr 06 13:33:30 2016 +0200 +++ b/libclient/malikania/js-window.h Fri Apr 08 14:16:47 2016 +0200 @@ -42,7 +42,7 @@ duk::StackAssert sa(ctx, 1); duk::getGlobal(ctx, "Malikania"); - duk::getGlobal(ctx, "Window"); + duk::getProperty(ctx, -1, "Window"); duk::getProperty(ctx, -1, "prototype"); duk::remove(ctx, -2); duk::remove(ctx, -2); diff -r dc47ce56ce36 -r 56cc058200b5 libclient/malikania/window.cpp --- a/libclient/malikania/window.cpp Wed Apr 06 13:33:30 2016 +0200 +++ b/libclient/malikania/window.cpp Fri Apr 08 14:16:47 2016 +0200 @@ -28,9 +28,14 @@ { } -Window::Window(Window &&) noexcept = default; +Window::Window(Window &&) = default; + +Window::~Window() = default; -Window::~Window() noexcept = default; +void Window::poll() +{ + m_backend->poll(*this); +} void Window::clear() { @@ -108,6 +113,6 @@ m_backend->drawText(text, font, point); } -Window &Window::operator=(Window &&) noexcept = default; +Window &Window::operator=(Window &&) = default; } // !malikania diff -r dc47ce56ce36 -r 56cc058200b5 libclient/malikania/window.h --- a/libclient/malikania/window.h Wed Apr 06 13:33:30 2016 +0200 +++ b/libclient/malikania/window.h Fri Apr 08 14:16:47 2016 +0200 @@ -24,6 +24,7 @@ * @brief Window and drawing. */ +#include #include #include #include @@ -43,11 +44,51 @@ private: class Backend; + std::function m_onQuit; + std::function m_onKeyDown; + std::function m_onKeyUp; + std::unique_ptr m_backend; bool m_isOpen{true}; public: + inline void onQuit() + { + if (m_onQuit) { + m_onQuit(); + } + } + + inline void onKeyDown(unsigned key) + { + if (m_onKeyDown) { + m_onKeyDown(key); + } + } + + inline void onKeyUp(unsigned key) + { + if (m_onKeyUp) { + m_onKeyUp(key); + } + } + + inline void setOnQuit(std::function fn) noexcept + { + m_onQuit = std::move(fn); + } + + inline void setOnKeyDown(std::function fn) noexcept + { + m_onKeyDown = std::move(fn); + } + + inline void setOnKeyUp(std::function fn) noexcept + { + m_onKeyUp = std::move(fn); + } + /** * Create a window. * @@ -61,12 +102,12 @@ /** * Move constructor defaulted. */ - Window(Window &&) noexcept; + Window(Window &&); /** * Virtual destructor defaulted. */ - virtual ~Window() noexcept; + virtual ~Window(); /** * Tells if the window is open. @@ -99,6 +140,11 @@ } /** + * Poll pending events. + */ + void poll(); + + /** * Clear the window content with the current drawing color. */ void clear(); @@ -214,7 +260,7 @@ * * @return this */ - Window &operator=(Window &&) noexcept; + Window &operator=(Window &&); }; } // !malikania diff -r dc47ce56ce36 -r 56cc058200b5 libcommon/malikania/js.h --- a/libcommon/malikania/js.h Wed Apr 06 13:33:30 2016 +0200 +++ b/libcommon/malikania/js.h Fri Apr 08 14:16:47 2016 +0200 @@ -304,8 +304,7 @@ Context(const Context &) = delete; Context &operator=(const Context &) = delete; - Context(const Context &&) = delete; - Context &operator=(const Context &&) = delete; + public: /** @@ -316,6 +315,9 @@ { } + Context(Context &&) noexcept = default; + Context &operator=(Context &&) noexcept = default; + /** * Convert the context to the native Duktape/C type. * @@ -2235,7 +2237,7 @@ delete static_cast *>(duk_to_pointer(ctx, -1)); duk_pop(ctx); duk_push_null(ctx); - duk_put_prop_string(ctx, 0, "\xff""\xff""js-ptr"); + duk_put_prop_string(ctx, 0, "\xff""\xff""js-shared-ptr"); return 0; }, 1);