From ac0ec4bb71e344e759016574531256951c9ba820 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Fri, 25 Aug 2023 21:57:42 -0400 Subject: [PATCH] In-work: Python segfaults when adding new objects to module --- src/GameEngine.cpp | 16 +-- src/McRFPy_API.cpp | 125 +++++++++++++++++++++-- src/Resources.cpp | 6 ++ src/Resources.h | 10 ++ src/UI.cpp | 35 +++++++ src/UI.h | 236 ++++++++++++++++++++++++++++++++++++++++++++ src/UIMenu.cpp | 9 ++ src/UIMenu.h | 4 + src/UITestScene.cpp | 83 ++++++++++++++++ src/UITestScene.h | 21 ++++ 10 files changed, 529 insertions(+), 16 deletions(-) create mode 100644 src/Resources.cpp create mode 100644 src/Resources.h create mode 100644 src/UI.cpp create mode 100644 src/UI.h create mode 100644 src/UITestScene.cpp create mode 100644 src/UITestScene.h diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index 7fbbf86..f023a17 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -4,16 +4,20 @@ #include "ActionCode.h" #include "McRFPy_API.h" #include "PythonScene.h" +#include "UITestScene.h" +#include "Resources.h" GameEngine::GameEngine() { - font.loadFromFile("./assets/JetbrainsMono.ttf"); + Resources::font.loadFromFile("./assets/JetbrainsMono.ttf"); + Resources::game = this; window.create(sf::VideoMode(1024, 768), "McRogueFace - r/RoguelikeDev Tutorial Run"); visible = window.getDefaultView(); window.setFramerateLimit(30); - scene = "menu"; + scene = "uitest"; //std::cout << "Constructing MenuScene" << std::endl; scenes["menu"] = new MenuScene(this); + scenes["uitest"] = new UITestScene(this); //std::cout << "Constructed MenuScene" < McRFPy_API::menus; @@ -104,23 +105,127 @@ static PyModuleDef mcrfpyModule = { NULL, NULL, NULL, NULL }; +// +// Point class dump +// + +// Point class example + + class Point +{ +public: + float x, y; + float magnitude() { + return std::sqrt(x*x + y*y); + } +}; + + + +static PyObject* PyPoint_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + Point* self; + self = (Point*)type->tp_alloc(type, 0); + if (self != nullptr) + { + self->x = 0.0f; + self->y = 0.0f; + } + return (PyObject*)self; +} + + +// Method to initialize the Point object +static int PyPoint_init(Point* self, PyObject* args, PyObject* kwds) +{ + static const char* keywords[] = { "x", "y", nullptr }; + float x = 0.0f, y = 0.0f; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ff", const_cast(keywords), &x, &y)) + { + return -1; + } + + self->x = x; + self->y = y; + + return 0; +} + +// Method to calculate the magnitude of the Point +static PyObject* PyPoint_magnitude(Point* self) +{ + float mag = self->magnitude(); + return PyFloat_FromDouble(mag); +} + +static PyMethodDef PyPoint_methods[] = { + {"magnitude", (PyCFunction)PyPoint_magnitude, METH_NOARGS, + "Vector length, or distance from origin."}, + {NULL, NULL, 0, NULL} +}; + +static PyMemberDef PyPoint_members[] = { + {"x", T_FLOAT, offsetof(Point, x), 0}, + {"y", T_FLOAT, offsetof(Point, y), 0}, + {NULL} +}; + + +static PyTypeObject PyPointType = { + .tp_name = "mcrfpy.Point", + .tp_basicsize = sizeof(Point), + //.tp_itemsize = 0, + //.tp_dealloc = NULL, + //.tp_repr = NULL, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + //.tp_doc = PyDoc_STR("Custom point object. (x, y)"), + //.tp_methods = PyPoint_methods, + .tp_members = PyPoint_members, + //.tp_init = (initproc)PyPoint_init, + //.tp_new = PyPoint_new, //PyType_GenericNew ? +}; + + +// +// End Dump +// + // Module initializer fn, passed to PyImport_AppendInittab PyObject* PyInit_mcrfpy() { PyObject* m = PyModule_Create(&mcrfpyModule); - - if (m == NULL) return NULL; - - /* - // add C++ classes exposed to Python here - Py_INCREF(&PyUIMenuType); - if (PyModule_AddObject(m, "Menu", (PyObject *) & PyUIMenuType) < 0) + + if (m == NULL) { - Py_DECREF(&PyUIMenuType); + std::cout << "ohno, module didn't ):\n"; return NULL; } + + /* + // This code runs, but Python segfaults when accessing the UIFrame type. + std::cout << "Adding UIFrame object to module\n"; + Py_INCREF(&mcrfpydef::PyUIFrameType); + if (PyModule_AddObject(m, "UIFrame", (PyObject *) & mcrfpydef::PyUIFrameType) < 0) + { + std::cout << "Failed to add UIFrame object\n"; + Py_DECREF(&mcrfpydef::PyUIFrameType); + //return NULL; + } + std::cout << "Returning module\n"; */ - + + Py_INCREF(&PyPointType); + if (PyModule_AddObject(m, "Point", (PyObject *) &PyPointType) < 0) { + std::cout << "ohno, couldn't add\n"; + Py_DECREF(&PyPointType); + Py_DECREF(m); + return NULL; + } + return m; } @@ -190,7 +295,7 @@ void McRFPy_API::setSpriteTexture(int ti) void McRFPy_API::api_init() { // build API exposure before python initialization - PyImport_AppendInittab("mcrfpy", PyInit_mcrfpy); + PyImport_AppendInittab("mcrfpy", &PyInit_mcrfpy); // use full path version of argv[0] from OS to init python init_python(narrow_string(executable_filename()).c_str()); diff --git a/src/Resources.cpp b/src/Resources.cpp new file mode 100644 index 0000000..e47b8c7 --- /dev/null +++ b/src/Resources.cpp @@ -0,0 +1,6 @@ +#include "Resources.h" + +// Resources class members memory allocation + +sf::Font Resources::font; +GameEngine* Resources::game; \ No newline at end of file diff --git a/src/Resources.h b/src/Resources.h new file mode 100644 index 0000000..168ac63 --- /dev/null +++ b/src/Resources.h @@ -0,0 +1,10 @@ +#include "Common.h" + +class GameEngine; // forward declared + +class Resources +{ +public: + static sf::Font font; + static GameEngine* game; +}; \ No newline at end of file diff --git a/src/UI.cpp b/src/UI.cpp new file mode 100644 index 0000000..08db2a5 --- /dev/null +++ b/src/UI.cpp @@ -0,0 +1,35 @@ +#include "UI.h" +#include "Resources.h" +#include "GameEngine.h" + +void UIDrawable::render() +{ + //std::cout << "Rendering base UIDrawable\n"; + render(sf::Vector2f()); +} + +void UIFrame::render(sf::Vector2f offset) +{ + //std::cout << "Rendering UIFrame w/ offset\n"; + box.move(offset); + Resources::game->getWindow().draw(box); + box.move(-offset); + for (auto drawable : children) { + drawable->render(offset + box.getPosition()); + } +} + +void UICaption::render(sf::Vector2f offset) +{ + //std::cout << "Rendering Caption with offset\n"; + text.move(offset); + Resources::game->getWindow().draw(text); + text.move(-offset); +} + +void UISprite::render(sf::Vector2f offset) +{ + sprite.move(offset); + Resources::game->getWindow().draw(sprite); + sprite.move(-offset); +} \ No newline at end of file diff --git a/src/UI.h b/src/UI.h new file mode 100644 index 0000000..d6e650e --- /dev/null +++ b/src/UI.h @@ -0,0 +1,236 @@ +#include "Common.h" +#include "Python.h" +#include "structmember.h" + +class UIDrawable +{ +public: + UIDrawable* parent; + void render(); + virtual void render(sf::Vector2f) = 0; + //virtual sf::Rect aabb(); // not sure I care about this yet + //virtual sf::Vector2i position(); + bool handle_event(/* ??? click, scroll, keystroke*/); + std::string action; +}; + +class UIFrame: public UIDrawable +{ +public: + sf::RectangleShape box; + std::vector children; + void render(sf::Vector2f) override final; +}; + +class UICaption: public UIDrawable +{ +public: + sf::Text text; + void render(sf::Vector2f) override final; +}; + +class UISprite: public UIDrawable +{ +public: + void render(sf::Vector2f) override final; + int texture_index, sprite_index; + float scale; + sf::Sprite sprite; +}; + +namespace mcrfpydef { + + + static PyObject* PyUIFrame_new(PyTypeObject* type, PyObject* args, PyObject* kwds) + { + std::cout << "New called\n"; + UIFrame* self; + self = (UIFrame*)type->tp_alloc(type, 0); + if (self != nullptr) + { + } + return (PyObject*)self; + } + + static int PyUIFrame_init(UIFrame* self, PyObject* args, PyObject* kwds) + { + std::cout << "Init called\n"; + static const char* keywords[] = { "x", "y", nullptr }; // TODO: keywords and other python objects to configure box (sf::RectangleShape) + float x = 0.0f, y = 0.0f; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ff", const_cast(keywords), &x, &y)) + { + return -1; + } + + //self->x = x; + //self->y = y; + + return 0; + } + + static PyObject* PyUIFrame_repr(UIFrame* self) + { + std::ostringstream ss; + ss << ""; + + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + static PyObject* PyUIFrame_setSize(UIFrame* self, PyObject* args) + { + float w, h; + if (!PyArg_ParseTuple(args, "ff", &w, &h)) return (PyObject*)-1; + self->box.setSize(sf::Vector2f(w, h)); + Py_INCREF(Py_None); + //return PyFloat_FromDouble(mag); + return Py_None; + } + + static PyMethodDef PyUIFrame_methods[] = { + {"set_size", (PyCFunction)PyUIFrame_setSize, METH_VARARGS, + "Set the width and height of the UIFrame's visible box"}, + {NULL, NULL, 0, NULL} + }; + + /* + static PyMemberDef PyUIFrame_members[] = { + {"box", T_OBJECT, offsetof(UIFrame, box), 0}, + {NULL} + }; + */ + + static PyTypeObject PyUIFrameType = { + //PyVarObject_HEAD_INIT(NULL, 0) + //.tp_base = NULL, + .tp_name = "mcrfpy.UIFrame", + .tp_basicsize = sizeof(UIFrame), + .tp_itemsize = 0, + .tp_dealloc = [](PyObject* obj) { Py_TYPE(obj)->tp_free(obj); }, + //.tp_repr = (reprfunc)PyUIFrame_repr, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Custom UIFrame object"), + //.tp_methods = PyUIFrame_methods, + //.tp_members = PyUIFrame_members, + .tp_init = (initproc)PyUIFrame_init, + .tp_new = PyUIFrame_new, //PyType_GenericNew ? + }; + + // module + /* + static PyModuleDef ui_module = { + PyModuleDef_HEAD_INIT, + .m_name = "ui", + .m_size = -1, + }; + PyMODINIT_FUNC PyInit_my_module(void) { + PyObject* module = PyModule_Create(&my_module); + if (module == NULL) { + return NULL; + } + + if (PyType_Ready(&MyType_Type) < 0) { + return NULL; + } + + Py_INCREF(&MyType_Type); + PyModule_AddObject(module, "UIFrame", (PyObject*)&MyType_Type); + return module; + } + */ + + // Point class example + + class Point +{ +public: + float x, y; + float magnitude() { + return std::sqrt(x*x + y*y); + } +}; + + + +static PyObject* PyPoint_new(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + Point* self; + self = (Point*)type->tp_alloc(type, 0); + if (self != nullptr) + { + self->x = 0.0f; + self->y = 0.0f; + } + return (PyObject*)self; +} + + +// Method to initialize the Point object +static int PyPoint_init(Point* self, PyObject* args, PyObject* kwds) +{ + static const char* keywords[] = { "x", "y", nullptr }; + float x = 0.0f, y = 0.0f; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ff", const_cast(keywords), &x, &y)) + { + return -1; + } + + self->x = x; + self->y = y; + + return 0; +} + +// Method to calculate the magnitude of the Point +static PyObject* PyPoint_magnitude(Point* self) +{ + float mag = self->magnitude(); + return PyFloat_FromDouble(mag); +} + +static PyMethodDef PyPoint_methods[] = { + {"magnitude", (PyCFunction)PyPoint_magnitude, METH_NOARGS, + "Vector length, or distance from origin."}, + {NULL, NULL, 0, NULL} +}; + +static PyMemberDef PyPoint_members[] = { + {"x", T_FLOAT, offsetof(Point, x), 0}, + {"y", T_FLOAT, offsetof(Point, y), 0}, + {NULL} +}; + + +static PyTypeObject PyPointType = { + .tp_name = "mcrfpy.Point", + .tp_basicsize = sizeof(Point), + //.tp_itemsize = 0, + //.tp_dealloc = NULL, + //.tp_repr = NULL, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + //.tp_doc = PyDoc_STR("Custom point object. (x, y)"), + //.tp_methods = PyPoint_methods, + //.tp_members = PyPoint_members, + //.tp_init = (initproc)PyPoint_init, + //.tp_new = PyPoint_new, //PyType_GenericNew ? +}; + +/* +static PyModuleDef PyPointModule = { + PyModuleDef_HEAD_INIT, + .m_name = "point", + .m_doc = "Custom point module.", + .m_size = -1, + //.m_methods = PyPoint_methods, +}; +*/ + +} \ No newline at end of file diff --git a/src/UIMenu.cpp b/src/UIMenu.cpp index 34cda11..8e3d537 100644 --- a/src/UIMenu.cpp +++ b/src/UIMenu.cpp @@ -1,5 +1,6 @@ #include "UIMenu.h" #include "Common.h" +#include "Resources.h" UIMenu::UIMenu(sf::Font & _font) : font(_font) @@ -10,6 +11,14 @@ UIMenu::UIMenu(sf::Font & _font) box.setFillColor(sf::Color(0,0,255)); } +UIMenu::UIMenu() +: font(Resources::font) +{ + box.setSize(sf::Vector2f(300, 400)); + box.setPosition(sf::Vector2f(300, 250)); + box.setFillColor(sf::Color(0,0,255)); +} + void UIMenu::render(sf::RenderWindow & window) { window.draw(box); diff --git a/src/UIMenu.h b/src/UIMenu.h index bb490bb..9e445db 100644 --- a/src/UIMenu.h +++ b/src/UIMenu.h @@ -10,9 +10,13 @@ public: //UIMenu() {}; sf::Font & font; UIMenu(sf::Font & _font); + UIMenu(); std::vector captions; std::vector