Back to index

gcompris  8.2.2
chess.c
Go to the documentation of this file.
00001 /* gcompris - chess.c
00002  *
00003  * Copyright (C) 2002 Bruno Coudoin
00004  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU General Public License as published by
00007  *   the Free Software Foundation; either version 2 of the License, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details.
00014  *
00015  *   You should have received a copy of the GNU General Public License
00016  *   along with this program; if not, write to the Free Software
00017  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00020 #include <stdarg.h>
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <unistd.h>
00025 #include <signal.h>
00026 
00027 #include "chess_notation.h"
00028 
00029 #include "gcompris/gcompris.h"
00030 
00031 #if defined _WIN32 || defined __WIN32__
00032 # undef WIN32   /* avoid warning on mingw32 */
00033 # define WIN32
00034 #endif
00035 
00036 #define SOUNDLISTFILE PACKAGE
00037 
00038 static GcomprisBoard *gcomprisBoard = NULL;
00039 static gboolean board_paused = TRUE;
00040 
00041 static GPid    gnuchess_pid;
00042 static void    start_board (GcomprisBoard *agcomprisBoard);
00043 static void    pause_board (gboolean pause);
00044 static void    end_board (void);
00045 static gboolean       is_our_board (GcomprisBoard *gcomprisBoard);
00046 static void    set_level (guint level);
00047 static int     gamewon;
00048 static void    game_won(void);
00049 
00050 static void    engine_local_destroy (GPid gnuchess_pid);
00051 
00052 static gboolean  engine_local_cb     (GIOChannel *source,
00053                                   GIOCondition condition,
00054                                   gpointer data);
00055 static gboolean  engine_local_err_cb (GIOChannel *source,
00056                                   GIOCondition condition,
00057                                   gpointer data);
00058 static void    display_white_turn  (gboolean whiteturn);
00059 static void    display_info      (gchar *info);
00060 static int     get_square_from_coord (double x, double y);
00061 
00062 #define CHESSBOARD_X 50
00063 #define CHESSBOARD_Y 20
00064 #define SQUARE_WIDTH 60
00065 #define SQUARE_HEIGHT       60
00066 #define WHITE_COLOR  0xFFFF99FF
00067 #define BLACK_COLOR  0x9999FFFF
00068 #define WHITE_COLOR_H       0x99FF99FF
00069 #define BLACK_COLOR_H       0x99FF99FF
00070 
00071 #define TURN_X              (BOARDWIDTH-(BOARDWIDTH-(CHESSBOARD_X+(SQUARE_WIDTH*8)))/2)
00072 #define TURN_Y              (CHESSBOARD_Y+15)
00073 
00074 #define INFO_X              TURN_X
00075 #define INFO_Y              (TURN_Y+40)
00076 
00077 /* Game Type */
00078 #define COMPUTER     1
00079 #define PARTYEND     2
00080 #define MOVELEARN    3
00081 
00082 static char gameType = COMPUTER;
00083 
00084 static GnomeCanvasGroup *boardRootItem = NULL;
00085 
00086 static GIOChannel *read_chan;
00087 static GIOChannel *write_chan;
00088 
00089 static gint read_cb;
00090 static gint err_cb;
00091 
00092 static Position *position;
00093 
00094 /*
00095  * Contains the squares structure
00096  */
00097 typedef struct {
00098   GnomeCanvasItem    *square_item;
00099   GnomeCanvasItem    *piece_item;
00100   Square              square;
00101 } GSquare;
00102 
00103 static GSquare  *currentHighlightedGsquare;
00104 
00105 static GnomeCanvasItem      *turn_item = NULL;
00106 static GnomeCanvasItem      *info_item = NULL;
00107 
00108 /* Need more space to fit notation.h definition */
00109 static GSquare *chessboard[100];
00110 
00111 static GnomeCanvasItem      *chess_create_item(GnomeCanvasGroup *parent);
00112 static void           chess_destroy_all_items(void);
00113 static void           chess_next_level(void);
00114 static gint           item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
00115 static gint           item_event_black(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
00116 static gboolean              start_child (char        *cmd,
00117                                   GIOChannel **read_chan,
00118                                   GIOChannel **write_chan,
00119                                   GPid     *gnuchess_pid);
00120 
00121 static void           write_child (GIOChannel  *write_chan,
00122                                   char        *format,
00123                                   ...);
00124 
00125 
00126 /* Description of this plugin */
00127 static BoardPlugin menu_bp =
00128   {
00129     NULL,
00130     NULL,
00131     "Learning Chess",
00132     "Play chess against tux in a learning mode",
00133     "Bruno Coudoin <bruno.coudoin@free.fr>",
00134     NULL,
00135     NULL,
00136     NULL,
00137     NULL,
00138     start_board,
00139     pause_board,
00140     end_board,
00141     is_our_board,
00142     NULL,
00143     NULL,
00144     set_level,
00145     NULL,
00146     NULL,
00147     NULL,
00148     NULL
00149   };
00150 
00151 /*
00152  * Main entry point mandatory for each Gcompris's game
00153  * ---------------------------------------------------
00154  *
00155  */
00156 
00157 GET_BPLUGIN_INFO(chess)
00158 
00159 /*
00160  * in : boolean TRUE = PAUSE : FALSE = CONTINUE
00161  *
00162  */
00163 static void pause_board (gboolean pause)
00164 {
00165   if(gcomprisBoard==NULL)
00166     return;
00167 
00168   if(gamewon == TRUE && pause == FALSE) /* the game is won */
00169     {
00170       game_won();
00171     }
00172 
00173   board_paused = pause;
00174 }
00175 
00176 void gnuchess_died(int signum)
00177 {
00178   gnuchess_pid = 0;
00179   gc_dialog(_("Error: The external program gnuchess died unexpectedly"), gc_board_stop);
00180 }
00181 
00182 /*
00183  */
00184 static void start_board (GcomprisBoard *agcomprisBoard)
00185 {
00186 
00187   gnuchess_pid = 0;
00188 
00189 #ifndef WIN32
00190   signal(SIGTRAP, gnuchess_died);
00191   signal(SIGPIPE, gnuchess_died);
00192   if (!g_file_test (GNUCHESS, G_FILE_TEST_EXISTS))
00193     {
00194     gc_dialog(_("Error: The external program gnuchess is required\nto play chess in gcompris.\n"
00195               "Find this program on http://www.rpmfind.net or in your\nGNU/Linux distribution\n"
00196               "And check it is located here: "GNUCHESS),
00197              gc_board_stop);
00198     return;
00199   }
00200 #endif
00201 
00202   if(agcomprisBoard!=NULL)
00203     {
00204 
00205       gcomprisBoard=agcomprisBoard;
00206 
00207       /* Default mode */
00208       if(!gcomprisBoard->mode)
00209        gameType=COMPUTER;
00210       else if(g_strncasecmp(gcomprisBoard->mode, "computer", 1)==0)
00211        gameType=COMPUTER;
00212       else if(g_strncasecmp(gcomprisBoard->mode, "partyend", 1)==0)
00213        gameType=PARTYEND;
00214       else if(g_strncasecmp(gcomprisBoard->mode, "movelearn", 1)==0)
00215        gameType=MOVELEARN;
00216 
00217       gcomprisBoard->level=1;
00218       gcomprisBoard->maxlevel=1;
00219       gcomprisBoard->sublevel=1;
00220       gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
00221 
00222       switch(gameType)
00223        {
00224        case PARTYEND:
00225        case MOVELEARN:
00226          gcomprisBoard->maxlevel=9;
00227          gc_bar_set(GC_BAR_LEVEL);
00228          break;
00229        default:
00230          gc_bar_set(0);
00231        }
00232 
00233       if(start_child (GNUCHESS, &read_chan,
00234                     &write_chan, &gnuchess_pid)==FALSE)
00235        {
00236          gc_dialog(_("Error: The external program gnuchess is mandatory\n"
00237                     "to play chess in gcompris.\n"
00238                     "First install it, and check it is in "GNUCHESS), gc_board_stop);
00239          return;
00240        }
00241 
00242       read_cb = g_io_add_watch (read_chan, G_IO_IN|G_IO_PRI,
00243                             engine_local_cb, NULL);
00244       err_cb = g_io_add_watch (read_chan, G_IO_HUP,
00245                             engine_local_err_cb, NULL);
00246 
00247       write_child (write_chan, "xboard\n");
00248       write_child (write_chan, "protover 2\n");
00249       write_child (write_chan, "post\n");
00250       write_child (write_chan, "easy\n");
00251       write_child (write_chan, "level 100 1 0\n");
00252       write_child (write_chan, "depth 1\n");
00253       write_child (write_chan, "time 500\n");
00254 
00255       chess_next_level();
00256 
00257       gamewon = FALSE;
00258       pause_board(FALSE);
00259 
00260     }
00261 }
00262 
00263 /* ======================================= */
00264 static void end_board ()
00265 {
00266 #ifndef WIN32
00267   signal(SIGTRAP, NULL);
00268   signal(SIGPIPE, NULL);
00269 #endif
00270   if(gcomprisBoard!=NULL)
00271     {
00272       pause_board(TRUE);
00273       chess_destroy_all_items();
00274     }
00275   gcomprisBoard = NULL;
00276 
00277   turn_item     = NULL;
00278   info_item     = NULL;
00279 
00280 
00281   engine_local_destroy(gnuchess_pid);
00282 }
00283 
00284 /* ======================================= */
00285 static void set_level (guint level)
00286 {
00287 
00288   if(gcomprisBoard!=NULL)
00289     {
00290       gcomprisBoard->level=level;
00291       gcomprisBoard->sublevel=1;
00292       chess_next_level();
00293     }
00294 }
00295 /* ======================================= */
00296 static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
00297 {
00298   if (gcomprisBoard)
00299     {
00300       if(g_strcasecmp(gcomprisBoard->type, "chess")==0)
00301        {
00302          /* Set the plugin entry */
00303          gcomprisBoard->plugin=&menu_bp;
00304 
00305          return TRUE;
00306        }
00307     }
00308   return FALSE;
00309 }
00310 
00311 /*-------------------------------------------------------------------------------*/
00312 /*-------------------------------------------------------------------------------*/
00313 /* set initial values for the next level */
00314 static void chess_next_level()
00315 {
00316   register Square square;
00317   register gshort rank;
00318   gchar *img;
00319 
00320   img = gc_skin_image_get("gcompris-bg.jpg");
00321   gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
00322                        img);
00323   g_free(img);
00324 
00325   gc_bar_set_level(gcomprisBoard);
00326 
00327   chess_destroy_all_items();
00328   gamewon = FALSE;
00329 
00330   /* Initial position */
00331   position = POSITION (position_new_initial ());
00332 
00333   switch(gameType)
00334     {
00335     case PARTYEND:
00336       position_set_initial_partyend(position, gcomprisBoard->level);
00337       break;
00338     case MOVELEARN:
00339       position_set_initial_movelearn(position, gcomprisBoard->level);
00340       break;
00341     }
00342   /* Init our internal chessboard */
00343   for (rank = 1; rank <= 8; rank++) {
00344     for (square = A1 + ((rank - 1) * 10);
00345         square <= H1 + ((rank - 1) * 10);
00346         square++) {
00347 
00348        GSquare *gsquare;
00349 
00350        gsquare = g_malloc(sizeof(GSquare));
00351 
00352        chessboard[square] = gsquare;
00353        chessboard[square]->piece_item = NULL;
00354        chessboard[square]->square = square;
00355 
00356     }
00357   }
00358 
00359   /* Try the next level */
00360   chess_create_item(gnome_canvas_root(gcomprisBoard->canvas));
00361 }
00362 
00363 /* ==================================== */
00364 /* Destroy all the items */
00365 static void chess_destroy_all_items()
00366 {
00367   register Square square;
00368   register gshort rank;
00369 
00370   if(boardRootItem!=NULL)
00371     gtk_object_destroy (GTK_OBJECT(boardRootItem));
00372 
00373   boardRootItem = NULL;
00374   turn_item     = NULL;
00375   info_item     = NULL;
00376 
00377   if(position!=NULL)
00378     gtk_object_destroy (GTK_OBJECT (position));
00379 
00380   position = NULL;
00381 
00382   for (rank = 1; rank <= 8; rank++) {
00383     for (square = A1 + ((rank - 1) * 10);
00384         square <= H1 + ((rank - 1) * 10);
00385         square++) {
00386 
00387       if(chessboard[square]!=NULL)
00388        {
00389          g_free(chessboard[square]);
00390          chessboard[square]=NULL;
00391        }
00392     }
00393   }
00394 }
00395 
00396 /* ==================================== */
00397 static GnomeCanvasItem *
00398 chess_create_item(GnomeCanvasGroup *parent)
00399 {
00400   guint color;
00401   GnomeCanvasItem *item = NULL;
00402   Square square;
00403   Piece piece;
00404   gshort rank;
00405   gboolean white_side = TRUE;
00406   guint empty_case = 0;
00407   gboolean need_slash = TRUE;
00408 
00409   boardRootItem = GNOME_CANVAS_GROUP(
00410                                  gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
00411                                                      gnome_canvas_group_get_type (),
00412                                                      "x", (double) 0,
00413                                                      "y", (double) 0,
00414 
00415                                                      NULL));
00416 
00417   for (rank = 1; rank <= 8; rank++) {
00418     for (square = A1 + ((rank - 1) * 10);
00419         square <= H1 + ((rank - 1) * 10);
00420         square++) {
00421       int x,y;
00422 
00423       x = square % 10 - 1;
00424       y = square / 10 - 2;
00425 
00426       color=((x+y)%2?BLACK_COLOR:WHITE_COLOR);
00427 
00428       item  = gnome_canvas_item_new (boardRootItem,
00429                                  gnome_canvas_rect_get_type (),
00430                                  "x1", (double) CHESSBOARD_X + (x * SQUARE_WIDTH),
00431                                  "y1", (double) CHESSBOARD_Y + ((7-y) * SQUARE_HEIGHT),
00432                                  "x2", (double) CHESSBOARD_X + (x * SQUARE_WIDTH) + SQUARE_WIDTH -1,
00433                                  "y2", (double)  CHESSBOARD_Y + ((7-y) * SQUARE_HEIGHT) + SQUARE_HEIGHT -1,
00434                                  "fill_color_rgba", color,
00435                                  "outline_color", "black",
00436                                  "width_units", (double)2,
00437                                  NULL);
00438       chessboard[square]->square_item = item;
00439     }
00440   }
00441 
00442   /* Enter the gnuchess edit mode */
00443   write_child (write_chan, "force\n");
00444   write_child (write_chan, "new\n");
00445   write_child (write_chan, "setboard ");
00446 
00447   empty_case = 0;
00448   need_slash = FALSE;
00449 
00450   /* Display the pieces */
00451   for (rank = 8; rank >= 1; rank--) {
00452     for (square = A1 + ((rank - 1) * 10);
00453         square <= H1 + ((rank - 1) * 10);
00454         square++)
00455       {
00456        GdkPixbuf *pixmap = NULL;
00457        char *str;
00458        gint x, y;
00459 
00460        piece = position->square[square];
00461 
00462        x = square % 10 - 1;
00463        y = square / 10 - 2;
00464 
00465        //     printf ( "%c%s\n", piece_to_ascii(piece), san);
00466 
00467        if(need_slash)
00468          {
00469            write_child (write_chan, "/");
00470            need_slash = FALSE;
00471          }
00472 
00473        if(piece!=NONE)
00474          {
00475 
00476            if( (white_side && BPIECE(piece)) ||
00477               (!white_side && WPIECE(piece)) )
00478              {
00479               white_side = !white_side;
00480               //            write_child (write_chan, "c\n");
00481              }
00482            if(empty_case>0)
00483              write_child (write_chan, "%d", empty_case);
00484 
00485            empty_case=0;
00486 
00487            write_child (write_chan, "%c", piece_to_ascii(piece));
00488          }
00489        else
00490          {
00491            empty_case++;
00492          }
00493 
00494        if(x==7)
00495          {
00496            if(empty_case>0)
00497              write_child (write_chan, "%d", empty_case);
00498 
00499            empty_case=0;
00500 
00501            need_slash = TRUE;
00502          }
00503 
00504        //     printf("square=%d piece=%d x=%d y=%d\n", square, piece, x, y);
00505        if(piece != EMPTY)
00506          {
00507            if(BPIECE(piece))
00508              str = g_strdup_printf("chess/B%c.png", piece_to_ascii(piece));
00509            else
00510              str = g_strdup_printf("chess/W%c.png", piece_to_ascii(piece));
00511 
00512            pixmap = gc_pixmap_load(str);
00513            //     g_warning("loading piece %s\n",   str);
00514            g_free(str);
00515            item = gnome_canvas_item_new (boardRootItem,
00516                                      gnome_canvas_pixbuf_get_type (),
00517                                      "pixbuf", pixmap,
00518                                      "x", (double)CHESSBOARD_X + (x * SQUARE_WIDTH) +
00519                                      (guint)((SQUARE_WIDTH-gdk_pixbuf_get_width(pixmap))/2),
00520                                      "y", (double) CHESSBOARD_Y + ((7-y) * SQUARE_HEIGHT) +
00521                                      (guint)((SQUARE_HEIGHT-gdk_pixbuf_get_height(pixmap))/2),
00522                                      NULL);
00523 
00524            chessboard[square]->piece_item = item;
00525            if(WPIECE(piece))
00526              gtk_signal_connect(GTK_OBJECT(item), "event",
00527                              (GtkSignalFunc) item_event, NULL);
00528            else
00529              gtk_signal_connect(GTK_OBJECT(item), "event",
00530                              (GtkSignalFunc) item_event_black, NULL);
00531 
00532            gdk_pixbuf_unref(pixmap);
00533          }
00534       }
00535   }
00536 
00537   /* Quit the gnuchess edit mode */
00538   write_child (write_chan, " w KQkq\n");
00539 
00540   display_white_turn(TRUE);
00541 
00542   return NULL;
00543 }
00544 /* ==================================== */
00545 static void game_won()
00546 {
00547   gcomprisBoard->sublevel++;
00548 
00549   if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
00550     /* Try the next level */
00551     gcomprisBoard->sublevel=1;
00552     gcomprisBoard->level++;
00553     if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
00554       gc_bonus_end_display(BOARD_FINISHED_RANDOM);
00555       return;
00556     }
00557     gc_sound_play_ogg ("sounds/bonus.ogg", NULL);
00558   }
00559   chess_next_level();
00560 }
00561 
00562 static void display_white_turn(gboolean whiteturn)
00563 {
00564 
00565   if(turn_item == NULL)
00566     {
00567 
00568       turn_item = gnome_canvas_item_new (boardRootItem,
00569                                     gnome_canvas_text_get_type (),
00570                                     "text",       " ",
00571                                     "font",       gc_skin_font_board_big,
00572                                     "x", (double) TURN_X,
00573                                     "y", (double) TURN_Y,
00574                                     "anchor",     GTK_ANCHOR_CENTER,
00575                                     "fill_color_rgba", gc_skin_color_content,
00576                                     NULL);
00577     }
00578 
00579   gnome_canvas_item_set(turn_item, "text", (whiteturn ? _("White's Turn") : _("Black's Turn")),
00580                      NULL);
00581 }
00582 
00583 
00584 static void display_info(gchar *info)
00585 {
00586 
00587   if(info_item == NULL)
00588     {
00589       info_item = gnome_canvas_item_new (boardRootItem,
00590                                     gnome_canvas_text_get_type (),
00591                                     "text",       " ",
00592                                     "font",       gc_skin_font_board_big,
00593                                     "x", (double) INFO_X,
00594                                     "y", (double) INFO_Y,
00595                                     "anchor",     GTK_ANCHOR_CENTER,
00596                                     "fill_color_rgba", gc_skin_color_subtitle,
00597                                     NULL);
00598     }
00599 
00600   gnome_canvas_item_set(info_item, "text", info,
00601                      NULL);
00602 }
00603 
00604 
00605 /*
00606  * Move a piece to the given position using chess_notation notation
00607  *
00608  */
00609 static void move_piece_to(Square from, Square to)
00610 {
00611   GSquare *source_square, *dest_square;
00612   GnomeCanvasItem *item;
00613   guint x, y;
00614   double ofset_x, ofset_y;
00615   double x1, y1, x2, y2;
00616   Piece piece = NONE;
00617 
00618 
00619   g_warning("move_piece_to from=%d to=%d\n", from, to);
00620 
00621   source_square = chessboard[from];
00622   item = source_square->piece_item;
00623   source_square->piece_item = NULL;
00624 
00625   if(item == NULL)
00626     {
00627       g_warning("Warning: Problem in chess.c, bad move request in move_piece_to\n");
00628       return;
00629     }
00630 
00631   /* If we are promoting a pawn */
00632   if(position_get_color_to_move(position)==BLACK)
00633     {
00634       if (to & 128) {
00635        piece = ((to & 127) >> 3 ) + WP - 1;
00636        to = (to & 7) + A8;
00637        printf("  Promoting white piece to %d\n", piece);
00638       }
00639     }
00640   else
00641     {
00642       if (to & 128) {
00643        piece = ((to & 127) >> 3) + BP - 1;
00644        to = (to & 7) + A1;
00645        printf("  Promoting black piece to %d\n", piece);
00646       }
00647     }
00648 
00649   /* Show the moved piece */
00650   gnome_canvas_item_set(source_square->square_item,
00651                      "outline_color",
00652                      (BPIECE(position->square[to])?"red":"blue"),
00653                      NULL);
00654 
00655   display_white_turn(BPIECE(position->square[to]));
00656 
00657   x = to % 10;
00658   y = to / 10 -1;
00659 
00660   g_warning("   move_piece_to to    x=%d y=%d\n", x, y);
00661 
00662   dest_square = chessboard[to];
00663 
00664   /* Show the moved piece */
00665   gnome_canvas_item_set(dest_square->square_item,
00666                      "outline_color",
00667                      (BPIECE(position->square[to])?"red":"blue"),
00668                      NULL);
00669 
00670   if(dest_square->piece_item != NULL)
00671     /* Oups I loose a piece */
00672     gtk_object_destroy (GTK_OBJECT(dest_square->piece_item));
00673 
00674   dest_square->piece_item    = item;
00675 
00676   /* Find the ofset to move the piece */
00677   gnome_canvas_item_get_bounds  (item,
00678                              &x1,
00679                              &y1,
00680                              &x2,
00681                              &y2);
00682 
00683 
00684   ofset_x = (CHESSBOARD_X + SQUARE_WIDTH  * (x-1)) - x1 + (SQUARE_WIDTH  - (x2-x1))/2;
00685   ofset_y = (CHESSBOARD_Y + SQUARE_HEIGHT * (8-y)) - y1 + (SQUARE_HEIGHT - (y2-y1))/2;
00686 
00687   gnome_canvas_item_move(item, ofset_x, ofset_y);
00688 
00689   /* Manage rock */
00690   if(position->square[to]==WK && from==E1 && to==C1)
00691     move_piece_to(A1, D1);
00692   else if(position->square[to]==WK && from==E1 && to==G1)
00693     move_piece_to(H1, F1);
00694   else if(position->square[to]==BK && from==E8 && to==C8)
00695     move_piece_to(A8, D8);
00696   else if(position->square[to]==BK && from==E8 && to==G8)
00697     move_piece_to(H8, F8);
00698 
00699   /* Manage promotion */
00700   if(piece != NONE)
00701     {
00702       GdkPixbuf *pixmap = NULL;
00703       char *str;
00704       g_warning("  WARNING promoting a pawn from=%d to=%d piece=%d\n", from, to, piece);
00705       g_warning("  piece_to_ascii returns %c\n", piece_to_ascii(piece));
00706 
00707       if(BPIECE(piece))
00708        str = g_strdup_printf("chess/B%c.png", piece_to_ascii(piece));
00709       else
00710        str = g_strdup_printf("chess/W%c.png", piece_to_ascii(piece));
00711 
00712       pixmap = gc_pixmap_load(str);
00713       g_free(str);
00714       g_warning("loading piece %c\n",  piece_to_ascii(piece));
00715       gnome_canvas_item_set (dest_square->piece_item,
00716                           "pixbuf", pixmap,
00717                           NULL);
00718 
00719     }
00720 
00721   /* Display check info */
00722   if(position_white_king_attack(position))
00723     display_info(_("White checks"));
00724   else if(position_black_king_attack(position))
00725     display_info(_("Black checks"));
00726   else
00727     display_info(" ");
00728 
00729 }
00730 
00731 /*
00732  * Return a square suitable for position functions
00733  */
00734 static int
00735 get_square_from_coord (double x, double y)
00736 {
00737 
00738   return (A1 + (Square) ((x - CHESSBOARD_X) / SQUARE_WIDTH)
00739          + 10 * (7 - (Square)((y - CHESSBOARD_Y) / SQUARE_HEIGHT)));
00740 
00741 }
00742 
00743 void hightlight_possible_moves(GSquare *gsquare)
00744 {
00745   Square square_test;
00746   guint color;
00747   register Square square;
00748   register gshort rank;
00749   short    current_color;
00750 
00751   if(currentHighlightedGsquare == gsquare)
00752     return;
00753 
00754   /* Remember the current color to move */
00755   current_color = position_get_color_to_move(position);
00756 
00757   if(WPIECE(position->square[gsquare->square]))
00758     position_set_color_to_move(position, WHITE);
00759   else
00760     position_set_color_to_move(position, BLACK);
00761 
00762   for (rank = 1; rank <= 8; rank++) {
00763     for (square = A1 + ((rank - 1) * 10);
00764         square <= H1 + ((rank - 1) * 10);
00765         square++) {
00766 
00767 
00768        square_test = position_move_normalize (position, gsquare->square, chessboard[square]->square);
00769 
00770        if (square_test)
00771          {
00772            color=((rank+square)%2?BLACK_COLOR_H:WHITE_COLOR_H);
00773 
00774            gnome_canvas_item_set(chessboard[square]->square_item,
00775                               "fill_color_rgba", color,
00776                               "outline_color", "black",
00777                               NULL);
00778          }
00779        else
00780          {
00781            color=((rank+square)%2?BLACK_COLOR:WHITE_COLOR);
00782 
00783            gnome_canvas_item_set(chessboard[square]->square_item,
00784                               "fill_color_rgba", color,
00785                               "outline_color", "black",
00786                               NULL);
00787          }
00788       }
00789   }
00790 
00791   /* Set back the current color to move */
00792   position_set_color_to_move(position, current_color);
00793 
00794   /* Show the current piece */
00795   gnome_canvas_item_set(gsquare->square_item,
00796                      "outline_color",
00797                      (BPIECE(position->square[gsquare->square])?"red":"blue"),
00798                      NULL);
00799 
00800 }
00801 
00802 /* ==================================== */
00803 static gint
00804 item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
00805 {
00806    static double x, y;
00807    static GSquare *gsquare;
00808    double new_x, new_y;
00809    GdkCursor *fleur;
00810    static int dragging;
00811    double item_x, item_y;
00812 
00813   if(board_paused)
00814     return FALSE;
00815 
00816   item_x = event->button.x;
00817   item_y = event->button.y;
00818   gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
00819 
00820   switch (event->type)
00821     {
00822     case GDK_BUTTON_PRESS:
00823       {
00824        guint x1, y1;
00825        Square square;
00826 
00827        square = get_square_from_coord(event->button.x, event->button.y);
00828        x1 = square % 10;
00829        y1 = square / 10 -1;
00830        gsquare = chessboard[square];
00831 
00832        x = item_x;
00833        y = item_y;
00834 
00835        fleur = gdk_cursor_new(GDK_FLEUR);
00836        gnome_canvas_item_raise_to_top(item);
00837        gc_canvas_item_grab(item,
00838                             GDK_POINTER_MOTION_MASK |
00839                             GDK_BUTTON_RELEASE_MASK,
00840                             fleur,
00841                             event->button.time);
00842        gdk_cursor_destroy(fleur);
00843        dragging = TRUE;
00844 
00845        hightlight_possible_moves(gsquare);
00846       }
00847       break;
00848     case GDK_MOTION_NOTIFY:
00849        if (dragging && (event->motion.state & GDK_BUTTON1_MASK))
00850          {
00851            new_x = item_x;
00852            new_y = item_y;
00853 
00854            gnome_canvas_item_move(item, new_x - x, new_y - y);
00855            x = new_x;
00856            y = new_y;
00857          }
00858        break;
00859 
00860      case GDK_BUTTON_RELEASE:
00861        if(dragging)
00862         {
00863           guint x, y;
00864           double ofset_x, ofset_y;
00865           double x1, y1, x2, y2;
00866           char pos[6];
00867           Square to;
00868 
00869           to = get_square_from_coord(event->button.x, event->button.y);
00870           g_warning("===== Source square = %d Destination square = %d\n", gsquare->square,
00871                 to);
00872 
00873           to = position_move_normalize (position, gsquare->square, to);
00874           if (to) {
00875             position_move (position, gsquare->square, to);
00876 
00877             x = 1 + (event->button.x - CHESSBOARD_X) / SQUARE_WIDTH;
00878             y = 1 + (event->button.y - CHESSBOARD_Y) / SQUARE_HEIGHT;
00879             move_to_ascii((char *)&pos, gsquare->square, to);
00880 
00881             /* Tell gnuchess what our move is */
00882             write_child (write_chan, (char *)&pos);
00883             write_child (write_chan, "\n");
00884             move_piece_to(gsquare->square, to);
00885           }
00886           else
00887             {
00888               g_warning("====== MOVE from %d REFUSED\n", gsquare->square);
00889 
00890               /* Find the ofset to move the piece back to where it was*/
00891               gnome_canvas_item_get_bounds  (item,
00892                                          &x1,
00893                                          &y1,
00894                                          &x2,
00895                                          &y2);
00896 
00897               x = gsquare->square % 10;
00898               y = gsquare->square / 10 -1;
00899 
00900               ofset_x = (CHESSBOARD_X + SQUARE_WIDTH  * (x-1)) - x1 + (SQUARE_WIDTH  - (x2-x1))/2;
00901               ofset_y = (CHESSBOARD_Y + SQUARE_HEIGHT * (8-y)) - y1 + (SQUARE_HEIGHT - (y2-y1))/2;
00902               g_warning("ofset = x=%f y=%f\n", ofset_x, ofset_y);
00903 
00904               gnome_canvas_item_move(item, ofset_x, ofset_y);
00905             }
00906 
00907           gc_canvas_item_ungrab(item, event->button.time);
00908           dragging = FALSE;
00909 
00910           position_display(position);
00911 
00912         }
00913        break;
00914 
00915     default:
00916       break;
00917     }
00918 
00919   return FALSE;
00920 }
00921 
00922 /* ==================================== */
00923 /* The user clicked on a black piece    */
00924 static gint
00925 item_event_black(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
00926 {
00927    static GSquare *gsquare;
00928 
00929   if(board_paused)
00930     return FALSE;
00931 
00932   switch (event->type)
00933     {
00934     case GDK_BUTTON_PRESS:
00935       {
00936        Square square;
00937 
00938        square = get_square_from_coord(event->button.x, event->button.y);
00939        gsquare = chessboard[square];
00940 
00941        hightlight_possible_moves(gsquare);
00942       }
00943       break;
00944     default:
00945       break;
00946     }
00947   return(FALSE);
00948 }
00949 
00950 /*======================================================================*/
00951 /*======================================================================*/
00952 /*======================================================================*/
00953 /*======================================================================*/
00954 static void
00955 engine_local_destroy (GPid gnuchess_pid)
00956 {
00957 
00958   if(!gnuchess_pid)
00959     return;
00960 
00961   g_warning("engine_local_destroy () \n");
00962   write_child (write_chan, "quit\n");
00963 
00964   g_source_remove(read_cb);
00965   g_source_remove(err_cb);
00966 
00967   g_io_channel_close (read_chan);
00968   g_io_channel_unref (read_chan);
00969 
00970   g_io_channel_close (write_chan);
00971   g_io_channel_unref (write_chan);
00972 
00973   if(gnuchess_pid)
00974     g_spawn_close_pid(gnuchess_pid);
00975 }
00976 
00980 static gboolean
00981 engine_local_cb (GIOChannel *source,
00982                GIOCondition condition,
00983                gpointer data)
00984 {
00985   gchar buf[1000];
00986   char *b=buf;
00987   GError *err = NULL;
00988   char *p,*q;
00989   gsize len = 0;
00990   GIOStatus status = G_IO_STATUS_NORMAL;
00991 
00992   g_warning("engine_local_cb");
00993 
00994   status = g_io_channel_read_chars(source,
00995                                buf,
00996                                1000,
00997                                &len,
00998                                &err);
00999 
01000   g_warning("g_io_channel_read_line len=%d", (int)len);
01001   if(status == G_IO_STATUS_ERROR)
01002     {
01003       g_warning("g_io_channel_read_chars error=%s",
01004               err->message);
01005       /* FIXME: Not sure what to do */
01006       return FALSE;
01007     }
01008 
01009   if(status != G_IO_STATUS_NORMAL)
01010     {
01011       g_warning("g_io_channel_read_chars error=%d",
01012               status);
01013       /* FIXME: Not sure what to do */
01014       return FALSE;
01015     }
01016 
01017   g_warning("engine_local_cb read=%s\n", buf);
01018 
01019   if (len > 0) {
01020     b[len] = 0;
01021     b += len;
01022   }
01023 
01024   while (1) {
01025     char tmp;
01026 
01027     q = strchr (buf,'\n');
01028     if (q == NULL) break;
01029     tmp = *(q+1);
01030     *(q+1) = '\0';
01031 
01032     *q='\0';
01033     *(q+1) = tmp;
01034 
01035     g_warning("engine_local_cb read=%s\n", buf);
01036 
01037     /* parse for  NUMBER ... MOVE */
01038     if (isdigit (*buf))
01039       {
01040        if ((p = strstr (buf, "...")))
01041          {
01042            Square from, to;
01043 
01044            g_warning("computer number moves to %s\n", p+4);
01045 
01046            if (san_to_move (position, p+4, &from, &to))
01047              ascii_to_move (position, p+4, &from, &to);
01048 
01049            position_move (position, from, to);
01050            move_piece_to(from , to);
01051          }
01052        else if ((p = strstr (buf, " ")))
01053          {
01054            /* It's a legal move case */
01055            g_warning("Legal move to %s\n", p+1);
01056          }
01057       }
01058 
01059     /* parse for move MOVE */
01060     if (!strncmp ("My move is : ",buf,13))
01061       {
01062        Square from, to;
01063 
01064        p = strstr (buf, ":");
01065        g_warning("computer moves to %s\n", p+1);
01066 
01067        if (san_to_move (position, p+1, &from, &to))
01068          ascii_to_move (position, p+1, &from, &to);
01069 
01070        position_move (position, from, to);
01071        move_piece_to(from , to);
01072       }
01073 
01074     /* parse for illegal move */
01075     if (!strncmp ("Illegal move",buf,12))
01076       {
01077        g_warning("Illegal move to %s : SHOULD NOT HAPPEN", buf+31);
01078       }
01079 
01080     if (!strncmp ("0-1",buf,3))
01081       {
01082        display_info(_("Black mates"));
01083       }
01084 
01085     if (!strncmp ("1-0",buf,3))
01086       {
01087        display_info(_("White mates"));
01088       }
01089 
01090     if (!strncmp ("1/2-1/2",buf,7))
01091       {
01092        display_info(_("Drawn game"));
01093       }
01094 
01095     /* parse for feature */
01096     if (!strncmp ("feature",buf,7))
01097       {
01098        write_child(write_chan, "accepted setboard\n");
01099        write_child(write_chan, "accepted analyze\n");
01100        write_child(write_chan, "accepted ping\n");
01101        write_child(write_chan, "accepted draw\n");
01102        write_child(write_chan, "accepted variants\n");
01103        write_child(write_chan, "accepted myname\n");
01104        write_child(write_chan, "accepted done\n");
01105       }
01106 
01107     memmove (buf, q+1, sizeof(buf) - ( q + 1 - buf));
01108     b -= (q + 1 - buf);
01109   }
01110 
01111   return TRUE;
01112 }
01113 
01114 static gboolean
01115 engine_local_err_cb (GIOChannel *source,
01116                    GIOCondition condition,
01117                    gpointer data)
01118 {
01119   gnuchess_pid = 0;
01120   gc_dialog(_("Error: The external program gnuchess died unexpectingly"), gc_board_stop);
01121   return FALSE;
01122 }
01123 
01124 /*----------------------------------------
01125  * Subprocess creation
01126  * Return TRUE if gnuchess is started, false instead
01127  *----------------------------------------*/
01128 
01129 static gboolean
01130 start_child (char *cmd,
01131             GIOChannel **read_chan,
01132             GIOChannel **write_chan,
01133             GPid *Child_Process)
01134 {
01135   gint   Child_In, Child_Out, Child_Err;
01136   GError *gerror = NULL;
01137 
01138   gchar *Child_Argv[]={ cmd, "-e", NULL };
01139 
01140   g_warning("Ready to start child");
01141 
01142   if (!g_spawn_async_with_pipes(NULL, Child_Argv, NULL,
01143                             G_SPAWN_SEARCH_PATH,
01144                             NULL, NULL, Child_Process, &Child_In, &Child_Out,
01145                             &Child_Err, &gerror)) {
01146 
01147     g_warning("Error message '%s'", gerror->message);
01148     g_warning("Error code    '%d'", gerror->code);
01149     g_error_free (gerror);
01150     g_warning("In order to play chess, you need to have gnuchess installed as " GNUCHESS);
01151     return(FALSE);
01152 
01153   }
01154 
01155   g_warning("gnuchess subprocess is started");
01156 
01157   *read_chan = g_io_channel_unix_new (Child_Out);
01158   *write_chan = g_io_channel_unix_new (Child_In);
01159 
01160   if(g_io_channel_set_encoding(*write_chan, NULL, NULL) != G_IO_STATUS_NORMAL)
01161     g_warning("Failed to set NULL encoding");
01162 
01163   if(g_io_channel_set_flags (*read_chan, G_IO_FLAG_NONBLOCK, NULL) != G_IO_STATUS_NORMAL)
01164     g_warning("Failed to set NON BLOCKING IO");
01165 
01166   if(g_io_channel_set_flags (*write_chan, G_IO_FLAG_NONBLOCK, NULL) != G_IO_STATUS_NORMAL)
01167     g_warning("Failed to set NON BLOCKING IO");
01168 
01169   return(TRUE);
01170 }
01171 
01175 static void
01176 write_child (GIOChannel *write_chan, char *format, ...)
01177 {
01178   GIOStatus err;
01179   va_list ap;
01180   gchar *buf;
01181   gsize len;
01182   GError *error = NULL;
01183 
01184   va_start (ap, format);
01185 
01186   buf = g_strdup_vprintf (format, ap);
01187 
01188   err = g_io_channel_write_chars (write_chan, buf, strlen (buf), &len, &error);
01189 
01190   if (err == G_IO_STATUS_ERROR)
01191     g_error ("Error writing: %s\n", error->message);
01192 
01193   if (err != G_IO_STATUS_NORMAL)
01194     g_warning ("Writing to child process failed");
01195   else
01196     g_warning ("Wrote '%s' to gnuchess", buf);
01197 
01198   err = g_io_channel_flush (write_chan, &error);
01199   if (err == G_IO_STATUS_ERROR)
01200     g_error ("Error flushing: %s\n", error->message);
01201 
01202   va_end (ap);
01203 
01204   g_free (buf);
01205 }