This is a bare bones save_as() function:
gint save_as(GtkWidget *parent, struct buffers B)
{
GtkWidget *file_chooser = gtk_file_chooser_dialog_new("Save As", GTK_WINDOW(parent), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_OK, NULL);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), "Untitled");
gint response = gtk_dialog_run(GTK_DIALOG(file_chooser));
switch(response)
{
case GTK_RESPONSE_OK:
GFile *file = g_file_new_for_path(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser)));
GtkTextIter *start;
gtk_text_buffer_get_start(B.buffer0, &start);
GtkTextIter *end;
gtk_text_buffer_get_end(B.buffer0, &end);
// program abnormally terminates on the following line
gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE);
g_file_replace_contents(file, contents, strlen(contents), NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, NULL);
g_free(contents);
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_OK;
break;
case GTK_RESPONSE_CANCEL:
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
// user pressed X
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
This is pretty much all we need to diagnose and fix the problem.
Here is the full message I get when I try to click on File -> Save As... and then on Save:
(test:4478): Gtk-WARNING **: 11:56:20.184: Invalid text buffer iterator: either the iterator is uninitialized, or the characters/pixbufs/widgets in the buffer have been modified since the iterator was created.
You must use marks, character numbers, or line numbers to preserve a position across buffer modifications.
You can apply tags and insert marks without invalidating your iterators, but any mutation that affects 'indexable' buffer contents (contents that can be referred to by character offset) will invalidate all outstanding iterators
(test:4478): Gtk-CRITICAL **: 11:56:20.184: gtk_text_buffer_get_text: assertion 'gtk_text_iter_get_buffer (start) == buffer' failed
Segmentation fault (core dumped)
For some reason, it seems that the iterators start and end do not belong to B.buffer0. Apart from GTK+3 documentation, I also followed answers to this question as a guide.
Why is this happening and how it can be fixed?
I also tried changing the following lines:
gtk_text_buffer_get_start_iter(B.buffer0, &start); to gtk_text_buffer_get_start_iter(B.buffer0, start);
gtk_text_buffer_get_start_iter(B.buffer0, &end); to gtk_text_buffer_get_start_iter(B.buffer0, end);
gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE); to gchar *contents = gtk_text_buffer_get_text(B.buffer0, start, end, FALSE);
The only error I get is:
Segmentation fault (core dumped)
This is also supposed to be the correct way to send arguments according to the documentation.
Also, I tried replacing contents = gtk_text_buffer_get_text(B.buffer0, start, end, FALSE); with contents = gtk_text_iter_get_text(start, end); but I get the same error.
I also noticed that I get two warnings during compiling after applying those changes:
src/save.c:96:5: warning: ‘start’ may be used uninitialized in this function [-Wmaybe-uninitialized]
gtk_text_buffer_get_start_iter(B.buffer0, start);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/save.c:98:5: warning: ‘end’ may be used uninitialized in this function [-Wmaybe-uninitialized]
gtk_text_buffer_get_end_iter(B.buffer0, end);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This obviously means that gtk_text_buffer_get_start/end_iter() fails to associate start and end with B.buffer0, but why? It says that start and end are passed uninitialized, but isn't that normal since I'm initializing them now? Or I should do something before that?
Definition of struct buffer is in a file called buffer.h. Here it is (not the whole file, just the struct definition):
struct buffers
{
GtkTextBuffer *buffer0;
GtkTextBuffer *buffer1;
GtkTextBuffer *buffer2;
};
The instance I use is:
struct buffers Buffer;
This is a global variable, so buffer.h is included in every file that uses Buffer. And for the same purpose (globality), of course it is declared extern struct buffers Buffer; in the header file associated with the source file in which it is defined.
This buffer is passed to a function save_as() like this:
gint response = save_as(main_window, Buffer);
In case you're wondering why I just don't use global variable instead of passing it as a parameter, it is because I can just pass any variable of type struct buffers to a save_file() and let it do the job, instead of having another function for each struct buffers variable.
I tried initializing iterators upon their definition:
GtkTextIter *start = NULL;
GtkTextIter *end = NULL;
Everything compiles without any warnings. Unfortunately, this doesn't solve the problem. Upon clicking File -> Save As and then Save, the program exits and I get messages:
(test:4081): Gtk-CRITICAL **: 19:48:29.843: gtk_text_buffer_get_start_iter: assertion 'iter != NULL' failed
(test:4081): Gtk-CRITICAL **: 19:48:29.844: gtk_text_buffer_get_end_iter: assertion 'iter != NULL' failed
(test:4081): Gtk-CRITICAL **: 19:48:29.844: gtk_text_buffer_get_text: assertion 'start != NULL' failed
Why is it even important that my start and end iterators are NULL at least in the first two cases? Aren't those two functions supposed to set start and end iterators to the start and the end of a file, respectively? Therefore, why does it matter to what iterators are set prior to that?
Finally solved it. It looks like that GtkTextIter is a little bit different than most GTK types.
Here is how it should be done:
gint save_as(GtkWidget *parent, struct buffers B)
{
GtkWidget *file_chooser = gtk_file_chooser_dialog_new("Save As", GTK_WINDOW(parent), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_OK, NULL);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), "Untitled");
gint response = gtk_dialog_run(GTK_DIALOG(file_chooser));
switch(response)
{
case GTK_RESPONSE_OK:
GFile *file = g_file_new_for_path(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser)));
GtkTextIter start;
gtk_text_buffer_get_start(B.buffer0, &start);
GtkTextIter end;
gtk_text_buffer_get_end(B.buffer0, &end);
// program abnormally terminates on the following line
gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE);
g_file_replace_contents(file, contents, strlen(contents), NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, NULL);
g_free(contents);
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_OK;
break;
case GTK_RESPONSE_CANCEL:
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
// user pressed X
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
As you can see, the only difference in this code and the code from my question are the following two lines:
Wrong
GtkTextIter *start;
GtkTextIter *end;
Right
GtkTextIter start;
GtkTextIter end;
Most of the GTK types want you to make them pointers, but this one is an exception.
Related
I have the following code:
gpointer w[3];
GtkWidget *menu_item = gtk_menu_item_new();
w[0] = menu_item;
menu_item = gtk_menu_item_new();
w[1] = menu_item;
GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
w[2] = buffer;
This is all good till now. Let's now connect a signal:
g_signal_connect(w[0], "activate", G_CALLBACK(runner), w);
runner function is declared as:
void runner(gpointer root, gpointer w[]);
Testing the values of w array before entering runner and while in it shows that they (the values) are different. I need them to be the same. How can I accomplish that, and why they aren't identical? Also, segfault occurs.
I created a small program that is bare bones of the original one and that is supposed to recreate the conditions such that the problem occurs. Oddly enough, it runs fine.
#include <gtk/gtk.h>
void carry(gpointer root, gpointer a[])
{
g_print("\n");
g_print("%d\n", root);
g_print("%d\n", a[0]);
g_print("%d\n", a[1]);
g_print("%d\n", a[2]);
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
GtkWidget *menu_item;
GtkTextBuffer *buffer;
gpointer abc[3];
menu_item = gtk_menu_item_new();
abc[0] = menu_item;
g_print("%d\t%d\n", menu_item, abc[0]);
menu_item = gtk_menu_item_new();
abc[1] = menu_item;
g_print("%d\t%d\n", menu_item, abc[1]);
buffer = gtk_text_buffer_new(NULL);
abc[2] = buffer;
g_print("%d\t%d\n", buffer, abc[2]);
g_signal_connect(abc[2], "modified-changed", G_CALLBACK(carry), abc);
gtk_text_buffer_set_modified(abc[2], TRUE);
gtk_main();
return 0;
}
Which means that something else is problematic. I'll try something else now, like commenting lines and leaving only the relevant ones.
I didn't comment any lines yet, but I tried putting g_print in both the caller and the callee.
This is an output:
1162863440 1162863440
1162864736 1162864736
1163320992 1163320992
1162863440
-2
1162668992
973486176
The first three lines compare the original values with their copies in the array (in the sense of g_print("%d\t%d\n", menu_item, abc[0]); from the code above). As you can see, everything is assigned correctly. After a new line, we check those same values in the callee. root, the first parameter, always has the correct value. So there's no problem with that. abc[0] in the callee always has the value of -2. Seriously, every time I run the program it is -2. Other two (abc[1] and abc[2]) always have some garbage random values, but they change every time I run the program unlike abc[0].
I hope this will help in diagnosing and fixing the problem.
I tried passing both abc[0] and abc normally through a function (func(arg0, arg1, ...) instead of using g_signal_connect()) and there is no problem whatsoever.
This all can mean only one thing: g_signal_connect is messing with my values. It changes them for some unknown reason.
I guess I'll have to use a struct.
You're not supposed to use gpointers everywhere. A gpointer is a void *, so you're pretty much disabling all the type checking the compiler could do for you. Use GtkWidget * instead, and do proper casts using G_OBJECT(), GTK_TEXT_BUFFER() etc. macros.
You should also use typed callback arguments, as they appear in the documentation for each signal. For example for the activate signal:
void
user_function (GtkMenuItem *menuitem,
gpointer user_data)
And if you want to pass several items in the user-data field, pass a pointer or pointer to a structure instead of an array of pointers.
And if you have a segfault, well, just use a debugger to check where the problem is.
I wrote a simple program module to ask a user for a profile name. To do that I create windoww with entry widget, and two buttons (Ok and Cancel) organized in a grid. When user enters a profile name that already exists it informs him of that fact by creating dialog with "ok" button, and after he presses it, it goes back to picking a profile name (the window was not hidden nor destroyed in the meantime). The problem is that when I create a profile, and then spam the ok button (by placing something heavy on enter key and going to make tea) on both profile name chooser and the dialog (making a simple loop with creation and destruction of a dialog) the memory usage of the program increases.
TL;DR
Simply creating and destroying gtk window (and dialog) seems to cause a memory leak. Leaving app in a loop made it increase it's memory usage by ~1900% (from 10mb to 200mb).
No, I didn't test for memory leaks using apps designed for it.
Yes, I've set G_SLICE=always-malloc.
Yes, there is another thread running in the background of the program (but I'm sure it doesn't cause any leaks)
I can post screens from Windows Performance Monitor if you want more info on what happens in the memory.
The question is - is it a memory leak caused by me, or is it GTK's fault (I heard it has a lazy policy of memory management, but after some time memory usage drops from 200mb to 140mb and stays there)?
Here's the code:
// This callback racts to the ok and cancel buttons. If input was correcs
// or the user pressed cancel it destroys the window. Else it show error
// prompt. The showing error prompt seems to be the problem here.
void pickNameButtCB(GtkWidget *button, gpointer *call)
{
GtkWidget *window = gtk_widget_get_toplevel(button);
if( *((char*)call) == 'k')
{
GList *entryBase = gtk_container_get_children(GTK_CONTAINER(gtk_bin_get_child(GTK_BIN(window)))), *entry = entryBase;
for(size_t i=g_list_length(entry); i>0 && !GTK_IS_ENTRY(entry->data); --i)
entry = g_list_next(entry);
if(gtk_entry_get_text_length(GTK_ENTRY(entry->data)) > 0)
{
const char *temp = gtk_entry_get_text(GTK_ENTRY(entry->data));
char path[266];
strcpy(path, settingsDir);
strcat(path, temp);
strcat(path, ".prof");
if(settProfExists(path))
{
g_list_free(entryBase);
showError(GTK_WINDOW(window), GTK_MESSAGE_ERROR, "Profile with that name already exists!");
return;
}
// nothing here executes as well
}
else
{
/** doesn't execute when the memory leak happens */
}
g_list_free(entryBase);
}
gtk_widget_destroy(window);
gtk_main_quit();
}
void showError(GtkWindow *parent, GtkMessageType type, const char *str)
{
GtkWidget *window = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_OK, str);
g_signal_connect_swapped(window, "response", G_CALLBACK(gtk_widget_destroy), window);
gtk_dialog_run(GTK_DIALOG(window));
}
bool settProfExists(const char *path)
{
if(fileExists(path))
return true;
return false;
}
bool fileExists(const char *path)
{
struct stat info;
errno = 0;
if((stat(path, &info)) != 0)
if(errno == ENOENT)
return false;
return true;
}
Your code is too incomplete to find the root cause.
This line especially is doesn't make sense. We're missing the definition of entry and you're trying to do several operations on the same line (*entry = entryBase;).
GList *entryBase = gtk_container_get_children(GTK_CONTAINER(gtk_bin_get_child(GTK_BIN(window)))), *entry = entryBase;
The leak may however be in functions you call for which you don't provide the code (settProfExists, newProfile). So please provide a minimal compilable example that reproduces the problem.
As for the style, there are several errors: you're traversing a list the way you'd do with an array. See how is implemented GList and you'll see that this is silly (hint: look at entry->prev and entry->next):
for(size_t i=g_list_length(entry); i>0 && !GTK_IS_ENTRY(entry->data); --i)
entry = g_list_next(entry);
Then an architectural problem: don't try to inspect your UI to guess where are the widgets, just create a structure, and pass you call parameter and the GtkEntry you're interested in, in a structure with the gpointer data argument of the callback.
You should also not make paths by hand: you're using a fixed length array that may not be long enough. Use the portable g_build_path provided by the GLib (and free the path with g_free after use), that will handle for you Windows/Linux directories separators.
For your dialog, as you run gtk_run_dialog, you may just directly call gtk_destroy_widget afterwards instead of connecting a signal:
GtkWidget *window = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_OK, str);
gtk_dialog_run(GTK_DIALOG(window));
gtk_widget_destroy(window);
I am developing a Gtk based application where I need to update every X seconds a GtkTextBuffer, by concatenating the text that is already there. Apparently it succeeds but sometimes "randomly" the application aborts and I assume that it is due to this element, as it wasn't happening before. It sometimes aborts when there is a second/third update (each update only adds one new line) and sometimes it does not abort at all. I have based the TextBuffer update function on simple chat applications behavior but I don't know what I am doing wrong. The update function is:
void updateTextWindow(char *new_msg){
g_print("Msg rcv: %s\n", new_msg);
GtkTextIter start, end;
GtkTextBuffer *buffer = gtk_text_view_get_buffer (global_text_window);
//global_text_window is a global gtkwidget where TextBuffer is embedded
gchar *previous_text;
gtk_text_buffer_get_bounds (buffer, &start, &end);
previous_text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
strcat(previous_text, "\n");
strcat(previous_text, new_msg);
gtk_text_buffer_set_text(buffer, previous_text, -1);
g_free(previous_text);
}
Moreover the related code in the main function where all GtkWidgets are declared and initializated is:
void create_ui (CustomData *data) {
[...]
GtkWidget *secondary_box; /* for text entry & buffer */
GtkScrolledWindow *scrolling_box;
GtkWidget *text_window;
GtkTextBuffer *buffer;
GtkWidget *text_entry;
secondary_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
gtk_widget_set_size_request(secondary_box, 600, -1);
scrolling_box = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(scrolling_box, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
text_entry = gtk_entry_new();
global_text_entry = text_entry;
gtk_entry_set_max_length(text_entry, 25);
text_window = gtk_text_view_new();
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_window));
gtk_text_buffer_set_text(buffer, "\n", -1);
gtk_text_view_set_editable(text_window, FALSE);
global_text_window = text_window;
gtk_scrolled_window_add_with_viewport(scrolling_box, text_window);
gtk_box_pack_end(GTK_CONTAINER(secondary_box), text_entry, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(text_entry), "activate", G_CALLBACK(text_entry_cb), &data);
gtk_box_pack_end(GTK_CONTAINER(secondary_box), scrolling_box, TRUE, TRUE, 0);
[...]
}
The update function is called from another thread, not from the "activate" callback for the text entry widget.
Thanks for your help
That code doesn't make sense. You're concatenating into a string that doesn't have space to hold more text, that's undefined behavior waiting to happen! Your posted answer doesn't make sense either, there's no point in creating a copy of the text like that. That might help just because it uses the heap a bit more, so perhaps you get luckier. Still undefined behavior to concatenate into previous_text.
If you want to append more text, there's no need to re-set the entire text contents. Just use gtk_text_buffer_insert() to insert new text at the end. Use gtk_text_buffer_get_iter_at_offset() first to get an iterator for the end of the buffer's contents.
UPDATE Oh, and also, you absolutely cannot call GTK+ from multiple threads. It is not a thread-safe library, so don't do that. It's a bit unclear if you do, but it sure sounds like it.
I'm new to using GTK. Here is a small section of my code. The aim is to copy the entire current line. The contents are stored in "line". "start" and "end" are textiter at start and end of line.
gtk_text_iter_set_line_offset (start, 0);
gtk_text_iter_forward_to_line_end (end);
line = gtk_text_iter_get_text (start, end);
gtk_clipboard_set_text (clipboard, line, -1);
And upon execution, Im getting the following error messages.
Gtk[27786]: CRITICAL: gtk_text_iter_set_line_offset: assertion 'iter != NULL' failed
Gtk[27786]: CRITICAL: gtk_text_iter_forward_to_line_end: assertion 'iter != NULL' failed
Gtk[27786]: CRITICAL: gtk_text_iter_get_text: assertion 'start != NULL' failed
Gtk[27786]: CRITICAL: gtk_clipboard_set_text: assertion 'text != NULL' failed
What is wrong with the code block? How can i resolve it?
Thanks all :)
You have probably got your iterators declared like GtkTextIter *start. Instead, according to the text widget conceptual overview in GTK's documentation, "GtkTextIter is a struct designed to be allocated on the stack; it's guaranteed to be copiable by value and never contain any heap-allocated data." This means you should not declare them as pointers:
GtkTextIter start, end;
// ...
gtk_text_iter_set_line_offset (&start, 0);
gtk_text_iter_forward_to_line_end (&end);
line = gtk_text_iter_get_text (&start, &end);
I am trying to create a multi-thread, jpg rotation program but I am having problems getting g_thread to work.
int processUserRequest (UserRequest *uRequest,
char * const* argv, int argc, int optind){
struct RotationData CurData;
CurData.argv=argv;
CurData.argc=argc;
CurData.optind=optind;
CurData.uRequest=uRequest;
gpointer user_data = &CurData;
int transform = FALSE;
int max_files = argc - optind;
int i;
gpointer data=&i;
GThreadPool *pool;
if(!g_thread_supported())
g_thread_init(NULL);
pool = g_thread_pool_new(MultiThreadRotation,user_data, 5, TRUE, NULL);
for(i=0;i
{
g_thread_pool_push(pool, &data,NULL);
}
//g_thread_pool_free (pool, TRUE,TRUE);
//Create a montage file
transform = createMontageFile (uRequest);
return transform;
}
The function MultiThreadRotation is suppose to be called by g_thread_pool_push, but it is not being good once. Can anyone help, I am quite the novice.
Also, I thought about outputting the error from g_thread_pool_push, how would you output a GError *error message?
First off, in the code you pasted, there's a bug in the for statement.
Assuming that's fixed, here are a few remarks.
I'm not sure why this is failing, but you can get some indication from the GError's "message" member, which is a human-readable C string you can use with printf() or whatever you like. Unfortunately, you've set the GError arguments to NULL in the g_thread_*() calls.
This routine leaks the thread pool; you should call g_thread_pool_free() before exiting it.
If you're doing other threading in your program, and you care about performance, you should think carefully about whether you want these threads to be exclusive or shared. That's set with the argument to g_thread_pool_new() which you've set to TRUE (exclusive).