I'm trying to interface with the xfce4-settings-manager which I was successfully able to do in the standard c gtk+-3.0 libraries, but I've been struggling to replicate it in gtkmm3. xfce4-settings-manager passes a --socked-id option to the client, and the client is meant to use a GtkPlug to connect to the socket via the id. As I mentioned before I was successful in writing it in C, and I've put that code in a github gist here. I prefer to use C++ as a means to learn the language in a more applied way and also because of its higher functionality to C.
I've struggled with the proper way to handle arguments to the proper way to use Gtk::Plug with hours of research and little results. If anyone can provide some insight into the proper way/documentation to handle command line arguments and GtkPlugs in gtkmm3 that would be greatly appreciated, and if you could provide any examples that would also be greatly appreciated. Thank you in advance!
Here is an example similar to yours, in C++ with Gtkmm 3:
#include <string>
#include <gtkmm.h>
#include <gtkmm/plug.h>
// Simple command line argument parser.
//
// Documented here:
//
// https://gitlab.gnome.org/GNOME/glibmm/-/blob/master/examples/options/main.cc
//
class CmdArgParser : public Glib::OptionGroup
{
public:
CmdArgParser(const std::string& p_name, const std::string& p_description, const std::string& p_help)
: Glib::OptionGroup{p_name, p_description, p_help}
{
// Define the 'socket ID' argument options:
Glib::OptionEntry socketIDArg;
socketIDArg.set_long_name("socket-id");
socketIDArg.set_short_name('s');
socketIDArg.set_flags(Glib::OptionEntry::FLAG_IN_MAIN);
socketIDArg.set_description("Settings manager socket");
// Register it in the parser. It value will be recorded in m_socketID for later usage.
add_entry(socketIDArg, m_socketID);
}
// Override this to handle errors. I skipped it for simplicity.
// void on_error(Glib::OptionContext& context, const Glib::Error& error) override;
::Window GetSocketID() const
{
return m_socketID;
}
private:
int m_socketID = 0;
};
// This is what is going to be plugged into xfce4-settings-manager:
//
// Documented here:
//
// https://developer.gnome.org/gtkmm-tutorial/3.22/sec-plugs-sockets-example.html.en
//
class SettingsPlug : public Gtk::Plug
{
public:
SettingsPlug(::Window p_socketID)
: Gtk::Plug{p_socketID}
{
m_button.set_label("A plug with Gtkmm3!");
add(m_button);
show_all_children();
}
private:
Gtk::Button m_button;
};
int main(int argc, char** argv)
{
auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example.plug");
// Parse command line arguments and retreive socket ID:
Glib::init();
setlocale(LC_ALL, "");
Glib::OptionContext context;
CmdArgParser parser{
"Socket ID",
"Command line argument for socket ID communication.",
"No help available, sorry"
};
context.set_main_group(parser);
context.parse(argc, argv);
::Window socketID = parser.GetSocketID();
// Handle plug:
SettingsPlug plug{socketID};
plug.show();
app->run(plug);
return 0;
}
I have removed error handling and the usage of Glade files to simplify the code. You can build it with:
g++ main.cpp -o example.out `pkg-config --cflags --libs gtkmm-3.0`
Related
I have been trying to figure out why this is happening and maybe it is just due to inexperience at this point but could really use some help.
When I run my code, which is compiled into a DLL using C++20, I get that a debug assertion has failed with the expression being __acrt_first_block == header.
I narrowed down where the code is failing, but the weird part is that it runs just fine when I change the Init(std::string filePath function signature to not contain the parameter. The code is below and hope someone can help.
Logger.h
#pragma once
#include "../Core.h"
#include <memory>
#include <string>
#include "spdlog/spdlog.h"
namespace Ruby
{
class RUBY_API Logger
{
public:
static void Init(std::string filePath);
inline static std::shared_ptr<spdlog::logger>& GetCoreLogger() { return coreLogger; }
inline static std::shared_ptr<spdlog::logger>& GetClientLogger() { return clientLogger; }
private:
static std::shared_ptr<spdlog::logger> coreLogger;
static std::shared_ptr<spdlog::logger> clientLogger;
};
}
Logger.cpp
namespace Ruby
{
std::shared_ptr<spdlog::logger> Logger::coreLogger;
std::shared_ptr<spdlog::logger> Logger::clientLogger;
void Logger::Init(std::string filePath)
{
std::string pattern{ "%^[%r][%n][%l]: %v%$" };
auto fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filePath, true);
// Setup the console and file sinks
std::vector<spdlog::sink_ptr> coreSinks;
coreSinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
coreSinks.push_back(fileSink);
// Bind the sinks to the core logger.
coreLogger = std::make_shared<spdlog::logger>("RUBY", begin(coreSinks), end(coreSinks));
// Set the Patterns for the sinks
coreLogger->sinks()[0]->set_pattern(pattern);
coreLogger->sinks()[1]->set_pattern(pattern);
// Tell spdlog to flush the file loggers on trace or worse message (can be changed if necessary).
coreLogger->flush_on(spdlog::level::trace);
// Set the default level of the logger
coreLogger->set_level(spdlog::level::trace);
// Do the same for the client logger
std::vector<spdlog::sink_ptr> clientSinks;
clientSinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
clientSinks.push_back(fileSink);
clientLogger = std::make_shared<spdlog::logger>("APP", begin(clientSinks), end(clientSinks));
clientLogger->sinks()[0]->set_pattern(pattern);
clientLogger->sinks()[1]->set_pattern(pattern);
clientLogger->flush_on(spdlog::level::trace);
clientLogger->set_level(spdlog::level::trace);
}
}
Entrypoint.h
#pragma once
#ifdef RB_PLATFORM_WINDOWS
extern Ruby::Application* Ruby::CreateApplication();
int main(int argc, char** argv)
{
Ruby::Logger::Init("../Logs/Recent_Run.txt");
RB_CORE_INFO("Initialized the logger.");
auto app = Ruby::CreateApplication();
app->Run();
delete app;
return 0;
}
#else
#error Ruby only supports windows
#endif // RB_PLATFORM_WINDOWS
For anyone else who runs into a similar problem, here is how I fixed it.
Essentially the function signature for the Init() function was the problem. The std::string parameter was causing the debug assertion to fire, my best guess as of right now was because of move semantics but that part I am still not sure on. So there are a couple of ways that I found to fix this.
Method 1:
Make the parameter a const char*. I don't quite like this approach as it then relies on C style strings and if you are trying to write a program in modern C++, this is a huge step backwards.
Method 2:
Make the parameter a const std::string&. Making it a const reference to a string prevents the move semantics (again as far as I know) and the assertion no longer fires. I prefer this fix as it keeps the program in modern C++.
I hope this helps anyone who has similar issues, and be careful with statics and move semantics.
everybody.
I am working on a gtkmm app and need some help getting a "Close" button to work. As suggested by the gtkmm documentation, I derived a class for the main window object, created some members, and left the main() function mostly for reading the glade UI file, instantiating the form and starting the main loop.
There are 3 files, named conveniently for explanation: Declarations.h, Declarations.cpp, Program.cpp
In "Declarations.h" I have the class inherited from the Gtk Window:
#include <gtkmm.h>
class MainWindowClass : public Gtk::ApplicationWindow
{
protected:
Gtk::Button *button_close;
// other buttons here
protected:
void on_button_close_clicked();
// other callback functions here
public:
MainWindowClass(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refGlade); // Constructor
// Destructor, other public members
};
In "Declarations.cpp" I have the implementations:
#include "Declarations.h"
using namespace Gtk;
// Implementing the constructor
MainWindowClass::MainWindowClass(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &refGlade) :
Gtk::Window(cobject), builder(refGlade)
{
builder->get_widget("button_close", button_close);
// Getting other widgets from the glade file
button_close->signal_clicked().connect(sigc::mem_fun(*this, &MainWindowClass::on_button_close_clicked));
// Connecting other callback functions here
}
// Implementing the callback for the "Close" button, ** PROBLEM IS HERE **
void MainWindowClass::on_button_close_clicked()
{
//gtk_main_quit(); Apparently GTK+/C only, compiler doesn't complain but causes a segfault when clicking the button
//Gtk::Application::quit(); Won't compile
}
The Program.cpp reads the UI from a file and starts the main program loop:
#include <gtkmm.h>
#include "Declarations.h"
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv, "Damn this close button");
Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_file("Program_UI.glade");
MainWindowClass our_main_window;
return app->run(our_main_window);
}
I am omitting some non-relevant code (of other objects and callbacks) because they work, it is the close procedure that is causing me trouble, though closing the app with "X" works.
I have also thought about trying to call a quit() or destroy() function (if they exist) of "app", but then the callback function doesn't know "app" exists.
What do you guys suggest?
Thanks a lot.
** Edit: fixed this using FormMain::hide(), which is inherited from GtkWindow.
I thought the static procedure Gtk::Main::hide() would do it, but the compiler says that hide() is not a member of Gtk::Main...
Well, moving forward one step at a time.
Used FormMain::hide() (inherited from GtkWindow). The static procedure Gtk::Main::hide() was not being recognized by the compiler.
I'm trying to use wxWidgets to create a open/save FileDialog window in a cross-platform way. So I've looked at the examples in the documentation. I also want to create standalone windows, with no parent, because I am not using any other instance of a wxApp/wxWindow elsewhere in my program.
Additionally, I need to have my own main function, so I don't want to use a macro such as IMPLEMENT_APP. I tried to follow the instructions given here, and came up with the following minimal program:
#include <wx/wx.h>
#include <string>
#include <iostream>
std::string openFile() {
wxFileDialog openFileDialog(NULL, _("Open XYZ file"), "", "",
"XYZ files (*.xyz)|*.xyz", wxFD_OPEN|wxFD_FILE_MUST_EXIST);
if (openFileDialog.Show() == wxID_CANCEL)
return ""; // the user changed idea...
// proceed loading the file chosen by the user;
return "something";
}
int main(int argc, char *argv[]) {
std::cout << wxEntryStart(argc, argv) << std::endl;
std::string s = openFile();
wxEntryCleanup();
}
And here is the CMakeLists.txt I used to compile the code:
CMake_Minimum_Required(VERSION 2.8.11)
Project(test)
Find_Package(wxWidgets REQUIRED)
Include(${wxWidgets_USE_FILE})
Add_Executable(test main.cpp)
Target_Link_Libraries(test ${wxWidgets_LIBRARIES})
Still, when I run this program, I get a Segmentation Fault, despite the wxEntryStart returning true, and I have no idea where the problem comes from. Any tip?
I wouldn't be so bold with stripping down wx's initialization code. It may work today, but in the next version, who knows...
This is what I use:
class MyApp : public wxApp { };
wxIMPLEMENT_APP_NO_MAIN(MyApp);
int main()
{
wxDISABLE_DEBUG_SUPPORT();
int dummy = 0;
if(!wxEntryStart(dummy, static_cast<wxChar**>(nullptr)))
return 1;
auto onx1 = on_exit([]{ wxEntryCleanup(); }); //using RAII for cleanup
//Other initialization, create main window, show it.
wxGetApp().OnRun(); //This starts the event loop.
//In your case, it looks like ShowModal's event loop is enough,
//so you don't need this.
}
I think those macros, ugly as they are, provide much better insulation against future changes in the library initialization code.
Ok, after some fiddling here is a code sample that works for me. Comments welcome on what'd be the best practices. What I did was keep ShowModal() instead of Show() in the openFile function. I also created the instance of singleton wxApp. The final code is here:
#include <wx/wx.h>
#include <string>
#include <iostream>
std::string openFile() {
wxFileDialog openFileDialog(NULL, _("Open XYZ file"), "", "",
"XYZ files (*.xyz)|*.xyz", wxFD_OPEN|wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() == wxID_CANCEL)
return ""; // the user changed idea...
// proceed loading the file chosen by the user;
return "something";
}
int main(int argc, char *argv[]) {
wxApp::SetInstance( new wxApp() );
wxEntryStart(argc, argv);
std::string s = openFile();
wxEntryCleanup();
}
Not sure this is completely leak-free, as valgrind seems to complain a little after exit. Any tip about whether I could also put the wxEntryStart() in the openFile() function welcome (I am guaranteed that this is the only place where the wxWidgets lib is used, and I want an API as simple as possible).
I've written part of a class in C++ and I want to be able to use it in conjunction with a Python GUI, so I'm using Boost.Python to try and make it easy. The issue I'm running into is that in following their guide (http://www.boost.org/doc/libs/1_55_0/libs/python/doc/tutorial/doc/html/python/exposing.html), I keep getting the following exception whenever I run bjam:
PacketWarrior/pcap_ext.cc:21:5: error: too few template arguments for class template 'class_'
Obviously it's complaining at me for omitting what they claim are optional arguments to the 'class_' template function, but I can't figure out why. I'm assuming it's a compiler issue but I don't know how to fix it. I'm running OS X 10.9 and using darwin for the default toolset, but GCC throws the same error. My Boost version is 1_55_0 if that helps at all.
Class header file (header guards omitted):
#include <queue>
#include "pcap.h"
#include "Packet.h"
class PacketEngine {
public:
PacketEngine();
~PacketEngine();
const char** getAvailableDevices(char *error_buf);
bool selectDevice(const char* dev);
Packet getNextPacket();
private:
char *selected_device;
char **devices;
int num_devices;
std::queue<Packet> packet_queue;
};
The cc file containing the references to Boost.Python and my class:
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include "PacketEngine.h"
BOOST_PYTHON_MODULE(pcap_ext) {
using namespace boost::python;
class_<PacketEngine>("PacketEngine")
.def("getAvailableDevices", &PacketEngine::getAvailableDevices);
}
And my bjam file (irrelevant parts and comments omitted):
use-project boost : ../../../Downloads/boost_1_55_0 ;
project
: requirements <library>/boost/python//boost_python
<implicit-dependency>/boost//headers
: usage-requirements <implicit-dependency>/boost//headers
;
python-extension pcap_ext : PacketWarrior/pcap_ext.cc ;
install convenient_copy
: pcap_ext
: <install-dependencies>on <install-type>SHARED_LIB <install-type>PYTHON_EXTENSION
<location>.
;
local rule run-test ( test-name : sources + )
{
import testing ;
testing.make-test run-pyd : $(sources) : : $(test-name) ;
}
run-test pcap : pcap_ext pcap.py ;
Any ideas as to how to circumvent this exception are greatly appreciated! I looked into the obvious route of just adding the optional parameters but I don't think they're relevant to my project. The class_ definition can be found here:
http://www.boost.org/doc/libs/1_37_0/libs/python/doc/v2/class.html
In short, include either:
boost/python.hpp: The Boost.Python convenient header file.
boost/python/class.hpp: The header that defines boost::python::class_.
The current included header files are declaring class_ with no default template arguments from def_visitor.hpp.
Also, trying to directly expose PacketEngine::getAvailableDevices() will likely present a problem:
It accepts a char* argument, but strings are immutable in Python.
There are no types that automatically convert to/from a const char** in Boost.Python.
It may be reasonable for a Python user to expect PacketEngine.getAvailableDevices() to return an iterable type containing Python strs, or throw an exception on error. This can be accomplished in a non-intrusive manner by writing a helper or auxiliary function that delegates to original function, but is exposed to Python as PacketEngine.getAvailableDevices().
Here is a complete example based on the original code:
#include <exception> // std::runtime_error
#include <boost/python.hpp>
namespace {
const char* devices_str[] = {
"device A",
"device B",
"device C",
NULL
};
} // namespace
class PacketEngine
{
public:
PacketEngine() : devices(devices_str) {}
const char** getAvailableDevices(char *error_buf)
{
// Mockup example to force an error on second call.
static bool do_error = false;
if (do_error)
{
strcpy(error_buf, "engine not responding");
}
do_error = true;
return devices;
}
private:
const char **devices;
};
/// #brief Auxiliary function for PacketEngine::getAvailableDevices that
/// provides a more Pythonic API. The original function accepts a
/// char* and returns a const char**. Both of these types are
/// difficult to use within Boost.Python, as strings are immutable
/// in Python, and Boost.Python is focused to providing
/// interoperability to C++, so the const char** type has no direct
/// support.
boost::python::list PacketEngine_getAvailableDevices(PacketEngine& self)
{
// Get device list and error from PacketEngine.
char error_buffer[256] = { 0 };
const char** devices = self.getAvailableDevices(error_buffer);
// On error, throw an exception. Boost.Python will catch it and
// convert it to a Python's exceptions.RuntimeError.
if (error_buffer[0])
{
throw std::runtime_error(error_buffer);
}
// Convert the c-string array to a list of Python strings.
namespace python = boost::python;
python::list device_list;
for (unsigned int i = 0; devices[i]; ++i)
{
const char* device = devices[i];
device_list.append(python::str(device, strlen(device)));
}
return device_list;
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<PacketEngine>("PacketEngine")
.def("getAvailableDevices", &PacketEngine_getAvailableDevices);
}
Interactive usage:
>>> import example
>>> engine = example.PacketEngine()
>>> for device in engine.getAvailableDevices():
... print device
...
device A
device B
device C
>>> devices = engine.getAvailableDevices()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: engine not responding
I am attempting to make an AIR Native Extension and after successful compilation of all components, Flash Builder 4.6 logs "Error #3500: The extension context does not have a method with the name...".
Here's the C++ code for the native DLL:
#include "stdafx.h"
#include "TestANE.h"
#include "FlashRuntimeExtensions.h"
#include <string>
#include <iostream>
#include <iomanip>
#include <algorithm>
using namespace std;
FREObject isSupported(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
FREObject result;
uint32_t isSupportedSwitch = 1;
FRENewObjectFromBool(isSupportedSwitch, &result);
return result;
}
FREObject getString(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
FREObject result;
const char *testString = "Hello World from C++!";
FRENewObjectFromUTF8(strlen(testString)+1, (const uint8_t *) testString, &result);
return result;
}
void taneContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctions, const FRENamedFunction** functions) {
*numFunctions = 2;
FRENamedFunction* func = (FRENamedFunction*) malloc(sizeof(FRENamedFunction) * (*numFunctions));
func[0].name = (const uint8_t*) "isSupported";
func[0].functionData = NULL;
func[0].function = &isSupported;
func[1].name = (const uint8_t*) "getString";
func[1].functionData = NULL;
func[1].function = &getString;
*functions = func;
}
void taneContextFinalizer(FREContext ctx) {
return;
}
void taneInitializer(void** extData, FREContextInitializer* ctxInitializer, FREContextFinalizer* ctxFinalizer) {
*ctxInitializer = &taneContextInitializer;
*ctxFinalizer = &taneContextFinalizer;
}
void taneFinalizer(void* extData) {
return;
}
Here's the code for the ActionScript 3 interface:
package com.tests.TestANE {
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.external.ExtensionContext;
public class TestANE extends EventDispatcher {
private var _ExtensionContext:ExtensionContext;
public function TestANE(target:IEventDispatcher=null) {
this._ExtensionContext = ExtensionContext.createExtensionContext("com.tests.TestANE", null);
super(target);
}
public function isSupported():Boolean {
return _ExtensionContext.call("isSupported") as Boolean;
}
public function getString():String {
return _ExtensionContext.call("getString") as String;
}
public function dispose():void {
this._ExtensionContext.dispose();
}
}
}
Any help in solving this issue would be appreciated.
Please see before here: http://forums.adobe.com/thread/923158
If you got this error when you have more than one extension inside the same project, please be careful: EVERY initializer end finalizer methods MUST have different and unique names between ALL the extensions.
NB. Not only the methods specified in extension.xml but also the ones you delagate to initialize the context. To be clear: NOT only the extension initializer but also the context initializer inside the project MUST have unique names between ALL the extensions.
Eg.:
void UNIQUE_NAME_ExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet,
FREContextFinalizer* ctxFinalizerToSet) {
NSLog(#"Entering ExtInitializer()");
*extDataToSet = NULL;
*ctxInitializerToSet = &UNIQUE_NAME_ContextInitializer;
*ctxFinalizerToSet = &UNIQUE_NAME_ContextFinalizer;
NSLog(#"Exiting ExtInitializer()");
}
void UNIQUE_NAME_ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx,
uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet)
{
*numFunctionsToTest = 1;
FRENamedFunction* func = (FRENamedFunction*) malloc(sizeof(FRENamedFunction) * 1);
func[0].name = (const uint8_t*) "scan";
func[0].functionData = NULL;
func[0].function = &scan;
*functionsToSet = func;
}
And the same for the finalizer.
I hope this helps.
To resolve symbol name conflicts between ANEs, there is a new ADT option in AIR 3.4 i.e. hideANELibSymbols
Refer: http://help.adobe.com/en_US/air/build/WS901d38e593cd1bac1e63e3d128cdca935b-8000.html
Also, to ease the process of getting started with writing an ANE for iOS, I've written an xcode ANE template.
Refer: https://github.com/divijkumar/xcode-template-ane
Do read the instructions on the github wiki page before getting started.
Had the same issue but on concrete machine (others work just fine) and nothing help until i rebuild my dll with release flag. It seems like debug version request some addination dll's installed on user pc, so make sure you build with release flag!
It appears that the C++ function "getString" has required arguments. So when you are calling _ExtensionContext.call("getString") as String;
C++ does not have a getString() method. It wants you to pass through arguments also. Add these to teh call method like so:
_ExtensionContext.call("getString", arg, arg1, arg2) as String;
The arguments you pass must be of the appropriate type specified by the c++ getString method.
See the reference:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/external/ExtensionContext.html#call()
I've solved this problem. I am not completely certain how as I started with another ANE project and built from there. I suspect however that the problem had to do with wrong package names in the ActionScript part of the code and the extension descriptor.
This question is old, but there is no solid answer. I ran into the same issue with an Android ANE. In your FREExtension function/class, you must return a new instance of FREContext and in that instance of FREContext, you must return the function map. I made the mistake of not returning the map and running around like a chicken with its head cut off trying to figure out what was going on. Hopefully that will help someone in the future.
I had this same issue and none of the other answers here solved it for me. I was trying to create a simple c++ ANE for the Blackberry Playbook and would get an error #3500 any time I tried to run it. The same exact code worked fine in C. It turns out the c++ compiler was doing method name mangling so it could not find the method I was calling. I solved it by wrapping my function definitions in extern "C" like this:
extern "C" {
void ExtensionInitializer(void** extDataToSet,
FREContextInitializer* ctxInitializerToSet,
FREContextFinalizer* ctxFinalizerToSet);
void ExtensionFinalizer();
void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx,
uint32_t* numFunctionsToSet, const FRENamedFunction** functionsToSet);
void ContextFinalizer(FREContext ctx);
FREObject myMethod(FREContext ctx, void* functionData, uint32_t argc,
FREObject argv[]);
}