PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/gcompris-12.05/src/gcompris/gcompris.c

#
C | 2114 lines | 1583 code | 337 blank | 194 comment | 245 complexity | 3e05d6df436da34aeb11fd4b62c5c500 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, CC0-1.0

Large files files are truncated, but you can click here to view the full file

  1. /* gcompris - gcompris.c
  2. *
  3. * Copyright (C) 2000, 2012 Bruno Coudoin
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <signal.h>
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <unistd.h>
  22. #include <time.h>
  23. #include <string.h>
  24. #ifdef WIN32
  25. // WIN32
  26. #elif MAC_INTEGRATION
  27. // MACOSX
  28. #else
  29. #include <X11/Xlib.h>
  30. #include <X11/Xutil.h>
  31. #include <X11/Xatom.h>
  32. #include <gdk/gdkx.h>
  33. #endif
  34. // Include Mac OS X menu synchronization on native OSX build
  35. #ifdef MAC_INTEGRATION
  36. #include "ige-mac-menu.h"
  37. #endif
  38. #include <glib/gstdio.h>
  39. #include "gcompris.h"
  40. #include "gc_core.h"
  41. #include "gcompris_config.h"
  42. #include "about.h"
  43. #include "bar.h"
  44. #include "sugar_gc.h"
  45. #include "status.h"
  46. #include <locale.h>
  47. #include "binreloc.h"
  48. /* get the default database name */
  49. #define DEFAULT_DATABASE "gcompris_sqlite.db"
  50. static double zoom_factor = 1.0;
  51. /* Multiple instance check */
  52. #define GC_LOCK_FILE "gcompris.lock"
  53. #define GC_LOCK_LIMIT 30 /* seconds */
  54. static GtkWidget *window;
  55. static GtkWidget *workspace;
  56. static GtkWidget *alignment;
  57. static GtkWidget *canvas;
  58. gchar * exec_prefix = NULL;
  59. //static gint pause_board_cb (GtkWidget *widget, gpointer data);
  60. static void quit_cb (GtkWidget *widget, gpointer data);
  61. static void map_cb (GtkWidget *widget, gpointer data);
  62. static gint board_widget_key_press_callback (GtkWidget *widget,
  63. GdkEventKey *event,
  64. gpointer client_data);
  65. void gc_terminate(int signum);
  66. static void single_instance_release();
  67. /*
  68. * For the Activation dialog
  69. */
  70. #ifdef ACTIVATION_CODE
  71. int gc_activation_check(const char *code);
  72. static void activation_enter_callback(GtkWidget *widget,
  73. GtkWidget *entry );
  74. static void activation_done();
  75. static int display_activation_dialog();
  76. static GooCanvasItem *activation_item;
  77. static GtkWidget *widget_activation_entry;
  78. #else
  79. #define display_activation_dialog() FALSE
  80. #endif
  81. static GcomprisProperties *properties = NULL;
  82. static gboolean is_mapped = FALSE;
  83. static gboolean fullscreen;
  84. static gboolean mute;
  85. static guint gc_cursor_current;
  86. /****************************************************************************/
  87. /* Some constants. */
  88. static GooCanvasItem *backgroundimg = NULL;
  89. static GooCanvasItem *backgroundsvgimg = NULL;
  90. static gchar *gc_locale = NULL;
  91. static gchar *gc_user_default_locale = NULL;
  92. static gboolean gc_debug = FALSE;
  93. /****************************************************************************/
  94. /* Command line params */
  95. /*** gcompris-popttable */
  96. static gint popt_fullscreen = FALSE;
  97. static gint popt_window = FALSE;
  98. static gint popt_sound = FALSE;
  99. static gint popt_mute = FALSE;
  100. static gint popt_cursor = FALSE;
  101. static gint popt_nocursor = FALSE;
  102. static gint popt_version = FALSE;
  103. static gint popt_difficulty_filter = FALSE;
  104. static gint popt_debug = FALSE;
  105. static gint popt_nolockcheck = FALSE;
  106. static gchar *popt_root_menu = NULL;
  107. static gchar *popt_package_data_dir = NULL;
  108. static gchar *popt_package_skin_dir = NULL;
  109. static gchar *popt_plugin_dir = NULL;
  110. static gchar *popt_python_plugin_dir = NULL;
  111. static gchar *popt_locale_dir = NULL;
  112. static gchar *popt_menu_dir = NULL;
  113. static gint popt_administration = FALSE;
  114. static gchar *popt_database = NULL;
  115. static gint popt_create_db = FALSE;
  116. static gint popt_reread_menu = FALSE;
  117. static gchar *popt_profile = NULL;
  118. static gint *popt_profile_list = FALSE;
  119. static gchar *popt_config_dir = NULL;
  120. static gchar *popt_user_dir = NULL;
  121. static gint popt_experimental = FALSE;
  122. static gint popt_no_quit = FALSE;
  123. static gint popt_no_config = FALSE;
  124. static gint popt_no_level = FALSE;
  125. static gint popt_no_database = FALSE;
  126. static gchar *popt_server = NULL;
  127. static gint *popt_web_only = NULL;
  128. static gchar *popt_cache_dir = NULL;
  129. static gchar *popt_drag_mode = NULL;
  130. static gint popt_no_zoom = FALSE;
  131. static gint popt_test = FALSE;
  132. static gdouble popt_timing_base = 1.0;
  133. static gdouble popt_timing_mult = 1.0;
  134. static GOptionEntry options[] = {
  135. {"fullscreen", 'f', 0, G_OPTION_ARG_NONE, &popt_fullscreen,
  136. N_("run GCompris in fullscreen mode."), NULL},
  137. {"window", 'w', 0, G_OPTION_ARG_NONE, &popt_window,
  138. N_("run GCompris in window mode."), NULL},
  139. {"sound", 's', 0, G_OPTION_ARG_NONE, &popt_sound,
  140. N_("run GCompris with sound enabled."), NULL},
  141. {"mute", 'm', 0, G_OPTION_ARG_NONE, &popt_mute,
  142. N_("run GCompris without sound."), NULL},
  143. {"cursor", 'c', 0, G_OPTION_ARG_NONE, &popt_cursor,
  144. N_("run GCompris with the default system cursor."), NULL},
  145. {"nocursor", 'C', 0, G_OPTION_ARG_NONE, &popt_nocursor,
  146. N_("run GCompris without cursor (touch screen mode)."), NULL},
  147. {"difficulty", 'd', 0, G_OPTION_ARG_INT, &popt_difficulty_filter,
  148. N_("display only activities with this difficulty level."), NULL},
  149. {"debug", 'D', 0, G_OPTION_ARG_NONE, &popt_debug,
  150. N_("display debug informations on the console."), NULL},
  151. {"version", 'v', 0, G_OPTION_ARG_NONE, &popt_version,
  152. N_("Print the version of " PACKAGE), NULL},
  153. {"root-menu", 'l', 0, G_OPTION_ARG_STRING, &popt_root_menu,
  154. N_("Run GCompris with local menu"
  155. " (e.g -l /reading will let you play only activities in the reading directory, -l /strategy/connect4 only the connect4 activity)."
  156. " Use '-l list' to list all the availaible activities and their descriptions."), NULL},
  157. {"package_data_dir", 'A', 0, G_OPTION_ARG_STRING, &popt_package_data_dir,
  158. N_("GCompris will find the data dir in this directory"), NULL},
  159. {"package_skin_dir", 'S', 0, G_OPTION_ARG_STRING, &popt_package_skin_dir,
  160. N_("GCompris will find the skins in this directory"), NULL},
  161. {"plugin_dir", 'L', 0, G_OPTION_ARG_STRING, &popt_plugin_dir,
  162. N_("GCompris will find the activity plugins in this directory"), NULL},
  163. {"python_plugin_dir", 'P', 0, G_OPTION_ARG_STRING, &popt_python_plugin_dir,
  164. N_("GCompris will find the python activity in this directory"), NULL},
  165. {"locale_dir", '\0', 0, G_OPTION_ARG_STRING, &popt_locale_dir,
  166. N_("GCompris will find the locale file (.mo translation) in this directory"), NULL},
  167. {"menu_dir", 'M', 0, G_OPTION_ARG_STRING, &popt_menu_dir,
  168. N_("GCompris will find the activities menu in this directory"), NULL},
  169. {"administration", 'a', 0, G_OPTION_ARG_NONE, &popt_administration,
  170. N_("Run GCompris in administration and user-management mode"), NULL},
  171. {"database", 'b', 0, G_OPTION_ARG_STRING, &popt_database,
  172. N_("Use alternate database for profiles [$HOME/.config/gcompris/gcompris_sqlite.db]"), NULL},
  173. {"create-db",'\0', 0, G_OPTION_ARG_NONE, &popt_create_db,
  174. N_("Create the alternate database for profiles"), NULL},
  175. {"reread-menu",'\0', 0, G_OPTION_ARG_NONE, &popt_reread_menu,
  176. N_("Re-read XML Menus and store them in the database"), NULL},
  177. {"profile",'p', 0, G_OPTION_ARG_STRING, &popt_profile,
  178. N_("Set the profile to use. Use 'gcompris -a' to create profiles"), NULL},
  179. {"profile-list",'\0', 0, G_OPTION_ARG_NONE, &popt_profile_list,
  180. N_("List all available profiles. Use 'gcompris -a' to create profiles"), NULL},
  181. {"config-dir",'\0', 0, G_OPTION_ARG_STRING, &popt_config_dir,
  182. N_("Config directory location: [$HOME/.config/gcompris]. Alternate is to set $XDG_CONFIG_HOME."), NULL},
  183. {"user-dir",'\0', 0, G_OPTION_ARG_STRING, &popt_user_dir,
  184. N_("The location of user directories: [$HOME/My GCompris]"), NULL},
  185. {"experimental",'\0', 0, G_OPTION_ARG_NONE, &popt_experimental,
  186. N_("Run the experimental activities"), NULL},
  187. {"disable-quit",'\0', 0, G_OPTION_ARG_NONE, &popt_no_quit,
  188. N_("Disable the quit button"), NULL},
  189. {"disable-config",'\0', 0, G_OPTION_ARG_NONE, &popt_no_config,
  190. N_("Disable the config button"), NULL},
  191. {"disable-level",'\0', 0, G_OPTION_ARG_NONE, &popt_no_level,
  192. N_("Disable the level button"), NULL},
  193. {"disable-database",'\0', 0, G_OPTION_ARG_NONE, &popt_no_database,
  194. N_("Disable the database (slower start and no user log)"), NULL},
  195. {"server", '\0', 0, G_OPTION_ARG_STRING, &popt_server,
  196. N_("GCompris will get images, sounds and activity data from this server if not found locally."), NULL},
  197. {"web-only", '\0', 0, G_OPTION_ARG_NONE, &popt_web_only,
  198. N_("Only when --server is provided, disable check for local resource first."
  199. " Data are always taken from the web server."), NULL},
  200. {"cache-dir", '\0', 0, G_OPTION_ARG_STRING, &popt_cache_dir,
  201. N_("In server mode, specify the cache directory used to avoid useless downloads."), NULL},
  202. {"drag-mode", 'g', 0, G_OPTION_ARG_STRING, &popt_drag_mode,
  203. N_("Global drag and drop mode: normal, 2clicks, both. Default mode is normal."), NULL},
  204. {"nolockcheck", '\0', 0, G_OPTION_ARG_NONE, &popt_nolockcheck,
  205. N_("Do not avoid the execution of multiple instances of GCompris."), NULL},
  206. {"no-zoom",'\0', 0, G_OPTION_ARG_NONE, &popt_no_zoom,
  207. N_("Disable maximization zoom"), NULL},
  208. {"timing-base",'\0', 0, G_OPTION_ARG_DOUBLE, &popt_timing_base,
  209. N_("Increase activities' timeout delays; useful values > 1.0; 1.0 to not change hardcoded value"), NULL},
  210. {"timing-mult",'\0', 0, G_OPTION_ARG_DOUBLE, &popt_timing_mult,
  211. N_("How activities' timeout delays are growing for several actors; useful values < 1.0; 1.0 to not change hardcoded value"), NULL},
  212. {"test",'\0', 0, G_OPTION_ARG_NONE, &popt_test,
  213. N_("For test purpose, run in a loop all the activities"), NULL},
  214. { NULL }
  215. };
  216. /* Remove any dialog box */
  217. static void gc_close_all_dialog() {
  218. gc_dialog_close();
  219. gc_help_stop();
  220. gc_config_stop();
  221. gc_about_stop();
  222. gc_selector_file_stop();
  223. gc_selector_images_stop();
  224. }
  225. /* Return the zoom factor we are currently using for our window
  226. *
  227. */
  228. double gc_zoom_factor_get()
  229. {
  230. return zoom_factor;
  231. }
  232. static gint
  233. _gc_size_allocate_event_callback (GtkWidget *widget,
  234. GtkAllocation *allocation,
  235. gpointer client_data)
  236. {
  237. double xratio, yratio;
  238. double canvas_width, canvas_height;
  239. yratio=allocation->height/(float)(BOARDHEIGHT);
  240. xratio=allocation->width/(float)BOARDWIDTH;
  241. zoom_factor = MIN(xratio, yratio);
  242. g_message("The screen_width=%f screen_height=%f ratio=%f\n",
  243. (double)allocation->width, (double)allocation->height, zoom_factor);
  244. if (!properties->zoom && zoom_factor > 1.)
  245. zoom_factor = 1.;
  246. canvas_width = BOARDWIDTH * zoom_factor;
  247. canvas_height = BOARDHEIGHT * zoom_factor;
  248. gtk_widget_set_size_request(canvas, canvas_width, canvas_height);
  249. goo_canvas_set_scale (GOO_CANVAS(canvas), zoom_factor);
  250. return FALSE;
  251. }
  252. static gint
  253. board_widget_key_press_callback (GtkWidget *widget,
  254. GdkEventKey *event,
  255. gpointer client_data)
  256. {
  257. int kv = event->keyval;
  258. if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_b)
  259. || (event->keyval == GDK_B))) {
  260. if(properties->bar_hidden){
  261. g_message("Restoring the control bar\n");
  262. properties->bar_hidden=FALSE;
  263. gc_bar_hide(properties->bar_hidden);
  264. }
  265. else{
  266. g_message("Hidding the control bar\n");
  267. properties->bar_hidden=TRUE;
  268. gc_bar_hide(properties->bar_hidden);
  269. };
  270. goo_canvas_update(GOO_CANVAS(canvas));
  271. return TRUE;
  272. }
  273. if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_r)
  274. || (event->keyval == GDK_R))) {
  275. g_message("Refreshing the canvas\n");
  276. goo_canvas_update(GOO_CANVAS(canvas));
  277. return TRUE;
  278. }
  279. else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_q)
  280. || (event->keyval == GDK_Q))) {
  281. gc_exit();
  282. return TRUE;
  283. }
  284. else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_w)
  285. || (event->keyval == GDK_W))) {
  286. gc_close_all_dialog();
  287. if (gc_board_get_current()->previous_board != NULL)
  288. gc_board_stop();
  289. return TRUE;
  290. }
  291. else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_f)
  292. || (event->keyval == GDK_F))) {
  293. /* Toggle fullscreen */
  294. if (fullscreen)
  295. gc_fullscreen_set(FALSE);
  296. else
  297. gc_fullscreen_set(TRUE);
  298. return TRUE;
  299. }
  300. else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_m)
  301. || (event->keyval == GDK_M))) {
  302. /* Toggle Mute */
  303. if (mute)
  304. gc_sound_bg_resume();
  305. else
  306. gc_sound_bg_pause();
  307. mute = ! mute;
  308. return TRUE;
  309. }
  310. switch (event->keyval)
  311. {
  312. case GDK_Escape:
  313. gc_close_all_dialog();
  314. if (gc_board_get_current()->previous_board != NULL)
  315. gc_board_stop();
  316. return TRUE;
  317. case GDK_F5:
  318. g_message("Refreshing the canvas\n");
  319. goo_canvas_update(GOO_CANVAS(canvas));
  320. return TRUE;
  321. case GDK_KP_Multiply:
  322. break;
  323. case GDK_KP_0:
  324. case GDK_KP_Insert:
  325. event->keyval=GDK_0;
  326. break;
  327. case GDK_KP_1:
  328. case GDK_KP_End:
  329. event->keyval=GDK_1;
  330. break;
  331. case GDK_KP_2:
  332. case GDK_KP_Down:
  333. event->keyval=GDK_2;
  334. break;
  335. case GDK_KP_3:
  336. case GDK_KP_Page_Down:
  337. event->keyval=GDK_3;
  338. break;
  339. case GDK_KP_4:
  340. case GDK_KP_Left:
  341. event->keyval=GDK_4;
  342. break;
  343. case GDK_KP_5:
  344. case GDK_KP_Begin:
  345. event->keyval=GDK_5;
  346. break;
  347. case GDK_KP_6:
  348. case GDK_KP_Right:
  349. event->keyval=GDK_6;
  350. break;
  351. case GDK_KP_7:
  352. case GDK_KP_Home:
  353. event->keyval=GDK_7;
  354. break;
  355. case GDK_KP_8:
  356. case GDK_KP_Up:
  357. event->keyval=GDK_8;
  358. break;
  359. case GDK_KP_9:
  360. case GDK_KP_Page_Up:
  361. event->keyval=GDK_9;
  362. break;
  363. default:
  364. break;
  365. }
  366. /* pass through the IM context */
  367. if (gc_board_get_current() && (!gc_board_get_current()->disable_im_context))
  368. {
  369. if (gtk_im_context_filter_keypress (properties->context, event))
  370. {
  371. g_message("%d key is handled by context", kv);
  372. return TRUE;
  373. }
  374. }
  375. g_message("%d key is NOT handled by context", kv);
  376. /* If the board needs to receive key pressed */
  377. /* NOTE: If a board receives key press, it must bind the ENTER Keys to OK
  378. * whenever possible
  379. */
  380. if (gc_board_get_current_board_plugin()!=NULL
  381. && gc_board_get_current_board_plugin()->key_press)
  382. {
  383. return(gc_board_get_current_board_plugin()->key_press (event->keyval, NULL, NULL));
  384. }
  385. else if (gc_board_get_current_board_plugin()!=NULL
  386. && gc_board_get_current_board_plugin()->ok &&
  387. (event->keyval == GDK_KP_Enter ||
  388. event->keyval == GDK_Return ||
  389. event->keyval == GDK_KP_Space))
  390. {
  391. /* Else we send the OK signal. */
  392. gc_board_get_current_board_plugin()->ok ();
  393. return TRUE;
  394. }
  395. /* Event not handled; try parent item */
  396. return FALSE;
  397. };
  398. guint gc_cursor_get()
  399. {
  400. return gc_cursor_current;
  401. }
  402. /* Set the cursor to be invisible
  403. * (Useful for touch screens)
  404. */
  405. void gc_cursor_hide()
  406. {
  407. char in_cursor[] = {0x0};
  408. GdkCursor *cursor;
  409. GdkBitmap *bp;
  410. GdkColor color = {0, 0, 0, 0};
  411. bp = gdk_bitmap_create_from_data(NULL , in_cursor , 1, 1);
  412. cursor = gdk_cursor_new_from_pixmap(bp , bp ,
  413. &color , &color ,
  414. 1 , 1);
  415. gdk_window_set_cursor(window->window , cursor);
  416. gdk_cursor_unref(cursor);
  417. }
  418. #if 0
  419. // READY FOR GTK 3
  420. void gc_cursor_hide()
  421. {
  422. GdkCursor *cursor;
  423. cairo_surface_t *s;
  424. GdkPixbuf *pixbuf;
  425. cairo_t *cr;
  426. s = cairo_image_surface_create (CAIRO_FORMAT_A1, 1, 1);
  427. cr = cairo_create (s);
  428. cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
  429. cairo_rectangle(cr, 0, 0, 1, 1);
  430. cairo_fill(cr);
  431. cairo_destroy(cr);
  432. pixbuf = gdk_pixbuf_get_from_surface (s,
  433. 0, 0,
  434. 1, 1);
  435. cairo_surface_destroy (s);
  436. cursor = gdk_cursor_new_from_pixbuf (NULL, pixbuf, 0, 0);
  437. g_object_unref (pixbuf);
  438. gdk_window_set_cursor(window->window , cursor);
  439. gdk_cursor_unref(cursor);
  440. }
  441. #endif
  442. void gc_cursor_set(guint gdk_cursor_type)
  443. {
  444. GdkCursor *cursor = NULL;
  445. if (properties->nocursor)
  446. return;
  447. // Little hack to force gcompris to use the default cursor
  448. if(gdk_cursor_type==GCOMPRIS_DEFAULT_CURSOR)
  449. gdk_cursor_type=properties->defaultcursor;
  450. // I suppose there is less than GCOMPRIS_FIRST_CUSTOM_CURSOR cursors defined in gdkcursors.h !
  451. if (gdk_cursor_type < GCOMPRIS_FIRST_CUSTOM_CURSOR) {
  452. cursor = gdk_cursor_new(gdk_cursor_type);
  453. gdk_window_set_cursor (window->window, cursor);
  454. gdk_cursor_unref(cursor);
  455. } else { // we use a custom cursor
  456. GdkPixbuf *cursor_pixbuf = NULL;
  457. switch (gdk_cursor_type) {
  458. case GCOMPRIS_DEFAULT_CURSOR :
  459. cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/default.png");
  460. break;
  461. case GCOMPRIS_LINE_CURSOR :
  462. cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/line.png");
  463. break;
  464. case GCOMPRIS_RECT_CURSOR :
  465. cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/rect.png");
  466. break;
  467. case GCOMPRIS_FILLRECT_CURSOR :
  468. cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/fillrect.png");
  469. break;
  470. case GCOMPRIS_CIRCLE_CURSOR :
  471. cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/circle.png");
  472. break;
  473. case GCOMPRIS_FILLCIRCLE_CURSOR :
  474. cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/fillcircle.png");
  475. break;
  476. case GCOMPRIS_FILL_CURSOR :
  477. cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/fill.png");
  478. break;
  479. case GCOMPRIS_DEL_CURSOR :
  480. cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/del.png");
  481. break;
  482. case GCOMPRIS_SELECT_CURSOR :
  483. cursor_pixbuf = gc_skin_pixmap_load_or_null("cursors/select.png");
  484. break;
  485. default :
  486. return;
  487. break;
  488. }
  489. if(cursor_pixbuf)
  490. {
  491. cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
  492. cursor_pixbuf, 0, 0);
  493. gdk_window_set_cursor(window->window, cursor);
  494. gdk_cursor_unref(cursor);
  495. #if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
  496. gdk_pixbuf_unref(cursor_pixbuf);
  497. #else
  498. g_object_unref(cursor_pixbuf);
  499. #endif
  500. }
  501. else
  502. {
  503. /* The cursor image was not found, falback to default one */
  504. properties->defaultcursor = GDK_LEFT_PTR;
  505. gc_cursor_set(GCOMPRIS_DEFAULT_CURSOR);
  506. return;
  507. }
  508. }
  509. gc_cursor_current = gdk_cursor_type;
  510. }
  511. /**
  512. * Return the main canvas we run in
  513. */
  514. GooCanvas *gc_get_canvas()
  515. {
  516. return GOO_CANVAS (canvas);
  517. }
  518. GtkWidget *gc_get_window()
  519. {
  520. return window;
  521. }
  522. static void
  523. _set_svg_background(GooCanvasItem *parent, gchar *file, gchar *id)
  524. {
  525. RsvgHandle *rsvg_handle;
  526. rsvg_handle = gc_rsvg_load (file);
  527. if(backgroundsvgimg)
  528. g_object_set(backgroundsvgimg,
  529. "svg-handle", rsvg_handle,
  530. "svg-id", id,
  531. NULL);
  532. else
  533. backgroundsvgimg = goo_canvas_svg_new (parent,
  534. rsvg_handle,
  535. NULL);
  536. goo_canvas_item_lower(backgroundsvgimg, NULL);
  537. g_object_unref(rsvg_handle);
  538. }
  539. static void
  540. _set_pixmap_background(GooCanvasItem *parent, gchar *file)
  541. {
  542. GdkPixbuf *background_pixmap;
  543. background_pixmap = gc_pixmap_load (file);
  544. if(backgroundimg)
  545. g_object_set(backgroundimg,
  546. "pixbuf", background_pixmap,
  547. NULL);
  548. else
  549. backgroundimg = goo_canvas_image_new (parent,
  550. background_pixmap,
  551. 0.0,
  552. 0.0,
  553. "width", (gdouble) BOARDWIDTH,
  554. "height", (gdouble) BOARDHEIGHT,
  555. NULL);
  556. goo_canvas_item_lower(backgroundimg, NULL);
  557. #if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
  558. gdk_pixbuf_unref(background_pixmap);
  559. #else
  560. g_object_unref(background_pixmap);
  561. #endif
  562. }
  563. void _clear_svg_background()
  564. {
  565. if(backgroundsvgimg)
  566. goo_canvas_item_remove(backgroundsvgimg);
  567. backgroundsvgimg = NULL;
  568. }
  569. void _clear_pixmap_background()
  570. {
  571. if(backgroundimg)
  572. goo_canvas_item_remove(backgroundimg);
  573. backgroundimg = NULL;
  574. }
  575. void
  576. gc_set_background(GooCanvasItem *parent, gchar *file)
  577. {
  578. g_assert(parent);
  579. g_assert(file);
  580. if(g_str_has_suffix(file, ".svg") ||
  581. g_str_has_suffix(file, ".svgz"))
  582. {
  583. if(backgroundimg)
  584. goo_canvas_item_remove(backgroundimg);
  585. backgroundimg = NULL;
  586. _set_svg_background(parent, file, NULL);
  587. }
  588. else
  589. {
  590. _clear_svg_background();
  591. _set_pixmap_background(parent, file);
  592. }
  593. }
  594. void
  595. gc_set_background_by_id(GooCanvasItem *parent, RsvgHandle *rsvg_handle,
  596. gchar *id)
  597. {
  598. g_assert(parent);
  599. g_assert(rsvg_handle);
  600. _clear_pixmap_background();
  601. if(backgroundsvgimg)
  602. g_object_set(backgroundsvgimg,
  603. "svg-handle", rsvg_handle,
  604. "svg-id", id,
  605. NULL);
  606. else
  607. backgroundsvgimg = goo_canvas_svg_new (parent,
  608. rsvg_handle,
  609. "svg-id", id,
  610. NULL);
  611. goo_canvas_item_lower(backgroundsvgimg, NULL);
  612. }
  613. void
  614. gc_set_default_background(GooCanvasItem *parent)
  615. {
  616. gc_set_background_by_id(parent,
  617. gc_skin_rsvg_get(),
  618. "#BACKGROUND");
  619. }
  620. static void
  621. init_workspace()
  622. {
  623. workspace = gtk_vbox_new(FALSE, 0);
  624. gtk_container_add(GTK_CONTAINER(window), workspace);
  625. GtkWidget *background = gtk_event_box_new();
  626. GdkColor black = {0, 0, 0, 0};
  627. gtk_widget_modify_bg(background, GTK_STATE_NORMAL, &black);
  628. gtk_box_pack_end(GTK_BOX(workspace), background, TRUE, TRUE, 0);
  629. alignment = gtk_alignment_new(.5, .5, 0., 0.);
  630. g_signal_connect (GTK_OBJECT (alignment), "size-allocate",
  631. G_CALLBACK (_gc_size_allocate_event_callback), NULL);
  632. gtk_container_add(GTK_CONTAINER(background), alignment);
  633. gtk_container_add(GTK_CONTAINER(alignment), canvas);
  634. gtk_widget_set_size_request (GTK_WIDGET(canvas), BOARDWIDTH, BOARDHEIGHT);
  635. goo_canvas_set_bounds (GOO_CANVAS(canvas),
  636. 0, 0,
  637. BOARDWIDTH,
  638. BOARDHEIGHT);
  639. g_object_set (G_OBJECT(canvas), "background-color", "#000", NULL);
  640. #ifdef MAC_INTEGRATION
  641. GtkWidget *quit_item;
  642. quit_item = gtk_menu_item_new();
  643. ige_mac_menu_set_quit_menu_item(GTK_MENU_ITEM (quit_item));
  644. g_signal_connect(GTK_OBJECT (quit_item),
  645. "activate", G_CALLBACK (quit_cb), NULL);
  646. gtk_widget_show (quit_item);
  647. #endif
  648. }
  649. static GcomprisBoard *get_board_to_start()
  650. {
  651. GcomprisBoard *board_to_start;
  652. /* By default, the menu will be started */
  653. board_to_start = properties->menu_board;
  654. /* Get and Run the root menu */
  655. if(properties->administration)
  656. {
  657. board_to_start = gc_menu_section_get("/administration/administration");
  658. }
  659. else
  660. {
  661. /* If we have a profile defined, run the login screen
  662. * (the login screen is a board that uppon login completion
  663. * starts the menu)
  664. */
  665. if(properties->profile && properties->profile->group_ids)
  666. {
  667. gboolean found = FALSE;
  668. GList *group_id;
  669. for (group_id = properties->profile->group_ids; group_id != NULL; group_id = group_id->next)
  670. if (g_list_length(gc_db_users_from_group_get( *((int *) group_id->data))) > 0){
  671. found = TRUE;
  672. break;
  673. }
  674. /* No profile start normally */
  675. if (found)
  676. board_to_start = gc_menu_section_get("/login/login");
  677. else {
  678. board_to_start = gc_menu_section_get(properties->root_menu);
  679. /* this will set user information to system one */
  680. gc_profile_set_current_user(NULL);
  681. }
  682. }
  683. else
  684. /* this will set user information to system one */
  685. gc_profile_set_current_user(NULL);
  686. }
  687. return board_to_start;
  688. }
  689. static void setup_window ()
  690. {
  691. window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  692. /*
  693. * Set an icon for gcompris
  694. * ------------------------
  695. */
  696. {
  697. GdkPixbuf *icon_pixbuf = NULL;
  698. gchar *iconfile = gc_file_find_absolute("%s/%s",
  699. "/usr/share/icons/hicolor/48x48/apps", "gcompris.png",
  700. NULL);
  701. if(iconfile)
  702. {
  703. icon_pixbuf = gdk_pixbuf_new_from_file(iconfile, NULL);
  704. g_free(iconfile);
  705. if (icon_pixbuf)
  706. {
  707. gtk_window_set_icon (GTK_WINDOW (window), icon_pixbuf);
  708. #if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
  709. gdk_pixbuf_unref (icon_pixbuf);
  710. #else
  711. g_object_unref (icon_pixbuf);
  712. #endif
  713. }
  714. }
  715. else
  716. g_message ("Failed to find icon file: 'gcompris.png'");
  717. }
  718. gtk_window_set_title(GTK_WINDOW (window), "GCompris");
  719. /*
  720. * Set the main window
  721. * -------------------
  722. */
  723. gtk_window_set_default_size(GTK_WINDOW(window), BOARDWIDTH, BOARDHEIGHT);
  724. gtk_window_set_wmclass(GTK_WINDOW(window), "gcompris", "GCompris");
  725. gtk_widget_realize (window);
  726. g_signal_connect (GTK_OBJECT (window), "delete_event",
  727. G_CALLBACK (quit_cb), NULL);
  728. g_signal_connect (GTK_OBJECT (window), "map_event",
  729. G_CALLBACK (map_cb), NULL);
  730. // Set the cursor
  731. if (properties->nocursor)
  732. gc_cursor_hide();
  733. else
  734. gc_cursor_set(GCOMPRIS_DEFAULT_CURSOR);
  735. canvas = goo_canvas_new();
  736. g_object_set (canvas,
  737. "has-tooltip", TRUE,
  738. NULL);
  739. g_object_set (G_OBJECT(goo_canvas_get_root_item(GOO_CANVAS(canvas))),
  740. "can-focus", TRUE,
  741. NULL);
  742. g_signal_connect_after (GTK_OBJECT (window), "key_press_event",
  743. (GCallback) board_widget_key_press_callback, 0);
  744. g_signal_connect_after (canvas,
  745. "key_press_event",
  746. (GCallback) board_widget_key_press_callback, 0);
  747. gc_im_init(window);
  748. init_workspace();
  749. #if GDK_PIXBUF_MAJOR >= 2 && GDK_PIXBUF_MINOR >= 18
  750. gtk_widget_set_can_focus(canvas, TRUE);
  751. #endif
  752. gtk_widget_grab_focus (canvas);
  753. }
  754. #ifdef ACTIVATION_CODE
  755. /** Display the activation dialog for the windows version
  756. *
  757. * return TRUE is the dialog is display, FALSE instead.
  758. */
  759. int
  760. display_activation_dialog()
  761. {
  762. int board_count = 0;
  763. int gc_board_number_in_demo = 0;
  764. GList *list;
  765. gint key_is_valid = 0;
  766. key_is_valid = gc_activation_check(properties->key);
  767. if(key_is_valid == 1)
  768. return FALSE;
  769. else if(key_is_valid == 2)
  770. {
  771. g_free ( gc_prop_get()->key );
  772. gc_prop_get()->key = strdup("");
  773. gc_prop_save(properties);
  774. }
  775. /* Count non menu boards */
  776. for (list = gc_menu_get_boards(); list != NULL; list = list->next)
  777. {
  778. GcomprisBoard *board = list->data;
  779. if (strcmp(board->type, "menu") != 0 &&
  780. strncmp(board->section, "/experimental", 13) != 0)
  781. {
  782. board_count++;
  783. if(board->demo)
  784. gc_board_number_in_demo++;
  785. }
  786. }
  787. /* Entry area */
  788. widget_activation_entry = gtk_entry_new();
  789. gtk_entry_set_max_length(GTK_ENTRY(widget_activation_entry), 6);
  790. activation_item = \
  791. goo_canvas_widget_new (goo_canvas_get_root_item(GOO_CANVAS(canvas)),
  792. GTK_WIDGET(widget_activation_entry),
  793. BOARDWIDTH / 2 - 50,
  794. BOARDHEIGHT - 90,
  795. 100.0,
  796. 30.0,
  797. NULL);
  798. g_signal_connect(GTK_OBJECT(widget_activation_entry), "activate",
  799. GTK_SIGNAL_FUNC(activation_enter_callback),
  800. NULL);
  801. char *msg = g_strdup_printf( \
  802. _("GCompris is free software released under the GPL License. "
  803. "In order to support its development, this version "
  804. "provides only %d of the %d activities. You can get the "
  805. "full version for a small fee at\n<http://gcompris.net>\n"
  806. "The GNU/Linux version does not have this restriction. "
  807. "If you also believe that we should teach freedom to children, "
  808. "please consider using GNU/Linux. Get more information at "
  809. "FSF:\n<http://www.fsf.org/philosophy>"),
  810. gc_board_number_in_demo, board_count);
  811. gc_dialog(msg, activation_done);
  812. g_free(msg);
  813. gtk_widget_show(GTK_WIDGET(widget_activation_entry));
  814. gtk_entry_set_text(GTK_ENTRY(widget_activation_entry), "CODE");
  815. gtk_widget_grab_focus(GTK_WIDGET(widget_activation_entry));
  816. return TRUE;
  817. }
  818. /**
  819. * Return -1 if the code is not valid
  820. * 0 if the code is valid but out of date
  821. * 1 if the code is valid and under 2 years
  822. * 2 for the demo code
  823. */
  824. gint gc_activation_check(const char *code)
  825. {
  826. #ifdef DISABLE_ACTIVATION_CODE
  827. return 1;
  828. #else
  829. int value = 0;
  830. int i;
  831. char crc1 = 0;
  832. char crc2 = 0;
  833. char codeddate[5];
  834. if (properties->administration)
  835. return 1;
  836. if(strlen(code) != 6)
  837. return -1;
  838. // A special code to test the full version without
  839. // saving the activation
  840. if (strncmp(code, "121212", 6) == 0)
  841. {
  842. return 2;
  843. }
  844. for(i=3; i>=0; i--)
  845. {
  846. value |= code[i] & 0x07;
  847. value = value << 3;
  848. crc1 = (crc1 ^ code[i]) & 0x07;
  849. }
  850. value = value >> 3;
  851. crc1 = 0x30 | crc1;
  852. crc2 = 0x30 | (code[2] ^ code[3]);
  853. if(crc1 != code[4])
  854. return(-1);
  855. if(crc2 != code[5])
  856. return(-1);
  857. codeddate[3] = 0x30 | (value & 0x000F);
  858. value = value >> 4;
  859. codeddate[2] = 0x30 | (value & 0x0001);
  860. value = value >> 1;
  861. codeddate[1] = 0x30 | (value & 0x000F);
  862. value = value >> 4;
  863. codeddate[0] = 0x30 | (value & 0x0003);
  864. codeddate[4] = '\0';
  865. if(atoi(codeddate) + 200 >= atoi(BUILD_DATE))
  866. return(1);
  867. else
  868. return(0);
  869. #endif
  870. }
  871. /* Check the activation code
  872. *
  873. */
  874. static void
  875. activation_enter_callback( GtkWidget *entry,
  876. GtkWidget *notused )
  877. {
  878. const char *code = gtk_entry_get_text(GTK_ENTRY(entry));
  879. switch(gc_activation_check(code))
  880. {
  881. case 1:
  882. case 2:
  883. gc_prop_get()->key = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
  884. gc_prop_save(properties);
  885. gtk_entry_set_text(GTK_ENTRY(entry), "GOOD");
  886. break;
  887. case 0:
  888. gtk_entry_set_text(GTK_ENTRY(entry), "EXPIRE");
  889. break;
  890. case -1:
  891. gtk_entry_set_text(GTK_ENTRY(entry), "WRONG");
  892. break;
  893. }
  894. }
  895. /* Callback for the activation dialog
  896. *
  897. */
  898. static void
  899. activation_done()
  900. {
  901. if ((strcmp((char *)gtk_entry_get_text(GTK_ENTRY(widget_activation_entry)), "CODE") != 0) &&
  902. (strcmp((char *)gtk_entry_get_text(GTK_ENTRY(widget_activation_entry)), "GOOD") != 0) &&
  903. (strcmp((char *)gtk_entry_get_text(GTK_ENTRY(widget_activation_entry)), "WRONG") != 0))
  904. {
  905. activation_enter_callback(GTK_WIDGET(widget_activation_entry), NULL);
  906. }
  907. gc_board_play( get_board_to_start());
  908. goo_canvas_item_remove (activation_item);
  909. }
  910. #endif
  911. /** Call me to end an activity
  912. *
  913. */
  914. void gc_board_end()
  915. {
  916. if (gc_board_get_current()->previous_board) {
  917. /* Run the previous board */
  918. gc_board_play(gc_board_get_current()->previous_board);
  919. }
  920. }
  921. void _set_geometry_hints(gboolean state)
  922. {
  923. GdkGeometry hints;
  924. hints.base_width = 615;
  925. hints.base_height = 400;
  926. hints.min_width = hints.base_width;
  927. hints.min_height = hints.base_height;
  928. hints.width_inc = 1;
  929. hints.height_inc = 1;
  930. hints.min_aspect = (float)BOARDWIDTH/BOARDHEIGHT;
  931. if (state)
  932. /* Warning: this is a workaround for GTK-OSX */
  933. hints.max_aspect = (float)10; /* Fullscreen case */
  934. else
  935. hints.max_aspect = (float)BOARDWIDTH/BOARDHEIGHT;
  936. gint geom_mask = GDK_HINT_RESIZE_INC |
  937. GDK_HINT_MIN_SIZE |
  938. GDK_HINT_BASE_SIZE;
  939. if (!sugar_detected())
  940. geom_mask |= GDK_HINT_ASPECT;
  941. gtk_window_set_geometry_hints (GTK_WINDOW (window), NULL, &hints, geom_mask);
  942. }
  943. /** \brief toggle full screen mode
  944. *
  945. *
  946. */
  947. void gc_fullscreen_set(gboolean state)
  948. {
  949. static gint window_x = 0;
  950. static gint window_y = 0;
  951. static gint window_w = BOARDWIDTH;
  952. static gint window_h = BOARDHEIGHT;
  953. fullscreen = state;
  954. _set_geometry_hints(state);
  955. if(state)
  956. {
  957. gtk_window_get_position ( (GtkWindow*)( window ), &window_x, &window_y );
  958. gtk_window_get_size ( GTK_WINDOW ( window ), &window_w, &window_h );
  959. #ifdef WIN32
  960. // WARNING: Doing this is required on Windows
  961. // but keep the window hidden on GNU/Linux
  962. gtk_widget_hide ( window );
  963. #elif MAC_INTEGRATION
  964. // WARNING: Doing this is required on MacOSX
  965. // but keep the window hidden on GNU/Linux
  966. gtk_widget_hide ( window );
  967. #else
  968. #endif
  969. gtk_window_set_decorated ( GTK_WINDOW ( window ), FALSE );
  970. gtk_window_set_type_hint ( GTK_WINDOW ( window ),
  971. GDK_WINDOW_TYPE_HINT_DESKTOP );
  972. if (sugar_detected())
  973. gtk_window_maximize ( GTK_WINDOW( window ) );
  974. else
  975. gtk_window_fullscreen ( GTK_WINDOW ( window ) );
  976. gtk_window_move ( GTK_WINDOW ( window ), 0, 0 );
  977. GdkScreen *screen = gtk_window_get_screen ( GTK_WINDOW ( window ) );
  978. gtk_window_resize ( GTK_WINDOW ( window ),
  979. gdk_screen_get_width (screen),
  980. gdk_screen_get_height (screen) );
  981. }
  982. else
  983. {
  984. gtk_widget_hide ( window );
  985. gtk_window_set_type_hint ( GTK_WINDOW ( window ),
  986. GDK_WINDOW_TYPE_HINT_NORMAL );
  987. gtk_window_unfullscreen ( GTK_WINDOW ( window ) );
  988. gtk_window_set_decorated ( GTK_WINDOW ( window ), TRUE );
  989. gtk_window_move ( GTK_WINDOW ( window ), window_x, window_y );
  990. gtk_window_resize ( GTK_WINDOW ( window ), window_w, window_h );
  991. }
  992. gtk_window_present ( GTK_WINDOW ( window ) );
  993. }
  994. /* Use these instead of the goo_canvas ones for proper fullscreen mousegrab
  995. handling. */
  996. int gc_canvas_item_grab (GooCanvasItem *item, unsigned int event_mask,
  997. GdkCursor *cursor, guint32 etime)
  998. {
  999. int retval;
  1000. retval = goo_canvas_pointer_grab(goo_canvas_item_get_canvas(item), item,
  1001. event_mask, cursor, etime);
  1002. if (retval != GDK_GRAB_SUCCESS)
  1003. return retval;
  1004. return retval;
  1005. }
  1006. void gc_canvas_item_ungrab (GooCanvasItem *item, guint32 etime)
  1007. {
  1008. goo_canvas_pointer_ungrab(goo_canvas_item_get_canvas(item), item, etime);
  1009. }
  1010. static void cleanup()
  1011. {
  1012. /* Do not loopback in exit */
  1013. signal(SIGINT, NULL);
  1014. signal(SIGSEGV, NULL);
  1015. single_instance_release(); /* Must be done before property destroy */
  1016. gc_board_stop();
  1017. gc_db_exit();
  1018. sugar_cleanup();
  1019. gc_fullscreen_set(FALSE);
  1020. gc_menu_destroy();
  1021. gc_net_destroy();
  1022. gc_cache_destroy();
  1023. gc_prop_destroy(gc_prop_get());
  1024. }
  1025. void gc_exit()
  1026. {
  1027. g_signal_emit_by_name(G_OBJECT(window), "delete_event");
  1028. }
  1029. static void quit_cb (GtkWidget *widget, gpointer data)
  1030. {
  1031. #ifdef DMALLOC
  1032. dmalloc_shutdown();
  1033. #endif
  1034. cleanup();
  1035. gtk_main_quit();
  1036. gc_sound_close();
  1037. /*
  1038. * Very important or GCompris crashes on exit when closed from the dialog
  1039. * It's like if code in the dialog callback continue after the gtk_main_quit is done
  1040. */
  1041. exit(0);
  1042. }
  1043. /*
  1044. * We want GCompris to be set as fullscreen the later possible
  1045. *
  1046. */
  1047. static void map_cb (GtkWidget *widget, gpointer data)
  1048. {
  1049. if(is_mapped == FALSE)
  1050. {
  1051. is_mapped = TRUE;
  1052. GcomprisBoard *board_to_start;
  1053. if (! gc_skin_load(properties->skin) )
  1054. {
  1055. gc_status_init("");
  1056. gchar *filename = \
  1057. g_strdup_printf("%s/%s/skin.xml",
  1058. properties->package_skin_dir,
  1059. properties->skin);
  1060. gc_status_set_msg(_("Failed to load the skin '%s'"
  1061. " (Check the file exists and is readable)"),
  1062. filename);
  1063. g_free(filename);
  1064. return;
  1065. }
  1066. gc_set_default_background (goo_canvas_get_root_item (GOO_CANVAS(canvas)));
  1067. gc_fullscreen_set(properties->fullscreen);
  1068. gc_bar_start(GTK_CONTAINER(workspace), GOO_CANVAS(canvas));
  1069. gc_board_init();
  1070. if (sugar_delayed_start())
  1071. gc_status_init(_("Retrieving remote data..."));
  1072. else
  1073. {
  1074. gc_status_init("");
  1075. /* Load all the menu once */
  1076. gc_menu_load();
  1077. /* Save the root_menu */
  1078. properties->menu_board = gc_menu_section_get(properties->root_menu);
  1079. gc_status_close();
  1080. board_to_start = get_board_to_start();
  1081. if(!board_to_start) {
  1082. gchar *tmpstr= g_strdup_printf("Couldn't find the board menu %s, or plugin execution error", properties->root_menu);
  1083. gc_dialog(tmpstr, NULL);
  1084. g_free(tmpstr);
  1085. } else if(!gc_board_check_file(board_to_start) || gc_board_is_demo_only(board_to_start) ) {
  1086. gchar *tmpstr= g_strdup_printf("Couldn't find the board menu, or plugin execution error");
  1087. gc_dialog(tmpstr, NULL);
  1088. g_free(tmpstr);
  1089. } else {
  1090. g_message("Fine, we got the gcomprisBoardMenu, xml boards parsing went fine");
  1091. if(!display_activation_dialog())
  1092. gc_board_play(board_to_start);
  1093. }
  1094. }
  1095. }
  1096. g_message("gcompris window is now mapped");
  1097. }
  1098. /*
  1099. * Process the cleanup of the child (no zombies)
  1100. * ---------------------------------------------
  1101. */
  1102. void gc_terminate(int signum)
  1103. {
  1104. g_message("GCompris got the %d signal, starting exit procedure", signum);
  1105. gc_exit();
  1106. }
  1107. static void load_properties ()
  1108. {
  1109. GError *error = NULL;
  1110. properties = gc_prop_new ();
  1111. /* Initialize the binary relocation API
  1112. * http://autopackage.org/docs/binreloc/
  1113. */
  1114. if(gbr_init (&error))
  1115. g_message("Binary relocation enabled");
  1116. else
  1117. {
  1118. if (error->code == GBR_INIT_ERROR_DISABLED)
  1119. g_message("Binary relocation disabled");
  1120. else
  1121. g_message("Binary relocation start failed with error %d", error->code);
  1122. g_error_free (error);
  1123. }
  1124. exec_prefix = gbr_find_exe_dir(NULL);
  1125. g_warning("exec_prefix %s\n", (exec_prefix==NULL ? "NONE" : exec_prefix));
  1126. {
  1127. gchar *pkg_data_dir = gbr_find_data_dir(PACKAGE_DATA_DIR);
  1128. gchar *pkg_clib_dir = gbr_find_lib_dir(PACKAGE_CLIB_DIR);
  1129. properties->package_data_dir = g_strconcat(pkg_data_dir, "/gcompris/boards", NULL);
  1130. properties->package_skin_dir = g_strconcat(pkg_data_dir, "/gcompris/boards/skins", NULL);
  1131. properties->package_locale_dir = gbr_find_locale_dir(PACKAGE_LOCALE_DIR);
  1132. properties->package_plugin_dir = g_strconcat(pkg_clib_dir, "/gcompris", NULL);
  1133. properties->package_python_plugin_dir = g_strconcat(pkg_data_dir, "/gcompris/python",
  1134. NULL);
  1135. properties->system_icon_dir = g_strconcat(pkg_data_dir, "/pixmaps", NULL);
  1136. properties->menu_dir = g_strdup(properties->package_data_dir);
  1137. g_free(pkg_data_dir);
  1138. g_free(pkg_clib_dir);
  1139. }
  1140. /* Display the directory value we have */
  1141. printf("package_data_dir = %s\n", properties->package_data_dir);
  1142. printf("package_skin_dir = %s\n", properties->package_skin_dir);
  1143. printf("package_menu_dir = %s\n", properties->menu_dir);
  1144. printf("package_locale_dir = %s\n", properties->package_locale_dir);
  1145. printf("package_plugin_dir = %s\n", properties->package_plugin_dir);
  1146. printf("package_python_plugin_dir= %s\n", properties->package_python_plugin_dir);
  1147. printf("user_dir = %s\n", properties->user_dir);
  1148. }
  1149. GcomprisProperties *gc_prop_get ()
  1150. {
  1151. return (properties);
  1152. }
  1153. /* Return the database file name
  1154. * Must be called after properties is initialised
  1155. */
  1156. gchar *gc_db_get_filename ()
  1157. {
  1158. g_assert(properties!=NULL);
  1159. return (properties->database);
  1160. }
  1161. /*
  1162. * This returns the locale for which text must be displayed
  1163. *
  1164. */
  1165. const gchar *gc_locale_get()
  1166. {
  1167. const gchar *locale;
  1168. /* First check locale got overrided by the user */
  1169. if(gc_locale != NULL)
  1170. return(gc_locale);
  1171. locale = g_getenv("LC_ALL");
  1172. if(locale == NULL)
  1173. locale = g_getenv("LC_CTYPE");
  1174. if(locale == NULL)
  1175. locale = g_getenv("LANG");
  1176. if(locale!=NULL)
  1177. return(locale);
  1178. return("en_US.UTF-8");
  1179. }
  1180. /*
  1181. * This return the user default locale like it was at program
  1182. * startup before we started changing the locale
  1183. *
  1184. */
  1185. char *gc_locale_get_user_default()
  1186. {
  1187. return gc_user_default_locale;
  1188. }
  1189. /*
  1190. * This set the locale for which text must be displayed
  1191. * If locale is NULL, "" or "NULL" then locale is set to the user's default locale
  1192. * if any or the user's system locale instead.
  1193. *
  1194. */
  1195. void
  1196. gc_locale_set(const gchar *locale)
  1197. {
  1198. if(!locale || locale[0] == '\0' || (g_strcmp0(locale, "NULL") == 0) )
  1199. {
  1200. if ( properties->locale && properties->locale[0] != '\0')
  1201. locale = properties->locale;
  1202. else
  1203. locale = gc_locale_get_user_default();
  1204. }
  1205. g_message("gc_locale_set '%s'\n", locale);
  1206. if(gc_locale != NULL)
  1207. g_free(gc_locale);
  1208. #if defined WIN32
  1209. /* On windows, it always works */
  1210. gc_locale = g_strdup(locale);
  1211. setlocale(LC_MESSAGES, locale);
  1212. setlocale(LC_ALL, locale);
  1213. #else
  1214. gc_locale = g_strdup(setlocale(LC_CTYPE, locale));
  1215. if (!gc_locale)
  1216. gc_locale = g_strdup(locale);
  1217. #endif
  1218. if(gc_locale!=NULL && strcmp(locale, gc_locale))
  1219. g_message("Requested locale '%s' got '%s'", locale, gc_locale);
  1220. if(gc_locale==NULL)
  1221. g_message("Failed to set requested locale %s got %s", locale, gc_locale);
  1222. /* Override the env locale to what the user requested */
  1223. /* This makes gettext to give us the new locale text */
  1224. gc_setenv ("LC_ALL", gc_locale_get());
  1225. gc_setenv ("LC_CTYPE", gc_locale_get());
  1226. gc_setenv ("LANGUAGE", gc_locale_get());
  1227. gc_setenv ("LANG", gc_locale_get());
  1228. /* This does update gettext translation uppon next gettext call */
  1229. /* Call for localization startup */
  1230. bindtextdomain (GETTEXT_PACKAGE, properties->package_locale_dir);
  1231. bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  1232. textdomain (GETTEXT_PACKAGE);
  1233. #ifndef WIN32
  1234. /* Make change known. */
  1235. {
  1236. extern int _nl_msg_cat_cntr;
  1237. ++_nl_msg_cat_cntr;
  1238. }
  1239. #endif
  1240. }
  1241. void gc_log_handler (const gchar *log_domain,
  1242. GLogLevelFlags log_level,
  1243. const gchar *message,
  1244. gpointer user_data) {
  1245. if(gc_debug)
  1246. g_printerr ("%s: %s\n\n", "gcompris", message);
  1247. }
  1248. /* Refresh canvas zoom e.g. after setting zoom setting */
  1249. void
  1250. gc_update_canvas_zoom()
  1251. {
  1252. _gc_size_allocate_event_callback(NULL, &alignment->allocation, NULL);
  1253. }
  1254. static void
  1255. start_bg_music (gchar *file)
  1256. {
  1257. g_message ("start_bg_music %s", file);
  1258. gc_sound_bg_reopen();
  1259. }
  1260. /* Single instance Check */
  1261. static void
  1262. single_instance_release()
  1263. {
  1264. gchar *lock_file = g_strdup_printf("%s/%s", properties->config_dir, GC_LOCK_FILE);
  1265. g_unlink(lock_file);
  1266. g_free(lock_file);
  1267. }
  1268. static void
  1269. single_instance_check()
  1270. {
  1271. gchar *lock_file = g_strdup_printf("%s/%s", properties->config_dir, GC_LOCK_FILE);
  1272. if(!popt_nolockcheck)
  1273. {
  1274. GTimeVal current_time;
  1275. g_get_current_time(&current_time);
  1276. if (g_file_test (lock_file, G_FILE_TEST_EXISTS))
  1277. {
  1278. char *result;
  1279. gsize length;
  1280. glong seconds;
  1281. /* Read it's content */
  1282. g_file_get_contents(lock_file,
  1283. &result,
  1284. &length,
  1285. NULL);
  1286. sscanf(result, "%ld", &seconds);
  1287. if(current_time.tv_sec - seconds < GC_LOCK_LIMIT)
  1288. {
  1289. printf(ngettext("GCompris won't start because the lock file is less than %d second old.\n",
  1290. "GCompris won't start because the lock file is less than %d seconds old.\n",
  1291. GC_LOCK_LIMIT),
  1292. GC_LOCK_LIMIT);
  1293. printf(_("The lock file is: %s\n"),
  1294. lock_file);
  1295. exit(0);
  1296. }
  1297. }
  1298. {
  1299. /* Update the date in it (in seconds) */
  1300. char date_str[64];
  1301. sprintf(date_str, "%ld", current_time.tv_sec);
  1302. g_file_set_contents(lock_file,
  1303. date_str,
  1304. strlen(date_str),
  1305. NULL);
  1306. }
  1307. }
  1308. // Do not free lock_file, the unlink needs it
  1309. }
  1310. gboolean test_board_end(gpointer data);
  1311. gboolean test_board_start(gpointer data) {
  1312. GList *full_list = (GList *) data;
  1313. GcomprisBoard *board = (GcomprisBoard*)full_list->data;
  1314. printf("Testing %s/%s : %s (%s) \n", board->section, board->name, board->title, board->description );
  1315. gc_board_run_next(board);
  1316. g_timeout_add(600, test_board_end, full_list);
  1317. return FALSE;
  1318. }
  1319. gboolean test_board_end(gpointer data) {
  1320. GList *full_list = (GList *) data;
  1321. gc_close_all_dialog();
  1322. gc_board_stop();
  1323. if (full_list->next) {
  1324. full_list = full_list->next;
  1325. g_timeout_add(600, test_board_start, full_list);
  1326. } else {
  1327. printf("TEST DONE\n");
  1328. }
  1329. return FALSE;
  1330. }
  1331. void gc_test() {
  1332. gc_db_init(FALSE /* ENABLE DATABASE */);
  1333. gc_board_init();
  1334. gc_menu_load();
  1335. // Create the list of all the activity in full_list
  1336. GList *full_list = NULL;
  1337. GList *list = NULL;
  1338. GList *menulist = NULL;
  1339. GList *menu_todo = NULL;
  1340. menu_todo = g_list_append(menu_todo,g_strdup("/"));
  1341. while ( menu_todo != NULL) {
  1342. menulist = gc_menu_getlist(menu_todo->data);
  1343. g_free(menu_todo->data);
  1344. menu_todo = menu_todo->next;
  1345. for(list = menulist; list != NULL; list = list->next) {
  1346. GcomprisBoard *board = list->data;
  1347. if (board){
  1348. if (strcmp(board->type,"menu")==0)
  1349. menu_todo = g_list_prepend(menu_todo, g_strdup_printf("%s/%s",board->section, board->name));
  1350. else {
  1351. full_list = g_list_append(full_list, board);
  1352. }
  1353. }
  1354. }
  1355. }
  1356. // Start the test in a few seconds to be sure everything is loaded
  1357. g_timeout_add(5000, test_board_start, full_list);
  1358. }
  1359. /*****************************************
  1360. * Main
  1361. *
  1362. */
  1363. int
  1364. main (int argc, char *argv[])
  1365. {
  1366. GError *error = NULL;
  1367. GOptionContext *context;
  1368. /* First, Remove the gnome crash dialog because it locks the user when in full screen */
  1369. signal(SIGSEGV, gc_terminate);
  1370. signal(SIGINT, gc_terminate);
  1371. load_properties();
  1372. bindtextdomain (GETTEXT_PACKAGE, properties->package_locale_dir);
  1373. bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  1374. textdomain (GETTEXT_PACKAGE);
  1375. /* To have some real random behaviour */
  1376. g_random_set_seed (time (NULL));
  1377. /* Default difficulty filter: non specified */
  1378. popt_difficulty_filter = -1;
  1379. gtk_init (&argc, &argv);
  1380. sugar_setup(&argc, &argv);
  1381. /* Argument parsing */
  1382. context = g_option_context_new("GCompris");
  1383. g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
  1384. g_option_context_add_group (context, gtk_get_option_group (TRUE));
  1385. g_option_context_parse (context, &argc, &argv, &error);
  1386. g_option_context_free(context);
  1387. /* Set the default message handler, it avoids message with option -D */
  1388. g_log_set_handler (NULL, G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL
  1389. | G_LOG_FLAG_RECURSION, gc_log_handler, NULL);
  1390. /*------------------------------------------------------------*/
  1391. if (sugar_jobject_root_menu())
  1392. popt_root_menu = (gchar*)sugar_jobject_root_menu();
  1393. if (popt_debug)
  1394. {
  1395. gc_debug = TRUE;
  1396. }
  1397. if (popt_config_dir)
  1398. {
  1399. if ((!g_file_test(popt_config_dir, G_FILE_TEST_IS_DIR)) ||
  1400. (g_access(popt_config_dir, popt_administration? W_OK : R_OK ) == -1))
  1401. {
  1402. printf("%s does not exists or is not %s\n", popt_config_dir,
  1403. popt_administration? "writable" : "readable" );
  1404. exit(0);
  1405. }
  1406. else
  1407. {
  1408. g_message("Using %s as config directory.", popt_config_dir);
  1409. g_free(properties->config_dir);
  1410. properties->config_dir = g_strdup(popt_config_dir);
  1411. }
  1412. printf("package_config_dir = %s\n", properties->config_dir);
  1413. }
  1414. /* Now we know where our config file is, load the saved config */
  1415. gc_prop_old_config_migration(properties);
  1416. gc_prop_load(properties, GC_PROP_FROM_USER_CONF);
  1417. /* We overwrite the user config with the administrator system config
  1418. It let the sysadmin to specify a certain set of value that are restaured
  1419. whatever the user saved
  1420. */
  1421. gc_prop_load(properties, GC_PROP_FROM_SYSTEM_CONF);
  1422. /* Set the locale */
  1423. #if defined WIN32
  1424. gc_user_default_locale = g_win32_getlocale();
  1425. // Set the user's choice locale
  1426. if(properties->locale[0]=='\0') {
  1427. gc_locale_set(gc_user_default_locale);
  1428. } else {
  1429. gc_locale_set(properties->locale);
  1430. }
  1431. #else
  1432. gc_user_default_locale = g_strdup(setlocale(LC_CTYPE, NULL));
  1433. // Set the user's choice locale
  1434. gc_locale_set(properties->locale);
  1435. #endif
  1436. if (popt_version)
  1437. {
  1438. printf (_("GCompris\nVersion: %s\nLicence: GPL\n"
  1439. "More info at http://gcompris.net\n"),
  1440. VERSION);
  1441. exit (0);
  1442. }
  1443. if (popt_fullscreen)
  1444. {
  1445. properties->fullscreen = TRUE;
  1446. }
  1447. if (popt_window)
  1448. {
  1449. properties->fullscreen = FALSE;
  1450. }
  1451. if (popt_mute)
  1452. {
  1453. g_message("Sound disabled");
  1454. properties->music = FALSE;
  1455. properties->fx = FALSE;
  1456. }
  1457. if (popt_sound)
  1458. {
  1459. g_message("Sound enabled");
  1460. properties->music = TRUE;
  1461. properties->fx = TRUE;
  1462. }
  1463. if (popt_cursor)
  1464. {
  1465. g_message("Default system cursor enabled");
  1466. properties->defaultcursor = GDK_LEFT_PTR;
  1467. }
  1468. if (popt_nocursor)
  1469. {
  1470. g_message("No cursors");
  1471. properties->nocursor = TRUE;
  1472. }
  1473. if (popt_experimental)
  1474. {
  1475. g_message("Experimental boards allowed");
  1476. properties->experimental = TRUE;
  1477. }
  1478. if (popt_no_quit)
  1479. {
  1480. g_message("Disable quit button");
  1481. properties->disable_quit = TRUE;
  1482. }
  1483. if (popt_no_config)
  1484. {
  1485. g_message("Disable config button");
  1486. properties->disable_config = TRUE;
  1487. }
  1488. if (popt_no_level)
  1489. {
  1490. g_message("Disable level button");
  1491. properties->disable_level = TRUE;
  1492. }
  1493. if (popt_no_database)
  1494. {
  1495. g_message("Disable database");
  1496. gc_db_init(TRUE /* DISABLE DATABASE */);
  1497. }
  1498. if (popt_difficulty_filter>=0)
  1499. {
  1500. /* This option provide less capacity than the GUI since we cannot set the filter_style */
  1501. g_message("Display only activities of level %d", popt_difficulty_filter);
  1502. properties->difficulty_filter = popt_difficulty_filter;
  1503. properties->filter_style = GCOMPRIS_FILTER_EQUAL;
  1504. }
  1505. if (popt_package_data_dir) {
  1506. printf("Overloaded package_data_dir = %s\n", popt_package_data_dir);
  1507. g_free(properties->package_data_dir);
  1508. properties->package_data_dir = g_strdup(popt_package_data_dir);
  1509. }
  1510. if (popt_package_skin_dir) {
  1511. printf("Overloaded package_skin_dir = %s\n", popt_package_skin_dir);
  1512. g_free(properties->package_skin_dir);
  1513. properties->package_skin_dir = g_strdup(popt_package_skin_dir);
  1514. }
  1515. if (popt_menu_dir) {
  1516. printf("Overloaded menu_dir = %s\n", popt_menu_dir);
  1517. g_free(properties->menu_dir);
  1518. properties->menu_dir = g_strdup(popt_menu_dir);
  1519. }
  1520. if (popt_plugin_dir) {
  1521. printf("Overloaded package_plugin_dir = %s\n", popt_plugin_dir);
  1522. g_free(properties->package_plugin_dir);
  1523. properties->package_plugin_dir = g_strdup(popt_plugin_dir);
  1524. }
  1525. if (popt_python_plugin_dir) {
  1526. printf("Overloaded package_python_plugin_dir = %s\n", popt_python_plugin_dir);
  1527. g_free(properties->package_python_plugin_dir);
  1528. properties->package_python_plugin_dir = g_strdup(popt_python_plugin_dir);
  1529. }
  1530. if (popt_locale_dir) {
  1531. printf("Overloaded locale_dir = %s\n", popt_locale_dir);
  1532. g_free(properties->package_locale_dir);
  1533. properties->package_locale_dir = g_strdup(popt_local

Large files files are truncated, but you can click here to view the full file