7 #include <gconf/gconf.h>
8 #include <gconf/gconf-client.h>
12 #include "../gui/gconf.h"
14 static GMainContext *mainContext;
15 static GMainLoop *mainLoop;
16 osso_context_t *ossoContext;
18 // Older versions of glib don't have this.
19 #ifndef g_warn_if_fail
20 #define g_warn_if_fail(expr) \
21 if G_UNLIKELY(expr) { \
22 g_warning("Non critical assertion failed at %s:%d \"%s\"", \
23 __FILE__, __LINE__, #expr); \
26 #if ! GLIB_CHECK_VERSION(2,14,0)
27 #define g_timeout_add_seconds(interval, function, data) \
28 g_timeout_add((interval) * 1000, function, data)
31 static volatile enum {
32 STARTUP_COMMAND_INVALID = -1,
33 STARTUP_COMMAND_UNKNOWN = 0,
35 STARTUP_COMMAND_CONTINUE,
36 STARTUP_COMMAND_RESTART,
40 static void loadSafeKeymap();
41 static void loadPlayer1Keymap(GConfClient* gcc);
42 static void loadPlayer2Keymap(GConfClient* gcc);
44 static gint ossoAppCallback(const gchar *interface, const gchar *method,
45 GArray *arguments, gpointer data, osso_rpc_t *retval)
47 retval->type = DBUS_TYPE_BOOLEAN;
49 if (startupCommand == STARTUP_COMMAND_UNKNOWN) {
50 // Only if we haven't received the startup command yet.
51 printf("Osso: Startup method is: %s\n", method);
53 if (strcmp(method, "game_run") == 0) {
54 startupCommand = STARTUP_COMMAND_RUN;
55 retval->value.b = TRUE;
56 } else if (strcmp(method, "game_continue") == 0) {
57 startupCommand = STARTUP_COMMAND_CONTINUE;
58 retval->value.b = TRUE;
59 } else if (strcmp(method, "game_restart") == 0) {
60 startupCommand = STARTUP_COMMAND_RESTART;
61 retval->value.b = TRUE;
62 } else if (strcmp(method, "game_close") == 0) {
63 // A bit weird, but could happen
64 startupCommand = STARTUP_COMMAND_QUIT;
65 retval->value.b = TRUE;
67 startupCommand = STARTUP_COMMAND_INVALID;
68 retval->value.b = FALSE;
71 if (strcmp(method, "game_close") == 0) {
72 printf("Osso: quitting because of D-Bus close message\n");
73 S9xDoAction(kActionQuit);
74 retval->value.b = TRUE;
76 retval->value.b = FALSE;
83 static gboolean ossoTimeoutCallback(gpointer data)
85 if (startupCommand == STARTUP_COMMAND_UNKNOWN) {
86 // Assume that after N seconds we're not going to get a startup reason.
87 startupCommand = STARTUP_COMMAND_INVALID;
90 return FALSE; // This is a timeout, don't call us ever again.
93 static void ossoHwCallback(osso_hw_state_t *state, gpointer data)
95 if (state->shutdown_ind) {
96 // Shutting down. Try to quit gracefully.
97 S9xDoAction(kActionQuit);
99 if (Config.saver && state->system_inactivity_ind) {
100 // Screen went off, and power saving is active.
101 S9xDoAction(kActionQuit);
105 /** Called from main(), initializes Glib & libosso stuff if needed. */
108 char *dbusLaunch = getenv("DRNOKSNES_DBUS");
110 if (!dbusLaunch || dbusLaunch[0] != 'y') {
111 // Not launched from GUI, so we don't assume GUI features.
117 g_set_prgname("drnoksnes");
118 g_set_application_name("DrNokSnes");
119 mainContext = g_main_context_default();
120 mainLoop = g_main_loop_new(mainContext, FALSE);
121 ossoContext = osso_initialize("com.javispedro.drnoksnes", "1", 0, 0);
124 fprintf(stderr, "Error initializing libosso\n");
128 // At this point, we still don't know what the startup command is
129 startupCommand = STARTUP_COMMAND_UNKNOWN;
132 ret = osso_rpc_set_default_cb_f(ossoContext, ossoAppCallback, 0);
133 g_warn_if_fail(ret == OSSO_OK);
135 osso_hw_state_t hwStateFlags = { FALSE };
136 hwStateFlags.shutdown_ind = TRUE;
137 hwStateFlags.system_inactivity_ind = TRUE;
138 ret = osso_hw_set_event_cb(ossoContext, &hwStateFlags, ossoHwCallback, 0);
139 g_warn_if_fail(ret == OSSO_OK);
141 printf("Osso: Initialized libosso\n");
144 static osso_return_t invokeLauncherMethod(const char *method, osso_rpc_t *retval)
146 // The launcher seems to assume there is at least one parameter,
147 // even if the method doesn't actually require one.
148 return osso_rpc_run(ossoContext, "com.javispedro.drnoksnes.startup",
149 "/com/javispedro/drnoksnes/startup", "com.javispedro.drnoksnes.startup",
150 method, retval, DBUS_TYPE_INVALID);
155 if (!OssoOk()) return;
157 // Send a goodbye message to the launcher
159 osso_rpc_t retval = { 0 };
160 if (Config.snapshotSave) {
161 // If we saved game state, notify launcher to enter "paused" status.
162 ret = invokeLauncherMethod("game_pause", &retval);
164 ret = invokeLauncherMethod("game_close", &retval);
166 if (ret != OSSO_OK) {
167 printf("Osso: failed to notify launcher\n");
169 osso_rpc_free_val(&retval);
171 osso_deinitialize(ossoContext);
172 g_main_loop_unref(mainLoop);
173 g_main_context_unref(mainContext);
178 /** Called after loading the config file, loads settings from gconf. */
181 if (!OssoOk()) return;
183 GConfClient *gcc = gconf_client_get_default();
185 // GUI only allows fullscreen
186 Config.fullscreen = true;
188 // Get ROM filename from Gconf
189 gchar *romFile = gconf_client_get_string(gcc, kGConfRomFile, 0);
190 if (romFile && strlen(romFile) > 0) {
191 S9xSetRomFile(romFile);
193 printf("Exiting gracefully because there's no ROM in Gconf\n");
198 // Read most of the non-player specific settings
199 Config.saver = gconf_client_get_bool(gcc, kGConfSaver, 0);
200 Config.enableAudio = gconf_client_get_bool(gcc, kGConfSound, 0);
201 Settings.TurboMode = gconf_client_get_bool(gcc, kGConfTurboMode, 0);
202 Settings.Transparency = gconf_client_get_bool(gcc, kGConfTransparency, 0);
203 Settings.DisplayFrameRate = gconf_client_get_bool(gcc, kGConfDisplayFramerate, 0);
205 int frameskip = gconf_client_get_int(gcc, kGConfFrameskip, 0);
206 Settings.SkipFrames = (frameskip > 0 ? frameskip : AUTO_FRAMERATE);
208 gchar *scaler = gconf_client_get_string(gcc, kGConfScaler, 0);
209 if (scaler && strlen(scaler) > 0) {
211 Config.scaler = strdup(scaler);
215 int speedhacks = gconf_client_get_int(gcc, kGConfSpeedhacks, 0);
216 if (speedhacks <= 0) {
217 Settings.HacksEnabled = FALSE;
218 Settings.HacksFilter = FALSE;
219 } else if (speedhacks == 1) {
220 Settings.HacksEnabled = TRUE;
221 Settings.HacksFilter = TRUE;
223 Settings.HacksEnabled = TRUE;
224 Settings.HacksFilter = FALSE;
227 if (Settings.HacksEnabled && !Config.hacksFile) {
228 // Provide a default speedhacks file
229 gchar *romDir = g_path_get_dirname(romFile);
230 if (asprintf(&Config.hacksFile, "%s/snesadvance.dat", romDir)
232 Config.hacksFile = 0; // malloc error.
239 // Read player 1 controls
240 gchar key[kGConfPlayerPathBufferLen];
241 gchar *relKey = key + sprintf(key, kGConfPlayerPath, 1);
244 strcpy(relKey, kGConfPlayerKeyboardEnable);
245 if (gconf_client_get_bool(gcc, key, NULL)) {
246 Config.joypad1Enabled = true;
247 loadPlayer1Keymap(gcc);
249 // We allow controls to be enabled from the command line
254 strcpy(relKey, kGConfPlayerTouchscreenEnable);
255 if (gconf_client_get_bool(gcc, key, NULL)) {
256 Config.touchscreenInput = 1;
258 strcpy(relKey, kGConfPlayerTouchscreenShow);
259 if (gconf_client_get_bool(gcc, key, NULL)) {
260 Config.touchscreenShow = true;
264 // Read player 2 controls
265 relKey = key + sprintf(key, kGConfPlayerPath, 2);
268 strcpy(relKey, kGConfPlayerKeyboardEnable);
269 if (gconf_client_get_bool(gcc, key, NULL)) {
270 Config.joypad2Enabled = true;
271 loadPlayer2Keymap(gcc);
275 strcpy(relKey, kGConfPlayerTouchscreenEnable);
276 if (gconf_client_get_bool(gcc, key, NULL)) {
277 Config.touchscreenInput = 2;
279 strcpy(relKey, kGConfPlayerTouchscreenShow);
280 if (gconf_client_get_bool(gcc, key, NULL)) {
281 Config.touchscreenShow = true;
285 // Time to read the startup command from D-Bus
287 // Timeout after 3 seconds, and assume we didn't receive any.
288 guint timeout = g_timeout_add_seconds(3, ossoTimeoutCallback, 0);
289 g_warn_if_fail(timeout > 0);
291 // Iterate the event loop since we want to catch the initial dbus messages
292 while (startupCommand == STARTUP_COMMAND_UNKNOWN) {
293 // This is not busylooping since we are blocking here
294 g_main_context_iteration(mainContext, TRUE);
297 // The command we received from the launcher will tell us if we have to
298 // load a snapshot file.
299 switch (startupCommand) {
300 case STARTUP_COMMAND_RUN:
301 case STARTUP_COMMAND_RESTART:
302 Config.snapshotLoad = false;
303 Config.snapshotSave = true;
305 case STARTUP_COMMAND_CONTINUE:
306 Config.snapshotLoad = true;
307 Config.snapshotSave = true;
309 case STARTUP_COMMAND_QUIT:
310 Config.snapshotLoad = false;
311 Config.snapshotSave = false;
312 Config.quitting = true;
315 Config.snapshotLoad = false;
316 Config.snapshotSave = false;
320 g_object_unref(G_OBJECT(gcc));
323 /** This is called periodically from the main loop.
324 Iterates the GLib loop to get D-Bus events.
326 void OssoPollEvents()
328 if (!OssoOk()) return;
330 g_main_context_iteration(mainContext, FALSE);
333 typedef struct ButtonEntry {
334 const char * gconf_key;
339 /** This arrays contains generic info about each of the mappable buttons the
341 static const ButtonEntry buttons[] = {
343 #define P(x) SNES_##x##_MASK
344 #define A(x) kAction##x
345 #define BUTTON(description, slug, actions, d, f) \
346 { G_STRINGIFY(slug), actions, false },
347 #define ACTION(description, slug, actions, d, f) \
348 { G_STRINGIFY(slug), actions, true },
351 #include "../gui/buttons.inc"
360 /** This loads a keymap for player 1 that will allow him to exit the app. */
361 static void loadSafeKeymap()
363 // Map quit to fullscreen, escape and task switch.
364 Config.action[72] = kActionQuit;
365 Config.action[9] = kActionQuit;
366 Config.action[71] = kActionQuit;
369 static void loadPlayer1Keymap(GConfClient* gcc)
371 // Build player 1 keyboard gconf key relative path
372 gchar key[kGConfPlayerPathBufferLen];
373 gchar *relKey = key + sprintf(key,
374 kGConfPlayerPath kGConfPlayerKeyboardPath "/", 1);
376 // If the user does not map fullscreen or quit
377 bool quit_mapped = false;
379 printf("Hgw: Using gconf key mappings\n");
380 // Thus ignoring config file key mappings
381 ZeroMemory(Config.joypad1Mapping, sizeof(Config.joypad1Mapping));
382 ZeroMemory(Config.action, sizeof(Config.action));
385 for (i = 0; buttons[i].gconf_key; i++) {
386 strcpy(relKey, buttons[i].gconf_key);
387 scancode = gconf_client_get_int(gcc, key, NULL);
389 if (scancode <= 0 || scancode > 255) continue;
391 if (buttons[i].is_action) {
392 Config.action[scancode] |= buttons[i].mask;
393 if (buttons[i].mask & (kActionQuit | kActionToggleFullscreen)) {
397 Config.joypad1Mapping[scancode] |= buttons[i].mask;
401 #if MAEMO && !CONF_EXIT_BUTTON
404 // Newbie user won't know how to quit game.
405 if (!Config.joypad1Mapping[72] && !Config.action[72]) {
406 // Fullscreen key is not mapped, map
407 Config.action[72] = kActionQuit;
410 if (!quit_mapped && !Config.joypad1Mapping[9] && !Config.action[9]) {
411 // Escape key is not mapped, map
412 // But only if we couldn't map quit to fullscreen. Some people
413 // actually want Quit not to be mapped.
414 Config.action[9] = kActionQuit;
418 // Force mapping of fullscreen to Quit if can't map anywhere else.
419 Config.joypad1Mapping[72] = 0;
420 Config.action[72] = kActionQuit;
424 // If task switch key is not mapped, map it to Quit by default.
425 if (!Config.action[71] && !Config.joypad1Mapping[71]) {
426 Config.action[71] = kActionQuit;
431 // This version is simpler since we don't need safeguards.
432 static void loadPlayer2Keymap(GConfClient* gcc)
434 // Build player 2 keyboard gconf key relative path
435 gchar key[kGConfPlayerPathBufferLen];
436 gchar *relKey = key + sprintf(key,
437 kGConfPlayerPath kGConfPlayerKeyboardPath "/", 2);
439 // Ignore config file key mappings
440 ZeroMemory(Config.joypad2Mapping, sizeof(Config.joypad2Mapping));
443 for (i = 0; buttons[i].gconf_key; i++) {
444 if (buttons[i].is_action) continue;
446 strcpy(relKey, buttons[i].gconf_key);
447 scancode = gconf_client_get_int(gcc, key, NULL);
449 // Ignore out of range values
450 if (scancode <= 0 || scancode > 255) continue;
452 Config.joypad2Mapping[scancode] |= buttons[i].mask;