From d265009b66cbc69ca6edf502dc23199a01d62220 Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Sat, 27 Jul 2024 20:22:50 +0200 Subject: [PATCH] FLUID: fixes crash bug in Fl_Sys_Men_Bar inside a Class. #977 --- fluid/Fl_Menu_Type.cxx | 124 +++++++++++++++++++++++++++++++++++---- fluid/Fl_Menu_Type.h | 12 ++++ fluid/Fl_Type.cxx | 13 ++++ fluid/Fl_Widget_Type.cxx | 11 ++++ 4 files changed, 148 insertions(+), 12 deletions(-) diff --git a/fluid/Fl_Menu_Type.cxx b/fluid/Fl_Menu_Type.cxx index 6ab652e14..bf88d57eb 100644 --- a/fluid/Fl_Menu_Type.cxx +++ b/fluid/Fl_Menu_Type.cxx @@ -290,19 +290,49 @@ void Fl_Menu_Item_Type::write_static(Fd_Code_Writer& f) { f.write_c("\n"); // Matt: disabled f.tag(FD_TAG_MENU_CALLBACK, get_uid()); f.write_c("}\n"); + + // If the menu item is part of a Class or Widget Class, FLUID generates + // a dummy static callback which retrieves a pointer to the class and then + // calls the original callback from within the class context. + // k is the name of the enclosing class (or classes) if (k) { + // Implement the callback as a static member function f.write_c("void %s::%s(Fl_Menu_* o, %s v) {\n", k, cn, ut); - f.write_c("%s((%s*)(o", f.indent(1), k); + // Find the Fl_Menu_ container for this menu item Fl_Type* t = parent; while (t->is_a(ID_Menu_Item)) t = t->parent; - Fl_Type *q = 0; - // Go up one more level for Fl_Input_Choice, as these are groups themselves - if (t && t->is_a(ID_Input_Choice)) - f.write_c("->parent()"); - for (t = t->parent; t && t->is_widget() && !is_class(); q = t, t = t->parent) - f.write_c("->parent()"); - if (!q || !q->is_a(ID_Widget_Class)) - f.write_c("->user_data()"); - f.write_c("))->%s_i(o,v);\n}\n", cn); + if (t) { + Fl_Widget_Type *tw = (t->is_widget()) ? static_cast(t) : NULL; + Fl_Type *q = NULL; + // Generate code to call the callback + if (tw->is_a(ID_Menu_Bar) && ((Fl_Menu_Bar_Type*)tw)->is_sys_menu_bar()) { + // Fl_Sys_Menu_Bar removes itself from any parent on macOS, so we + // wrapped it in a class and remeber the parent class in a new + // class memeber variable. + Fl_Menu_Bar_Type *tmb = (Fl_Menu_Bar_Type*)tw; + f.write_c("%s%s* sys_menu_bar = ((%s*)o);\n", f.indent(1), + tmb->sys_menubar_proxy_name(), tmb->sys_menubar_proxy_name()); + f.write_c("%s%s* parent_class = ((%s*)sys_menu_bar->_parent_class);\n", + f.indent(1), k, k); + f.write_c("%sparent_class->%s_i(o,v);\n}\n", + f.indent(1), cn); + } else { + f.write_c("%s((%s*)(o", f.indent(1), k); + // The class pointer is in the user_data field of the top widget + if (t && t->is_a(ID_Input_Choice)) { + // Go up one more level for Fl_Input_Choice, as these are groups themselves + f.write_c("->parent()"); + } + // Now generate code to find the topmost widget in this class + for (t = t->parent; t && t->is_widget() && !is_class(); q = t, t = t->parent) + f.write_c("->parent()"); + // user_data is cast into a pointer to the + if (!q || !q->is_a(ID_Widget_Class)) + f.write_c("->user_data()"); + f.write_c("))->%s_i(o,v);\n}\n", cn); + } + } else { + f.write_c("#error Enclosing Fl_Menu_* not found\n"); + } } } if (image) { @@ -700,10 +730,80 @@ Fl_Type* Fl_Input_Choice_Type::click_test(int, int) { Fl_Menu_Bar_Type Fl_Menu_Bar_type; +Fl_Menu_Item menu_bar_type_menu[] = { + {"Fl_Menu_Bar",0,0,(void*)0}, + {"Fl_Sys_Menu_Bar",0,0,(void*)1}, + {0}}; + +Fl_Menu_Bar_Type::Fl_Menu_Bar_Type() +: _proxy_name(NULL) +{ +} + +Fl_Menu_Bar_Type::~Fl_Menu_Bar_Type() { + if (_proxy_name) + ::free(_proxy_name); +} + +/** + \brief Return true if this is an Fl_Sys_Menu_Bar. + This test fails if subclass() is the name of a class that the user may have + derived from Fl_Sys_Menu_Bar. + */ +bool Fl_Menu_Bar_Type::is_sys_menu_bar() { + if (o->type()==1) return true; + return ( subclass() && (strcmp(subclass(), "Fl_Sys_Menu_Bar")==0) ); +} + +const char *Fl_Menu_Bar_Type::sys_menubar_name() { + if (subclass()) + return subclass(); + else + return "Fl_Sys_Menu_Bar"; +} + +const char *Fl_Menu_Bar_Type::sys_menubar_proxy_name() { + if (!_proxy_name) + _proxy_name = (char*)::malloc(128); + ::snprintf(_proxy_name, 63, "%s_Proxy", sys_menubar_name()); + return _proxy_name; +} + + +void Fl_Menu_Bar_Type::write_static(Fd_Code_Writer& f) { + super::write_static(f); + if (is_sys_menu_bar()) { + f.write_h_once("#include "); + if (is_in_class()) { + // Make room for a pointer to the enclosing class. + f.write_c_once( // must be less than 1024 bytes! + "\nclass %s: public %s {\n" + "public:\n" + " %s(int x, int y, int w, int h, const char *l=NULL)\n" + " : %s(x, y, w, h, l) { }\n" + " void *_parent_class;\n" + "};\n", + sys_menubar_proxy_name(), sys_menubar_name(), + sys_menubar_proxy_name(), sys_menubar_name() + ); + } + } +} + +void Fl_Menu_Bar_Type::write_code1(Fd_Code_Writer& f) { + super::write_code1(f); + if (is_sys_menu_bar() && is_in_class()) { + f.write_c("%s((%s*)%s)->_parent_class = (void*)this;\n", + f.indent(), sys_menubar_proxy_name(), name() ? name() : "o"); + } +} + +//void Fl_Menu_Bar_Type::write_code2(Fd_Code_Writer& f) { +// super::write_code2(f); +//} + //////////////////////////////////////////////////////////////// // Shortcut entry item in panel: - - void shortcut_in_cb(Fl_Shortcut_Button* i, void* v) { if (v == LOAD) { if (current_widget->is_button()) diff --git a/fluid/Fl_Menu_Type.h b/fluid/Fl_Menu_Type.h index 3824270a6..13c1573ec 100644 --- a/fluid/Fl_Menu_Type.h +++ b/fluid/Fl_Menu_Type.h @@ -35,6 +35,7 @@ extern Fl_Menu_Item dummymenu[]; extern Fl_Menu_Item button_type_menu[]; extern Fl_Menu_Item menu_item_type_menu[]; +extern Fl_Menu_Item menu_bar_type_menu[]; /** \brief Manage all types on menu items. @@ -261,13 +262,24 @@ public: class Fl_Menu_Bar_Type : public Fl_Menu_Base_Type { typedef Fl_Menu_Base_Type super; + Fl_Menu_Item *subtypes() FL_OVERRIDE {return menu_bar_type_menu;} public: + Fl_Menu_Bar_Type(); + ~Fl_Menu_Bar_Type() FL_OVERRIDE; const char *type_name() FL_OVERRIDE {return "Fl_Menu_Bar";} const char *alt_type_name() FL_OVERRIDE {return "fltk::MenuBar";} Fl_Widget *widget(int X,int Y,int W,int H) FL_OVERRIDE {return new Fl_Menu_Bar(X,Y,W,H);} Fl_Widget_Type *_make() FL_OVERRIDE {return new Fl_Menu_Bar_Type();} + void write_static(Fd_Code_Writer& f) FL_OVERRIDE; + void write_code1(Fd_Code_Writer& f) FL_OVERRIDE; +// void write_code2(Fd_Code_Writer& f) FL_OVERRIDE; ID id() const FL_OVERRIDE { return ID_Menu_Bar; } bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Menu_Bar) ? true : super::is_a(inID); } + bool is_sys_menu_bar(); + const char *sys_menubar_name(); + const char *sys_menubar_proxy_name(); +protected: + char *_proxy_name; }; diff --git a/fluid/Fl_Type.cxx b/fluid/Fl_Type.cxx index 1654bfd20..a9e2bcb4b 100644 --- a/fluid/Fl_Type.cxx +++ b/fluid/Fl_Type.cxx @@ -1006,6 +1006,19 @@ const char *Fl_Type::callback_name(Fd_Code_Writer& f) { return f.unique_id(this, "cb", name(), label()); } +/** + \brief Return the class name if this type is inside a Class or Widget Class. + + This methods traverses up the hirarchy to find out if this Type is located + inside a Class or Widget Class. It then return the name of that class. If + need_nest is set, class_name searches all the way up the tree and concatenates + the names of classes within classes, separated by a "::". + + \param need_nest if clear, search up one level to the first enclosing class. + If set, recurse all the way up to the top node. + \return the name of the enclosing class, or names of the enclosing classes + in a static buffe (don't call free), or NULL if this Type is not inside a class + */ const char* Fl_Type::class_name(const int need_nest) const { Fl_Type* p = parent; while (p) { diff --git a/fluid/Fl_Widget_Type.cxx b/fluid/Fl_Widget_Type.cxx index 5864b6ab6..160234aa4 100644 --- a/fluid/Fl_Widget_Type.cxx +++ b/fluid/Fl_Widget_Type.cxx @@ -54,6 +54,11 @@ int Fl_Widget_Type::is_widget() const {return 1;} int Fl_Widget_Type::is_public() const {return public_;} const char* subclassname(Fl_Type* l) { + if (l->is_a(ID_Menu_Bar)) { + Fl_Menu_Bar_Type *mb = static_cast(l); + if (mb->is_sys_menu_bar()) + return mb->sys_menubar_name(); + } if (l->is_widget()) { Fl_Widget_Type* p = (Fl_Widget_Type*)l; const char* c = p->subclass(); @@ -2991,6 +2996,12 @@ void Fl_Widget_Type::write_code1(Fd_Code_Writer& f) { f.write_c("new %s(0, 0, %d, %d", t, o->w(), o->h()); else f.write_c("new %s(%d, %d", t, o->w(), o->h()); + } else if (is_a(ID_Menu_Bar) + && ((Fl_Menu_Bar_Type*)this)->is_sys_menu_bar() + && is_in_class()) { + f.write_c("(%s*)new %s(%d, %d, %d, %d", + t, ((Fl_Menu_Bar_Type*)this)->sys_menubar_proxy_name(), + o->x(), o->y(), o->w(), o->h()); } else { f.write_c("new %s(%d, %d, %d, %d", t, o->x(), o->y(), o->w(), o->h()); }