I mean, I suppose I could put mutexes in the incremented variables, at least for writes. I don't see another solution as far as the pixbuf array is concerned. The loop which GTK works probably would have difficulty working with outside interaction.
I ultimately had to make the graphics code a socket server since it would get a "cannot complete display error" when trying to project it through a ssh -X so I had to have the MPI based simulation send character streams over the socket for this code to process. Would that work in the timeout loop, does EVERYTHING GTK based have to be in the GTK thread? I figured that would lead to problems so I isolated the most data intensive parts outside the loop and used the ints to synchronized.
Here is the graphics code, the MPI code is over 1000 lines long, it just sends characters anyway. The pixbuf.c code is largely the same, a modification of the put_pixel example method in the api.
Code:
#include <stdlib.h>
#include <gtk/gtk.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <pthread.h>
#include "pixbuf.c"
#define CYCLE_LEN 35
#define FRAME_DELAY 5
#define FRAME_BUFFER_SIZE 200
#define NUM_GENS 200
#define PORT 3131
/* demo window */
static GtkWidget *window = NULL;
/* Current frames */
static GdkPixbuf *frame;
int current_master_frame = 0;
int current_thread_frame = 0;
//frame_buffer and row buffer
GdkPixbuf *buffer[FRAME_BUFFER_SIZE];
guchar *row;
/* Widgets */
static GtkWidget *da;
//other
static guint timeout_id;
static int frame_num;
//functions
void *gtk_function(void *arg);
static gint expose_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data);
static gint timeout (gpointer data);
static void cleanup_callback (GtkObject *object, gpointer data);
int main(int argc, char *argv[])
{
int res, i, j, n, total, current_frame, bytes_read, status;
pthread_t a_thread;
int server_socket, client_socket;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
//this might be troublesome
gtk_init(&argc, &argv);
if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fprintf(stderr,"Server: Socket create error: %s\n", strerror(errno));
return EXIT_FAILURE;
}
//Set up socket so port can be immediately reused:
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &status, sizeof(i));
//set up socket particulars
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
server_addr.sin_family = AF_INET;
if ((bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr))) == -1) {
fprintf(stderr,"Server: Bind error: %s\n", strerror(errno));
return EXIT_FAILURE;
}
if ((listen(server_socket, 5)) == -1) {
fprintf(stderr,"Server: Listen error: %s\n", strerror(errno));
return EXIT_FAILURE;
}
unsigned int c_len = sizeof(client_addr);
client_addr.sin_port = PORT;
if ((client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &c_len)) == -1) {
fprintf(stderr,"Server: Connection error: %s\n", strerror(errno));
}
printf("Connection Accepted.\n");
//init pixbufs
for (i = 0; i < FRAME_BUFFER_SIZE; i++) {
buffer[i] = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
FALSE, 8, X_RES, Y_RES);
}
res = pthread_create(&a_thread, NULL, gtk_function, NULL);
if (res != 0) {
perror("Thread Creation failed");
exit(EXIT_FAILURE);
}
sleep(2);
char ready = 31;
if (row == NULL)
row = malloc(X_RES * sizeof(guchar));
for(n = 0; n<NUM_GENS; n++) {
if (current_master_frame - current_thread_frame < FRAME_BUFFER_SIZE) {
current_frame = current_master_frame % FRAME_BUFFER_SIZE;
for (i = 0; i < Y_RES; i++) {
write(client_socket, &ready, 1); //send ready
if((bytes_read = read(client_socket, row, X_RES)) == -1) {
fprintf(stderr,"Client: Read Error: %d Bytes read: %s\n",
bytes_read, strerror(errno));
return EXIT_FAILURE;
}
// printf("Read: %d\n", bytes_read);
put_pixels(buffer[current_frame], i, row);
}
// sleep(1);
current_master_frame++;
printf("Current Master Frame: %d, Thread Frame: %d Diff: %d\n",
current_master_frame, current_thread_frame,
current_master_frame - current_thread_frame);
}
else {
printf("Other thread stalled\n");
printf("Current Master Frame: %d, Thread Frame: %d Diff: %d\n",
current_master_frame, current_thread_frame,
current_master_frame - current_thread_frame);
sleep(1);
}
}
exit(EXIT_SUCCESS);
}
void *gtk_function(void *arg) {
int i;
// gtk_init((int*) arg, NULL);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_screen (GTK_WINDOW (window),
gtk_widget_get_screen (window));
gtk_window_set_title (GTK_WINDOW (window), "Pixbufs");
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
g_signal_connect (window, "destroy",
G_CALLBACK (cleanup_callback), NULL);
gtk_widget_set_size_request (window, X_RES, Y_RES);
frame = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, X_RES, Y_RES);
da = gtk_drawing_area_new ();
g_signal_connect (da, "expose-event",
G_CALLBACK (expose_cb), NULL);
gtk_container_add (GTK_CONTAINER (window), da);
timeout_id = g_timeout_add (FRAME_DELAY, timeout, NULL);
if (!gtk_widget_get_visible (window))
{
gtk_widget_show_all (window);
}
else
{
gtk_widget_destroy (window);
window = NULL;
g_object_unref (frame);
}
gtk_main();
pthread_exit("GTK done.");
}
/* Expose callback for the drawing area */
static gint
expose_cb (GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
guchar *pixels;
int rowstride;
rowstride = gdk_pixbuf_get_rowstride (frame);
pixels = gdk_pixbuf_get_pixels (frame) + rowstride * event->area.y + event->area.x * 3;
gdk_draw_rgb_image_dithalign (widget->window,
widget->style->black_gc,
event->area.x, event->area.y,
event->area.width, event->area.height,
GDK_RGB_DITHER_NORMAL,
pixels, rowstride,
event->area.x, event->area.y);
return TRUE;
}
/* Timeout handler to regenerate the frame */
static gint
timeout (gpointer data)
{
int catch_up = current_master_frame - current_thread_frame;
if(catch_up > 0) {
int current_frame = current_thread_frame % FRAME_BUFFER_SIZE;
gdk_pixbuf_copy_area(buffer[current_frame], 0, 0, X_RES, Y_RES,
frame, 0, 0);
gtk_widget_queue_draw (da);
current_thread_frame++;
}
return TRUE;
}
static void
cleanup_callback (GtkObject *object,
gpointer data)
{
g_source_remove (timeout_id);
timeout_id = 0;
}