// Compile with: // gcc gchat.c `pkg-config gtk+-3.0 --cflags --libs` -o gchat // gtk 3.0 needs to be installed, including the -dev packages #include #include #include #include #include #include #include #include #include #include GtkWidget *message_view, *log_view; GtkTextBuffer *message_buffer, *log_buffer; int skt; GtkWidget *grid; // This is NOT in the GUI thread! // That means we can have a race condition // GTK has thread safety, but not when used this way void* listen_for_updates(void* arg){ uint16_t name_length; ssize_t length; char name[128]; char buffer[1024*1024]; buffer[0] = 0; for(;;){ length = read(skt, &name_length, 2); if(length != 2) break; length = recv(skt, name, name_length, MSG_WAITALL); if(length != name_length) break; name[length] = 0; uint32_t message_length; length = read(skt, &message_length, 4); if(length != 4) break; char *message_buffer = (char*)malloc(message_length - 1); length = recv(skt, message_buffer, message_length, MSG_WAITALL); if(length != message_length) break; message_buffer[length] = 0; sprintf(buffer + strlen(buffer), "%s: %s\n", name, message_buffer); free(message_buffer); int add_text(gpointer data){ log_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (log_view)); gtk_text_buffer_set_text (log_buffer, buffer, -1); return 0; } gdk_threads_add_idle(add_text, 0); // gtk_grid_attach(GTK_GRID(grid), server_view, 0, 7, 6, 3); // printf("Succeeded in grid attach\n"); } } char* get_text_of_textview(GtkWidget *text_view) { GtkTextIter start, end; GtkTextBuffer *buffer = gtk_text_view_get_buffer((GtkTextView *)text_view); gchar *text; gtk_text_buffer_get_bounds(buffer, &start, &end); text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); return text; } static void send_name (GtkWidget *widget, gpointer data) { char type = 1; char* the_text = get_text_of_textview(message_view); uint16_t textlen = strlen(the_text); write(skt, &type, 1); write(skt, &textlen, 2); write(skt, the_text, textlen); } static void send_message (GtkWidget *widget, gpointer data) { char type = 2; char* the_text = get_text_of_textview(message_view); uint32_t textlen = strlen(the_text); write(skt, &type, 1); write(skt, &textlen, 4); write(skt, the_text, textlen); // Also put the message in our own log } static void activate (GtkApplication *app, gpointer user_data){ GtkWidget *window; GtkWidget *button; printf("We're here\n"); /* create a new window, and set its title */ window = gtk_application_window_new (app); gtk_window_set_title ((GtkWindow*)window, "Window"); gtk_container_set_border_width (GTK_CONTAINER (window), 10); /* Here we construct the container that is going pack our buttons */ grid = gtk_grid_new (); /* Pack the container in the window */ gtk_container_add (GTK_CONTAINER (window), grid); button = gtk_button_new_with_label ("Send Message"); g_signal_connect (button, "clicked", G_CALLBACK (send_message), NULL); /* Place the first button in the grid cell (0, 6), and make it fill * just 1 cell horizontally and vertically (ie no spanning) */ gtk_grid_attach (GTK_GRID (grid), button, 0, 6, 1, 1); button = gtk_button_new_with_label ("Change Name"); g_signal_connect (button, "clicked", G_CALLBACK (send_name), NULL); gtk_grid_attach (GTK_GRID (grid), button, 1, 6, 1, 1); button = gtk_button_new_with_label ("Quit"); g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window); /* Place the Quit button in the grid cell (0, 1), and make it * span 2 columns. */ gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 2, 1); message_view = gtk_text_view_new (); message_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (message_view)); gtk_text_buffer_set_text (message_buffer, "Write your message here", -1); gtk_grid_attach(GTK_GRID(grid), message_view, 0, 2, 6, 3); log_view = gtk_text_view_new (); log_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (log_view)); gtk_widget_set_hexpand(log_view, TRUE); gtk_widget_set_vexpand(log_view, TRUE); gtk_text_buffer_set_text (log_buffer, "Chat log goes here", -1); gtk_grid_attach(GTK_GRID(grid), log_view, 0, 7, 6, 3); /* Now you might put the view in a container and display it on the * screen; when the user edits the text, signals on the buffer * will be emitted, such as "changed", "insert_text", and so on. */ /* Now that we are done packing our widgets, we show them all * in one go, by calling gtk_widget_show_all() on the window. * This call recursively calls gtk_widget_show() on all widgets * that are contained in the window, directly or indirectly. */ gtk_widget_show_all (window); pthread_t T; pthread_create(&T, 0, listen_for_updates, 0); int length = 0; } int main(int argc, char ** argv){ if(argc < 3){ printf("Usage: %s hostname port\n", argv[0]); return 1; } struct sockaddr_in sad; sad.sin_port = htons(atoi(argv[2])); sad.sin_family = AF_INET; skt = socket(AF_INET, SOCK_STREAM, 0); if(skt == -1) goto err; struct hostent* entry = gethostbyname(argv[1]); struct in_addr **addr_list = (struct in_addr**)entry->h_addr_list; struct in_addr* c_addr = addr_list[0]; char* ip_string = inet_ntoa(*c_addr); sad.sin_addr = *c_addr; printf("Connecting to: %s\n", ip_string); if(connect(skt, (struct sockaddr*)&sad, sizeof(struct sockaddr_in))) goto err; GtkApplication *app; int status; printf("About to start the GUI\n"); app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); printf("Calling g_application_run\n"); status = g_application_run (G_APPLICATION (app), 0, 0); g_object_unref (app); close(skt); return 0; err: perror("gchat"); close(skt); return 1; }