Why does GTK2's gtk_widget_add_accelerator not add a shortcut sometimes? - gtk2

I'm seeing this issue where gtk_widget_add_accelerator doesn't work intermittently. Some menu items have a short cut and some don't on a GTK2 application running on Ubuntu 16.04. I installed the symbols and source to see what is going on and the signal check inside gtk_widget_add_accelerator is failing:
g_signal_query (g_signal_lookup (accel_signal, G_OBJECT_TYPE (widget)), &query);
if (!query.signal_id ||
!(query.signal_flags & G_SIGNAL_ACTION) ||
query.return_type != G_TYPE_NONE ||
query.n_params)
{
/* hmm, should be elaborate enough */
g_warning (G_STRLOC ": widget `%s' has no activatable signal \"%s\" without arguments",
G_OBJECT_TYPE_NAME (widget), accel_signal);
return;
}
So I copied that block of code to just before I call gtk_widget_add_accelerator like this:
const char *Signal = "activate";
GSignalQuery query;
g_signal_query (g_signal_lookup (Signal, G_OBJECT_TYPE (w)), &query);
if (!stricmp(Sc, "Ctrl+C")) // This is the short cut that doesn't work
{
if (!query.signal_id)
printf("Bad sig id\n");
else if (!(query.signal_flags & G_SIGNAL_ACTION))
printf("No sig act\n");
else if (query.return_type != G_TYPE_NONE)
printf("Ret type err\n");
else if (query.n_params)
printf("Param err.\n");
else
printf("Pre-cond ok.\n");
}
gtk_widget_add_accelerator( w,
Signal,
Menu->AccelGrp,
GtkKey,
mod,
Gtk::GTK_ACCEL_VISIBLE
);
And it prints 'Pre-cond ok.' which means there is a valid signal at my application level but NOT inside the GTK2 library. So is there a build problem? Mismatched headers? IDK.
So I started looking at exactly what I'm building against. The make file uses these flags:
Libs = \
-lmagic \
-static-libgcc \
`pkg-config --libs gtk+-2.0`
Inc = \
-I./include/common \
-I./include/linux/Gtk \
-I/usr/include/gstreamer-1.0 \
-I/usr/lib/x86_64-linux-gnu/gstreamer-1.0/include \
`pkg-config --cflags gtk+-2.0` \
-Iinclude/common \
-Iinclude/linux \
-Iinclude/linux/Gtk
Seems pretty standard stuff right?
So I'm somewhat at a loss as to why this isn't working. Any ideas?
For reference this is the code that creates the signal:
Info = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(Txt));
Gtk::gulong ret = Gtk::g_signal_connect_data(Info,
"activate",
(Gtk::GCallback) MenuItemCallback,
this,
NULL,
Gtk::G_CONNECT_SWAPPED);
And the contents of 'query' in my app:
(gdb) p query
$1 = {signal_id = 132, signal_name = 0x7ffff76c4859 "activate",
itype = 17714704,
signal_flags = (Gtk::G_SIGNAL_RUN_FIRST | Gtk::G_SIGNAL_ACTION),
return_type = 4, n_params = 0, param_types = 0x0}
And when I step into the GTK2 library:
(gdb) p query
$3 = {signal_id = 132, signal_name = 0x7ffff76c4859 "activate",
itype = 17714704, signal_flags = (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
return_type = 4, n_params = 0, param_types = 0x0}
But it only gets to that stage after the g_warning line. Maybe that's due to compiler optimization though.

Ok. It seems the debug symbols for Gtk were leading me down the wrong path. I built and installed GTK2 from source and it was much better.
gtk_widget_add_accelerator is actually doing the right thing and not falling into it's signal error handler. The real issue is that I'm replacing the menu item with a different object in LgiMenuItem::Icon when I convert the item to one that supports an icon as well. This deletes the accelerator that I added earlier. I noticed this when all the missing accelerators had icons.
So the "solution" as such is to re-add the shortcut after converting the menu item to one that supports an icon. Maybe down the track I'll re-factor the code to be more efficient but at this point I'm just happy it works.

Related

How to make NSOpenPanel accept keyboard and mouse events in objective-c?

I have C++ console application getting written in XCode, and I need to open a file selector dialog. To do this I'm using Cocoa with objective-c. I'm trying to open an NSOpenPanel to use it for this purpose. I'm using the following code currently:
const char* openDialog()
{
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
[openDlg setCanChooseFiles:YES];
[openDlg setFloatingPanel:YES];
if ( [openDlg runModal] == NSOKButton )
{
for( NSURL* URL in [openDlg URLs] )
{
NSLog( #"%#", [URL path] );
return [URL.path UTF8String];
}
}
return NULL;
}
This works, however the created file selector doesnt accept mouse and keyboard events properly. It's hard to explain, but for example when I run the code from XCode when hovering above the window the mouse still behaves as if were in XCode, showing the caret symbol. When i run the application from the terminal whenever I type something it sends the input to the terminal, even though the file selector is "in front". Command clicking gives the mouse events properly to the file selector though.
I looked through NSOpenPanel's documentation and googled the problem extensively but I couldn't find an answer to this.
/*
To run in Terminal: clang openpanel.m -fobjc-arc -framework Cocoa -o openpanel && ./openpanel
*/
#import <Cocoa/Cocoa.h>
int main() {
NSApplication *application = [NSApplication sharedApplication];
[application setActivationPolicy:NSApplicationActivationPolicyAccessory];
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
[openDlg setCanChooseFiles:YES];
[openDlg setFloatingPanel:YES];
if ( [openDlg runModal] == NSModalResponseOK ) {
for( NSURL* URL in [openDlg URLs] ) {
NSLog( #"%#", [URL path] );
}
}
return 0;
}
Thanks to #Willeke.

Wt a.exe file won't run

I am trying to start developing in WT, but it's not working out. I am using Windows 8, downloaded Wt 3.3.1, and had downloaded the codeblocks-12.11mingw-setup_user.exe which has the GCC compiler and GDB debugger. But I am not using code blocks, because the compiler didn't like the cmake preproccessor directives in WtConfig.h. So I tried to compile manually (I am a newb at using this type of technique, so I had to look it up).
I have my project as:
└───HelloWorldWt
└───source
├───bin
│ ├───Debug
│ │ └───CMakeFiles
│ │ └───CMakeFiles
│ └───Release
├───build
└───source
| └───CMakeFiles
| └───wt_project.wt.dir
| |___CMakeLists.txt
| |
| |___main.cpp
|____CMakeLists.txt
The main.cpp has (this is the HelloWorld example from http://www.webtoolkit.eu/wt/examples/):
/*
* Copyright (C) 2008 Emweb bvba, Heverlee, Belgium.
*
* See the LICENSE file for terms of use.
*/
#include <Wt/WApplication>
#include <Wt/WBreak>
#include <Wt/WContainerWidget>
#include <Wt/WLineEdit>
#include <Wt/WPushButton>
#include <Wt/WText>
// c++0x only, for std::bind
// #include <functional>
using namespace Wt;
/*
* A simple hello world application class which demonstrates how to react
* to events, read input, and give feed-back.
*/
class HelloApplication : public WApplication
{
public:
HelloApplication(const WEnvironment& env);
private:
WLineEdit *nameEdit_;
WText *greeting_;
void greet();
};
/*
* The env argument contains information about the new session, and
* the initial request. It must be passed to the WApplication
* constructor so it is typically also an argument for your custom
* application constructor.
*/
HelloApplication::HelloApplication(const WEnvironment& env)
: WApplication(env)
{
setTitle("Hello world"); // application title
root()->addWidget(new WText("Your name, please ? ")); // show some text
nameEdit_ = new WLineEdit(root()); // allow text input
nameEdit_->setFocus(); // give focus
WPushButton *button
= new WPushButton("Greet me.", root()); // create a button
button->setMargin(5, Left); // add 5 pixels margin
root()->addWidget(new WBreak()); // insert a line break
greeting_ = new WText(root()); // empty text
/*
* Connect signals with slots
*
* - simple Wt-way
*/
button->clicked().connect(this, &HelloApplication::greet);
/*
* - using an arbitrary function object (binding values with boost::bind())
*/
nameEdit_->enterPressed().connect
(boost::bind(&HelloApplication::greet, this));
/*
* - using a c++0x lambda:
*/
// b->clicked().connect(std::bind([=]() {
// greeting_->setText("Hello there, " + nameEdit_->text());
// }));
}
void HelloApplication::greet()
{
/*
* Update the text, using text input into the nameEdit_ field.
*/
greeting_->setText("Hello there, " + nameEdit_->text());
}
WApplication *createApplication(const WEnvironment& env)
{
/*
* You could read information from the environment to decide whether
* the user has permission to start a new application
*/
return new HelloApplication(env);
}
int main(int argc, char **argv)
{
/*
* Your main method may set up some shared resources, but should then
* start the server application (FastCGI or httpd) that starts listening
* for requests, and handles all of the application life cycles.
*
* The last argument to WRun specifies the function that will instantiate
* new application objects. That function is executed when a new user surfs
* to the Wt application, and after the library has negotiated browser
* support. The function should return a newly instantiated application
* object.
*/
int retval = WRun(argc, argv, &createApplication);
char* ch = new ch();
cin() >> ch;
return retval;
}
The HelloWorldWt/CMakeLists.txt has:
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT(WT_HELLO_WORLD)
SET (WT_CONNECTOR "wtfcgi" CACHE STRING "Connector used (wthttp or wtfcgi)")
ADD_SUBDIRECTORY(source)
The HelloWorldWt/source/CMakeLists.txt has
SET(WT_PROJECT_SOURCE
main.cpp
)
SET(WT_PROJECT_TARGET wt_project.wt)
ADD_EXECUTABLE(${WT_PROJECT_TARGET} ${WT_PROJECT_SOURCE})
TARGET_LINK_LIBRARIES(${WT_PROJECT_TARGET} ${WT_CONNECTOR} wt)
INCLUDE_DIRECTORIES("C:/Users/Me/My Code Libraries/wt-3.3.1/src")
I then ran
cmake .. -G "MinGW Makefiles" from the MyCode directory
That created a few files,
this created cmake_install.cmake, among other files.
I then ran: cmake .. -G "MinGW Makefiles" from HelloWorldWt/source
then I ran: cmake -P cmake_install.cmake
I then had:
My Code\HelloWorldWt\source\build\CMakeFiles\2.8.12\CompilerIdCXX\a.exe file, and I clicked that program to run it, and a console window just opened then closed.
what am I missing here?, I am trying to get a Wt application running, but can't seem to do it yet
(Maybe I should note that when I use the command:
cmake -P cmake_install.cmake
the cmd console, replies with
-- Install configuration: ""
and then goes back to the prompt. - If that helps).
My Code\HelloWorldWt\source\build\CMakeFiles\2.8.12\CompilerIdCXX\a.exe
Is not the file you want to run. It is an internal CMake test cmake creates during configuration to verify that the selected compiler even compiles and detect the target architecture.
You executable will be called
My Code\HelloWorldWt\source\build\wt_project.wt.exe
when you actually compile it.
To compile it, you either call make, or other appropriate build command depending on the selected generator, or you can ask cmake to call it for you with the command:
cmake --build .
The code you pasted contains syntax error—
cin() >> ch;
should be
std::cin >> ch;
(and ch should be a char, not char *)—which confirms you didn't yet try to compile it.
I should add that brief look at the WT documentation suggests the resulting executable should also need a bunch of options before it does anything interesting too.
We are using g++ since its a c++ interface (opposed to gcc), and scons as the build model. This works well and was pretty simple to deploy. I would suggest trying the next Ubuntu 14.04 release as it will contain a stable Wt version in its packages.

c++ : convert symbols to code line numbers programmatically

I'm developing under Linux/gcc
I currently use the following to get a stack trace on custom thrown exceptions. Demangled functions names and line numbers are as expected, but I would like to avoid the use of addr2line to have a full control on the formatting of the output strings.
static void posix_print_stack_trace()
{
int i, trace_size = 0;
char **messages = (char **)NULL;
trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
messages = backtrace_symbols(stack_traces, trace_size);
for (i = 0; i < trace_size; ++i)
{
if (addr2line(program_invocation_name, stack_traces[i]) != 0)
{
printf(" error determining line # for: %s\n", messages[i]);
}
}
if (messages) { free(messages); }
}
static int addr2line(char const * const program_name, void const * const addr)
{
char addr2line_cmd[512] = {0};
sprintf(addr2line_cmd,"addr2line -C -f -p -i -e %.256s %p", program_name, addr);
return system(addr2line_cmd);
}
Note : The use of -f for displaying the functions names in play in the stack trace and -C to display them demangled.
Q : Does anyone could point me on a programmatic solution ?
(And if possibly give me some advices on how to get it working as well with MinGW/gcc).
NB : Or may be simply using gdb in some way could help in getting more customized output ?
Thanks for the help.
EDIT : It looks like for the windows part, it is doable that way : https://stackoverflow.com/a/6207030/1715716
EDIT : The above points to a Microsoft Visual only solution, so is finally useless to me.
You probably could use or adapt (at least on Linux, and systems using ELF and DWARF) the libbacktrace by Ian Taylor, which is currently inside GCC source tree. See here; in principle it should be usable independently of GCC (provided you obey its BSD-like license).

Libconfig edit config value

I am using libconfig to read/wirte config files in my C++ game.
Right now I just have this one config file called video.cfg:
#Video config file
video:
{
fps:
{
limited = true;
value = 20;
};
};
This config file handles the video settings of the game.
I am trying to write a very basic console program that modifies this values based on user input. However I have no idea how to do this. I can't find anything in libconfig manual and nothing on Google.
So how do you edit values in Libconfig?
#include <libconfig.h>
int main() {
config_t cfg;
config_setting_t *vid_fps_lim = 0;
config_setting_t *vid_fps_val = 0;
config_init(&cfg);
if (config_read_file(&cfg, "myconfig") == CONFIG_TRUE) {
/* lookup the settings we want */
vid_fps_lim = config_lookup(&cfg, "video.fps.limited");
vid_fps_val = config_lookup(&cfg, "video.fps.value");
/* print the current settings */
printf("video.fps.limited = %i\n", config_setting_get_bool(vid_fps_lim));
printf("video.fps.value = %i\n", config_setting_get_int(vid_fps_val));
/* modify the settings */
config_setting_set_bool(vid_fps_lim, 1);
config_setting_set_int(vid_fps_val, 60);
/* write the modified config back */
config_write_file(&cfg, "myconfig");
}
config_destroy(&cfg);
return 0;
}
I named the file "lcex.c" and the config file "myconfig" It builds and runs on my Debian Linux machine using the following...
gcc `pkg-config --cflags libconfig` lcex.c -o lcex `pkg-config --libs libconfig`
./lcex
Open your config file after running the app and you should see that the values have been updated.
Disclaimer...error handling left out to make it easier to read. I didn't build with -Wall, etc. As with any API, read the docs and handle potential errors.
I came across this question while searching for a way to have libconfig write output to a string instead of a file. I see that there's no acceptable answer here, so I thought I would provide one for posterity, even though the question is over 3 years old.
#include <stdint.h>
#include <string>
#include "libconfig.h++"
int32_t
main (void) {
libconfig::Config config;
std::string file = "test.conf";
try {
config.readFile(file.c_str());
libconfig::Setting &limited = config.lookup("video.fps.limited");
libconfig::Setting &value = config.lookup("video.fps.value");
limited = false;
value = 60;
config.writeFile(file.c_str());
}
catch (...) {
// Do something reasonable with exceptions here. Do not catch (...)
}
return 0;
}
Hope that helps someone!

Need Help Tracking Down EXC_BAD_ACCESS on Function Entry on MacOS

I have a program that gets a KERN_PROTECTION_FAILURE with EXC_BAD_ACCESS in a very strange place when running multithreaded and I haven't the faintest idea how to troubleshoot it further. This is on MacOS 10.6 using GCC.
The very strange place that it gets this is when entering a function. Not on the first line of the function, but the actual jump to the function GetMachineFactors():
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0xb00009ec
[Switching to process 28242]
0x00012592 in GetMachineFactors () at ../sysinfo/OSX.cpp:168
168 MachineFactors* GetMachineFactors()
(gdb) bt
#0 0x00012592 in GetMachineFactors () at ../sysinfo/OSX.cpp:168
#1 0x000156d0 in CollectMachineFactorsThreadProc (parameter=0x200280) at Threads.cpp:341
#2 0x952f681d in _pthread_start ()
#3 0x952f66a2 in thread_start ()
(gdb)
If I run this non-threaded, it runs great, no issues:
#include "MachineFactors.h"
int main( int argc, char** argv )
{
MachineFactors* factors = GetMachineFactors();
std::string str = CreateJSONObject(factors);
cout << str;
delete factors;
return 0;
}
If I run this in a pthread, I get the EXC_BAD_ACCESS above.
THREAD_FUNCTION CollectMachineFactorsThreadProc( LPVOID parameter )
{
Main* client = (Main*) parameter;
if ( parameter == NULL )
{
ERRORLOG( "No data passed to machine identification thread. Aborting." );
return 0;
}
MachineFactors* mfactors = GetMachineFactors(); // This is where it dies.
// If I don't call GetMachineFactors and do something like mfactors =
// new MachineFactors(); everything is good and the threads communicate and exit
// normally.
if (mfactors == NULL)
{
ERRORLOG("Failed to collect machine identification: GetMachineFactors returned NULL." << endl)
return 0;
}
client->machineFactors = CreateJSONObject(mfactors);
delete mfactors;
EVENT_RAISE(client->machineFactorsEvent);
return 0;
}
Here is an excerpt from the GetMachineFactors() code:
MachineFactors* GetMachineFactors() // Dies on this line in multi-threaded.
{
// printf( "Getting machine factors.\n"); // Tried with and without this, never prints.
factors = new MachineFactors();
factors->OSName = "MacOS";
factors->Manufacturer = "Apple";
///…
// gather various machine metrics here.
//…
return factors;
}
For reference, I am using a socketpair to wait on the thread to complete:
// From the header file I use for cross-platform defines (this runs on OSX, Windows, and Linux.
struct _waitt
{
int fds[2];
};
#define THREAD_FUNCTION void*
#define THREAD_REFERENCE pthread_t
#define MUTEX_REFERENCE pthread_mutex_t*
#define MUTEX_LOCK(m) pthread_mutex_lock(m)
#define MUTEX_UNLOCK pthread_mutex_unlock
#define EVENT_REFERENCE struct _waitt
#define EVENT_WAIT(m) do { char lc; if (read(m.fds[0], &lc, 1)) {} } while (0)
#define EVENT_RAISE(m) do { char lc = 'j'; if (write(m.fds[1], &lc, 1)) {} } while (0)
#define EVENT_NULL(m) do { m.fds[0] = -1; m.fds[1] = -1; } while (0)
Here is the code where I launch the thread.
void Main::CollectMachineFactors()
{
#ifdef WIN32
machineFactorsThread = CreateThread(NULL, 0, CollectMachineFactorsThreadProc, this, 0, 0);
if ( machineFactorsThread == NULL )
{
ERRORLOG( "Could not create thread for machine id: " << ERROR_NO << endl )
}
#else
int retval = pthread_create(&machineFactorsThread, NULL, CollectMachineFactorsThreadProc, this);
if (retval)
{
ERRORLOG( "Return code from machine id pthread_create() is " << retval << endl )
}
#endif
}
Here's the simple failure case of running this multithreaded. It always fails for this code with the stack trace above:
CollectMachineFactors();
EVENT_WAIT(machineFactorsEvent);
cout << machineFactors;
return 0;
At first I suspected a library problem. Here's my makefile:
# Main executable file
PROGRAM = sysinfo
# Object files
OBJECTS = Version.h Main.o Protocol.o Socket.o SSLConnection.o Stats.o TimeElapsed.o Formatter.o OSX.o Threads.o
# Include directories
INCLUDE = -Itaocrypt/include -IyaSSL/taocrypt/mySTL -IyaSSL/include -isysroot /Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.5
# Library settings
STATICLIBS = libtaocrypt.a libyassl.a -Wl,-rpath,. -ldl -lpthread -lz -lexpat
# Compile settings
RELCXX = g++ -g -ggdb -DDEBUG -Wall $(INCLUDE)
.SUFFIXES: .o .cpp
.cpp.o :
$(RELCXX) -c -Wall $(INCLUDE) -o $# $<
all: $(PROGRAM)
$(PROGRAM): $(OBJECTS)
$(RELCXX) -o $(PROGRAM) $(OBJECTS) $(STATICLIBS)
clean:
rm -f *.o $(PROGRAM)
I can't for the life of me see anything particularly odd or dangerous and I'm not sure where to look. The same threaded process works fine on any Linux machine I have tried. Any suggestions? Any tools I should try?
I can add more info if it would be helpful.
I can see a problem with your Windows code, but not the OSX code that's crashing on you.
It seems that you're not posting the actual code for GetMachineFactors, since the variable factors is not declared. But regarding debugging, you should not take the non-appearance of printf output as conclusive that that statement hasn't been executed. Use debugger facilities such as setting a breakpoint, using special debugger trace output, so on (not sure what gdb handles, it's a very primitive debugger, but perhaps Apple has better tools?).
For Windows, you should use the run time library's thread creation instead of Windows API CreateThread. That's because with CreateThread the runtime lib isn't informed. E.g, a new expression or other call that uses the runtime lib might fail.
Sorry I can't help more.
I think it could perhaps have something to do with the GetMachineFactors code that you haven't shown?
It turns out, and I can't explain why, that a fork() call combined with a socketpair() as the IPC mechanism was the workaround to get things going as intended.
I wish I knew why it was failing in the first place (headscratch) but that approach seems to have been a good workaround.
It almost seemed like the kind of "build out of whack" problem that could be caused by failing to run a 'make clean' after changing header files, but that wasn't the case here.