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 /** The dbus application service callback. Usually called by the launcher only. */
45 static gint ossoAppCallback(const gchar *interface, const gchar *method,
46 GArray *arguments, gpointer data, osso_rpc_t *retval)
48 retval->type = DBUS_TYPE_BOOLEAN;
50 if (startupCommand == STARTUP_COMMAND_UNKNOWN) {
51 // Only if we haven't received the startup command yet.
52 printf("Osso: Startup method is: %s\n", method);
54 if (strcmp(method, "game_run") == 0) {
55 startupCommand = STARTUP_COMMAND_RUN;
56 retval->value.b = TRUE;
57 } else if (strcmp(method, "game_continue") == 0) {
58 startupCommand = STARTUP_COMMAND_CONTINUE;
59 retval->value.b = TRUE;
60 } else if (strcmp(method, "game_restart") == 0) {
61 startupCommand = STARTUP_COMMAND_RESTART;
62 retval->value.b = TRUE;
63 } else if (strcmp(method, "game_close") == 0) {
64 // A bit weird, but could happen
65 startupCommand = STARTUP_COMMAND_QUIT;
66 retval->value.b = TRUE;
68 startupCommand = STARTUP_COMMAND_INVALID;
69 retval->value.b = FALSE;
72 if (strcmp(method, "game_close") == 0) {
73 printf("Osso: quitting because of D-Bus close message\n");
74 S9xDoAction(kActionQuit);
75 retval->value.b = TRUE;
77 retval->value.b = FALSE;
84 static gboolean ossoTimeoutCallback(gpointer data)
86 if (startupCommand == STARTUP_COMMAND_UNKNOWN) {
87 // Assume that after N seconds we're not going to get a startup reason.
88 startupCommand = STARTUP_COMMAND_INVALID;
91 return FALSE; // This is a timeout, don't call us ever again.
94 static void ossoHwCallback(osso_hw_state_t *state, gpointer data)
96 if (state->shutdown_ind) {
97 // Shutting down. Try to quit gracefully.
98 S9xDoAction(kActionQuit);
100 if (Config.saver && state->system_inactivity_ind) {
101 // Screen went off, and power saving is active.
102 S9xDoAction(kActionQuit);
106 /** Called from main(), initializes Glib & libosso stuff if needed. */
109 char *dbusLaunch = getenv("DRNOKSNES_DBUS");
111 if (!dbusLaunch || dbusLaunch[0] != 'y') {
112 // Not launched from GUI, so we don't assume GUI features.
118 g_set_prgname("drnoksnes");
119 g_set_application_name("DrNokSnes");
120 mainContext = g_main_context_default();
121 mainLoop = g_main_loop_new(mainContext, FALSE);
122 ossoContext = osso_initialize("com.javispedro.drnoksnes", "1", 0, 0);
125 fprintf(stderr, "Error initializing libosso\n");
129 // At this point, we still don't know what the startup command is
130 startupCommand = STARTUP_COMMAND_UNKNOWN;
133 ret = osso_rpc_set_default_cb_f(ossoContext, ossoAppCallback, 0);
134 g_warn_if_fail(ret == OSSO_OK);
136 osso_hw_state_t hwStateFlags = { FALSE };
137 hwStateFlags.shutdown_ind = TRUE;
138 hwStateFlags.system_inactivity_ind = TRUE;
139 ret = osso_hw_set_event_cb(ossoContext, &hwStateFlags, ossoHwCallback, 0);
140 g_warn_if_fail(ret == OSSO_OK);
142 printf("Osso: Initialized libosso\n");
145 static osso_return_t invokeLauncherMethod(const char *method, osso_rpc_t *retval)
147 // The launcher seems to assume there is at least one parameter,
148 // even if the method doesn't actually require one.
149 return osso_rpc_run(ossoContext, "com.javispedro.drnoksnes.startup",
150 "/com/javispedro/drnoksnes/startup", "com.javispedro.drnoksnes.startup",
151 method, retval, DBUS_TYPE_INVALID);
156 if (!OssoOk()) return;
158 // Send a goodbye message to the launcher
160 osso_rpc_t retval = { 0 };
161 if (Config.snapshotSave) {
162 // If we saved game state, notify launcher to enter "paused" status.
163 ret = invokeLauncherMethod("game_pause", &retval);
165 ret = invokeLauncherMethod("game_close", &retval);
167 if (ret != OSSO_OK) {
168 printf("Osso: failed to notify launcher\n");
170 osso_rpc_free_val(&retval);
172 osso_deinitialize(ossoContext);
173 g_main_loop_unref(mainLoop);
174 g_main_context_unref(mainContext);
179 /** Called after loading the config file, loads settings from gconf. */
182 if (!OssoOk()) return;
184 GConfClient *gcc = gconf_client_get_default();
186 // GUI only allows fullscreen
187 Config.fullscreen = true;
189 // Get ROM filename from Gconf
190 gchar *romFile = gconf_client_get_string(gcc, kGConfRomFile, 0);
191 if (romFile && strlen(romFile) > 0) {
192 S9xSetRomFile(romFile);
194 printf("Exiting gracefully because there's no ROM in Gconf\n");
199 // Read most of the non-player specific settings
200 Config.saver = gconf_client_get_bool(gcc, kGConfSaver, 0);
201 Config.enableAudio = gconf_client_get_bool(gcc, kGConfSound, 0);
202 Settings.TurboMode = gconf_client_get_bool(gcc, kGConfTurboMode, 0);
203 Settings.Transparency = gconf_client_get_bool(gcc, kGConfTransparency, 0);
204 Settings.DisplayFrameRate = gconf_client_get_bool(gcc, kGConfDisplayFramerate, 0);
206 int frameskip = gconf_client_get_int(gcc, kGConfFrameskip, 0);
207 Settings.SkipFrames = (frameskip > 0 ? frameskip : AUTO_FRAMERATE);
209 gchar *scaler = gconf_client_get_string(gcc, kGConfScaler, 0);
210 if (scaler && strlen(scaler) > 0) {
212 Config.scaler = strdup(scaler);
216 int speedhacks = gconf_client_get_int(gcc, kGConfSpeedhacks, 0);
217 if (speedhacks <= 0) {
218 Settings.HacksEnabled = FALSE;
219 Settings.HacksFilter = FALSE;
220 } else if (speedhacks == 1) {
221 Settings.HacksEnabled = TRUE;
222 Settings.HacksFilter = TRUE;
224 Settings.HacksEnabled = TRUE;
225 Settings.HacksFilter = FALSE;
228 if (Settings.HacksEnabled && !Config.hacksFile) {
229 // Provide a default speedhacks file
230 gchar *romDir = g_path_get_dirname(romFile);
231 if (asprintf(&Config.hacksFile, "%s/snesadvance.dat", romDir)
233 Config.hacksFile = 0; // malloc error.
240 // Read player 1 controls
241 gchar key[kGConfPlayerPathBufferLen];
242 gchar *relKey = key + sprintf(key, kGConfPlayerPath, 1);
245 strcpy(relKey, kGConfPlayerKeyboardEnable);
246 if (gconf_client_get_bool(gcc, key, NULL)) {
247 Config.joypad1Enabled = true;
248 loadPlayer1Keymap(gcc);
250 // We allow controls to be enabled from the command line
255 strcpy(relKey, kGConfPlayerTouchscreenEnable);
256 if (gconf_client_get_bool(gcc, key, NULL)) {
257 Config.touchscreenInput = 1;
259 strcpy(relKey, kGConfPlayerTouchscreenShow);
260 if (gconf_client_get_bool(gcc, key, NULL)) {
261 Config.touchscreenShow = true;
265 // Read player 2 controls
266 relKey = key + sprintf(key, kGConfPlayerPath, 2);
269 strcpy(relKey, kGConfPlayerKeyboardEnable);
270 if (gconf_client_get_bool(gcc, key, NULL)) {
271 Config.joypad2Enabled = true;
272 loadPlayer2Keymap(gcc);
276 strcpy(relKey, kGConfPlayerTouchscreenEnable);
277 if (gconf_client_get_bool(gcc, key, NULL)) {
278 Config.touchscreenInput = 2;
280 strcpy(relKey, kGConfPlayerTouchscreenShow);
281 if (gconf_client_get_bool(gcc, key, NULL)) {
282 Config.touchscreenShow = true;
286 // Time to read the startup command from D-Bus
288 // Timeout after 3 seconds, and assume we didn't receive any.
289 guint timeout = g_timeout_add_seconds(3, ossoTimeoutCallback, 0);
290 g_warn_if_fail(timeout > 0);
292 // Iterate the event loop since we want to catch the initial dbus messages
293 while (startupCommand == STARTUP_COMMAND_UNKNOWN) {
294 // This is not busylooping since we are blocking here
295 g_main_context_iteration(mainContext, TRUE);
298 // The command we received from the launcher will tell us if we have to
299 // load a snapshot file.
300 switch (startupCommand) {
301 case STARTUP_COMMAND_RUN:
302 case STARTUP_COMMAND_RESTART:
303 Config.snapshotLoad = false;
304 Config.snapshotSave = true;
306 case STARTUP_COMMAND_CONTINUE:
307 Config.snapshotLoad = true;
308 Config.snapshotSave = true;
310 case STARTUP_COMMAND_QUIT:
311 Config.snapshotLoad = false;
312 Config.snapshotSave = false;
313 Config.quitting = true;
316 Config.snapshotLoad = false;
317 Config.snapshotSave = false;
321 g_object_unref(G_OBJECT(gcc));
324 /** This is called periodically from the main loop.
325 Iterates the GLib loop to get D-Bus events.
327 void OssoPollEvents()
329 if (!OssoOk()) return;
331 g_main_context_iteration(mainContext, FALSE);
334 typedef struct ButtonEntry {
335 const char * gconf_key;
340 /** This arrays contains generic info about each of the mappable buttons the
342 static const ButtonEntry buttons[] = {
344 #define P(x) SNES_##x##_MASK
345 #define A(x) kAction##x
346 #define BUTTON(description, slug, actions, d, f) \
347 { G_STRINGIFY(slug), actions, false },
348 #define ACTION(description, slug, actions, d, f) \
349 { G_STRINGIFY(slug), actions, true },
352 #include "../gui/buttons.inc"
361 /** This loads a keymap for player 1 that will allow him to exit the app. */
362 static void loadSafeKeymap()
364 // Map quit to fullscreen, escape and task switch.
365 Config.action[72] = kActionQuit;
366 Config.action[9] = kActionQuit;
367 Config.action[71] = kActionQuit;
370 static void loadPlayer1Keymap(GConfClient* gcc)
372 // Build player 1 keyboard gconf key relative path
373 gchar key[kGConfPlayerPathBufferLen];
374 gchar *relKey = key + sprintf(key,
375 kGConfPlayerPath kGConfPlayerKeyboardPath "/", 1);
377 // If the user does not map fullscreen or quit
378 bool quit_mapped = false;
380 printf("Hgw: Using gconf key mappings\n");
381 // Thus ignoring config file key mappings
382 ZeroMemory(Config.joypad1Mapping, sizeof(Config.joypad1Mapping));
383 ZeroMemory(Config.action, sizeof(Config.action));
386 for (i = 0; buttons[i].gconf_key; i++) {
387 strcpy(relKey, buttons[i].gconf_key);
388 scancode = gconf_client_get_int(gcc, key, NULL);
390 if (scancode <= 0 || scancode > 255) continue;
392 if (buttons[i].is_action) {
393 Config.action[scancode] |= buttons[i].mask;
394 if (buttons[i].mask & (kActionQuit | kActionToggleFullscreen)) {
398 Config.joypad1Mapping[scancode] |= buttons[i].mask;
402 #if MAEMO && !CONF_EXIT_BUTTON
405 // Newbie user won't know how to quit game.
406 if (!Config.joypad1Mapping[72] && !Config.action[72]) {
407 // Fullscreen key is not mapped, map
408 Config.action[72] = kActionQuit;
411 if (!quit_mapped && !Config.joypad1Mapping[9] && !Config.action[9]) {
412 // Escape key is not mapped, map
413 // But only if we couldn't map quit to fullscreen. Some people
414 // actually want Quit not to be mapped.
415 Config.action[9] = kActionQuit;
419 // Force mapping of fullscreen to Quit if can't map anywhere else.
420 Config.joypad1Mapping[72] = 0;
421 Config.action[72] = kActionQuit;
425 // If task switch key is not mapped, map it to Quit by default.
426 if (!Config.action[71] && !Config.joypad1Mapping[71]) {
427 Config.action[71] = kActionQuit;
432 // This version is simpler since we don't need safeguards.
433 static void loadPlayer2Keymap(GConfClient* gcc)
435 // Build player 2 keyboard gconf key relative path
436 gchar key[kGConfPlayerPathBufferLen];
437 gchar *relKey = key + sprintf(key,
438 kGConfPlayerPath kGConfPlayerKeyboardPath "/", 2);
440 // Ignore config file key mappings
441 ZeroMemory(Config.joypad2Mapping, sizeof(Config.joypad2Mapping));
444 for (i = 0; buttons[i].gconf_key; i++) {
445 if (buttons[i].is_action) continue;
447 strcpy(relKey, buttons[i].gconf_key);
448 scancode = gconf_client_get_int(gcc, key, NULL);
450 // Ignore out of range values
451 if (scancode <= 0 || scancode > 255) continue;
453 Config.joypad2Mapping[scancode] |= buttons[i].mask;