/* gcompris - python.c * * Copyright (C) 2003, 2008 GCompris Developpement Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #define NO_IMPORT_PYGOBJECT #define NO_IMPORT_PYGTK #include #include #include "gcompris/gcompris.h" #include "py-gcompris-board.h" #include "py-mod-gcompris.h" #include "py-gcompris-profile.h" static GcomprisBoard *gcomprisBoard = NULL; static PyObject* python_gcomprisBoard = NULL; static PyObject* python_board_module = NULL; static PyObject* python_board_instance = NULL; static GcomprisBoard *gcomprisBoard_config = NULL; static PyObject* python_gcomprisBoard_config = NULL;; static PyObject* python_board_config_module = NULL; static PyObject* python_board_config_instance = NULL; static void pythonboard_init (GcomprisBoard *agcomprisBoard); static void pythonboard_start (GcomprisBoard *agcomprisBoard); static void pythonboard_pause (gboolean pause); static void pythonboard_end (void); static gboolean pythonboard_is_our_board (GcomprisBoard *agcomprisBoard); static gint pythonboard_key_press (guint keyval, gchar *commit_str, gchar *preedit_str); static void pythonboard_ok (void); static void pythonboard_set_level (guint level); static void pythonboard_config(void); static void pythonboard_repeat (void); static void pythongc_board_config_start (GcomprisBoard *agcomprisBoard, GcomprisProfile *aProfile); static void pythongc_board_config_stop (void); static gboolean pythonboard_is_ready = FALSE; /* Description of this plugin */ static BoardPlugin menu_bp = { NULL, NULL, N_("Python Board"), N_("Special board that embeds python into GCompris."), "Olivier Samyn ", pythonboard_init, NULL, NULL, NULL, pythonboard_start, pythonboard_pause, pythonboard_end, pythonboard_is_our_board, pythonboard_key_press, pythonboard_ok, pythonboard_set_level, pythonboard_config, pythonboard_repeat, pythongc_board_config_start, pythongc_board_config_stop }; static BoardPlugin menu_bp_no_config = { NULL, NULL, N_("Python Board"), N_("Special board that embeds python into gcompris."), "Olivier Samyn ", pythonboard_init, NULL, NULL, NULL, pythonboard_start, pythonboard_pause, pythonboard_end, pythonboard_is_our_board, pythonboard_key_press, pythonboard_ok, pythonboard_set_level, pythonboard_config, pythonboard_repeat, NULL, NULL }; /* * Return the plugin structure (Common to all gcompris boards) */ GET_BPLUGIN_INFO(python) /* * Tests if a python interpreter is available * and that all required imports can be loaded. */ static GList *config_boards= NULL; /* * Create the import string to be added to the python path * The plugin directory is passed in properties->package_python_plugin_dir * It accepts several directory separated by a ":" character */ static gchar *get_pythonpath() { gchar *pythonpath; gchar *plugin_dir = NULL; GcomprisProperties *properties = gc_prop_get(); /* Add the python plugins dir to the python's search path */ gchar **plugin_dirs = g_strsplit( properties->package_python_plugin_dir , ":", -1 ); int i; for ( i = 0 ; i < g_strv_length( plugin_dirs ); i++ ) { if ( plugin_dir ) { plugin_dir = g_strconcat(plugin_dir, "; sys.path.append('", plugin_dirs[i], "')", NULL); } else { plugin_dir = g_strdup_printf("sys.path.append('%s')", plugin_dirs[i] ); } } pythonpath = g_strdup_printf("import sys; %s", plugin_dir ); g_free(plugin_dir); g_strfreev (plugin_dirs); return pythonpath; } GList * get_pythonboards_list() { GList *pythonboards_list = NULL; GList *boards_list = gc_menu_get_boards(); GList *list; GcomprisBoard *board; for (list = boards_list; list != NULL; list = list->next){ board = (GcomprisBoard *) list->data; if (g_ascii_strncasecmp(board->type, "python", 6)==0) pythonboards_list = g_list_append(pythonboards_list, board); } return pythonboards_list; } /* This loops over all the boards and for the python one * checks if they have a config method defined. If so * they are put in a list. */ static void init_config_boards() { static gboolean called_once = FALSE; GList *python_boards; GList *list; PyObject* globals; static char *python_args[]={ "" }; static char* python_prog_name="gcompris"; char* boardclass; char* board_file_name; PyObject* main_module; char* boarddir; GcomprisProperties *properties = gc_prop_get(); PyObject* module_dict; PyObject* py_boardclass; if (called_once) return; called_once = TRUE; if(!Py_IsInitialized()){ /* Initialize the python interpreter */ Py_SetProgramName(python_prog_name); Py_Initialize(); PySys_SetArgv(1, python_args); init_pygobject(); main_module = PyImport_AddModule("__main__"); globals = PyModule_GetDict(main_module); if(globals==NULL){ g_print("Cannot get info from the python interpreter. Seems there is a problem with this one.\n"); return; } /* Add the python plugins dir to the python's search path */ boarddir = get_pythonpath(); PyRun_SimpleString(boarddir); g_free(boarddir); /* Load the gcompris modules */ python_gcompris_module_init(); } else { main_module = PyImport_AddModule("__main__"); /* Borrowed reference */ globals = PyModule_GetDict(main_module); /* Borrowed reference */ } /* Get the list of python boards */ python_boards = get_pythonboards_list(); /* Search in the list each one with a config entry */ for(list = python_boards; list != NULL; list = list->next) { GcomprisBoard *board = (GcomprisBoard *) list->data; /* Python is now initialized we create some usefull variables */ board_file_name = strchr(board->type, ':')+1; boardclass = g_strdup_printf("Gcompris_%s", board_file_name); /* Test if board come with --python_plugin_dir option */ g_message("python_plugin_dir '%s' file_name '%s'", properties->package_python_plugin_dir, board_file_name); /* Insert the board module into the python's interpreter */ python_board_module = PyImport_ImportModuleEx(board_file_name, globals, globals, NULL); if(python_board_module!=NULL){ /* Get the module dictionnary */ module_dict = PyModule_GetDict(python_board_module); /* Get the python board class */ py_boardclass = PyDict_GetItemString(module_dict, boardclass); if (PyObject_HasAttrString( py_boardclass, "config_start")) { config_boards = g_list_append(config_boards, board); g_message("The board '%s' has a configuration entry", board_file_name); } } g_free(boardclass); } g_list_free(python_boards); /* Finalize the python interpreter */ Py_Finalize(); } static void pythonboard_init (GcomprisBoard *agcomprisBoard){ PyObject* main_module; PyObject* globals; gchar* execstr; if (pythonboard_is_ready) return ; /* Initialize the python interpreter */ Py_Initialize(); static char *python_args[]={ "" }; PySys_SetArgv( 1, python_args); pythonboard_is_ready = TRUE; main_module = PyImport_AddModule("__main__"); /* Borrowed reference */ globals = PyModule_GetDict(main_module); /* Borrowed reference */ if(globals==NULL){ g_warning("! Python disabled: Cannot get info from the python interpreter.\n"); pythonboard_is_ready = FALSE; } else { /* Add the python plugins dir to the python's search path */ execstr = get_pythonpath(); g_message("Executing %s\n", execstr); if(PyRun_SimpleString(execstr)!=0){ pythonboard_is_ready = FALSE; g_warning("! Python disabled: Cannot add plugins dir into search path\n"); } else { /* Try to import pygtk modules */ g_free(execstr); execstr = g_strdup("import gtk; import gtk.gdk"); if(PyRun_SimpleString(execstr)!=0){ pythonboard_is_ready = FALSE; g_warning("! Python disabled: Cannot import pygtk modules\n"); } else { /* Try to import gcompris modules */ /* Load the gcompris modules */ python_gcompris_module_init(); g_free(execstr); execstr = g_strdup("import gcompris; import gcompris.bonus; " "import gcompris.score; import gcompris.sound;" "import gcompris.skin; import gcompris.timer;" "import gcompris.utils; import gcompris.anim"); if(PyRun_SimpleString(execstr)!=0){ pythonboard_is_ready = FALSE; g_warning("! Python disabled: Cannot import gcompris modules\n"); } } g_free(execstr); } /* Finalize the python interpreter */ Py_Finalize(); } } /* * Start the board. * In this case: * - initialize python interpreter * - import gcompris functions/objects * - import gtk/gnome functions/objects * - load the python written board * - call the board start function */ static void pythonboard_start (GcomprisBoard *agcomprisBoard){ PyObject* main_module; PyObject* py_function_result; PyObject* module_dict; PyObject* py_boardclass; PyObject* py_boardclass_args; PyObject* globals; static char *python_args[]={ "" }; static char* python_prog_name="gcompris"; char* boarddir; char* boardclass; char* board_file_name; if(agcomprisBoard!=NULL){ /* Initialize the python interpreter */ Py_SetProgramName(python_prog_name); Py_Initialize(); PySys_SetArgv(1, python_args); init_pygobject(); main_module = PyImport_AddModule("__main__"); globals = PyModule_GetDict(main_module); if(globals==NULL){ g_print("Cannot get info from the python interpreter. Seems there is a problem with this one.\n"); return; } else { gcomprisBoard = agcomprisBoard; } /* Add the python plugins dir to the python's search path */ boarddir = get_pythonpath(); PyRun_SimpleString(boarddir); g_free(boarddir); /* Load the gcompris modules */ python_gcompris_module_init(); /* Python is now initialized we create some usefull variables */ board_file_name = strchr(agcomprisBoard->type, ':')+1; boardclass = g_strdup_printf("Gcompris_%s", board_file_name); /* Insert the board module into the python's interpreter */ python_board_module = PyImport_ImportModuleEx(board_file_name, globals, globals, NULL); if(python_board_module!=NULL){ /* Get the module dictionnary */ module_dict = PyModule_GetDict(python_board_module); /* Get the python board class */ py_boardclass = PyDict_GetItemString(module_dict, boardclass); /* Create a python gcompris board */ python_gcomprisBoard=gcompris_new_pyGcomprisBoardObject(agcomprisBoard); /* Create an instance of the board class */ py_boardclass_args = PyTuple_New(1); Py_INCREF(python_gcomprisBoard); PyTuple_SetItem(py_boardclass_args, 0, python_gcomprisBoard); python_board_instance = PyInstance_New(py_boardclass, py_boardclass_args, NULL); Py_DECREF(py_boardclass_args); /* Call the function */ py_function_result = PyObject_CallMethod(python_board_instance, "start", NULL); if( py_function_result != NULL){ Py_DECREF(py_function_result); } else { PyErr_Print(); } } else { PyErr_Print(); } g_free(boardclass); } } /* * Pause the board. */ static void pythonboard_pause (gboolean pause){ PyObject* result = NULL; result = PyObject_CallMethod(python_board_instance, "pause", "i", pause); if( result != NULL){ Py_DECREF(result); } else { PyErr_Print(); } } /* * End the board. * In this case: * - call the board end function * - finalise python interpreter */ static void pythonboard_end (void){ PyObject* result = NULL; if(python_gcomprisBoard!=NULL){ result = PyObject_CallMethod(python_board_instance, "end", NULL); if( result == NULL){ PyErr_Print(); } else { Py_DECREF(result); } Py_XDECREF(python_board_module); Py_XDECREF(python_board_instance); Py_XDECREF(python_gcomprisBoard); //FIXME: COMMENTED BECAUSE THIS MAKES GCOMPRIS CRASHES AFTER ENTERING/EXITING // ANY PHYTHON PLUGIN LESS THAN 10 TIMES //Py_Finalize(); } } /* * Return TRUE if the board is a python one. */ static gboolean pythonboard_is_our_board (GcomprisBoard *gcomprisBoard){ if (gcomprisBoard->plugin) return TRUE; if(pythonboard_is_ready) { if (gcomprisBoard!=NULL) { if (g_ascii_strncasecmp(gcomprisBoard->type, "python", 6)==0) { init_config_boards(); /* Set the plugin entry */ if (g_list_find (config_boards, gcomprisBoard)){ gcomprisBoard->plugin = &menu_bp; } else { gcomprisBoard->plugin = &menu_bp_no_config; } return TRUE; } } } gcomprisBoard->plugin=NULL; return FALSE; } /* * Key press */ static gint pythonboard_key_press (guint keyval, gchar *commit_str, gchar *preedit_str){ PyObject* result = NULL; result = PyObject_CallMethod(python_board_instance, "key_press", "iss", keyval, commit_str, preedit_str); if (result==NULL) return FALSE; if (PyInt_Check(result) && (PyInt_AsLong(result)>0)){ Py_DECREF(result); return TRUE; } else { Py_DECREF(result); return FALSE; } } /* * OK button pressed */ static void pythonboard_ok (void){ PyObject* result = NULL; result = PyObject_CallMethod(python_board_instance, "ok", NULL); if( result != NULL){ Py_DECREF(result); } else { PyErr_Print(); } } /* * Set Level */ static void pythonboard_set_level (guint level){ PyObject* result = NULL; result = PyObject_CallMethod(python_board_instance, "set_level", "i", level); if( result != NULL){ Py_DECREF(result); } else { PyErr_Print(); } } /* * Config */ static void pythonboard_config(void){ PyObject* result = NULL; result = PyObject_CallMethod(python_board_instance, "config", NULL); if( result != NULL){ Py_DECREF(result); } else { PyErr_Print(); } } /* * Repeat */ static void pythonboard_repeat (void){ PyObject* result = NULL; result = PyObject_CallMethod(python_board_instance, "repeat", NULL); if( result != NULL){ Py_DECREF(result); } else { PyErr_Print(); } } /* * Start the board config_start. * In this case: * - initialize python interpreter * - import gcompris functions/objects * - import gtk/gnome functions/objects * - load the python written board * - call the board start function */ /* * Normally python in already running when config_start is called. * If not config_stop has to stop it. */ static gboolean python_run_by_config = FALSE; static void pythongc_board_config_start (GcomprisBoard *agcomprisBoard, GcomprisProfile *aProfile ) { PyObject* py_function_result; PyObject* module_dict; PyObject* py_boardclass; PyObject* py_boardclass_args; PyObject* globals; static char *python_args[]={ "" }; static char* python_prog_name="gcompris"; char* boardclass; char* board_file_name; PyObject* main_module; char* boarddir; g_assert (agcomprisBoard != NULL); if(!Py_IsInitialized()){ /* Initialize the python interpreter */ Py_SetProgramName(python_prog_name); Py_Initialize(); PySys_SetArgv(1, python_args); init_pygobject(); main_module = PyImport_AddModule("__main__"); globals = PyModule_GetDict(main_module); if(globals==NULL){ g_print("Cannot get info from the python interpreter. Seems there is a problem with this one.\n"); return; } else { gcomprisBoard_config = agcomprisBoard; } boarddir = get_pythonpath(); PyRun_SimpleString(boarddir); g_free(boarddir); /* Load the gcompris modules */ python_gcompris_module_init(); python_run_by_config = TRUE; } else { main_module = PyImport_AddModule("__main__"); /* Borrowed reference */ globals = PyModule_GetDict(main_module); /* Borrowed reference */ } /* Python is now initialized we create some usefull variables */ board_file_name = strchr(agcomprisBoard->type, ':')+1; boardclass = g_strdup_printf("Gcompris_%s", board_file_name); /* Insert the board module into the python's interpreter */ python_board_config_module = PyImport_ImportModuleEx(board_file_name, globals, globals, NULL); if(python_board_config_module!=NULL){ /* Get the module dictionnary */ module_dict = PyModule_GetDict(python_board_config_module); /* Get the python board class */ py_boardclass = PyDict_GetItemString(module_dict, boardclass); /* Create a python gcompris board */ python_gcomprisBoard_config=gcompris_new_pyGcomprisBoardObject(agcomprisBoard); /* Create an instance of the board class */ py_boardclass_args = PyTuple_New(1); Py_INCREF(python_gcomprisBoard_config); PyTuple_SetItem(py_boardclass_args, 0, python_gcomprisBoard_config); python_board_config_instance = PyInstance_New(py_boardclass, py_boardclass_args, NULL); Py_DECREF(py_boardclass_args); py_function_result = PyObject_CallMethod(python_board_config_instance, "config_start", "O", gcompris_new_pyGcomprisProfileObject(aProfile)); if( py_function_result != NULL){ Py_DECREF(py_function_result); } else { PyErr_Print(); } } else { PyErr_Print(); } g_free(boardclass); } /* * End the board. * In this case: * - call the board end function * - finalise python interpreter */ static void pythongc_board_config_stop (void){ PyObject* result = NULL; if(python_gcomprisBoard_config!=NULL){ result = PyObject_CallMethod(python_board_config_instance, "config_stop", NULL); if( result == NULL){ PyErr_Print(); } else { Py_DECREF(result); } Py_XDECREF(python_board_config_module); Py_XDECREF(python_board_config_instance); Py_XDECREF(python_gcomprisBoard_config); if (python_run_by_config){ Py_Finalize(); python_run_by_config = FALSE; } } }