python.c 19.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/* gcompris - python.c
 *
 * Copyright (C) 2003 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 2 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, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <Python.h>
#include <pygobject.h>
#include "gcompris/gcompris.h"
#include "py-gcompris-board.h"
24
#include "py-mod-gcompris.h"
25
#include "py-gcompris-profile.h"
26 27

static GcomprisBoard *gcomprisBoard = NULL;
28

29 30
static PyObject* python_gcomprisBoard = NULL;
static PyObject* python_board_module = NULL;
31
static PyObject* python_board_instance = NULL;
32

33
static GcomprisBoard *gcomprisBoard_config = NULL;
34 35 36 37 38
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);
39 40 41 42 43 44
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);

45
static gint	 pythonboard_key_press (guint keyval, gchar *commit_str, gchar *preedit_str);
46 47 48 49
static void	 pythonboard_ok (void);
static void	 pythonboard_set_level (guint level);
static void	 pythonboard_config(void);
static void	 pythonboard_repeat (void);
50
static void	 pythongc_board_config_start (GcomprisBoard *agcomprisBoard,
51
					   GcomprisProfile *aProfile);
52
static void	 pythongc_board_config_stop (void);
53

54 55
static gboolean  pythonboard_is_ready = FALSE;

56 57

/* Description of this plugin */
58
static BoardPlugin menu_bp =
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
  {
    NULL,
    NULL,
    N_("Python Board"),
    N_("Special board that embeds python into gcompris."),
    "Olivier Samyn <osamyn@ulb.ac.be>",
    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,
78 79
    pythongc_board_config_start,
    pythongc_board_config_stop
80
  };
81

82
static BoardPlugin *bp_board = NULL;
83 84

/*
85
 * Return the plugin structure (Common to all gcompris boards)
86 87
 */

88
GET_BPLUGIN_INFO(python)
89 90


91 92 93 94
/*
 * Tests if a python interpreter is available
 * and that all required imports can be loaded.
 */
95 96 97

static GList *config_boards= NULL;

98 99
GList *
get_pythonboards_list()
100 101
{
  GList *pythonboards_list = NULL;
102
  GList *boards_list = gc_menu_get_boards();
103 104 105 106 107 108 109 110 111 112 113 114
  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;
}

115
static void
116
pythonboard_init (GcomprisBoard *agcomprisBoard){
117 118 119
  PyObject* main_module;
  PyObject* globals;
  gchar* execstr;
120
  gchar* userplugindir;
121

122 123
  char* board_file_name;
  char* boardclass;
124
  gchar *boarddir;
125 126 127
  PyObject* module_dict;
  PyObject* py_boardclass;

128
  GcomprisProperties *properties = gc_prop_get();
129

130 131 132
  if (pythonboard_is_ready)
    return ;

133 134 135
  /* Initialize the python interpreter */
  Py_Initialize();

136 137 138
  static char *python_args[]={ "" };
  PySys_SetArgv( 1, python_args);

139 140 141 142 143 144
  pythonboard_is_ready = TRUE;

  main_module = PyImport_AddModule("__main__"); /* Borrowed reference */
  globals = PyModule_GetDict(main_module); /* Borrowed reference */

  if(globals==NULL){
145
    g_warning("! Python disabled: Cannot get info from the python interpreter.\n");
146 147 148
    pythonboard_is_ready = FALSE;
  } else {
    /* Add the python plugins dir to the python's search path */
149
    execstr = g_strdup_printf("import sys; sys.path.append('%s')",properties->package_python_plugin_dir);
150 151
#ifndef DISABLE_USER_PLUGIN_DIR
    userplugindir = g_strconcat(g_get_home_dir(), "/.gcompris/Plugins/", NULL);
152
    execstr = g_strdup_printf("import sys; sys.path.append('%s/python'); sys.path.append('%s')",
153
			      userplugindir, properties->package_python_plugin_dir);
154 155
    g_free(userplugindir);
#else
156
    execstr = g_strdup_printf("import sys; sys.path.append('%s')",properties->package_python_plugin_dir );
157
#endif
158 159
    if(PyRun_SimpleString(execstr)!=0){
      pythonboard_is_ready = FALSE;
160
      g_warning("! Python disabled: Cannot add plugins dir into search path\n");
161 162 163
    } else {
      /* Load the gcompris modules */
      python_gcompris_module_init();
164

165 166
      /* Try to import pygtk modules */
      g_free(execstr);
167
      execstr = g_strdup("import gtk; import gtk.gdk");
168 169
      if(PyRun_SimpleString(execstr)!=0){
	pythonboard_is_ready = FALSE;
170
	g_warning("! Python disabled: Cannot import pygtk modules\n");
171 172 173
      } else {
	/* Try to import gnome-python modules */
	g_free(execstr);
174
	execstr = g_strdup("import gnome; import gnome.canvas");
175 176
	if(PyRun_SimpleString(execstr)!=0){
	  pythonboard_is_ready = FALSE;
177
	  g_warning("! Python disabled: Cannot import gnome-python modules\n");
178 179 180
	} else {
	  /* Try to import gcompris modules */
	  g_free(execstr);
181 182 183
	  execstr = g_strdup("import gcompris; import gcompris.bonus; "
			     "import gcompris.score; import gcompris.sound;"
			     "import gcompris.skin; import gcompris.timer;"
184
			     "import gcompris.utils; import gcompris.anim");
185 186
	  if(PyRun_SimpleString(execstr)!=0){
	    pythonboard_is_ready = FALSE;
187
	    g_warning("! Python disabled: Cannot import gcompris modules\n");
188 189 190
	  } else {
	    GList *python_boards;
	    GList *list;
191

192 193 194 195 196 197 198 199 200
	    /* Load the gcompris modules */
	    python_gcompris_module_init();

	    /* 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;
201

202 203 204
	      /* 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);
205 206 207 208 209

	      /* Test if board come with -L option */

	      g_warning("board_dir: %s package_data_dir %s",
			board->board_dir,
210
			properties->package_python_plugin_dir);
211

212 213
	      if (strcmp(board->board_dir, properties->package_python_plugin_dir)!=0){ 
		boarddir = g_strdup_printf("sys.path.append('%s/')", board->board_dir);
214 215 216 217 218
	      
		PyRun_SimpleString(boarddir);
		g_free(boarddir);
	      }
	      
219 220 221 222 223 224 225 226 227
	      /* 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);
228
	      
229 230 231 232 233 234 235 236
		/* 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_warning("The board '%s' has a configuration entry", 
			    board_file_name);
		}
237
	      }
238 239

	      g_free(boardclass);
240
	    }
241 242

	    g_list_free(python_boards);
243
	  }
244 245 246
	}
      }
    }
247
    g_free(execstr);
248 249 250 251 252

  }

  /* Finalize the python interpreter */
  Py_Finalize();
253

254 255
}

256
/*
257 258
 * Start the board.
 * In this case:
259 260 261 262 263 264
 * - initialize python interpreter
 * - import gcompris functions/objects
 * - import gtk/gnome functions/objects
 * - load the python written board
 * - call the board start function
 */
265
static void
266
pythonboard_start (GcomprisBoard *agcomprisBoard){
267 268 269
  PyObject* main_module;
  PyObject* py_function_result;
  PyObject* module_dict;
270 271
  PyObject* py_boardclass;
  PyObject* py_boardclass_args;
272 273 274 275
  PyObject* globals;
  static char *python_args[]={ "" };
  static char* python_prog_name="gcompris";
  char* boarddir;
276
  char* boardclass;
277
  char* board_file_name;
278 279
  gchar *userplugindir;

280
  GcomprisProperties	*properties = gc_prop_get();
281

282 283 284 285
  if(agcomprisBoard!=NULL){
    /* Initialize the python interpreter */
    Py_SetProgramName(python_prog_name);
    Py_Initialize();
286

287
    PySys_SetArgv(1, python_args);
288

289 290 291 292
    init_pygobject();

    main_module = PyImport_AddModule("__main__");
    globals = PyModule_GetDict(main_module);
293

294
    if(globals==NULL){
295
      g_print("Cannot get info from the python interpreter. Seems there is a problem with this one.\n");
296 297 298 299 300 301
      return;
    } else {
      gcomprisBoard = agcomprisBoard;
    }

    /* Add the python plugins dir to the python's search path */
302 303
#ifndef DISABLE_USER_PLUGIN_DIR
    userplugindir = g_strconcat(g_get_home_dir(), "/.gcompris/Plugins/", NULL);
304
    boarddir = g_strdup_printf("import sys; sys.path.append('%s/python'); sys.path.append('%s')",
305
			       userplugindir,
306
			       properties->package_python_plugin_dir);
307
#else
308
    boarddir = g_strdup_printf("import sys; sys.path.append('%s')",properties->package_python_plugin_dir );
309
#endif
310

311 312
    PyRun_SimpleString(boarddir);
    g_free(boarddir);
313

314 315 316 317 318 319 320 321 322 323

    /* Test if board come with -L option */
    if (strcmp(gcomprisBoard->board_dir, properties->package_data_dir)!=0){ 
      boarddir = g_strdup_printf("sys.path.append('%s/../python/')", gcomprisBoard->board_dir);
      
      PyRun_SimpleString(boarddir);
      g_free(boarddir);
    }


324 325 326
#ifndef DISABLE_USER_PLUGIN_DIR
    g_free(userplugindir);
#endif
327 328 329 330

    /* Load the gcompris modules */
    python_gcompris_module_init();

331
    /* Python is now initialized we create some usefull variables */
332 333
    board_file_name = strchr(agcomprisBoard->type, ':')+1;
    boardclass = g_strdup_printf("Gcompris_%s", board_file_name);
334

335
    /* Insert the board module into the python's interpreter */
336
    python_board_module = PyImport_ImportModuleEx(board_file_name,
337 338
 						  globals,
 						  globals,
339 340 341
 						  NULL);

    if(python_board_module!=NULL){
342
      /* Get the module dictionnary */
343
      module_dict = PyModule_GetDict(python_board_module);
344

345 346 347 348
      /* Get the python board class */
      py_boardclass = PyDict_GetItemString(module_dict, boardclass);

      /* Create a python gcompris board */
349 350
      python_gcomprisBoard=gcompris_new_pyGcomprisBoardObject(agcomprisBoard);

351 352 353 354 355 356
      /* 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);
357 358

      /* Call the function */
359 360 361 362
      py_function_result = PyObject_CallMethod(python_board_instance, "start", NULL);
      if( py_function_result != NULL){
	Py_DECREF(py_function_result);
      } else {
363
	PyErr_Print();
364
      }
365 366
    } else {
      PyErr_Print();
367
    }
368 369

    g_free(boardclass);
370 371 372 373 374 375
  }
}

/*
 * Pause the board.
 */
376
static void pythonboard_pause (gboolean pause){
377 378
  PyObject* result = NULL;

379 380 381 382
  result = PyObject_CallMethod(python_board_instance, "pause", "i", pause);
  if( result != NULL){
    Py_DECREF(result);
  } else {
383
    PyErr_Print();
384
  }
385 386 387 388 389 390 391 392
}

/*
 * End the board.
 * In this case:
 * - call the board end function
 * - finalise python interpreter
 */
393
static void pythonboard_end (void){
394
  PyObject* result = NULL;
395

396
  if(python_gcomprisBoard!=NULL){
397 398
    result = PyObject_CallMethod(python_board_instance, "end", NULL);
    if( result == NULL){
399
      PyErr_Print();
400 401 402
    } else {
      Py_DECREF(result);
    }
403
    Py_XDECREF(python_board_module);
404
    Py_XDECREF(python_board_instance);
405 406 407 408 409 410 411 412
    Py_XDECREF(python_gcomprisBoard);
    Py_Finalize();
  }
}

/*
 * Return TRUE if the board is a python one.
 */
413 414
static gboolean pythonboard_is_our_board (GcomprisBoard *agcomprisBoard){

415 416 417
  if (agcomprisBoard->plugin)
    return TRUE;

418 419
  if(pythonboard_is_ready) {
    if (agcomprisBoard!=NULL) {
420 421

      if (g_ascii_strncasecmp(agcomprisBoard->type, "python", 6)==0) {
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
  
	bp_board = g_malloc0(sizeof(BoardPlugin));
	
	bp_board->handle        = menu_bp.handle;
	bp_board->filename      = menu_bp.filename;
	bp_board->name          = menu_bp.name;
	bp_board->description   = menu_bp.description;
	bp_board->author        = menu_bp.author;
	bp_board->init          = menu_bp.init;
	bp_board->cleanup       = menu_bp.cleanup;
	bp_board->about         = menu_bp.about;
	bp_board->configure     = menu_bp.configure;
	bp_board->start_board   = menu_bp.start_board;
	bp_board->pause_board   = menu_bp.pause_board;
	bp_board->end_board     = menu_bp.end_board;
	bp_board->is_our_board  = menu_bp.is_our_board;
	bp_board->key_press     = menu_bp.key_press;
	bp_board->ok            = menu_bp.ok;
	bp_board->set_level     = menu_bp.set_level;
	bp_board->config        = menu_bp.config;
	bp_board->repeat        = menu_bp.repeat;
  
	if (g_list_find (config_boards, agcomprisBoard)){
	  bp_board->config_start  = menu_bp.config_start;
	  bp_board->config_stop   = menu_bp.config_stop;
	} else {
	  bp_board->config_start  = NULL;
	  bp_board->config_stop   = NULL;
	}


453
	/* Set the plugin entry */
454 455 456
	agcomprisBoard->plugin = bp_board;
	
	bp_board = NULL;
457 458 459 460
	
	//g_print("pythonboard: is our board = TRUE\n");
	
	return TRUE;
461
      }
462
    }
463
  }
464
  agcomprisBoard->plugin=NULL;
465
  return FALSE;
466 467 468 469 470
}

/*
 * Key press
 */
471
static gint pythonboard_key_press (guint keyval, gchar *commit_str, gchar *preedit_str){
472 473
  PyObject* result = NULL;

474
  result = PyObject_CallMethod(python_board_instance, "key_press", "iss", keyval, commit_str, preedit_str);
475 476 477

  if (result==NULL) return FALSE;

478
  if (PyInt_Check(result) && (PyInt_AsLong(result)>0)){
479 480 481 482 483 484 485 486 487 488 489
    Py_DECREF(result);
    return TRUE;
  } else {
    Py_DECREF(result);
    return FALSE;
  }
}

/*
 * OK button pressed
 */
490
static void pythonboard_ok (void){
491
  PyObject* result = NULL;
492 493 494 495
  result = PyObject_CallMethod(python_board_instance, "ok", NULL);
  if( result != NULL){
    Py_DECREF(result);
  } else {
496
    PyErr_Print();
497
  }
498 499 500 501 502
}

/*
 * Set Level
 */
503
static void pythonboard_set_level (guint level){
504 505
  PyObject* result = NULL;

506 507 508 509
  result = PyObject_CallMethod(python_board_instance, "set_level", "i", level);
  if( result != NULL){
    Py_DECREF(result);
  } else {
510
    PyErr_Print();
511
  }
512 513 514 515 516
}

/*
 * Config
 */
517
static void pythonboard_config(void){
518
  PyObject* result = NULL;
519 520 521 522
  result = PyObject_CallMethod(python_board_instance, "config", NULL);
  if( result != NULL){
    Py_DECREF(result);
  } else {
523
    PyErr_Print();
524
  }
525 526 527 528 529
}

/*
 * Repeat
 */
530
static void pythonboard_repeat (void){
531
  PyObject* result = NULL;
532 533 534 535
  result = PyObject_CallMethod(python_board_instance, "repeat", NULL);
  if( result != NULL){
    Py_DECREF(result);
  } else {
536
    PyErr_Print();
537
  }
538
}
539 540

/*
541
 * Start the board config_start.
542 543 544 545 546 547 548
 * 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
 */
549 550 551 552 553 554 555

/*
 * Normally python in already runningwhen config_start is called. If not config_stop has to stop it.
 */

static gboolean python_run_by_config = FALSE;

556
static void
557
pythongc_board_config_start (GcomprisBoard *agcomprisBoard,
558
			  GcomprisProfile *aProfile
559 560
			  )
{
561
  GcomprisProperties *properties = gc_prop_get();
562 563 564 565 566 567 568 569 570
  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;
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
  PyObject* main_module;
  char* boarddir;
  gchar *userplugindir;

  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;
    }

    /* Add the python plugins dir to the python's search path */
#ifndef DISABLE_USER_PLUGIN_DIR
    userplugindir = g_strconcat(g_get_home_dir(), "/.gcompris/Plugins/", NULL);
    boarddir = g_strdup_printf("import sys; sys.path.append('%s/python'); sys.path.append('%s'); sys.path.append('%s')",
			       userplugindir,
601
			       properties->package_python_plugin_dir,
602 603
			       gcomprisBoard_config->board_dir);
#else
604
    boarddir = g_strdup_printf("import sys; sys.path.append('%s')", properties->package_python_plugin_dir );
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
#endif

    PyRun_SimpleString(boarddir);
    g_free(boarddir);

#ifndef DISABLE_USER_PLUGIN_DIR
    g_free(userplugindir);
#endif

    /* 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 */
  }
624 625 626 627 628 629 630 631 632 633

  /* 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);
634

635 636 637
  if(python_board_config_module!=NULL){
    /* Get the module dictionnary */
    module_dict = PyModule_GetDict(python_board_config_module);
638

639 640
    /* Get the python board class */
    py_boardclass = PyDict_GetItemString(module_dict, boardclass);
641

642 643 644 645 646 647 648 649
    /* 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);
650 651

    py_function_result = PyObject_CallMethod(python_board_config_instance, 
652
					     "config_start", 
653 654
					     "O", 
					     gcompris_new_pyGcomprisProfileObject(aProfile));
655

656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
    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
 */
674
static void pythongc_board_config_stop (void){
675 676 677 678 679 680 681 682 683 684 685 686
  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);
687 688 689 690
    if (python_run_by_config){
      Py_Finalize();
      python_run_by_config = FALSE;
    }
691 692
  }
}