gcompris.c 53.4 KB
Newer Older
Bruno Coudoin's avatar
Bruno Coudoin committed
1 2
/* gcompris - gcompris.c
 *
3
 * Copyright (C) 2000, 2008 Bruno Coudoin
Bruno Coudoin's avatar
Bruno Coudoin committed
4
 *
5 6 7 8
 *   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.
Bruno Coudoin's avatar
Bruno Coudoin committed
9
 *
10 11 12 13
 *   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.
Bruno Coudoin's avatar
Bruno Coudoin committed
14
 *
15 16
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, see <http://www.gnu.org/licenses/>.
Bruno Coudoin's avatar
Bruno Coudoin committed
17 18
 */

19 20
#include <signal.h>

21 22 23 24
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
25
#include <string.h>
26

27 28
#ifdef WIN32
// WIN32
29
#elif MAC_INTEGRATION
30
// MACOSX
31
#else
32 33 34 35
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <gdk/gdkx.h>
36
#endif
37

38 39 40 41 42
// Include Mac OS X menu synchronization on native OSX build
#ifdef  MAC_INTEGRATION
#include "ige-mac-menu.h"
#endif

43 44
#include <glib/gstdio.h>

Bruno Coudoin's avatar
Bruno Coudoin committed
45
#include "gcompris.h"
46
#include "gc_core.h"
47
#include "gcompris_config.h"
48
#include "about.h"
49
#include "bar.h"
50
#include "status.h"
51
#include <locale.h>
Bruno Coudoin's avatar
Bruno Coudoin committed
52

53 54
#include "binreloc.h"

55 56 57
/* get the default database name */
#define DEFAULT_DATABASE "gcompris_sqlite.db"

58 59
static double zoom_factor = 1.0;

60 61 62 63
/* Multiple instance check */
#define GC_LOCK_FILE "gcompris.lock"
#define GC_LOCK_LIMIT 30 /* seconds */

64
static GtkWidget *window;
65 66
static GtkWidget *workspace;
static GtkWidget *alignment;
67
static GtkWidget *canvas;
Yves Combe's avatar
Yves Combe committed
68 69
gchar * exec_prefix = NULL;

Bruno Coudoin's avatar
Bruno Coudoin committed
70 71
//static gint pause_board_cb (GtkWidget *widget, gpointer data);
static void quit_cb (GtkWidget *widget, gpointer data);
72
static void map_cb  (GtkWidget *widget, gpointer data);
73 74
static gboolean _realize_callback (GtkWidget *widget, GdkEventExpose *event,
				   gpointer data);
Bruno Coudoin's avatar
Bruno Coudoin committed
75 76 77
static gint board_widget_key_press_callback (GtkWidget   *widget,
					    GdkEventKey *event,
					    gpointer     client_data);
78
void gc_terminate(int signum);
79
static void single_instance_release();
Bruno Coudoin's avatar
Bruno Coudoin committed
80

81
/*
Bruno Coudoin's avatar
Bruno Coudoin committed
82
 * For the Activation dialog
83
 */
84
#ifdef ACTIVATION_CODE
85
int gc_activation_check(const char *code);
86 87 88
static void activation_enter_callback(GtkWidget *widget,
				      GtkWidget *entry );
static void activation_done();
89
static int display_activation_dialog();
90
static GooCanvasItem *activation_item;
91
static GtkWidget *widget_activation_entry;
92
#else
93
#define display_activation_dialog() FALSE
94 95 96
#endif


97 98
static GcomprisProperties *properties = NULL;
static gboolean		   is_mapped = FALSE;
Bruno Coudoin's avatar
Bruno Coudoin committed
99
static gboolean		   fullscreen;
100
static gboolean		   mute;
101
static guint		   gc_cursor_current;
Bruno Coudoin's avatar
Bruno Coudoin committed
102 103 104 105

/****************************************************************************/
/* Some constants.  */

106
static GooCanvasItem *backgroundimg = NULL;
Bruno Coudoin's avatar
Bruno Coudoin committed
107
static GooCanvasItem *backgroundsvgimg = NULL;
108 109 110
static gchar           *gc_locale = NULL;
static gchar           *gc_user_default_locale = NULL;
static gboolean		gc_debug = FALSE;
Bruno Coudoin's avatar
Bruno Coudoin committed
111

112 113 114
/****************************************************************************/
/* Command line params */

115
/*** gcompris-popttable */
116 117 118 119 120
static gint popt_fullscreen	   = FALSE;
static gint popt_window		   = FALSE;
static gint popt_sound		   = FALSE;
static gint popt_mute		   = FALSE;
static gint popt_cursor		   = FALSE;
121
static gint popt_nocursor	   = FALSE;
122 123 124
static gint popt_version	   = FALSE;
static gint popt_difficulty_filter = FALSE;
static gint popt_debug		   = FALSE;
125
static gint popt_nolockcheck	   = FALSE;
126
static gchar *popt_root_menu       = NULL;
127
static gchar *popt_package_data_dir = NULL;
128
static gchar *popt_package_skin_dir = NULL;
129 130
static gchar *popt_plugin_dir      = NULL;
static gchar *popt_python_plugin_dir = NULL;
131
static gchar *popt_locale_dir      = NULL;
132
static gchar *popt_menu_dir        = NULL;
133 134 135 136 137 138
static gint popt_administration	   = FALSE;
static gchar *popt_database        = NULL;
static gint popt_create_db   	   = FALSE;
static gint popt_reread_menu   	   = FALSE;
static gchar *popt_profile	   = NULL;
static gint *popt_profile_list	   = FALSE;
139 140
static gchar *popt_config_dir	   = NULL;
static gchar *popt_user_dir	   = NULL;
141 142 143
static gint  popt_experimental     = FALSE;
static gint  popt_no_quit	   = FALSE;
static gint  popt_no_config        = FALSE;
144
static gint  popt_no_level         = FALSE;
145 146 147
static gchar *popt_server          = NULL;
static gint  *popt_web_only        = NULL;
static gchar *popt_cache_dir       = NULL;
148
static gchar *popt_drag_mode       = NULL;
149 150
static gchar *sugarBundleId        = NULL;
static gchar *sugarActivityId      = NULL;
151 152 153 154
static gint popt_sugar_look        = FALSE;
static gint popt_no_zoom           = FALSE;
static gdouble popt_timing_base    = 1.0;
static gdouble popt_timing_mult    = 1.0;
155 156 157

static GOptionEntry options[] = {
  {"fullscreen", 'f', 0, G_OPTION_ARG_NONE, &popt_fullscreen,
Bruno Coudoin's avatar
Bruno Coudoin committed
158
   N_("run GCompris in fullscreen mode."), NULL},
159

160
  {"window", 'w', 0, G_OPTION_ARG_NONE, &popt_window,
Bruno Coudoin's avatar
Bruno Coudoin committed
161
   N_("run GCompris in window mode."), NULL},
162

163
  {"sound", 's', 0, G_OPTION_ARG_NONE, &popt_sound,
Bruno Coudoin's avatar
Bruno Coudoin committed
164
   N_("run GCompris with sound enabled."), NULL},
165

166
  {"mute", 'm', 0, G_OPTION_ARG_NONE, &popt_mute,
Bruno Coudoin's avatar
Bruno Coudoin committed
167
   N_("run GCompris without sound."), NULL},
168

169
  {"cursor", 'c', 0, G_OPTION_ARG_NONE, &popt_cursor,
170 171 172 173
   N_("run GCompris with the default system cursor."), NULL},

  {"nocursor", 'C', 0, G_OPTION_ARG_NONE, &popt_nocursor,
   N_("run GCompris without cursor (touch screen mode)."), NULL},
174

175
  {"difficulty", 'd', 0, G_OPTION_ARG_INT, &popt_difficulty_filter,
176
   N_("display only activities with this difficulty level."), NULL},
177

178
  {"debug", 'D', 0, G_OPTION_ARG_NONE, &popt_debug,
179
   N_("display debug informations on the console."), NULL},
180

181
  {"version", 'v', 0, G_OPTION_ARG_NONE, &popt_version,
182
   N_("Print the version of " PACKAGE), NULL},
183

184
  {"root-menu", 'l', 0, G_OPTION_ARG_STRING, &popt_root_menu,
Bruno Coudoin's avatar
Bruno Coudoin committed
185 186 187
   N_("Run GCompris with local menu"
      " (e.g -l /reading will let you play only activities in the reading directory, -l /strategy/connect4 only the connect4 activity)."
      " Use '-l list' to list all the availaible activities and their descriptions."), NULL},
188

189 190 191
  {"package_data_dir", 'A', 0, G_OPTION_ARG_STRING, &popt_package_data_dir,
   N_("GCompris will find the data dir in this directory"), NULL},

192 193 194
  {"package_skin_dir", 'S', 0, G_OPTION_ARG_STRING, &popt_package_skin_dir,
   N_("GCompris will find the skins in this directory"), NULL},

195 196 197 198 199
  {"plugin_dir", 'L', 0, G_OPTION_ARG_STRING, &popt_plugin_dir,
   N_("GCompris will find the activity plugins in this directory"), NULL},

  {"python_plugin_dir", 'P', 0, G_OPTION_ARG_STRING, &popt_python_plugin_dir,
   N_("GCompris will find the python activity in this directory"), NULL},
200

201 202 203
  {"locale_dir", '\0', 0, G_OPTION_ARG_STRING, &popt_locale_dir,
   N_("GCompris will find the locale file (.mo translation) in this directory"), NULL},

204 205 206
  {"menu_dir", 'M', 0, G_OPTION_ARG_STRING, &popt_menu_dir,
   N_("GCompris will find the activities menu in this directory"), NULL},

207
  {"administration", 'a', 0, G_OPTION_ARG_NONE, &popt_administration,
208
   N_("Run GCompris in administration and user-management mode"), NULL},
209

210
  {"database", 'b', 0, G_OPTION_ARG_STRING, &popt_database,
211
   N_("Use alternate database for profiles [$HOME/.config/gcompris/gcompris_sqlite.db]"), NULL},
212

213
  {"create-db",'\0', 0, G_OPTION_ARG_NONE, &popt_create_db,
214
   N_("Create the alternate database for profiles"), NULL},
215

216
  {"reread-menu",'\0', 0, G_OPTION_ARG_NONE, &popt_reread_menu,
217
   N_("Re-read XML Menus and store them in the database"), NULL},
218

219
  {"profile",'p', 0, G_OPTION_ARG_STRING, &popt_profile,
220
   N_("Set the profile to use. Use 'gcompris -a' to create profiles"), NULL},
221

222
  {"profile-list",'\0', 0, G_OPTION_ARG_NONE, &popt_profile_list,
223
   N_("List all available profiles. Use 'gcompris -a' to create profiles"), NULL},
224

225
  {"config-dir",'\0', 0, G_OPTION_ARG_STRING, &popt_config_dir,
226
   N_("Config directory location: [$HOME/.config/gcompris]. Alternate is to set $XDG_CONFIG_HOME."), NULL},
227

228
  {"user-dir",'\0', 0, G_OPTION_ARG_STRING, &popt_user_dir,
229
   N_("The location of user directories: [$HOME/My GCompris]"), NULL},
230

231
  {"experimental",'\0', 0, G_OPTION_ARG_NONE, &popt_experimental,
232
   N_("Run the experimental activities"), NULL},
233

234
  {"disable-quit",'\0', 0, G_OPTION_ARG_NONE, &popt_no_quit,
Bruno Coudoin's avatar
Bruno Coudoin committed
235
   N_("Disable the quit button"), NULL},
236

237
  {"disable-config",'\0', 0, G_OPTION_ARG_NONE, &popt_no_config,
Bruno Coudoin's avatar
Bruno Coudoin committed
238
   N_("Disable the config button"), NULL},
239

240 241 242
  {"disable-level",'\0', 0, G_OPTION_ARG_NONE, &popt_no_level,
   N_("Disable the level button"), NULL},

243 244 245 246 247 248 249 250 251 252
  {"server", '\0', 0, G_OPTION_ARG_STRING, &popt_server,
   N_("GCompris will get images, sounds and activity data from this server if not found locally."), NULL},

  {"web-only", '\0', 0, G_OPTION_ARG_NONE, &popt_web_only,
   N_("Only when --server is provided, disable check for local resource first."
      " Data are always taken from the web server."), NULL},

  {"cache-dir", '\0', 0, G_OPTION_ARG_STRING, &popt_cache_dir,
   N_("In server mode, specify the cache directory used to avoid useless downloads."), NULL},

253
  {"drag-mode", 'g', 0, G_OPTION_ARG_STRING, &popt_drag_mode,
254
   N_("Global drag and drop mode: normal, 2clicks, both. Default mode is normal."), NULL},
255

256 257 258
  {"nolockcheck", '\0', 0, G_OPTION_ARG_NONE, &popt_nolockcheck,
   N_("Do not avoid the execution of multiple instances of GCompris."), NULL},

259 260 261 262 263 264
  {"sugarBundleId", '\0', 0, G_OPTION_ARG_STRING, &sugarBundleId,
   "Sugar Bundle Id", NULL},

  {"sugarActivityId", '\0', 0, G_OPTION_ARG_STRING, &sugarActivityId,
   "Sugar Activity Id", NULL},

265 266 267 268 269 270 271
  {"sugar",'\0', 0, G_OPTION_ARG_NONE, &popt_sugar_look,
   ("Use Sugar DE look&feel"), NULL},

  {"no-zoom",'\0', 0, G_OPTION_ARG_NONE, &popt_no_zoom,
   ("Disable maximization zoom"), NULL},

  {"timing-base",'\0', 0, G_OPTION_ARG_DOUBLE, &popt_timing_base,
Bruno Coudoin's avatar
Bruno Coudoin committed
272
   ("Increase activities' timeout delays; useful values > 1.0; 1.0 to not change hardcoded value"), NULL},
273 274

  {"timing-mult",'\0', 0, G_OPTION_ARG_DOUBLE, &popt_timing_mult,
Bruno Coudoin's avatar
Bruno Coudoin committed
275
   ("How activities' timeout delays are growing for several actors; useful values < 1.0; 1.0 to not change hardcoded value"), NULL},
276

277
  { NULL }
278 279
};

280
/* Remove any dialog box */
281
static void gc_close_all_dialog() {
282 283
  gc_dialog_close();
  gc_help_stop();
284
  gc_config_stop();
285 286 287
  gc_about_stop();
  gc_selector_file_stop();
  gc_selector_images_stop();
288 289
}

290 291 292 293 294 295 296 297
/* Return the zoom factor we are currently using for our window
 *
 */
double gc_zoom_factor_get()
{
  return zoom_factor;
}

298
static gint
299 300
_gc_size_allocate_event_callback (GtkWidget   *widget,
			      GtkAllocation *allocation,
301 302 303
			      gpointer     client_data)
{
  double xratio, yratio;
304
  double canvas_width, canvas_height;
305

306 307
  yratio=allocation->height/(float)(BOARDHEIGHT);
  xratio=allocation->width/(float)BOARDWIDTH;
308
  zoom_factor = MIN(xratio, yratio);
309
  g_message("The screen_width=%f screen_height=%f ratio=%f\n",
310
	    (double)allocation->width, (double)allocation->height, zoom_factor);
311

312 313 314 315 316
  if (!properties->zoom && zoom_factor > 1.)
      zoom_factor = 1.;

  canvas_width = BOARDWIDTH * zoom_factor;
  canvas_height = BOARDHEIGHT * zoom_factor;
317

318 319
  gtk_widget_set_size_request(canvas, canvas_width, canvas_height);
  goo_canvas_set_scale (GOO_CANVAS(canvas), zoom_factor);
320

321 322 323
  return FALSE;
}

Bruno Coudoin's avatar
Bruno Coudoin committed
324 325
static gint
board_widget_key_press_callback (GtkWidget   *widget,
326 327
				 GdkEventKey *event,
				 gpointer     client_data)
Bruno Coudoin's avatar
Bruno Coudoin committed
328
{
329
  int kv = event->keyval;
330

331 332
  if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_r)
					 || (event->keyval == GDK_R))) {
333
    g_message("Refreshing the canvas\n");
334
    goo_canvas_update(GOO_CANVAS(canvas));
335 336
    return TRUE;
  }
Bruno Coudoin's avatar
Bruno Coudoin committed
337
  else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_q)
338
					 || (event->keyval == GDK_Q))) {
339
    gc_exit();
340 341
    return TRUE;
  }
342 343 344 345 346 347 348 349
  else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_w)
					 || (event->keyval == GDK_W))) {
    gc_close_all_dialog();

    if (gc_board_get_current()->previous_board != NULL)
      gc_board_stop();
    return TRUE;
  }
Bruno Coudoin's avatar
Bruno Coudoin committed
350 351 352 353 354 355 356 357 358
  else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_f)
					 || (event->keyval == GDK_F))) {
    /* Toggle fullscreen */
    if (fullscreen)
      gc_fullscreen_set(FALSE);
    else
      gc_fullscreen_set(TRUE);
    return TRUE;
  }
359 360 361 362 363 364 365 366 367 368
  else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_m)
					 || (event->keyval == GDK_M))) {
    /* Toggle Mute */
    if (mute)
	gc_sound_bg_resume();
    else
	gc_sound_bg_pause();
    mute = ! mute;
    return TRUE;
  }
369

Bruno Coudoin's avatar
Bruno Coudoin committed
370 371 372
  switch (event->keyval)
    {
    case GDK_Escape:
373
      gc_close_all_dialog();
374

375
      if (gc_board_get_current()->previous_board != NULL)
376
	gc_board_stop();
Bruno Coudoin's avatar
Bruno Coudoin committed
377
      return TRUE;
378
    case GDK_F5:
379
      g_message("Refreshing the canvas\n");
380
      goo_canvas_update(GOO_CANVAS(canvas));
381
      return TRUE;
382

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
    case GDK_KP_Multiply:
      break;
    case GDK_KP_0:
    case GDK_KP_Insert:
      event->keyval=GDK_0;
      break;
    case GDK_KP_1:
    case GDK_KP_End:
      event->keyval=GDK_1;
      break;
    case GDK_KP_2:
    case GDK_KP_Down:
      event->keyval=GDK_2;
      break;
    case GDK_KP_3:
    case GDK_KP_Page_Down:
      event->keyval=GDK_3;
      break;
    case GDK_KP_4:
    case GDK_KP_Left:
      event->keyval=GDK_4;
      break;
    case GDK_KP_5:
    case GDK_KP_Begin:
      event->keyval=GDK_5;
      break;
    case GDK_KP_6:
    case GDK_KP_Right:
      event->keyval=GDK_6;
      break;
    case GDK_KP_7:
    case GDK_KP_Home:
      event->keyval=GDK_7;
      break;
    case GDK_KP_8:
    case GDK_KP_Up:
      event->keyval=GDK_8;
      break;
    case GDK_KP_9:
    case GDK_KP_Page_Up:
      event->keyval=GDK_9;
      break;
425
    default:
426 427 428 429
      break;
    }

  /* pass through the IM context */
430
  if (gc_board_get_current() && (!gc_board_get_current()->disable_im_context))
431 432
    {
      if (gtk_im_context_filter_keypress (properties->context, event))
Bruno Coudoin's avatar
Bruno Coudoin committed
433
	{
434
	  g_message("%d key is handled by context", kv);
435
	  return TRUE;
Bruno Coudoin's avatar
Bruno Coudoin committed
436 437 438
	}
    }

439
  g_message("%d key is NOT handled by context", kv);
440 441 442 443
  /* If the board needs to receive key pressed */
  /* NOTE: If a board receives key press, it must bind the ENTER Keys to OK
   *       whenever possible
   */
444
  if (gc_board_get_current_board_plugin()!=NULL
Bruno Coudoin's avatar
Bruno Coudoin committed
445
      && gc_board_get_current_board_plugin()->key_press)
446
    {
447
      return(gc_board_get_current_board_plugin()->key_press (event->keyval, NULL, NULL));
448
    }
449
  else if (gc_board_get_current_board_plugin()!=NULL
Bruno Coudoin's avatar
Bruno Coudoin committed
450
	   && gc_board_get_current_board_plugin()->ok &&
451 452 453
	   (event->keyval == GDK_KP_Enter ||
	    event->keyval == GDK_Return   ||
	    event->keyval == GDK_KP_Space))
454 455
    {
      /* Else we send the OK signal. */
456
      gc_board_get_current_board_plugin()->ok ();
457 458 459
      return TRUE;
    }

460
  /* Event not handled; try parent item */
Bruno Coudoin's avatar
Bruno Coudoin committed
461 462 463
  return FALSE;
};

464 465 466 467 468
guint gc_cursor_get()
{
  return gc_cursor_current;
}

469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
/* Set the cursor to be invisible
 * (Useful for touch screens)
 */
void gc_cursor_hide()
{
  char in_cursor[] = {0x0};
  GdkCursor *cursor;
  GdkBitmap *bp;
  GdkColor color = {0, 0, 0, 0};

  bp = gdk_bitmap_create_from_data(NULL , in_cursor , 1, 1);
  cursor = gdk_cursor_new_from_pixmap(bp , bp ,
				      &color , &color ,
				      1 , 1);
  gdk_window_set_cursor(window->window , cursor);
  gdk_cursor_unref(cursor);
}

487 488 489 490
void gc_cursor_set(guint gdk_cursor_type)
{
  GdkCursor *cursor = NULL;

491 492 493
  if (properties->nocursor)
    return;

494 495 496 497 498 499 500 501 502 503 504 505 506 507
  // Little hack to force gcompris to use the default cursor
  if(gdk_cursor_type==GCOMPRIS_DEFAULT_CURSOR)
    gdk_cursor_type=properties->defaultcursor;

  // I suppose there is less than GCOMPRIS_FIRST_CUSTOM_CURSOR cursors defined in gdkcursors.h !
  if (gdk_cursor_type < GCOMPRIS_FIRST_CUSTOM_CURSOR) {
    cursor = gdk_cursor_new(gdk_cursor_type);
    gdk_window_set_cursor (window->window, cursor);
    gdk_cursor_destroy(cursor);
  } else { // we use a custom cursor
    GdkPixbuf *cursor_pixbuf = NULL;

    switch (gdk_cursor_type) {
    case GCOMPRIS_DEFAULT_CURSOR :
508
      cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/default.png");
509 510
      break;
    case GCOMPRIS_LINE_CURSOR :
511
      cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/line.png");
512 513
      break;
    case GCOMPRIS_RECT_CURSOR :
514
      cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/rect.png");
515 516
      break;
    case GCOMPRIS_FILLRECT_CURSOR :
517
      cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/fillrect.png");
518 519
      break;
    case GCOMPRIS_CIRCLE_CURSOR :
520
      cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/circle.png");
521 522
      break;
    case GCOMPRIS_FILLCIRCLE_CURSOR :
523
      cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/fillcircle.png");
524 525
      break;
    case GCOMPRIS_FILL_CURSOR :
526
      cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/fill.png");
527 528
      break;
    case GCOMPRIS_DEL_CURSOR :
529
      cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/del.png");
530 531
      break;
    case GCOMPRIS_SELECT_CURSOR :
532
      cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/select.png");
533 534 535 536 537 538 539 540
      break;
    default :
      return;
      break;
    }

    if(cursor_pixbuf)
      {
541 542
	cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
					    cursor_pixbuf, 0, 0);
543 544 545 546
	gdk_window_set_cursor(window->window, cursor);
	gdk_cursor_unref(cursor);
	gdk_pixbuf_unref(cursor_pixbuf);
      }
547 548 549 550 551 552 553
    else
      {
	/* The cursor image was not found, falback to default one */
	properties->defaultcursor = GDK_LEFT_PTR;
	gc_cursor_set(GCOMPRIS_DEFAULT_CURSOR);
	return;
      }
554
  }
555
  gc_cursor_current = gdk_cursor_type;
556 557
}

Bruno Coudoin's avatar
Bruno Coudoin committed
558 559 560
/**
 * Return the main canvas we run in
 */
561
GooCanvas *gc_get_canvas()
Bruno Coudoin's avatar
Bruno Coudoin committed
562
{
563
  return GOO_CANVAS (canvas);
Bruno Coudoin's avatar
Bruno Coudoin committed
564 565
}

566
GtkWidget *gc_get_window()
567 568 569 570
{
  return window;
}

Bruno Coudoin's avatar
Bruno Coudoin committed
571
static void
572
_set_svg_background(GooCanvasItem *parent, gchar *file, gchar *id)
Bruno Coudoin's avatar
Bruno Coudoin committed
573
{
Bruno Coudoin's avatar
Bruno Coudoin committed
574
  RsvgHandle *rsvg_handle;
Bruno Coudoin's avatar
Bruno Coudoin committed
575

576
  rsvg_handle = gc_rsvg_load (file);
Bruno Coudoin's avatar
Bruno Coudoin committed
577 578 579

  if(backgroundsvgimg)
    g_object_set(backgroundsvgimg,
580
		 "svg-handle", rsvg_handle,
581
		 "svg-id", id,
Bruno Coudoin's avatar
Bruno Coudoin committed
582 583
		 NULL);
  else
584
    backgroundsvgimg = goo_canvas_svg_new (parent,
Bruno Coudoin's avatar
Bruno Coudoin committed
585 586 587 588 589 590 591 592 593 594 595 596
					 rsvg_handle,
					 NULL);
  goo_canvas_item_lower(backgroundsvgimg, NULL);

  g_object_unref(rsvg_handle);
}

static void
_set_pixmap_background(GooCanvasItem *parent, gchar *file)
{
  GdkPixbuf *background_pixmap;

597
  background_pixmap = gc_pixmap_load (file);
Bruno Coudoin's avatar
Bruno Coudoin committed
598 599

  if(backgroundimg)
600 601 602
    g_object_set(backgroundimg,
		 "pixbuf", background_pixmap,
		 NULL);
603
  else
604 605 606 607 608 609 610 611
    backgroundimg = goo_canvas_image_new (parent,
					  background_pixmap,
					  0.0,
					  0.0,
					  "width", (gdouble) BOARDWIDTH,
					  "height", (gdouble) BOARDHEIGHT,
					  NULL);
  goo_canvas_item_lower(backgroundimg, NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
612 613 614

  gdk_pixbuf_unref(background_pixmap);

Bruno Coudoin's avatar
Bruno Coudoin committed
615 616
}

617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
void _clear_svg_background()
{
  if(backgroundsvgimg)
    goo_canvas_item_remove(backgroundsvgimg);

  backgroundsvgimg = NULL;
}

void _clear_pixmap_background()
{
  if(backgroundimg)
    goo_canvas_item_remove(backgroundimg);

  backgroundimg = NULL;
}

Bruno Coudoin's avatar
Bruno Coudoin committed
633 634 635 636 637 638 639 640 641 642 643
void
gc_set_background(GooCanvasItem *parent, gchar *file)
{
  g_assert(parent);
  g_assert(file);

  if(g_str_has_suffix(file, ".svg") ||
     g_str_has_suffix(file, ".svgz"))
    {
      if(backgroundimg)
	goo_canvas_item_remove(backgroundimg);
644 645

      backgroundimg = NULL;
646
      _set_svg_background(parent, file, NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
647 648 649
    }
  else
    {
650
      _clear_svg_background();
Bruno Coudoin's avatar
Bruno Coudoin committed
651 652
      _set_pixmap_background(parent, file);
    }
Bruno Coudoin's avatar
Bruno Coudoin committed
653 654
}

655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
void
gc_set_background_by_id(GooCanvasItem *parent, RsvgHandle *rsvg_handle,
			gchar *id)
{
  g_assert(parent);
  g_assert(rsvg_handle);

  _clear_pixmap_background();

  if(backgroundsvgimg)
    g_object_set(backgroundsvgimg,
		 "svg-handle", rsvg_handle,
		 "svg-id", id,
		 NULL);
  else
    backgroundsvgimg = goo_canvas_svg_new (parent,
					   rsvg_handle,
					   "svg-id", id,
					   NULL);

  goo_canvas_item_lower(backgroundsvgimg, NULL);
}

678 679 680 681 682 683 684 685
void
gc_set_default_background(GooCanvasItem *parent)
{
  gc_set_background_by_id(parent,
			  gc_skin_rsvg_get(),
			  "#BACKGROUND");
}

686 687 688
static gboolean
_realize_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
689
#ifdef USE_SUGAR
690
  Window xwindow = GDK_WINDOW_XWINDOW(window->window);
691 692 693
  /*
   * Sugar requires properties to be set before the windows is realized
   */
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
  if (sugarBundleId)
    XChangeProperty(GDK_DISPLAY(),
		    xwindow,
		    XInternAtom(GDK_DISPLAY(), "_SUGAR_BUNDLE_ID", 0),
		    XInternAtom(GDK_DISPLAY(), "STRING", 0), 8,
		    PropModeReplace,
		    (unsigned char *) sugarBundleId, strlen(sugarBundleId));

  if (sugarActivityId)
    XChangeProperty(GDK_DISPLAY(),
		    xwindow,
		    XInternAtom(GDK_DISPLAY(), "_SUGAR_ACTIVITY_ID", 0),
		    XInternAtom(GDK_DISPLAY(), "STRING", 0), 8,
		    PropModeReplace,
		    (unsigned char *) sugarActivityId, strlen(sugarActivityId));
709
#endif
710 711 712 713

  return FALSE;
}

714
static void
715
init_workspace()
Bruno Coudoin's avatar
Bruno Coudoin committed
716
{
717 718
  workspace = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), workspace);
719

720 721 722 723
  GtkWidget *background = gtk_event_box_new();
  GdkColor black = {0, 0, 0, 0};
  gtk_widget_modify_bg(background, GTK_STATE_NORMAL, &black);
  gtk_box_pack_end(GTK_BOX(workspace), background, TRUE, TRUE, 0);
724

725 726 727 728
  alignment = gtk_alignment_new(.5, .5, 0., 0.);
  g_signal_connect (GTK_OBJECT (alignment), "size-allocate",
		      G_CALLBACK (_gc_size_allocate_event_callback), NULL);
  gtk_container_add(GTK_CONTAINER(background), alignment);
729

730 731
  gtk_container_add(GTK_CONTAINER(alignment), canvas);
  gtk_widget_set_size_request (GTK_WIDGET(canvas), BOARDWIDTH, BOARDHEIGHT);
732 733 734 735
  goo_canvas_set_bounds (GOO_CANVAS(canvas),
			 0, 0,
			 BOARDWIDTH,
			 BOARDHEIGHT);
736
  g_object_set (G_OBJECT(canvas), "background-color", "#000", NULL);
737 738 739 740 741 742 743 744 745 746 747

#ifdef MAC_INTEGRATION
  GtkWidget *quit_item;
  quit_item = gtk_menu_item_new();
  ige_mac_menu_set_quit_menu_item(GTK_MENU_ITEM (quit_item));
  gtk_signal_connect(GTK_OBJECT (quit_item),
		     "activate", GTK_SIGNAL_FUNC (quit_cb), NULL);
  gtk_widget_show (quit_item);

#endif

748
}
Bruno Coudoin's avatar
Bruno Coudoin committed
749

750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
static GcomprisBoard *get_board_to_start()
{
  GcomprisBoard *board_to_start;

  /* By default, the menu will be started */
  board_to_start = properties->menu_board;

  /* Get and Run the root menu */
  if(properties->administration)
    {
      board_to_start = gc_menu_section_get("/administration/administration");
    }
  else
    {
      /* If we have a profile defined, run the login screen
       * (the login screen is a board that uppon login completion
       * starts the menu)
       */
      if(properties->profile && properties->profile->group_ids)
	{
	  gboolean found = FALSE;

	  GList *group_id;

	  for (group_id = properties->profile->group_ids; group_id != NULL; group_id = group_id->next)
	    if (g_list_length(gc_db_users_from_group_get( *((int *) group_id->data))) > 0){
	      found = TRUE;
	      break;
	    }

	  /* No profile start normally */
	  if (found)
	    board_to_start = gc_menu_section_get("/login/login");
	  else {
	    board_to_start = gc_menu_section_get(properties->root_menu);
	    /* this will set user information to system one */
	    gc_profile_set_current_user(NULL);
	  }
	}
      else
	/* this will set user information to system one */
	gc_profile_set_current_user(NULL);
    }

  return board_to_start;
}

797 798
static void setup_window ()
{
799
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
Bruno Coudoin's avatar
Bruno Coudoin committed
800

801 802 803 804
  /*
   * Set an icon for gcompris
   * ------------------------
   */
805 806 807 808 809 810 811
  {
    GdkPixbuf *icon_pixbuf = NULL;
    gchar *iconfile = gc_file_find_absolute("%s/%s",
					    properties->system_icon_dir, "gcompris.png",
					    NULL);
    if(iconfile)
      {
812
	icon_pixbuf = gdk_pixbuf_new_from_file(iconfile, NULL);
813 814 815 816 817 818 819 820 821 822
	g_free(iconfile);

	if (icon_pixbuf)
	  {
	    gtk_window_set_icon (GTK_WINDOW (window), icon_pixbuf);
	    gdk_pixbuf_unref (icon_pixbuf);
	  }
      }
    else
      g_message ("Failed to find icon file: 'gcompris.png'");
823

824
  }
825

826
  gtk_window_set_title(GTK_WINDOW (window), "GCompris");
827 828 829 830 831

  /*
   * Set the main window
   * -------------------
   */
832

833
  gtk_window_set_default_size(GTK_WINDOW(window), BOARDWIDTH, BOARDHEIGHT);
834
  gtk_window_set_wmclass(GTK_WINDOW(window), "gcompris", "GCompris");
835 836
  g_signal_connect (GTK_OBJECT (window), "realize",
		    G_CALLBACK (_realize_callback), NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
837
  gtk_widget_realize (window);
Bruno Coudoin's avatar
Bruno Coudoin committed
838

Bruno Coudoin's avatar
Bruno Coudoin committed
839 840 841
  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
		      GTK_SIGNAL_FUNC (quit_cb), NULL);

842 843
  gtk_signal_connect (GTK_OBJECT (window), "map_event",
		      GTK_SIGNAL_FUNC (map_cb), NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
844

845
  // Set the cursor
846 847 848 849
  if (properties->nocursor)
    gc_cursor_hide();
  else
    gc_cursor_set(GCOMPRIS_DEFAULT_CURSOR);
850

851
  canvas     = goo_canvas_new();
Bruno Coudoin's avatar
Bruno Coudoin committed
852

853 854 855 856
  g_object_set (canvas,
		"has-tooltip", TRUE,
		NULL);

857 858 859 860
  g_object_set (G_OBJECT(goo_canvas_get_root_item(GOO_CANVAS(canvas))),
		"can-focus", TRUE,
		NULL);

Bruno Coudoin's avatar
Bruno Coudoin committed
861

862 863
  gtk_signal_connect_after (GTK_OBJECT (window), "key_press_event",
			    GTK_SIGNAL_FUNC (board_widget_key_press_callback), 0);
Bruno Coudoin's avatar
Bruno Coudoin committed
864 865 866 867
  g_signal_connect_after (canvas,
			  "key_press_event",
			  GTK_SIGNAL_FUNC (board_widget_key_press_callback), 0);

868
  gc_im_init(window);
869

870 871 872 873
  init_workspace();

  GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
  gtk_widget_grab_focus (canvas);
874

875 876
}

877
#ifdef ACTIVATION_CODE
878 879
/** Display the activation dialog for the windows version
 *
880
 * return TRUE is the dialog is display, FALSE instead.
881
 */
882
int
883 884
display_activation_dialog()
{
885
  int board_count = 0;
886
  int gc_board_number_in_demo = 0;
887
  GList *list;
888
  gint  key_is_valid = 0;
889

Bruno Coudoin's avatar
Bruno Coudoin committed
890
  key_is_valid = gc_activation_check(properties->key);
891

Bruno Coudoin's avatar
Bruno Coudoin committed
892
  if(key_is_valid == 1)
893
    return FALSE;
894 895 896 897 898 899
  else if(key_is_valid == 2)
    {
      g_free ( gc_prop_get()->key );
      gc_prop_get()->key = strdup("");
      gc_prop_save(properties);
    }
900 901 902 903 904
  /* Count non menu boards */
  for (list = gc_menu_get_boards(); list != NULL; list = list->next)
    {
      GcomprisBoard *board = list->data;
      if (strcmp(board->type, "menu") != 0 &&
905 906 907
	  strncmp(board->section, "/experimental", 13) != 0)
	{
	  board_count++;
908
	  if(board->demo)
909 910
	      gc_board_number_in_demo++;
	}
911 912
    }

913
  /* Entry area */
914 915
  widget_activation_entry = gtk_entry_new();
  gtk_entry_set_max_length(GTK_ENTRY(widget_activation_entry), 6);
916
  activation_item = \
917 918 919
    goo_canvas_widget_new (goo_canvas_get_root_item(GOO_CANVAS(canvas)),
			   GTK_WIDGET(widget_activation_entry),
			   BOARDWIDTH / 2 - 50,
920
			   BOARDHEIGHT - 90,
921 922
			   100.0,
			   30.0,
923 924 925 926 927
			   NULL);
  gtk_signal_connect(GTK_OBJECT(widget_activation_entry), "activate",
		     GTK_SIGNAL_FUNC(activation_enter_callback),
		     NULL);

928 929
  char *msg = g_strdup_printf( \
      _("GCompris is free software released under the GPL License. "
930
	"In order to support its development, this version "
931 932 933 934 935 936 937
	"provides only %d of the %d activities. You can get the "
	"full version for a small fee at\n<http://gcompris.net>\n"
	"The GNU/Linux version does not have this restriction. "
	"If you also believe that we should teach freedom to children, "
	"please consider using GNU/Linux. Get more information at "
	"FSF:\n<http://www.fsf.org/philosophy>"),
      gc_board_number_in_demo, board_count);
938 939
  gc_dialog(msg, activation_done);
  g_free(msg);
940

941 942 943
  gtk_widget_show(GTK_WIDGET(widget_activation_entry));
  gtk_entry_set_text(GTK_ENTRY(widget_activation_entry), "CODE");
  gtk_widget_grab_focus(GTK_WIDGET(widget_activation_entry));
944
  return TRUE;
Bruno Coudoin's avatar
Bruno Coudoin committed
945 946
}

947 948 949 950
/**
 * Return -1 if the code is not valid
 *        0  if the code is valid but out of date
 *        1  if the code is valid and under 2 years
951
 *        2  for the demo code
952
 */
953
gint gc_activation_check(const char *code)
954
{
Bruno Coudoin's avatar
Bruno Coudoin committed
955
#ifdef  DISABLE_ACTIVATION_CODE
Bruno Coudoin's avatar
Bruno Coudoin committed
956
  return 1;
Bruno Coudoin's avatar
Bruno Coudoin committed
957
#else
958 959 960 961
  int value = 0;
  int i;
  char crc1 = 0;
  char crc2 = 0;
962
  char codeddate[5];
963

964 965 966
  if (properties->administration)
    return 1;

967 968 969
  if(strlen(code) != 6)
    return -1;

970 971 972 973 974 975 976
  // A special code to test the full version without
  // saving the activation
  if (strncmp(code, "123321", 6) == 0)
    {
      return 2;
    }

977 978 979 980
  for(i=3; i>=0; i--)
    {
      value |= code[i] & 0x07;
      value = value << 3;
Bruno Coudoin's avatar
Bruno Coudoin committed
981
      crc1 = (crc1 ^ code[i]) & 0x07;
982 983 984 985
    }
  value = value >> 3;
  crc1 = 0x30 | crc1;
  crc2 = 0x30 | (code[2] ^ code[3]);
986

Bruno Coudoin's avatar
Bruno Coudoin committed
987 988 989 990 991
  if(crc1 != code[4])
    return(-1);

  if(crc2 != code[5])
    return(-1);
992

Bruno Coudoin's avatar
Bruno Coudoin committed
993
  codeddate[3] = 0x30 | (value & 0x000F);
994
  value = value >> 4;
995

Bruno Coudoin's avatar
Bruno Coudoin committed
996
  codeddate[2] = 0x30 | (value & 0x0001);
997
  value = value >> 1;
998

Bruno Coudoin's avatar
Bruno Coudoin committed
999
  codeddate[1] = 0x30 | (value & 0x000F);
1000
  value = value >> 4;
1001

Bruno Coudoin's avatar
Bruno Coudoin committed
1002
  codeddate[0] = 0x30 | (value & 0x0003);
1003 1004 1005 1006 1007 1008
  codeddate[4] = '\0';

  if(atoi(codeddate) + 200 >= atoi(BUILD_DATE))
    return(1);
  else
    return(0);
Bruno Coudoin's avatar
Bruno Coudoin committed
1009
#endif
1010 1011
}

1012 1013 1014 1015
/* Check the activation code
 *
 */
static void
1016 1017
activation_enter_callback( GtkWidget *entry,
			   GtkWidget *notused )
1018
{
1019 1020 1021
  const char *code = gtk_entry_get_text(GTK_ENTRY(entry));

  switch(gc_activation_check(code))
1022
    {
1023
    case 1:
1024
    case 2:
Bruno Coudoin's avatar
Bruno Coudoin committed
1025 1026
      gc_prop_get()->key = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
      gc_prop_save(properties);
1027 1028 1029 1030 1031 1032 1033 1034
      gtk_entry_set_text(GTK_ENTRY(entry), "GOOD");
      break;
    case 0:
      gtk_entry_set_text(GTK_ENTRY(entry), "EXPIRE");
      break;
    case -1:
      gtk_entry_set_text(GTK_ENTRY(entry), "WRONG");
      break;
1035 1036 1037 1038 1039 1040 1041 1042 1043
    }
}

/* Callback for the activation dialog
 *
 */
static void
activation_done()
{
1044 1045 1046 1047
  if ((strcmp((char *)gtk_entry_get_text(GTK_ENTRY(widget_activation_entry)), "CODE") != 0) &&
      (strcmp((char *)gtk_entry_get_text(GTK_ENTRY(widget_activation_entry)), "GOOD") != 0) &&
      (strcmp((char *)gtk_entry_get_text(GTK_ENTRY(widget_activation_entry)), "WRONG") != 0))
    {
1048
      activation_enter_callback(GTK_WIDGET(widget_activation_entry), NULL);
1049
    }
1050

1051
  gc_board_play( get_board_to_start());
1052
  goo_canvas_item_remove (activation_item);
1053 1054 1055 1056 1057 1058
}
#endif

/** Call me to end an activity
 *
 */
1059
void gc_board_end()
Bruno Coudoin's avatar
Bruno Coudoin committed
1060
{
1061
  if (gc_board_get_current()->previous_board) {
1062
    /* Run the previous board */
1063
    gc_board_play(gc_board_get_current()->previous_board);
1064
  }
Bruno Coudoin's avatar
Bruno Coudoin committed
1065 1066
}

1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
void _set_geometry_hints(gboolean state)
{
  GdkGeometry hints;
  hints.base_width = 615;
  hints.base_height = 400;
  hints.min_width = hints.base_width;
  hints.min_height = hints.base_height;
  hints.width_inc = 1;
  hints.height_inc = 1;
  hints.min_aspect = (float)BOARDWIDTH/BOARDHEIGHT;
  if (state)
    /* Warning: this is a workaround for GTK-OSX */
    hints.max_aspect = (float)10; /* Fullscreen case */
  else
    hints.max_aspect = (float)BOARDWIDTH/BOARDHEIGHT;
  gint geom_mask = GDK_HINT_RESIZE_INC |
                   GDK_HINT_MIN_SIZE |
                   GDK_HINT_BASE_SIZE;
  if (!popt_sugar_look)
      geom_mask |= GDK_HINT_ASPECT;
  gtk_window_set_geometry_hints (GTK_WINDOW (window), NULL, &hints, geom_mask);
}

1090 1091 1092 1093
/** \brief toggle full screen mode
 *
 *
 */
1094
void gc_fullscreen_set(gboolean state)
1095
{
1096 1097 1098 1099 1100
  static gint window_x = 0;
  static gint window_y = 0;
  static gint window_w = BOARDWIDTH;
  static gint window_h = BOARDHEIGHT;

Bruno Coudoin's avatar
Bruno Coudoin committed
1101
  fullscreen = state;
1102
  _set_geometry_hints(state);
1103 1104
  if(state)
    {
1105 1106
      gtk_window_get_position ( (GtkWindow*)( window ), &window_x, &window_y );
      gtk_window_get_size ( GTK_WINDOW ( window ), &window_w, &window_h );
1107
#ifdef WIN32
1108 1109 1110
      // WARNING: Doing this is required on Windows
      //          but keep the window hidden on GNU/Linux
      gtk_widget_hide ( window );
1111 1112
#elif MAC_INTEGRATION
      // WARNING: Doing this is required on MacOSX
1113
      //          but keep the window hidden on GNU/Linux
1114
      gtk_widget_hide ( window );
1115 1116
#else
#endif
1117 1118 1119
      gtk_window_set_decorated ( GTK_WINDOW ( window ), FALSE );
      gtk_window_set_type_hint ( GTK_WINDOW ( window ),
				 GDK_WINDOW_TYPE_HINT_DESKTOP );
1120
      if (popt_sugar_look)
1121
	gtk_window_maximize ( GTK_WINDOW( window ) );
1122
      else
1123 1124 1125 1126 1127 1128 1129 1130
        gtk_window_fullscreen ( GTK_WINDOW ( window ) );

      gtk_window_move ( GTK_WINDOW ( window ), 0, 0 );

      GdkScreen *screen = gtk_window_get_screen ( GTK_WINDOW ( window ) );
      gtk_window_resize ( GTK_WINDOW ( window ),
			  gdk_screen_get_width (screen),
			  gdk_screen_get_height (screen) );
1131 1132
    }
  else
1133
    {
1134 1135 1136 1137 1138 1139 1140
      gtk_widget_hide ( window );
      gtk_window_set_type_hint ( GTK_WINDOW ( window ),
				 GDK_WINDOW_TYPE_HINT_NORMAL );
      gtk_window_unfullscreen ( GTK_WINDOW ( window ) );
      gtk_window_set_decorated ( GTK_WINDOW ( window ), TRUE );
      gtk_window_move ( GTK_WINDOW ( window ), window_x, window_y );
      gtk_window_resize ( GTK_WINDOW ( window ), window_w, window_h );
1141
    }
1142
  gtk_window_present ( GTK_WINDOW ( window ) );
1143 1144 1145

}

1146
/* Use these instead of the goo_canvas ones for proper fullscreen mousegrab
1147
   handling. */
1148 1149
int gc_canvas_item_grab (GooCanvasItem *item, unsigned int event_mask,
			 GdkCursor *cursor, guint32 etime)
1150 1151
{
  int retval;
1152

1153 1154
  retval = goo_canvas_pointer_grab(goo_canvas_item_get_canvas(item), item,
				   event_mask, cursor, etime);
1155 1156
  if (retval != GDK_GRAB_SUCCESS)
    return retval;
1157

1158 1159 1160
  return retval;
}

1161
void gc_canvas_item_ungrab (GooCanvasItem *item, guint32 etime)
1162
{
1163
  goo_canvas_pointer_ungrab(goo_canvas_item_get_canvas(item), item, etime);
1164 1165
}

1166
static void cleanup()
Bruno Coudoin's avatar
Bruno Coudoin committed
1167
{
1168 1169 1170 1171
  /* Do not loopback in exit */
  signal(SIGINT,  NULL);
  signal(SIGSEGV, NULL);

1172
  single_instance_release(); /* Must be done before property destroy */
1173
  gc_board_stop();
1174
  gc_db_exit();
1175
  gc_fullscreen_set(FALSE);
1176
  gc_menu_destroy();
1177 1178
  gc_net_destroy();
  gc_cache_destroy();
1179
  gc_prop_destroy(gc_prop_get());
1180
}
1181

1182
void gc_exit()
1183 1184
{
  g_signal_emit_by_name(G_OBJECT(window), "delete_event");
1185
}
Bruno Coudoin's avatar
Bruno Coudoin committed
1186

1187 1188
static void quit_cb (GtkWidget *widget, gpointer data)
{
1189 1190 1191 1192

#ifdef DMALLOC
  dmalloc_shutdown();
#endif
1193 1194
  cleanup();
  gtk_main_quit();
1195
  gc_sound_close();
1196

1197 1198 1199 1200 1201
  /*
   * Very important or GCompris crashes on exit when closed from the dialog
   * It's like if code in the dialog callback continue after the gtk_main_quit is done
   */
  exit(0);
Bruno Coudoin's avatar
Bruno Coudoin committed
1202 1203
}

1204 1205 1206 1207 1208 1209 1210 1211
/*
 * We want GCompris to be set as fullscreen the later possible
 *
 */
static void map_cb (GtkWidget *widget, gpointer data)
{
  if(is_mapped == FALSE)
    {
Bruno Coudoin's avatar
Bruno Coudoin committed
1212
      is_mapped = TRUE;
1213 1214
      GcomprisBoard *board_to_start;

1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
      if (! gc_skin_load(properties->skin) )
	{
	  gc_status_init("");
	  gchar *filename = \
	    g_strdup_printf("%s/%s/skin.xml",
			    properties->package_skin_dir,
			    properties->skin);

	  gc_status_set_msg(_("Failed to load the skin '%s'"
			      " (Check the file exists and is readable)"),
			    filename);
	  g_free(filename);
	  return;
	}

1230 1231 1232 1233 1234
      gc_set_default_background (goo_canvas_get_root_item (GOO_CANVAS(canvas)));

      gc_fullscreen_set(properties->fullscreen);

      gc_status_init("");
1235

1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
      gc_board_init();
      /* Load all the menu once */
      gc_menu_load();
      /* Save the root_menu */
      properties->menu_board = gc_menu_section_get(properties->root_menu);

      gc_bar_start(GTK_CONTAINER(workspace), GOO_CANVAS(canvas));

      gc_status_close();

      board_to_start = get_board_to_start();

      if(!board_to_start) {
	gchar *tmpstr= g_strdup_printf("Couldn't find the board menu %s, or plugin execution error", properties->root_menu);
	gc_dialog(tmpstr, NULL);
	g_free(tmpstr);
      } else if(!gc_board_check_file(board_to_start)) {
	gchar *tmpstr= g_strdup_printf("Couldn't find the board menu, or plugin execution error");
	gc_dialog(tmpstr, NULL);
	g_free(tmpstr);
      } else {
	g_message("Fine, we got the gcomprisBoardMenu, xml boards parsing went fine");
	if(!display_activation_dialog())
	  gc_board_play(board_to_start);
      }

1262
    }
1263
  g_message("gcompris window is now mapped");
1264 1265
}

1266
/*
1267
 * Process the cleanup of the child (no zombies)
1268 1269
 * ---------------------------------------------
 */
1270
void gc_terminate(int signum)
1271 1272
{

1273
  g_message("GCompris got the %d signal, starting exit procedure", signum);
1274

1275
  gc_exit();
1276