GUI doesn't display when handling command line arguments - c

The problem is that program just handles the command line arguments and exits, instead of displaying a GUI too.
For example:
#include <gtk/gtk.h>
static gint print_cmd_arg(GApplication *app, GApplicationCommandLine *app_cmd, int *argc)
{
if (*argc > 1)
{
char **argv = g_application_command_line_get_arguments(app_cmd, argc);
GFile *file = g_file_new_for_commandline_arg(argv[1]);
if (g_file_query_exists(file, NULL))
{
char *text;
g_file_load_contents(file, NULL, &text, NULL, NULL, NULL);
g_print("%s", text);
g_free(text);
g_object_unref(file);
return 0;
}
else
{
g_print("File \'%s\' does not exist.\n", argv[1]);
g_object_unref(file);
return 1;
}
}
return 2;
}
static void activation(GtkApplication *app, gpointer user_data)
{
GtkWidget *window = gtk_application_window_new(app);
gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS);
gtk_window_set_title(GTK_WINDOW(window), "Test");
g_signal_connect(window, "delete-event", G_CALLBACK(gtk_widget_destroy), NULL);
GtkWidget *button = gtk_button_new_with_label("Quit");
g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_widget_destroy), window);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
}
int main(int argc, char **argv)
{
int status;
GtkApplication *test = gtk_application_new("this.is.only.a.test", G_APPLICATION_NON_UNIQUE | G_APPLICATION_HANDLES_COMMAND_LINE);
g_signal_connect(test, "activate", G_CALLBACK(activation), NULL);
g_signal_connect(G_APPLICATION(test), "command-line", G_CALLBACK(print_cmd_arg), &argc);
status = g_application_run(G_APPLICATION(test), argc, argv);
return status;
}
Try running this, you'll see that this program completely ignores function activation.
What I want this program to do is to both handle command line arguments and display GUI.
Also, I know I'm supposed to use g_application_command_line_set_exit_status() in print_cmd_arg instead of returns, but I don't know how to do this and get compiler warnings.

Handling command line is a complex subject in the GtkApplication/GApplication context due to added flexibility.
If you are "overriding" the command line handling/parsing then a variety of methods to do so will be available.
Information on this subject is somewhat scattered in the documentation. Some of it is here:
https://wiki.gnome.org/HowDoI/GtkApplication
https://wiki.gnome.org/HowDoI/GtkApplication/CommandLine
https://developer.gnome.org/gio//2.30/GApplication.html#g-application-run
https://developer.gnome.org/gtk3/stable/GtkApplication.html
https://developer.gnome.org/gtk3/stable/gtk-getting-started.html
From 1) you can read:
Dealing with the command line
Normally, GtkApplication will assume that arguments passed on the
command line are files to be opened. If no arguments are passed, then
it assumes that an application is being launched to show its main
window or an empty document. In the case that files were given, you
will receive these files (in the form of GFile) from the open signal.
Otherwise you will receive an activate signal. It is recommended that
new applications make use of this default handling of command line
arguments.
If you want to deal with command line arguments in more advanced ways,
there are several (complementary) mechanisms by which you can do this.
And an important bit is:
Note in particular that it is possible to use action activations
instead of activate or open. It is perfectly reasonable that an
application could start without an activate signal ever being emitted.
activate is only supposed to be the default "started with no options" signal. Actions are meant to be used for anything else.
So, there are various ways to handle arguments. The questions goes to how and what are you trying to do. If the arguments are files then maybe handle them with the open signal and avoid handling the command line.
To solve the actual problem, as you may conclude, activate won't be fired but you can do it by using g_application_activate in your command line callback, e.g.:
static gint print_cmd_arg(GApplication *app, GApplicationCommandLine *app_cmd, int *argc)
{
/* ADD THIS TO YOUR CODE */
g_application_activate(app);
/* ENDS HERE */
if (*argc > 1)
{
char **argv = g_application_command_line_get_arguments(app_cmd, argc);
GFile *file = g_file_new_for_commandline_arg(argv[1]);
if (g_file_query_exists(file, NULL))
{
char *text;
g_file_load_contents(file, NULL, &text, NULL, NULL, NULL);
g_print("%s", text);
g_free(text);
g_object_unref(file);
return 0;
}
else
{
g_print("File \'%s\' does not exist.\n", argv[1]);
g_object_unref(file);
return 1;
}
}
return 2;
}

Related

GTK application for looping other apps - problem with freeze UI

My problem is the operation of the for loop with GUI application. I am writing a program that opens other programs by clicking on the run button (for testing), during the run my UI starts to hang. How to make my UI work simultaneously with another application without freezing? And so that all elements are clickable during the for loop
main.c - gtk code
#include <gtk/gtk.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
GtkWidget *window;
GtkWidget *run_button;
GtkWidget *stop_button;
GtkWidget *button_box;
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS);
gtk_window_set_title(GTK_WINDOW(window), "Application - Launch GUI Tests");
gtk_window_set_default_size(GTK_WINDOW(window), 600, 480);
/* create button box - start / stop buttons */
button_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_set_homogeneous(GTK_BOX(button_box), TRUE);
stop_button = gtk_button_new_with_label("Stop");
run_button = gtk_button_new_with_label("Run");
/* button box pack */
gtk_box_pack_start(GTK_BOX(button_box), stop_button, FALSE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(button_box), run_button, FALSE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(window), button_box);
/* signals */
g_signal_connect(G_OBJECT(window), "destroy", gtk_main_quit, NULL);
g_signal_connect(G_OBJECT(run_button), "clicked", G_CALLBACK(on_run_button_clicked), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
when I click on the run button this code starts working (below)
void on_run_button_clicked(GtkWidget *run_button)
{
char *list[] = {"xfce4-terminal", "gnome-application", "test-app", "firefox"};
pid_t child;
int status;
for (int i = 0; *(list + i); i++) {
switch(child = fork()) {
case -1:
perror("child - fork error");
exit(EXIT_FAILURE);
case 0: /* child process exec another program */
execlp(list[i], list[i], NULL);
exit(127);
default: /* the parent waits until the program is closed */
wait(&status);
//check_status(status);
}
}
}
And the applications are started in turn until the user closes them, but the problem is that the main application starts to hang and the stop button is unavailable. How to fix it? Helps me, please
Looks like gtk_main() in main() isn't able to do its thing because on_run_button_clicked() is stuck in wait().
You need to make sure on_run_button_clicked() actually returns.

Non-standard device event input for GTK

I am using GTK as the user interface to a piece of hardware (a 3D printer). The hardware can create events as a result of user interactions with the machine (not the GUI). For example, they can remove the build plate which trips a switch thus sending a signal to my program that this has happened, but how do I get gtk_main() to recognize that this event has happened?
In other words, how do I get gtk_main() to watch for non-standard device input events?
You can use custom actions provided by GtkApplication.
In any case you must implement by yourself the monitoring code, either by polling your hardware status or by leveraging more advanced techniques (if possible).
/* gcc -o monitor monitor.c $(pkg-config --cflags --libs gtk+-3.0) */
#include <gtk/gtk.h>
static void
startup(GtkApplication *application)
{
GAction *action;
/* plate-removed is a custom action */
action = G_ACTION(g_simple_action_new("plate-removed", NULL));
g_action_map_add_action(G_ACTION_MAP(application), action);
g_object_unref(action);
/* You can connect your callbacks to the GAction::activate signal */
action = g_action_map_lookup_action(G_ACTION_MAP(application), "plate-removed");
g_signal_connect_swapped(action, "activate",
G_CALLBACK(g_print), "Plate has been removed\n");
}
static gpointer
worker_thread(gpointer user_data)
{
GApplication *application = G_APPLICATION(user_data);
for (;;) {
g_usleep(g_random_int_range(500000, 5000000));
/* Event occurred: emit the signal */
g_action_group_activate_action(G_ACTION_GROUP(application),
"plate-removed", NULL);
}
return NULL;
}
static gboolean
polling_loop(gpointer user_data)
{
GApplication *application = G_APPLICATION(user_data);
if (g_random_int_range(1, 20) == 8) {
/* Event occurred: emit the signal */
g_action_group_activate_action(G_ACTION_GROUP(application),
"plate-removed", NULL);
}
return G_SOURCE_CONTINUE;
}
static void
activate(GtkApplication *application)
{
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_application_add_window(application, GTK_WINDOW(window));
gtk_widget_show_all(window);
/* You can use whatever you want to monitor your hardware, in particular
* a polling loop or a worker thread */
g_timeout_add(50, polling_loop, application);
/* g_thread_new("WorkerThread", worker_thread, application); */
}
int
main(int argc, char *argv[])
{
GtkApplication *application;
int retval;
application = gtk_application_new(NULL, G_APPLICATION_FLAGS_NONE);
g_signal_connect(application, "startup", G_CALLBACK(startup), NULL);
g_signal_connect(application, "activate", G_CALLBACK(activate), NULL);
retval = g_application_run(G_APPLICATION(application), argc, argv);
g_object_unref(application);
return retval;
}

Cairo and GTK+, unknown draw event call and program stop

I'm working with Cairo and GTK3.0 and I got problems that I have no clue on how to solve.
Currently I have 2 oddities that I do not have the knowledge of to solve.
The draw event is called whenever the terminal floats over the drawing window.
The program does not pas the gtk_main(); function.
I will provide my code below, which is very basic, it is based on this: http://zetcode.com/gfx/cairo/cairobackends/ , the GTK window part.
I eventually just need a window that I can call the draw event on whenever I want in my code.
#include <cairo.h>
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int xStart;
int yStart;
int xEnd;
int yEnd;
} lineData;
int i = 0;
int gonnaDraw = 0;
lineData *lines;
int lineSize = 0;
/**
* The drawing with the cairo elements is done here.
* The new line is saved to an array.
* Eventually there should be a loop that draws all lines in the array.
*/
static void do_drawing(cairo_t *cr) {
printf("I'm endless, somehow.\n");
if(lineSize != 0) { //We only want to draw in the infinite for loop, not before it.
cairo_set_source_rgb(cr, 255, 0, 0);
cairo_set_line_width(cr, 1.0);
lineData newLine = { 10.0 + i, 50.0 + i, 100.0 + i, 50.0 + i };
//lines[lineSize - 1] = newLine;
cairo_move_to(cr, newLine.xStart, newLine.xEnd);
cairo_line_to(cr, newLine.yStart, newLine.yEnd);
cairo_stroke(cr);
i = i + 10;
printf("i: %d\n", i);
gonnaDraw = 1;
}
}
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) {
do_drawing(cr);
return FALSE;
}
int main (int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *darea;
printf("1\n");
gtk_init(&argc, &argv);
printf("2\n");
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
printf("3\n");
darea = gtk_drawing_area_new();
printf("4\n");
gtk_container_add(GTK_CONTAINER(window), darea);
printf("5\n");
g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
printf("6\n");
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 800, 800);
gtk_window_set_title(GTK_WINDOW(window), "GTK window");
printf("7\n");
gtk_widget_show_all(window);
printf("8\n");
gtk_main(); // If I'm removed I got no drawing window.
printf("9\n"); // I do not show up.
lines = (lineData *) malloc(lineSize * sizeof(lineData));
for(;;) {
if(gonnaDraw == 1) {
lineSize++;
printf("lineSize: %d\n", lineSize);
lines = (lineData *) realloc(lines, lineSize * sizeof(lineData));
gtk_widget_queue_draw(window);
gonnaDraw = 0;
}
}
return 0;
}
Compiled with standard method.
You almost understand the GTK+ drawing model now. Question 1 is a consequence of it: when another window goes over yours, the window system tells GTK+ that that area needs to be redrawn. This is what i meant when I talked about clipping.
Now the other part is understanding the GTK+ event model. All window systems operate in such a way that all programs run on a loop that consists of while the program is alive, get a message from the window system and act on it. This is what gtk_main() does. gtk_main() not returning is normal; it doesn't return until a call to gtk_main_quit(). Apart from using signals to perform actions when a widget is interacted with, there are two ways you can "hook into" the main loop: g_timeout_add(), which runs a function on schedule, and g_idle_add(), which runs a function when next possible. The former is useful if you want something to happen every so often; the latter is useful if you want a worker thread to signal the UI to update. The GLib documentation on main loops will tell you more. I'm still not sure what your end goal is, so I suppose you can try playing with both to see what happens.
– andlabs

Calling GTK_MAIN under Pthread(secondary thread) in C code

This is in my continuation of GTK understanding::
Is it right to call GTK_MAIN() under pthread from Main?? sample code ::
from main i call dispInit(argc, argv); from which i call g_thread_create(main_callback, NULL, FALSE, NULL);
** also i have not included g_idle_add in this code..this is just a reference code.
Please guide
#include <stdio.h>
#include <glib.h>
#include <gtk/gtk.h>
//#include "dispimage.h"
#include <windows.h>
#define sleep(n) Sleep(1000 * n)
GtkWidget* window;
void dispInit(int argc, char* argv[]);
void dispInfoPage(char* fileName, int duration);
gpointer main_callback(gpointer data)
{
gtk_main();
return 0;
}
void dispInit(int argc, char* argv[])
{
gdk_threads_init();
gdk_threads_enter();
printf("Initializing the display library\n");
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_resize(GTK_WINDOW(window), 640, 480);
gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
gtk_widget_realize( window );
gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
g_thread_create(main_callback, NULL, FALSE, NULL);
gdk_threads_leave();
}
void dispInfoPage(char* fileName, int duration)
{
int index;
gdk_threads_enter();
printf("Initializing dispInfoPage\n");
destroyWidget();
printf("Initializing dispInfoPage1\n");
GtkWidget *image;
image = gtk_image_new_from_file(fileName);
printf("Initializing dispInfoPage2\n");
gtk_container_add(GTK_CONTAINER(window), image);
gtk_widget_show(image);
gtk_widget_show(window);
printf("Initializing dispInfoPage4\n");
printf("Initializing dispInfoPage5\n");
gdk_threads_leave();
printf("Initializing dispInfoPage6\n");
}
void destroyWidget()
{
GList *children, *iter;
struct WidgetsAlive *temp, *prev, *next, *depTemp;
children = gtk_container_get_children(GTK_CONTAINER(window));
for(iter = children; iter != NULL; iter = g_list_next(iter)){
gtk_container_remove(GTK_CONTAINER(window),GTK_WIDGET(iter->data));
printf("Deleting Widget\n");
}
g_list_free(iter);
g_list_free(children);
}
int dispTextPage(char* fileName, int isJustifyCenter)
{
int index;
GtkWidget *textv;
GdkWindow *textv_window;
GdkPixmap *pixmap = NULL;
GtkTextBuffer *textBuffer;
gdk_threads_enter();
GdkColor color;
char debugBuf[128] = { '\0' };
char newfName[100]={'\0'};
char ext[4]={'\0'};
char temp[100]={'\0'};
int i;
FILE * fd;
destroyWidget();
textBuffer = gtk_text_buffer_new(NULL);
textv = gtk_text_view_new_with_buffer(textBuffer);
gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textv), 22);
gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textv), 20);
gtk_text_view_set_pixels_above_lines(GTK_TEXT_VIEW(textv),1);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textv), GTK_WRAP_CHAR);
if (isJustifyCenter == 1)
{
gtk_text_view_set_justification(GTK_TEXT_VIEW(textv), GTK_JUSTIFY_CENTER);
}
else
{
gtk_text_view_set_justification(GTK_TEXT_VIEW(textv), GTK_JUSTIFY_LEFT);
}
gtk_text_view_set_editable(GTK_TEXT_VIEW(textv), FALSE);
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textv), FALSE);
printf("tttt0");
gtk_container_add(GTK_CONTAINER(window), textv);
printf("tttt1");
textv_window = gtk_text_view_get_window (GTK_TEXT_VIEW (textv),
GTK_TEXT_WINDOW_TEXT);
gdk_color_parse ("#68604d", &color);
pixmap = gdk_pixmap_create_from_xpm ((GdkDrawable *) textv_window, NULL,
&color, fileName);
gdk_window_set_back_pixmap (textv_window, pixmap, FALSE);
g_object_unref(pixmap);
textBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textv));
gtk_text_buffer_create_tag (textBuffer, "Red", "foreground", "Red", NULL);
gtk_text_buffer_create_tag (textBuffer, "RedBold","foreground", "Red",NULL);
gtk_text_buffer_create_tag(textBuffer, "gray_bg", "background", "gray", NULL);
gtk_text_buffer_create_tag(textBuffer, "italic", "style", PANGO_STYLE_ITALIC, NULL);
gtk_text_buffer_create_tag(textBuffer, "bold","weight", PANGO_WEIGHT_BOLD, NULL);
gtk_text_buffer_create_tag (textBuffer, "RedFontWeight", "weight", 1000,NULL);
gtk_text_buffer_create_tag (textBuffer, "RedBoldFontWeight","weight", 1000,NULL);
gtk_widget_show(textv);
gtk_widget_show(window);
gdk_threads_leave();
return index;
}
void *fsmThread_RunFunction()
{
int pollMsgRetVal = -1;
printf("Now enter into for image");
dispInfoPage("../images/givefp.gif",1);
sleep(5);
dispInfoPage("../images/bootup.gif",1);
sleep(5);
dispInfoPage("../images/givefp.gif",1);
sleep(5);
dispInfoPage("../images/bootup.gif",1);
sleep(5);
printf("Now enter into for disptext");
dispTextPage("",0);
printf("Now exit for disptext");
}
int main(int argc, char *argv[])
{
GThread *fsmThreadId;
GError *error = NULL;
g_thread_init(NULL);
dispInit(argc, argv);
dispInfoPage("../images/bootup.gif",1);
sleep(5);
printf("Now creat ethread ");
fsmThreadId = g_thread_create(fsmThread_RunFunction,NULL,TRUE,&error);
if (error) {
fflush(stderr);
exit(1);
}
g_thread_join(fsmThreadId);
sleep(2);
printf("ENd of main");
return 0;
}
The short answer: yes, you can call gtk_main() from a thread other than the main C thread, as long as you consistently call all GTK API functions from the same thread throughout the lifetime of the process. More details follow.
According to the documentation, GTK and GDK are not thread-safe (they cannot be concurrently called from multiple threads), but they are thread-aware — they provide locking functions such as gdk_threads_enter and gdk_threads_leave that can be used to synchronize GTK calls accross multiple threads. However, the documentation goes on to say that "with the Win32 backend, GDK and GTK+ calls should not be attempted from multiple threads at all." So, if you care about portability to Windows, you will want to avoid attempting to call GTK API calls from multiple threads. Additionally, GTK 3 completely deprecated the use of the threading locks.
There is, however, one way to safely call into GTK from multiple threads that works on all architectures: place the GTK calls in a function and pass it as callback to g_idle_add (without any special locking) — from any thread. It will schedule the callback to be invoked by the GTK main loop, in whatever thread is running it. This is documented at the end of the description section of the documentation, above the start of the details section.
A word on terminology: the word "main thread" in C normally refers to the thread that runs the C main() function. In a GTK context, the "main thread" often confusingly refers to the thread running the GTK main loop. While the two can be (and typically are) one and the same, GTK doesn't really care what thread you call the main loop from, as long as you call all GTK functions (including gtk_main) from the same one. To avoid confusion, it is best to refer to this thread as the GTK thread, the main loop thread, or the GUI thread.

Glib signals - How to check if a handler of an instance is already blocked?

After a handler of an instance has been blocked with g_signal_handler_block, is it possible to check if the handler is still being blocked or has been unblocked by g_signal_handler_unblock in the meantime, apart from storing the state in a boolean variable for example?
I hoped something like that would be possible
g_signal_handler_block (selection, handler_id_row_selected);
if (g_signal_handler_is_blocked (selection, handler_id_row_selected))
g_print ("is still blocked");
But a "g_signal_handler_is_blocked" function does not exist. g_signal_handler_is_connected is not the right function to use, since the signal handler remains connected, thus the function returns TRUE.
I have tried g_signal_handler_find (), since there is G_SIGNAL_MATCH_UNBLOCKED as one of the match types, but it has not worked yet. Even though I have rewritten my code anyway, I still would like to know if it is possible, since i use the blocking/unblocking relatively often.
g_signal_handler_find here is working as expected. Here is my test case:
#include <gtk/gtk.h>
gboolean
g_signal_handlers_is_blocked_by_func(gpointer instance, GFunc func, gpointer data)
{
return g_signal_handler_find(instance,
G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA | G_SIGNAL_MATCH_UNBLOCKED,
0, 0, NULL, func, data) == 0;
}
static void
handler(void)
{
g_print("handler called\n");
}
static void
switch_blocking(GtkWidget *button)
{
GFunc func = (GFunc) handler;
if (g_signal_handlers_is_blocked_by_func(button, func, NULL)) {
g_signal_handlers_unblock_by_func(button, func, NULL);
g_print("handler unblocked\n");
} else {
g_signal_handlers_block_by_func(button, func, NULL);
g_print("handler blocked\n");
}
}
int
main(int argc, char **argv)
{
GtkWidget *window;
GtkWidget *button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label("Click me");
g_signal_connect_after(button, "clicked", G_CALLBACK(switch_blocking), NULL);
g_signal_connect(button, "clicked", G_CALLBACK(handler), NULL);
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
gtk_main();
return 0;
}

Resources