Search code examples
cmultithreadingpthreadsgtkglade

new window does not open when i use pthread


Im trying to make to make a chat app (IRC), and while working on a gui i could not show a new window and run the client in the same time, the server runs perfectly on the console. If i try to run a new window without the server it works fine. I would like to open a new window and the run the client so it can create text on the the window that was recently opened.

void on_login_clicked(int argc, char *argv[])
{ 
    char userlogin[20];
    char userpass[20];
    sprintf(userlogin,"%s",user_text );
    sprintf(userpass,"%s",pass_text );

    printf ("Entry contents: %s\n", userlogin);
    printf ("Entry contents: %s\n", userpass);

    struct userData *acc = malloc(sizeof(struct userData));
    char ipadd[] = "0.0.0.0";

    acc = login(userlogin, userpass);
    if(acc == NULL)
        errx(1,"Your account does not exist!");


    system("clear");

    struct sentDATA *data = malloc(sizeof(struct sentDATA));
    data->acc = acc;
    data->adressIP = ipadd;


    gtkstartnewwindow(argc,argv, data);
}

void gtkstartnewwindow(int argc, char *argv[],struct sentDATA *data)
{


    GtkWidget *window;
    GtkWidget *mainBox;
    GtkWidget *grid;
    GtkWidget *topBox;
    GtkWidget *botBox;
    GtkWidget *header;

    GtkWidget *name;
    GtkWidget *entry;   
    GtkWidget *message;
    GtkWidget *connectBtn;
    GtkWidget *imgBtn;
    GtkWidget *callBtn;

    connected = FALSE;

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_widget_set_size_request (window, 400, 500);

    gtk_window_set_title(GTK_WINDOW(window), "Chat client");

    mainBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);

    header = gtk_header_bar_new();

    name = gtk_label_new("");
    gtk_header_bar_set_custom_title(GTK_HEADER_BAR(header), name);

    connectBtn = gtk_button_new_with_label("Connect");
    gtk_widget_set_size_request(connectBtn, 70, 30);
    imgBtn = gtk_button_new_with_label("Send an image");
    gtk_widget_set_size_request(imgBtn, 70, 30);
    callBtn = gtk_button_new_with_label("Call someone");
    gtk_widget_set_size_request(callBtn, 70, 30);
    gtk_header_bar_pack_end(GTK_HEADER_BAR(header), imgBtn);
    gtk_header_bar_pack_start(GTK_HEADER_BAR(header), connectBtn);

    gtk_box_pack_start(GTK_BOX(mainBox), header, FALSE, FALSE, 0);

    grid = gtk_grid_new();
    gtk_grid_set_column_spacing (GTK_GRID(grid), 15);
    gtk_grid_set_row_spacing(GTK_GRID(grid), 5);

    gtk_container_set_border_width(GTK_CONTAINER(grid), 15);

    topBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    gtk_widget_set_hexpand(topBox, TRUE);


    chat = gtk_text_view_new();
    gtk_text_view_set_editable(GTK_TEXT_VIEW(chat), FALSE);
    gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(chat), FALSE);
    gtk_widget_set_vexpand (chat, TRUE);
    gtk_widget_set_hexpand (chat, TRUE);
    gtk_grid_attach(GTK_GRID(grid), chat, 0, 0, 1, 1);


    botBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    message = gtk_text_view_new();
    gtk_widget_set_size_request(message, 70, 30);
    gtk_widget_set_hexpand (message, TRUE); 


    gtk_grid_attach(GTK_GRID(grid), botBox, 0, 2, 2, 1);
    gtk_widget_set_vexpand (grid, TRUE);
    gtk_widget_set_hexpand (grid, TRUE);
    gtk_widget_set_halign (grid, GTK_ALIGN_FILL);
    gtk_widget_set_valign (grid, GTK_ALIGN_FILL);

    gtk_box_pack_start(GTK_BOX(mainBox), grid, TRUE, TRUE, 0);
    gtk_container_add(GTK_CONTAINER(window), mainBox);

         entry = gtk_entry_new ();
    gtk_entry_set_max_length (GTK_ENTRY (entry), 50);
    g_signal_connect (entry, "activate",G_CALLBACK (enter_callback), entry);
    gtk_entry_set_text (GTK_ENTRY (entry), "hello");    
    g_signal_connect(imgBtn, "clicked", G_CALLBACK (on_open_image), NULL);
    gtk_grid_attach(GTK_GRID(grid), entry, 0 , 1, 2, 1);

    //g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), G_OBJECT(window));
    gboolean runtime = TRUE;
    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(exit_app), &runtime);

    gtk_widget_show_all(window);

    g_message("GUI started");


    while (gtk_main_iteration_do(FALSE)) {
        if (!runtime)
            break;
        //other callback handling

        //this loop needs to be running infinitely, 
        //if you need to wait in your program anywhere, 
        //(and it cannot be done only once before the loop)
        //we will need to make it into threads

    }

    pthread_t thr1;
    pthread_create(&thr1,NULL,launchClient,data);   
    pthread_join(thr1,NULL);
}



void recieved_text (gchar *m) {
    GtkTextIter e;

    GtkTextBuffer *chatBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(chat));
    gtk_text_buffer_get_end_iter(chatBuf, &e);
    gtk_text_buffer_insert(chatBuf, &e, m, -1);
    gtk_text_buffer_insert(chatBuf, &e, "\n", 1);
} 


launchClient have also couple of pthreads. Is there any way to run launchClient and a new window at the same time? Moreover, to run recieved_text from launchClient?

Thanks


Solution

  • In GTK you must update the GUI (i.e. call widget related APIs) only from the main thread. You can do whatever you want from the other threads but whenever you need to update the user interface you should notify the main thread, and only there the GUI is updated.

    The notification step is quite easy to do because the underlying GLib APIs are thread-aware, i.e. GLib takes care of locking/unlocking the internal data when requested. There are many ways to do it: the easiest one I'm aware of is to use g_idle_add (gdk_threads_add_idle is just a wrapper kept for legacy reasons).

    In this answer I provided a basic example with 100 concurrent threads that updates a single widget.