Description
Hangman game written with Gtk+ 2.0 library
Hangman game written with Gtk+ 2.0 library
#include <gtk/gtk.h> #include <string.h> #include <stdlib.h> #include <errno.h> #if !defined(WIN32) #include <sys/stat.h> #include <fcntl.h> #endif #define GHANG_WORD ghang_get_word () #if !defined(WIN32) # define xrand devrand #else # define xrand rand #endif static void press_letter (GtkWidget *, GtkLabel *); static gboolean expose_event (GtkWidget *, GdkEventExpose *, gpointer); char *ghang_current_word = NULL; int ghang_current_word_len = 0; int ghang_current_err = 0; int ghang_max_word = 0; int ghang_word_processing = 0; GSList *list = NULL; GtkWidget *window, *draw; #if !defined(WIN32) gint64 devrand(void) { gint64 c; int fd; if ((fd = open("/dev/random", O_RDONLY)) == -1) { fprintf(stderr, "Cannot open `%s'.\n" "Error: %s.\n", "/dev/random", strerror(errno)); exit(EXIT_FAILURE); } if ( read(fd, &c, sizeof(gint64)) < 1 ) { fprintf(stderr, "Cannot read %d byte from `%s'.\n" "Error: %s.\n", sizeof(gint64), "/dev/random", strerror(errno)); exit(EXIT_FAILURE); } close(fd); return c; } #endif static void ghang_win (void) { GtkWidget *dialog; dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window), 0, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "<big><b>You Win</b></big>"); gtk_window_set_title (GTK_WINDOW (dialog), "Win"); gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window)); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); gtk_main_quit(); } static void ghang_lose (void) { GtkWidget *dialog; dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "<big><b>You Lose</b></big>"); gtk_window_set_title (GTK_WINDOW (dialog), "Lose"); gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window)); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); gtk_main_quit(); } gpointer _error (gchar * message) { GtkWidget *dialog; dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW (window), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, message); gtk_window_set_title (GTK_WINDOW (dialog), "Error"); gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window)); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); return NULL; } static void ghang_load_word (void) { GIOStatus res; char *line = NULL; GError *error = NULL; GString *str_error, *message; GIOChannel *of; /* Opens the file to read */ if (!(of = g_io_channel_new_file ("GHang.word", "r", &error))) { message = g_string_new ("<big><b>Error while opening the file</b></big>\n\n"); switch (error->code) { case G_FILE_ERROR_PERM: case G_FILE_ERROR_ACCES: g_string_append (message, "File cannot be read because you don't have\n" "sufficient rights."); break; case G_FILE_ERROR_NOENT: case G_FILE_ERROR_NXIO: case G_FILE_ERROR_NODEV: g_string_append (message, "File cannot be open because it wasn't found."); break; case G_FILE_ERROR_NAMETOOLONG: g_string_append (message, "File cannot be read because the name is too\n" "long. Usually max length is 255."); break; case G_FILE_ERROR_TXTBSY: g_string_append (message, "File cannot be open because is used by other\n" "operations or processes."); break; case G_FILE_ERROR_LOOP: g_string_append (message, "The file you're trying to open is a symbolic\n" "link to another symbolic link.\n" "This then creates an endless cycle of\n" "links and makes it impossible to determine\n" "the final file to be opened."); break; case G_FILE_ERROR_NOMEM: g_string_free (message, TRUE); message = g_string_new ("<b><big>Space in memory exhausted!</big></b>\n\n" "Space in virtual memory is exhausted, this makes\n" "it impossible to load the file into RAM."); break; case G_FILE_ERROR_IO: g_string_append (message, "File cannot be read because a I/O error has occurred.\n" "Check that the device you are trying to read from\n" "isn't damaged."); break; case G_FILE_ERROR_FAILED: default: g_string_append (message, "An unknown error has occurred while opening the file."); break; } _error (message->str); g_string_free (message, TRUE); g_error_free (error); exit (EXIT_FAILURE); } /* Reading all rows */ do { res = g_io_channel_read_line (of, &line, NULL, NULL, &error); /* Checks that no error occurred, if there were, * returns a dialog error and exit from while. */ switch (res) { case G_IO_STATUS_ERROR: str_error = g_string_new ("<big><b>Error in file type recognition</b></big>\n\n"); switch (error->code) { case G_IO_CHANNEL_ERROR_IO: g_string_append (str_error, "An I/O error does not allow you to read the\n" "file where magic numbers and relative descriptions\n" "are stored."); break; case G_IO_CHANNEL_ERROR_ISDIR: g_string_append (str_error, "File containing the magic numbers and relative\n" "descriptions cannot be read for an unknown error."); break; default: g_string_append (str_error, "File containing the magic numbers and relative\n" "descriptions cannot be read for a unknown error."); break; } _error (str_error->str); g_string_free (str_error, TRUE); g_error_free (error); exit (EXIT_FAILURE); break; case G_IO_STATUS_AGAIN: str_error = g_string_new ("<big><b>Error in file type recognition</b></big>"); g_string_append (str_error, "File containing the magic numbers and relative\n" "descriptions is temporarily unavailable"); _error (str_error->str); g_string_free (str_error, TRUE); g_error_free (error); exit (EXIT_FAILURE); break; case G_IO_STATUS_NORMAL: break; case G_IO_STATUS_EOF: break; } if (res == G_IO_STATUS_AGAIN || res == G_IO_STATUS_ERROR) { break; } if (res != G_IO_STATUS_EOF) { if ( line && *line != '\0' && strlen(line) > 3 ) { line[strlen(line)-1] = 0x00; list = g_slist_append (list, (gpointer) g_strdup (line)); ghang_max_word++; } g_free (line); } } while (res != G_IO_STATUS_EOF); g_io_channel_close (of); } static char * ghang_word(int i) { GSList *current; current = g_slist_nth(list, i); return ((char *) current->data); } static char * ghang_get_word (void) { char first, end; gboolean chk = FALSE; GString *str; int i; if ( !ghang_word_processing ) ghang_load_word (); i = (unsigned int) xrand()%ghang_max_word; ghang_current_word = g_ascii_strup (ghang_word(i), -1); first = *ghang_current_word; ghang_current_word_len = strlen (ghang_current_word); end = ghang_current_word[ghang_current_word_len-1]; str = g_string_new (g_strdup_printf("%c", first)); for ( i = 1; i < ghang_current_word_len-1; ++i ) { if ( ghang_current_word[i] == first || ghang_current_word[i] == end ) { g_string_append_c (str, ghang_current_word[i]); continue; } chk = TRUE; g_string_append_c (str, '_'); } g_string_append_c (str, end); if ( !chk ) { if ( ghang_word_processing == ghang_max_word ) { fprintf(stderr, "No valid string found.\n"); exit (EXIT_FAILURE); } ghang_word_processing++; return ghang_get_word(); } g_slist_free (list); return g_strdup_printf("<big><b>%s</b></big>", str->str); } int main(int argc, char **argv) { GtkWidget *title, *vbox, *text; GtkWidget *hbox, *letter; int i; gtk_init (&argc, &argv); /* Window */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW(window), 300, 430); gtk_window_set_title (GTK_WINDOW(window), "GHang"); /* Vertical Box */ vbox = gtk_vbox_new (FALSE, 2); gtk_container_add (GTK_CONTAINER(window), vbox); /* Title */ title = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL(title), "<big><b>GHang</b></big>\n" "<i>Semplice gioco dell'impiccato</i>"); gtk_label_set_justify (GTK_LABEL(title), GTK_JUSTIFY_CENTER); gtk_box_pack_start (GTK_BOX(vbox), title, FALSE, TRUE, 2); /* Separator */ gtk_box_pack_start (GTK_BOX(vbox), gtk_hseparator_new(), FALSE, TRUE, 0); /* Drawing Area */ draw = gtk_drawing_area_new (); GTK_WIDGET_SET_FLAGS (draw, GTK_CAN_FOCUS); gtk_box_pack_start (GTK_BOX(vbox), draw, TRUE, TRUE, 0); /* Text */ text = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL(text), GHANG_WORD); gtk_box_pack_start (GTK_BOX(vbox), text, FALSE, TRUE, 0); /* Separator */ gtk_box_pack_start (GTK_BOX(vbox), gtk_hseparator_new(), FALSE, TRUE, 0); /* HBox */ hbox = gtk_hbox_new (FALSE, 2); gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, TRUE, 2); /* Letter Button */ for ( i = 0; i < 13; ++i ) { letter = gtk_button_new_with_label (g_strdup_printf ("%c", i + 65)); gtk_button_set_relief (GTK_BUTTON(letter), GTK_RELIEF_NONE); gtk_box_pack_start (GTK_BOX(hbox), letter, FALSE, TRUE, 0); gtk_widget_set_usize (letter, 30, -1); if ( i == (ghang_current_word[0] - 65) || i == (ghang_current_word[ghang_current_word_len-1] - 65) ) { gtk_widget_set_sensitive (letter, FALSE); } else { g_signal_connect (G_OBJECT (letter), "clicked", G_CALLBACK (press_letter), (gpointer) GTK_LABEL(text)); } } hbox = gtk_hbox_new (FALSE, 2); gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, TRUE, 2); for ( i = 13; i < 26; ++i ) { letter = gtk_button_new_with_label (g_strdup_printf ("%c", i + 65)); gtk_button_set_relief (GTK_BUTTON(letter), GTK_RELIEF_NONE); gtk_box_pack_start (GTK_BOX(hbox), letter, FALSE, TRUE, 0); gtk_widget_set_usize (letter, 30, -1); if ( i == (ghang_current_word[0] - 65) || i == (ghang_current_word[ghang_current_word_len-1] - 65) ) { gtk_widget_set_sensitive (letter, FALSE); } else { g_signal_connect (G_OBJECT (letter), "clicked", G_CALLBACK (press_letter), (gpointer) GTK_LABEL(text)); } } g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL); g_signal_connect (G_OBJECT (draw), "expose-event", G_CALLBACK (expose_event), NULL); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL); gtk_widget_show_all (window); gtk_main(); return 0; } static void ghang_write_err (void) { GdkWindow *window = draw->window; GdkGC *gc = draw->style->fg_gc[GTK_WIDGET_STATE(draw)]; int i; switch ( ghang_current_err ) { case 1: gdk_draw_arc (window, gc, TRUE, 203, 95, 45, 45, 0, 64 * 360); break; case 2: for ( i = 0; i < 7; ++i ) gdk_draw_line (window, gc, 222 + i, 135, 222 + i, 210); break; case 3: for ( i = 0; i < 7; ++i ) gdk_draw_line (window, gc, 228, 150 + i, 260, 170 + i); break; case 4: for ( i = 0; i < 7; ++i ) gdk_draw_line (window, gc, 223, 150 + i, 191, 170 + i); break; case 5: for ( i = 0; i < 11; ++i ) gdk_draw_line (window, gc, 228, 198 + i, 250, 233 + i); break; case 6: for ( i = 0; i < 11; ++i ) gdk_draw_line (window, gc, 223, 198 + i, 201, 233 + i); break; } } static void press_letter (GtkWidget *button, GtkLabel *text) { char c, *t; int i; gboolean chk[2] = { FALSE }; GString *str; c = *((char *) gtk_button_get_label (GTK_BUTTON (button))); t = (char *) gtk_label_get_text (text); str = g_string_new (""); for ( i = 0; i < ghang_current_word_len; ++i ) { if ( ghang_current_word[i] == c ) { g_string_append_c(str, c); chk[0] = TRUE; continue; } switch ( t[i] ) { case '_': g_string_append_c(str, '_'); chk[1] = TRUE; break; default: g_string_append_c(str, t[i]); break; } } gtk_label_set_markup (text, g_strdup_printf("<big><b>%s</b></big>", str->str)); g_string_free (str, TRUE); gtk_widget_set_sensitive (button, FALSE); if ( !chk[0] && chk[1] ) { ghang_current_err++; ghang_write_err (); if ( ghang_current_err == 6 ) { ghang_lose(); } } else if ( !chk[1] ) { ghang_win(); } } static gboolean expose_event (GtkWidget *draw, GdkEventExpose *event, gpointer data) { GdkWindow *window = draw->window; GdkGC *gc = draw->style->fg_gc[GTK_WIDGET_STATE(draw)]; int i, c; gdk_draw_rectangle (window, gc, FALSE, 10, 10, 390, 290); for ( i = 0; i < 10; ++i ) { gdk_draw_line (window, gc, 100 + i, 30, 100 + i, 280); gdk_draw_line (window, gc, 30, 275 + i, 200, 275 + i); gdk_draw_line (window, gc, 100, 30 + i, 230, 30 + i); gdk_draw_line (window, gc, 221 + i, 30, 221 + i, 100); for ( c = i; c < i+5; ++c ) gdk_draw_line (window, gc, 150 + c, 39, 110, 79 + c); } return TRUE; }