Back to index

gcompris  8.2.2
python.c
Go to the documentation of this file.
00001 /* gcompris - python.c
00002  *
00003  * Copyright (C) 2003 GCompris Developpement Team
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 <Python.h>
00021 #include <pygobject.h>
00022 #include "gcompris/gcompris.h"
00023 #include "py-gcompris-board.h"
00024 #include "py-mod-gcompris.h"
00025 #include "py-gcompris-profile.h"
00026 
00027 static GcomprisBoard *gcomprisBoard = NULL;
00028 
00029 static PyObject* python_gcomprisBoard = NULL;
00030 static PyObject* python_board_module = NULL;
00031 static PyObject* python_board_instance = NULL;
00032 
00033 static GcomprisBoard *gcomprisBoard_config = NULL;
00034 static PyObject* python_gcomprisBoard_config = NULL;;
00035 static PyObject* python_board_config_module = NULL;
00036 static PyObject* python_board_config_instance = NULL;
00037 
00038 static void    pythonboard_init (GcomprisBoard *agcomprisBoard);
00039 static void    pythonboard_start (GcomprisBoard *agcomprisBoard);
00040 static void    pythonboard_pause (gboolean pause);
00041 static void    pythonboard_end (void);
00042 
00043 static gboolean       pythonboard_is_our_board (GcomprisBoard *agcomprisBoard);
00044 
00045 static gint    pythonboard_key_press (guint keyval, gchar *commit_str, gchar *preedit_str);
00046 static void    pythonboard_ok (void);
00047 static void    pythonboard_set_level (guint level);
00048 static void    pythonboard_config(void);
00049 static void    pythonboard_repeat (void);
00050 static void    pythongc_board_config_start (GcomprisBoard *agcomprisBoard,
00051                                       GcomprisProfile *aProfile);
00052 static void    pythongc_board_config_stop (void);
00053 
00054 static gboolean  pythonboard_is_ready = FALSE;
00055 
00056 
00057 /* Description of this plugin */
00058 static BoardPlugin menu_bp =
00059   {
00060     NULL,
00061     NULL,
00062     N_("Python Board"),
00063     N_("Special board that embeds python into gcompris."),
00064     "Olivier Samyn <osamyn@ulb.ac.be>",
00065     pythonboard_init,
00066     NULL,
00067     NULL,
00068     NULL,
00069     pythonboard_start,
00070     pythonboard_pause,
00071     pythonboard_end,
00072     pythonboard_is_our_board,
00073     pythonboard_key_press,
00074     pythonboard_ok,
00075     pythonboard_set_level,
00076     pythonboard_config,
00077     pythonboard_repeat,
00078     pythongc_board_config_start,
00079     pythongc_board_config_stop
00080   };
00081 
00082 static BoardPlugin menu_bp_no_config =
00083   {
00084     NULL,
00085     NULL,
00086     N_("Python Board"),
00087     N_("Special board that embeds python into gcompris."),
00088     "Olivier Samyn <osamyn@ulb.ac.be>",
00089     pythonboard_init,
00090     NULL,
00091     NULL,
00092     NULL,
00093     pythonboard_start,
00094     pythonboard_pause,
00095     pythonboard_end,
00096     pythonboard_is_our_board,
00097     pythonboard_key_press,
00098     pythonboard_ok,
00099     pythonboard_set_level,
00100     pythonboard_config,
00101     pythonboard_repeat,
00102     NULL,
00103     NULL
00104   };
00105 
00106 /*
00107  * Return the plugin structure (Common to all gcompris boards)
00108  */
00109 
00110 GET_BPLUGIN_INFO(python)
00111 
00112 
00113 /*
00114  * Tests if a python interpreter is available
00115  * and that all required imports can be loaded.
00116  */
00117 
00118 static GList *config_boards= NULL;
00119 
00120 GList *
00121 get_pythonboards_list()
00122 {
00123   GList *pythonboards_list = NULL;
00124   GList *boards_list = gc_menu_get_boards();
00125   GList *list;
00126   GcomprisBoard *board;
00127 
00128   for (list = boards_list; list != NULL; list = list->next){
00129     board = (GcomprisBoard *) list->data;
00130     if (g_ascii_strncasecmp(board->type, "python", 6)==0)
00131       pythonboards_list = g_list_append(pythonboards_list, board);
00132   }
00133 
00134   return pythonboards_list;
00135 }
00136 
00137 static void
00138 pythonboard_init (GcomprisBoard *agcomprisBoard){
00139   PyObject* main_module;
00140   PyObject* globals;
00141   gchar* execstr;
00142   gchar* userplugindir;
00143 
00144   char* board_file_name;
00145   char* boardclass;
00146   gchar *boarddir;
00147   PyObject* module_dict;
00148   PyObject* py_boardclass;
00149 
00150   GcomprisProperties *properties = gc_prop_get();
00151 
00152   if (pythonboard_is_ready)
00153     return ;
00154 
00155   /* Initialize the python interpreter */
00156   Py_Initialize();
00157 
00158   static char *python_args[]={ "" };
00159   PySys_SetArgv( 1, python_args);
00160 
00161   pythonboard_is_ready = TRUE;
00162 
00163   main_module = PyImport_AddModule("__main__"); /* Borrowed reference */
00164   globals = PyModule_GetDict(main_module); /* Borrowed reference */
00165 
00166   if(globals==NULL){
00167     g_warning("! Python disabled: Cannot get info from the python interpreter.\n");
00168     pythonboard_is_ready = FALSE;
00169   } else {
00170     /* Add the python plugins dir to the python's search path */
00171 #ifndef DISABLE_USER_PLUGIN_DIR
00172     userplugindir = g_strconcat(g_get_home_dir(), "/.gcompris/Plugins/", NULL);
00173     execstr = g_strdup_printf("import sys; sys.path.append('%s/python'); sys.path.append('%s')",
00174                            userplugindir, properties->package_python_plugin_dir);
00175     g_free(userplugindir);
00176 #else
00177     execstr = g_strdup_printf("import sys; sys.path.append('%s')",properties->package_python_plugin_dir );
00178 #endif
00179 
00180     g_warning("Executing %s\n", execstr);
00181     if(PyRun_SimpleString(execstr)!=0){
00182       pythonboard_is_ready = FALSE;
00183       g_warning("! Python disabled: Cannot add plugins dir into search path\n");
00184     } else {
00185       /* Try to import pygtk modules */
00186       g_free(execstr);
00187       execstr = g_strdup("import gtk; import gtk.gdk");
00188       if(PyRun_SimpleString(execstr)!=0){
00189        pythonboard_is_ready = FALSE;
00190        g_warning("! Python disabled: Cannot import pygtk modules\n");
00191       } else {
00192        /* Try to import gcompris modules */
00193 
00194        /* Load the gcompris modules */
00195        python_gcompris_module_init();
00196 
00197        g_free(execstr);
00198        execstr = g_strdup("import gcompris; import gcompris.bonus; "
00199                         "import gcompris.score; import gcompris.sound;"
00200                         "import gcompris.skin; import gcompris.timer;"
00201                         "import gcompris.utils; import gcompris.anim");
00202        if(PyRun_SimpleString(execstr)!=0){
00203          pythonboard_is_ready = FALSE;
00204          g_warning("! Python disabled: Cannot import gcompris modules\n");
00205        } else {
00206          GList *python_boards;
00207          GList *list;
00208 
00209          /* Get the list of python boards */
00210          python_boards = get_pythonboards_list();
00211 
00212          /* Search in the list each one with a config entry */
00213          for(list = python_boards; list != NULL; list = list->next) {
00214            GcomprisBoard *board = (GcomprisBoard *) list->data;
00215 
00216            /* Python is now initialized we create some usefull variables */
00217            board_file_name = strchr(board->type, ':')+1;
00218            boardclass = g_strdup_printf("Gcompris_%s", board_file_name);
00219 
00220            /* Test if board come with -L option */
00221 
00222            g_warning("board_dir: %s package_data_dir %s",
00223                     board->board_dir,
00224                     properties->package_python_plugin_dir);
00225 
00226            if (strcmp(board->board_dir, properties->package_python_plugin_dir)!=0){
00227              boarddir = g_strdup_printf("sys.path.append('%s/')", board->board_dir);
00228 
00229              PyRun_SimpleString(boarddir);
00230              g_free(boarddir);
00231            }
00232 
00233            /* Insert the board module into the python's interpreter */
00234            python_board_module = PyImport_ImportModuleEx(board_file_name,
00235                                                    globals,
00236                                                    globals,
00237                                                    NULL);
00238 
00239            if(python_board_module!=NULL){
00240              /* Get the module dictionnary */
00241              module_dict = PyModule_GetDict(python_board_module);
00242 
00243              /* Get the python board class */
00244              py_boardclass = PyDict_GetItemString(module_dict, boardclass);
00245 
00246              if (PyObject_HasAttrString( py_boardclass, "config_start")) {
00247               config_boards = g_list_append(config_boards, board);
00248               g_warning("The board '%s' has a configuration entry",
00249                        board_file_name);
00250              }
00251            }
00252 
00253            g_free(boardclass);
00254          }
00255 
00256          g_list_free(python_boards);
00257        }
00258       }
00259     }
00260     g_free(execstr);
00261 
00262   }
00263 
00264   /* Finalize the python interpreter */
00265   Py_Finalize();
00266 
00267 }
00268 
00269 /*
00270  * Start the board.
00271  * In this case:
00272  * - initialize python interpreter
00273  * - import gcompris functions/objects
00274  * - import gtk/gnome functions/objects
00275  * - load the python written board
00276  * - call the board start function
00277  */
00278 static void
00279 pythonboard_start (GcomprisBoard *agcomprisBoard){
00280   PyObject* main_module;
00281   PyObject* py_function_result;
00282   PyObject* module_dict;
00283   PyObject* py_boardclass;
00284   PyObject* py_boardclass_args;
00285   PyObject* globals;
00286   static char *python_args[]={ "" };
00287   static char* python_prog_name="gcompris";
00288   char* boarddir;
00289   char* boardclass;
00290   char* board_file_name;
00291   gchar *userplugindir;
00292 
00293   GcomprisProperties *properties = gc_prop_get();
00294 
00295   if(agcomprisBoard!=NULL){
00296     /* Initialize the python interpreter */
00297     Py_SetProgramName(python_prog_name);
00298     Py_Initialize();
00299 
00300     PySys_SetArgv(1, python_args);
00301 
00302     init_pygobject();
00303 
00304     main_module = PyImport_AddModule("__main__");
00305     globals = PyModule_GetDict(main_module);
00306 
00307     if(globals==NULL){
00308       g_print("Cannot get info from the python interpreter. Seems there is a problem with this one.\n");
00309       return;
00310     } else {
00311       gcomprisBoard = agcomprisBoard;
00312     }
00313 
00314     /* Add the python plugins dir to the python's search path */
00315 #ifndef DISABLE_USER_PLUGIN_DIR
00316     userplugindir = g_strconcat(g_get_home_dir(), "/.gcompris/Plugins/", NULL);
00317     boarddir = g_strdup_printf("import sys; sys.path.append('%s/python'); sys.path.append('%s')",
00318                             userplugindir,
00319                             properties->package_python_plugin_dir);
00320 #else
00321     boarddir = g_strdup_printf("import sys; sys.path.append('%s')",properties->package_python_plugin_dir );
00322 #endif
00323 
00324     PyRun_SimpleString(boarddir);
00325     g_free(boarddir);
00326 
00327 
00328     /* Test if board come with -L option */
00329     if (strcmp(gcomprisBoard->board_dir, properties->package_data_dir)!=0){
00330       boarddir = g_strdup_printf("sys.path.append('%s/../python/')", gcomprisBoard->board_dir);
00331 
00332       PyRun_SimpleString(boarddir);
00333       g_free(boarddir);
00334     }
00335 
00336 
00337 #ifndef DISABLE_USER_PLUGIN_DIR
00338     g_free(userplugindir);
00339 #endif
00340 
00341     /* Load the gcompris modules */
00342     python_gcompris_module_init();
00343 
00344     /* Python is now initialized we create some usefull variables */
00345     board_file_name = strchr(agcomprisBoard->type, ':')+1;
00346     boardclass = g_strdup_printf("Gcompris_%s", board_file_name);
00347 
00348     /* Insert the board module into the python's interpreter */
00349     python_board_module = PyImport_ImportModuleEx(board_file_name,
00350                                             globals,
00351                                             globals,
00352                                             NULL);
00353 
00354     if(python_board_module!=NULL){
00355       /* Get the module dictionnary */
00356       module_dict = PyModule_GetDict(python_board_module);
00357 
00358       /* Get the python board class */
00359       py_boardclass = PyDict_GetItemString(module_dict, boardclass);
00360 
00361       /* Create a python gcompris board */
00362       python_gcomprisBoard=gcompris_new_pyGcomprisBoardObject(agcomprisBoard);
00363 
00364       /* Create an instance of the board class */
00365       py_boardclass_args = PyTuple_New(1);
00366       Py_INCREF(python_gcomprisBoard);
00367       PyTuple_SetItem(py_boardclass_args, 0, python_gcomprisBoard);
00368       python_board_instance = PyInstance_New(py_boardclass, py_boardclass_args, NULL);
00369       Py_DECREF(py_boardclass_args);
00370 
00371       /* Call the function */
00372       py_function_result = PyObject_CallMethod(python_board_instance, "start", NULL);
00373       if( py_function_result != NULL){
00374        Py_DECREF(py_function_result);
00375       } else {
00376        PyErr_Print();
00377       }
00378     } else {
00379       PyErr_Print();
00380     }
00381 
00382     g_free(boardclass);
00383   }
00384 }
00385 
00386 /*
00387  * Pause the board.
00388  */
00389 static void pythonboard_pause (gboolean pause){
00390   PyObject* result = NULL;
00391 
00392   result = PyObject_CallMethod(python_board_instance, "pause", "i", pause);
00393   if( result != NULL){
00394     Py_DECREF(result);
00395   } else {
00396     PyErr_Print();
00397   }
00398 }
00399 
00400 /*
00401  * End the board.
00402  * In this case:
00403  * - call the board end function
00404  * - finalise python interpreter
00405  */
00406 static void pythonboard_end (void){
00407   PyObject* result = NULL;
00408 
00409   if(python_gcomprisBoard!=NULL){
00410     result = PyObject_CallMethod(python_board_instance, "end", NULL);
00411     if( result == NULL){
00412       PyErr_Print();
00413     } else {
00414       Py_DECREF(result);
00415     }
00416     Py_XDECREF(python_board_module);
00417     Py_XDECREF(python_board_instance);
00418     Py_XDECREF(python_gcomprisBoard);
00419     Py_Finalize();
00420   }
00421 }
00422 
00423 /*
00424  * Return TRUE if the board is a python one.
00425  */
00426 static gboolean pythonboard_is_our_board (GcomprisBoard *gcomprisBoard){
00427 
00428   if (gcomprisBoard->plugin)
00429     return TRUE;
00430 
00431   if(pythonboard_is_ready) {
00432     if (gcomprisBoard!=NULL) {
00433 
00434       if (g_ascii_strncasecmp(gcomprisBoard->type, "python", 6)==0) {
00435 
00436        /* Set the plugin entry */
00437        if (g_list_find (config_boards, gcomprisBoard)){
00438          gcomprisBoard->plugin = &menu_bp;
00439        } else {
00440          gcomprisBoard->plugin = &menu_bp_no_config;
00441        }
00442 
00443        return TRUE;
00444       }
00445     }
00446   }
00447   gcomprisBoard->plugin=NULL;
00448   return FALSE;
00449 }
00450 
00451 /*
00452  * Key press
00453  */
00454 static gint pythonboard_key_press (guint keyval, gchar *commit_str, gchar *preedit_str){
00455   PyObject* result = NULL;
00456 
00457   result = PyObject_CallMethod(python_board_instance, "key_press", "iss", keyval, commit_str, preedit_str);
00458 
00459   if (result==NULL) return FALSE;
00460 
00461   if (PyInt_Check(result) && (PyInt_AsLong(result)>0)){
00462     Py_DECREF(result);
00463     return TRUE;
00464   } else {
00465     Py_DECREF(result);
00466     return FALSE;
00467   }
00468 }
00469 
00470 /*
00471  * OK button pressed
00472  */
00473 static void pythonboard_ok (void){
00474   PyObject* result = NULL;
00475   result = PyObject_CallMethod(python_board_instance, "ok", NULL);
00476   if( result != NULL){
00477     Py_DECREF(result);
00478   } else {
00479     PyErr_Print();
00480   }
00481 }
00482 
00483 /*
00484  * Set Level
00485  */
00486 static void pythonboard_set_level (guint level){
00487   PyObject* result = NULL;
00488 
00489   result = PyObject_CallMethod(python_board_instance, "set_level", "i", level);
00490   if( result != NULL){
00491     Py_DECREF(result);
00492   } else {
00493     PyErr_Print();
00494   }
00495 }
00496 
00497 /*
00498  * Config
00499  */
00500 static void pythonboard_config(void){
00501   PyObject* result = NULL;
00502   result = PyObject_CallMethod(python_board_instance, "config", NULL);
00503   if( result != NULL){
00504     Py_DECREF(result);
00505   } else {
00506     PyErr_Print();
00507   }
00508 }
00509 
00510 /*
00511  * Repeat
00512  */
00513 static void pythonboard_repeat (void){
00514   PyObject* result = NULL;
00515   result = PyObject_CallMethod(python_board_instance, "repeat", NULL);
00516   if( result != NULL){
00517     Py_DECREF(result);
00518   } else {
00519     PyErr_Print();
00520   }
00521 }
00522 
00523 /*
00524  * Start the board config_start.
00525  * In this case:
00526  * - initialize python interpreter
00527  * - import gcompris functions/objects
00528  * - import gtk/gnome functions/objects
00529  * - load the python written board
00530  * - call the board start function
00531  */
00532 
00533 /*
00534  * Normally python in already runningwhen config_start is called. If not config_stop has to stop it.
00535  */
00536 
00537 static gboolean python_run_by_config = FALSE;
00538 
00539 static void
00540 pythongc_board_config_start (GcomprisBoard *agcomprisBoard,
00541                        GcomprisProfile *aProfile
00542                        )
00543 {
00544   GcomprisProperties *properties = gc_prop_get();
00545   PyObject* py_function_result;
00546   PyObject* module_dict;
00547   PyObject* py_boardclass;
00548   PyObject* py_boardclass_args;
00549   PyObject* globals;
00550   static char *python_args[]={ "" };
00551   static char* python_prog_name="gcompris";
00552   char* boardclass;
00553   char* board_file_name;
00554   PyObject* main_module;
00555   char* boarddir;
00556   gchar *userplugindir;
00557 
00558   g_assert (agcomprisBoard != NULL);
00559 
00560   if(!Py_IsInitialized()){
00561     /* Initialize the python interpreter */
00562     Py_SetProgramName(python_prog_name);
00563     Py_Initialize();
00564 
00565     PySys_SetArgv(1, python_args);
00566 
00567     init_pygobject();
00568 
00569     main_module = PyImport_AddModule("__main__");
00570     globals = PyModule_GetDict(main_module);
00571 
00572     if(globals==NULL){
00573       g_print("Cannot get info from the python interpreter. Seems there is a problem with this one.\n");
00574       return;
00575     } else {
00576       gcomprisBoard_config = agcomprisBoard;
00577     }
00578 
00579     /* Add the python plugins dir to the python's search path */
00580 #ifndef DISABLE_USER_PLUGIN_DIR
00581     userplugindir = g_strconcat(g_get_home_dir(), "/.gcompris/Plugins/", NULL);
00582     boarddir = g_strdup_printf("import sys; sys.path.append('%s/python'); sys.path.append('%s'); sys.path.append('%s')",
00583                             userplugindir,
00584                             properties->package_python_plugin_dir,
00585                             gcomprisBoard_config->board_dir);
00586 #else
00587     boarddir = g_strdup_printf("import sys; sys.path.append('%s')", properties->package_python_plugin_dir );
00588 #endif
00589 
00590     PyRun_SimpleString(boarddir);
00591     g_free(boarddir);
00592 
00593 #ifndef DISABLE_USER_PLUGIN_DIR
00594     g_free(userplugindir);
00595 #endif
00596 
00597     /* Load the gcompris modules */
00598     python_gcompris_module_init();
00599 
00600     python_run_by_config = TRUE;
00601 
00602   }
00603   else {
00604     main_module = PyImport_AddModule("__main__"); /* Borrowed reference */
00605     globals = PyModule_GetDict(main_module); /* Borrowed reference */
00606   }
00607 
00608   /* Python is now initialized we create some usefull variables */
00609   board_file_name = strchr(agcomprisBoard->type, ':')+1;
00610   boardclass = g_strdup_printf("Gcompris_%s", board_file_name);
00611 
00612   /* Insert the board module into the python's interpreter */
00613   python_board_config_module = PyImport_ImportModuleEx(board_file_name,
00614                                                  globals,
00615                                                  globals,
00616                                                  NULL);
00617 
00618   if(python_board_config_module!=NULL){
00619     /* Get the module dictionnary */
00620     module_dict = PyModule_GetDict(python_board_config_module);
00621 
00622     /* Get the python board class */
00623     py_boardclass = PyDict_GetItemString(module_dict, boardclass);
00624 
00625     /* Create a python gcompris board */
00626     python_gcomprisBoard_config=gcompris_new_pyGcomprisBoardObject(agcomprisBoard);
00627     /* Create an instance of the board class */
00628     py_boardclass_args = PyTuple_New(1);
00629     Py_INCREF(python_gcomprisBoard_config);
00630     PyTuple_SetItem(py_boardclass_args, 0, python_gcomprisBoard_config);
00631     python_board_config_instance = PyInstance_New(py_boardclass, py_boardclass_args, NULL);
00632     Py_DECREF(py_boardclass_args);
00633 
00634     py_function_result = PyObject_CallMethod(python_board_config_instance,
00635                                         "config_start",
00636                                         "O",
00637                                         gcompris_new_pyGcomprisProfileObject(aProfile));
00638 
00639     if( py_function_result != NULL){
00640       Py_DECREF(py_function_result);
00641     } else {
00642       PyErr_Print();
00643     }
00644   } else {
00645     PyErr_Print();
00646   }
00647 
00648   g_free(boardclass);
00649 }
00650 
00651 /*
00652  * End the board.
00653  * In this case:
00654  * - call the board end function
00655  * - finalise python interpreter
00656  */
00657 static void pythongc_board_config_stop (void){
00658   PyObject* result = NULL;
00659 
00660   if(python_gcomprisBoard_config!=NULL){
00661     result = PyObject_CallMethod(python_board_config_instance, "config_stop", NULL);
00662     if( result == NULL){
00663       PyErr_Print();
00664     } else {
00665       Py_DECREF(result);
00666     }
00667     Py_XDECREF(python_board_config_module);
00668     Py_XDECREF(python_board_config_instance);
00669     Py_XDECREF(python_gcomprisBoard_config);
00670     if (python_run_by_config){
00671       Py_Finalize();
00672       python_run_by_config = FALSE;
00673     }
00674   }
00675 }