From 1a7186f7452f765bb53f765de3c1ff6c37fc5e13 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Sun, 7 Apr 2024 22:51:31 -0400 Subject: [PATCH] Squashed commit of the following: [standardize_font_handling] closes #60, closes #5, closes #68 The major functionality added here was proper use of types in the module, by importing after finalization. commit 5009fa0fb9f78b257e9c5e7a1fbaae3826bca2f5 Author: John McCardle Date: Sun Apr 7 22:44:15 2024 -0400 PyFont - use the new standard method for instancing commit a19781b56a87b68309e63c88e4eb68d22523267d Author: John McCardle Date: Sun Apr 7 15:21:17 2024 -0400 Many hours of pain & research behind this small commit. Safe object building by not messing with types before interpreter is fully initialized commit 159658521c64a0cee8dd97ae7a2872a4eea2d9fa Author: John McCardle Date: Sun Mar 31 21:41:45 2024 -0400 Font mostly working, just a few weird bugs with the types of the default items added to the module --- src/McRFPy_API.cpp | 40 +++++++++++++++++++++++++--- src/McRFPy_API.h | 13 +++++++--- src/PyFont.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++ src/PyFont.h | 39 ++++++++++++++++++++++++++++ src/PyTexture.cpp | 47 +++++++++++++++++++++++++++++++-- src/PyTexture.h | 5 +++- src/UI.h | 7 +++-- src/scripts/game.py | 4 +++ 8 files changed, 207 insertions(+), 11 deletions(-) create mode 100644 src/PyFont.cpp create mode 100644 src/PyFont.h diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index 9f4a013..a1aa1d4 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -9,6 +9,11 @@ std::vector McRFPy_API::soundbuffers; sf::Music McRFPy_API::music; sf::Sound McRFPy_API::sfx; +std::shared_ptr McRFPy_API::default_font; +std::shared_ptr McRFPy_API::default_texture; +PyObject* McRFPy_API::mcrf_module; + + static PyMethodDef mcrfpyMethods[] = { {"registerPyAction", McRFPy_API::_registerPyAction, METH_VARARGS, "Register a callable Python object to correspond to an action string. (actionstr, callable)"}, @@ -39,8 +44,15 @@ static PyMethodDef mcrfpyMethods[] = { }; static PyModuleDef mcrfpyModule = { - PyModuleDef_HEAD_INIT, "mcrfpy", NULL, -1, mcrfpyMethods, - NULL, NULL, NULL, NULL + PyModuleDef_HEAD_INIT, /* m_base - Always initialize this member to PyModuleDef_HEAD_INIT. */ + "mcrfpy", /* m_name */ + NULL, /* m_doc - Docstring for the module; usually a docstring variable created with PyDoc_STRVAR is used. */ + -1, /* m_size - Setting m_size to -1 means that the module does not support sub-interpreters, because it has global state. */ + mcrfpyMethods, /* m_methods */ + NULL, /* m_slots - An array of slot definitions ... When using single-phase initialization, m_slots must be NULL. */ + NULL, /* traverseproc m_traverse - A traversal function to call during GC traversal of the module object */ + NULL, /* inquiry m_clear - A clear function to call during GC clearing of the module object */ + NULL /* freefunc m_free - A function to call during deallocation of the module object */ }; // Module initializer fn, passed to PyImport_AppendInittab @@ -72,11 +84,19 @@ PyObject* PyInit_mcrfpy() auto t = pytypes[i]; while (t != nullptr) { - PyType_Ready(t); + /*std::cout << */ PyType_Ready(t); /*<< std::endl; */ PyModule_AddType(m, t); t = pytypes[i++]; } + // Add default_font and default_texture to module + McRFPy_API::default_font = std::make_shared("assets/JetbrainsMono.ttf"); + McRFPy_API::default_texture = std::make_shared("assets/kenney_tinydungeon.png", 16, 16); + //PyModule_AddObject(m, "default_font", McRFPy_API::default_font->pyObject()); + //PyModule_AddObject(m, "default_texture", McRFPy_API::default_texture->pyObject()); + PyModule_AddObject(m, "default_font", Py_None); + PyModule_AddObject(m, "default_texture", Py_None); + //McRFPy_API::mcrf_module = m; return m; } @@ -128,9 +148,13 @@ PyStatus init_python(const char *program_name) #endif status = Py_InitializeFromConfig(&config); + + PyConfig_Clear(&config); + return status; } +/* void McRFPy_API::setSpriteTexture(int ti) { int tx = ti % texture_width, ty = ti / texture_width; @@ -139,6 +163,7 @@ void McRFPy_API::setSpriteTexture(int ti) ty * texture_size, texture_size, texture_size)); } +*/ // functionality //void McRFPy_API:: @@ -155,6 +180,15 @@ void McRFPy_API::api_init() { //texture_sprite_count = texture_width * texture_height; //texture.setSmooth(false); + // Add default_font and default_texture to module + McRFPy_API::mcrf_module = PyImport_ImportModule("mcrfpy"); + std::cout << PyUnicode_AsUTF8(PyObject_Repr(McRFPy_API::mcrf_module)) << std::endl; + + //PyModule_AddObject(McRFPy_API::mcrf_module, "default_font", McRFPy_API::default_font->pyObject()); + PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_font", McRFPy_API::default_font->pyObject()); + //PyModule_AddObject(McRFPy_API::mcrf_module, "default_texture", McRFPy_API::default_texture->pyObject()); + PyObject_SetAttrString(McRFPy_API::mcrf_module, "default_texture", McRFPy_API::default_texture->pyObject()); + //sprite.setTexture(texture); //sprite.setScale(sf::Vector2f(4.0f, 4.0f)); //setSpriteTexture(0); diff --git a/src/McRFPy_API.h b/src/McRFPy_API.h index 025706b..08d034e 100644 --- a/src/McRFPy_API.h +++ b/src/McRFPy_API.h @@ -3,6 +3,9 @@ #include "Python.h" #include +#include "PyFont.h" +#include "PyTexture.h" + class GameEngine; // forward declared (circular members) class McRFPy_API @@ -14,10 +17,14 @@ private: McRFPy_API(); + public: - inline static sf::Sprite sprite; - inline static sf::Texture texture; - static void setSpriteTexture(int); + static PyObject* mcrf_module; + static std::shared_ptr default_font; + static std::shared_ptr default_texture; + //inline static sf::Sprite sprite; + //inline static sf::Texture texture; + //static void setSpriteTexture(int); inline static GameEngine* game; static void api_init(); static void api_shutdown(); diff --git a/src/PyFont.cpp b/src/PyFont.cpp new file mode 100644 index 0000000..7773d52 --- /dev/null +++ b/src/PyFont.cpp @@ -0,0 +1,63 @@ +#include "PyFont.h" +#include "McRFPy_API.h" + + +PyFont::PyFont(std::string filename) +: source(filename) +{ + font = sf::Font(); + font.loadFromFile(source); +} + +PyObject* PyFont::pyObject() +{ + auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Font"); + //PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyFontType, 0); + PyObject* obj = PyFont::pynew(type, Py_None, Py_None); + try { + ((PyFontObject*)obj)->data = shared_from_this(); + } + catch (std::bad_weak_ptr& e) + { + std::cout << "Bad weak ptr: shared_from_this() failed in PyFont::pyObject(); did you create a PyFont outside of std::make_shared? enjoy your segfault, soon!" << std::endl; + } + // TODO - shared_from_this will raise an exception if the object does not have a shared pointer. Constructor should be made private; write a factory function + return obj; +} + +PyObject* PyFont::repr(PyObject* obj) +{ + PyFontObject* self = (PyFontObject*)obj; + std::ostringstream ss; + if (!self->data) + { + ss << ""; + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + auto& pfont = *(self->data); + ss << ""; + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + +Py_hash_t PyFont::hash(PyObject* obj) +{ + auto self = (PyFontObject*)obj; + return reinterpret_cast(self->data.get()); +} + +int PyFont::init(PyFontObject* self, PyObject* args, PyObject* kwds) +{ + static const char* keywords[] = { "filename", nullptr }; + char* filename; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", const_cast(keywords), &filename)) + return -1; + self->data = std::make_shared(filename); + return 0; +} + +PyObject* PyFont::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + return (PyObject*)type->tp_alloc(type, 0); +} diff --git a/src/PyFont.h b/src/PyFont.h new file mode 100644 index 0000000..99a12b9 --- /dev/null +++ b/src/PyFont.h @@ -0,0 +1,39 @@ +#pragma once +#include "Common.h" +#include "Python.h" + +class PyFont; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; +} PyFontObject; + +class PyFont : public std::enable_shared_from_this +{ +private: + std::string source; +public: + PyFont(std::string filename); + sf::Font font; + PyObject* pyObject(); + static PyObject* repr(PyObject*); + static Py_hash_t hash(PyObject*); + static int init(PyFontObject*, PyObject*, PyObject*); + static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL); +}; + +namespace mcrfpydef { + static PyTypeObject PyFontType = { + .tp_name = "mcrfpy.Font", + .tp_basicsize = sizeof(PyFontObject), + .tp_itemsize = 0, + .tp_repr = PyFont::repr, + //.tp_hash = PyFont::hash, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("SFML Font Object"), + //.tp_base = &PyBaseObject_Type, + .tp_init = (initproc)PyFont::init, + .tp_new = PyType_GenericNew, //PyFont::pynew, + }; +} diff --git a/src/PyTexture.cpp b/src/PyTexture.cpp index 7ce1b7b..8f8d4df 100644 --- a/src/PyTexture.cpp +++ b/src/PyTexture.cpp @@ -1,5 +1,5 @@ #include "PyTexture.h" - +#include "McRFPy_API.h" PyTexture::PyTexture(std::string filename, int sprite_w, int sprite_h) : source(filename), sprite_width(sprite_w), sprite_height(sprite_h) @@ -28,9 +28,36 @@ sf::Sprite PyTexture::sprite(int index, sf::Vector2f pos, sf::Vector2f s) PyObject* PyTexture::pyObject() { - PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyTextureType, 0); + // method 1: works but with type weirdness + //PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyTextureType, 0); + //Py_SET_TYPE(obj, &mcrfpydef::PyTextureType); + + // method 2: does not work (segfault on use of the mcrfpy.Texture object) + //PyObject* obj = PyTexture::pynew(&mcrfpydef::PyTextureType, Py_None, Py_None); + + // method 3: does not work (segfault on use of the mcrfpy.Texture object) + std::cout << "Find type" << std::endl; + auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"); + //auto type = obj->ob_type; + //auto type = &mcrfpydef::PyTextureType; + //std::cout << "assigned value 0x" << std::hex << reinterpret_cast(type) << std::endl; + //std::cout << "Found PyTextureType: " << PyUnicode_AsUTF8(PyObject_Repr((PyObject*)type)) << std::endl; + //std::cout << "PyTextureType metatype: " << PyUnicode_AsUTF8(PyObject_Repr((PyObject_Type((PyObject*)type)))) << std::endl; + //std::cout << "tp_alloc: 0x" << std::hex << reinterpret_cast (type->tp_alloc) << std::endl << + // "tp_new: 0x" << std::hex << reinterpret_cast(type->tp_new) << std::endl; + //PyObject* obj = ((PyTypeObject*)type)->tp_new((PyTypeObject*)type, Py_None, Py_None); + PyObject* obj = PyTexture::pynew(type, Py_None, Py_None); + //Py_SET_TYPE(obj, type); + + // method 4: call the type object? + + std::cout << "Instantiated" << std::endl; + //Py_SET_TYPE(obj, &mcrfpydef::PyTextureType); + + //PyObject_CallFunction(&mcrfpydef::PyTextureType, try { ((PyTextureObject*)obj)->data = shared_from_this(); + std::cout << "Sideloaded texture: " << PyUnicode_AsUTF8(PyObject_Repr(obj)) << std::endl; } catch (std::bad_weak_ptr& e) { @@ -40,6 +67,22 @@ PyObject* PyTexture::pyObject() return obj; } +PyObject* PyTexture::repr(PyObject* obj) +{ + PyTextureObject* self = (PyTextureObject*)obj; + std::ostringstream ss; + if (!self->data) + { + ss << ""; + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + auto& ptex = *(self->data); + ss << ""; + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + Py_hash_t PyTexture::hash(PyObject* obj) { auto self = (PyTextureObject*)obj; diff --git a/src/PyTexture.h b/src/PyTexture.h index eea1838..df7b3c7 100644 --- a/src/PyTexture.h +++ b/src/PyTexture.h @@ -21,6 +21,7 @@ public: sf::Sprite sprite(int index, sf::Vector2f pos = sf::Vector2f(0, 0), sf::Vector2f s = sf::Vector2f(1.0, 1.0)); PyObject* pyObject(); + static PyObject* repr(PyObject*); static Py_hash_t hash(PyObject*); static int init(PyTextureObject*, PyObject*, PyObject*); static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL); @@ -31,10 +32,12 @@ namespace mcrfpydef { .tp_name = "mcrfpy.Texture", .tp_basicsize = sizeof(PyTextureObject), .tp_itemsize = 0, + .tp_repr = PyTexture::repr, .tp_hash = PyTexture::hash, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("SFML Texture Object"), + //.tp_base = &PyBaseObject_Type, .tp_init = (initproc)PyTexture::init, - .tp_new = PyTexture::pynew, + .tp_new = PyType_GenericNew, //PyTexture::pynew, }; } diff --git a/src/UI.h b/src/UI.h index 1a47a40..c845baf 100644 --- a/src/UI.h +++ b/src/UI.h @@ -13,6 +13,7 @@ #include "PyColor.h" //#include "PyLinkedColor.h" #include "PyVector.h" +#include "PyFont.h" enum PyObjectsEnum : int { @@ -409,7 +410,7 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c * Begin PyFontType defs * */ - + /* typedef struct { PyObject_HEAD std::shared_ptr data; @@ -444,6 +445,7 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c return (PyObject*)self; } }; + */ /* * @@ -792,13 +794,14 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c // // Set Font // + std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); return -1; } else if (font != NULL) { auto font_obj = (PyFontObject*)font; - self->data->text.setFont(*font_obj->data); + self->data->text.setFont(font_obj->data->font); self->font = font; Py_INCREF(font); } else diff --git a/src/scripts/game.py b/src/scripts/game.py index 8e28bc3..87eaf5a 100644 --- a/src/scripts/game.py +++ b/src/scripts/game.py @@ -2,6 +2,10 @@ import mcrfpy font = mcrfpy.Font("assets/JetbrainsMono.ttf") texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16) +print("[game.py] Default texture:") +print(mcrfpy.default_texture) +print(type(mcrfpy.default_texture)) + # build test widgets mcrfpy.createScene("pytest")