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 createActionMappingsOnly();
41 static void parseGConfKeyMappings(GConfClient* gcc);
43 static gint ossoAppCallback(const gchar *interface, const gchar *method,
44 GArray *arguments, gpointer data, osso_rpc_t *retval)
46 retval->type = DBUS_TYPE_BOOLEAN;
48 if (startupCommand == STARTUP_COMMAND_UNKNOWN) {
49 // Only if we haven't received the startup command yet.
50 printf("Osso: Startup method is: %s\n", method);
52 if (strcmp(method, "game_run") == 0) {
53 startupCommand = STARTUP_COMMAND_RUN;
54 retval->value.b = TRUE;
55 } else if (strcmp(method, "game_continue") == 0) {
56 startupCommand = STARTUP_COMMAND_CONTINUE;
57 retval->value.b = TRUE;
58 } else if (strcmp(method, "game_restart") == 0) {
59 startupCommand = STARTUP_COMMAND_RESTART;
60 retval->value.b = TRUE;
61 } else if (strcmp(method, "game_close") == 0) {
62 // A bit weird, but could happen
63 startupCommand = STARTUP_COMMAND_QUIT;
64 retval->value.b = TRUE;
66 startupCommand = STARTUP_COMMAND_INVALID;
67 retval->value.b = FALSE;
70 if (strcmp(method, "game_close") == 0) {
71 printf("Osso: quitting because of D-Bus close message\n");
72 S9xDoAction(kActionQuit);
73 retval->value.b = TRUE;
75 retval->value.b = FALSE;
82 static gboolean ossoTimeoutCallback(gpointer data)
84 if (startupCommand == STARTUP_COMMAND_UNKNOWN) {
85 // Assume that after N seconds we're not going to get a startup reason.
86 startupCommand = STARTUP_COMMAND_INVALID;
89 return FALSE; // This is a timeout, don't call us ever again.
92 static void ossoHwCallback(osso_hw_state_t *state, gpointer data)
94 if (state->shutdown_ind) {
95 // Shutting down. Try to quit gracefully.
96 S9xDoAction(kActionQuit);
98 if (Config.saver && state->system_inactivity_ind) {
99 // Screen went off, and power saving is active.
100 S9xDoAction(kActionQuit);
104 /** Called from main(), initializes Glib & libosso stuff if needed. */
107 char *dbusLaunch = getenv("DRNOKSNES_DBUS");
109 if (!dbusLaunch || dbusLaunch[0] != 'y') {
110 // Not launched from GUI, so we don't assume GUI features.
116 g_set_prgname("drnoksnes");
117 g_set_application_name("DrNokSnes");
118 mainContext = g_main_context_default();
119 mainLoop = g_main_loop_new(mainContext, FALSE);
120 ossoContext = osso_initialize("com.javispedro.drnoksnes", "1", 0, 0);
123 fprintf(stderr, "Error initializing libosso\n");
127 // At this point, we still don't know what the startup command is
128 startupCommand = STARTUP_COMMAND_UNKNOWN;
131 ret = osso_rpc_set_default_cb_f(ossoContext, ossoAppCallback, 0);
132 g_warn_if_fail(ret == OSSO_OK);
134 osso_hw_state_t hwStateFlags = { FALSE };
135 hwStateFlags.shutdown_ind = TRUE;
136 hwStateFlags.system_inactivity_ind = TRUE;
137 ret = osso_hw_set_event_cb(ossoContext, &hwStateFlags, ossoHwCallback, 0);
138 g_warn_if_fail(ret == OSSO_OK);
140 printf("Osso: Initialized libosso\n");
143 static osso_return_t invokeLauncherMethod(const char *method, osso_rpc_t *retval)
145 // The launcher seems to assume there is at least one parameter,
146 // even if the method doesn't actually require one.
147 return osso_rpc_run(ossoContext, "com.javispedro.drnoksnes.startup",
148 "/com/javispedro/drnoksnes/startup", "com.javispedro.drnoksnes.startup",
149 method, retval, DBUS_TYPE_INVALID);
154 if (!OssoOk()) return;
156 // Send a goodbye message to the launcher
158 osso_rpc_t retval = { 0 };
159 if (Config.snapshotSave) {
160 // If we saved game state, notify launcher to enter "paused" status.
161 ret = invokeLauncherMethod("game_pause", &retval);
163 ret = invokeLauncherMethod("game_close", &retval);
165 if (ret != OSSO_OK) {
166 printf("Osso: failed to notify launcher\n");
168 osso_rpc_free_val(&retval);
170 osso_deinitialize(ossoContext);
171 g_main_loop_unref(mainLoop);
172 g_main_context_unref(mainContext);
177 /** Called after loading the config file, loads settings from gconf. */
180 if (!OssoOk()) return;
182 GConfClient *gcc = gconf_client_get_default();
184 // GUI only allows fullscreen
185 Config.fullscreen = true;
187 // Get ROM filename from Gconf
188 gchar *romFile = gconf_client_get_string(gcc, kGConfRomFile, 0);
189 if (romFile && strlen(romFile) > 0) {
190 S9xSetRomFile(romFile);
192 printf("Exiting gracefully because there's no ROM in Gconf\n");
197 // Read most of the non-player specific settings
198 Config.saver = gconf_client_get_bool(gcc, kGConfSaver, 0);
199 Config.enableAudio = gconf_client_get_bool(gcc, kGConfSound, 0);
200 Settings.TurboMode = gconf_client_get_bool(gcc, kGConfTurboMode, 0);
201 Settings.Transparency = gconf_client_get_bool(gcc, kGConfTransparency, 0);
202 Settings.DisplayFrameRate = gconf_client_get_bool(gcc, kGConfDisplayFramerate, 0);
204 int frameskip = gconf_client_get_int(gcc, kGConfFrameskip, 0);
205 Settings.SkipFrames = (frameskip > 0 ? frameskip : AUTO_FRAMERATE);
207 gchar *scaler = gconf_client_get_string(gcc, kGConfScaler, 0);
208 if (scaler && strlen(scaler) > 0) {
210 Config.scaler = strdup(scaler);
214 int speedhacks = gconf_client_get_int(gcc, kGConfSpeedhacks, 0);
215 if (speedhacks <= 0) {
216 Settings.HacksEnabled = FALSE;
217 Settings.HacksFilter = FALSE;
218 } else if (speedhacks == 1) {
219 Settings.HacksEnabled = TRUE;
220 Settings.HacksFilter = TRUE;
222 Settings.HacksEnabled = TRUE;
223 Settings.HacksFilter = FALSE;
226 if (Settings.HacksEnabled && !Config.hacksFile) {
227 // Provide a default speedhacks file
228 gchar *romDir = g_path_get_dirname(romFile);
229 if (asprintf(&Config.hacksFile, "%s/snesadvance.dat", romDir)
231 Config.hacksFile = 0; // malloc error.
238 // Read player 1 controls
239 gchar key[kGConfPlayerPathBufferLen];
240 gchar *relKey = key + sprintf(key, kGConfPlayerPath, 1);
242 strcpy(relKey, kGConfPlayerKeyboardEnable);
243 if (gconf_client_get_bool(gcc, key, NULL)) {
244 parseGConfKeyMappings(gcc);
246 createActionMappingsOnly();
249 // Time to read the startup command from D-Bus
251 // Timeout after 3 seconds, and assume we didn't receive any.
252 guint timeout = g_timeout_add_seconds(3, ossoTimeoutCallback, 0);
253 g_warn_if_fail(timeout > 0);
255 // Iterate the event loop since we want to catch the initial dbus messages
256 while (startupCommand == STARTUP_COMMAND_UNKNOWN) {
257 // This is not busylooping since we are blocking here
258 g_main_context_iteration(mainContext, TRUE);
261 // The command we received from the launcher will tell us if we have to
262 // load a snapshot file.
263 switch (startupCommand) {
264 case STARTUP_COMMAND_RUN:
265 case STARTUP_COMMAND_RESTART:
266 Config.snapshotLoad = false;
267 Config.snapshotSave = true;
269 case STARTUP_COMMAND_CONTINUE:
270 Config.snapshotLoad = true;
271 Config.snapshotSave = true;
273 case STARTUP_COMMAND_QUIT:
274 Config.snapshotLoad = false;
275 Config.snapshotSave = false;
276 Config.quitting = true;
279 Config.snapshotLoad = false;
280 Config.snapshotSave = false;
284 g_object_unref(G_OBJECT(gcc));
287 /** This is called periodically from the main loop.
288 Iterates the GLib loop to get D-Bus events.
290 void OssoPollEvents()
292 if (!OssoOk()) return;
294 g_main_context_iteration(mainContext, FALSE);
297 typedef struct ButtonEntry {
298 const char * gconf_key;
303 /** This arrays contains generic info about each of the mappable buttons the
305 static const ButtonEntry buttons[] = {
307 #define P(x) SNES_##x##_MASK
308 #define A(x) kAction##x
309 #define BUTTON(description, slug, actions, d, f) \
310 { G_STRINGIFY(slug), actions, false },
311 #define ACTION(description, slug, actions, d, f) \
312 { G_STRINGIFY(slug), actions, true },
315 #include "../gui/buttons.inc"
324 static void createActionMappingsOnly()
326 // Map quit to fullscreen, escape and task switch.
327 Config.action[72] = kActionQuit;
328 Config.action[9] = kActionQuit;
329 Config.action[71] = kActionQuit;
332 static void parseGConfKeyMappings(GConfClient* gcc)
334 // Build player 1 keyboard gconf key relative path
335 gchar key[kGConfPlayerPathBufferLen];
336 gchar *relKey = key + sprintf(key,
337 kGConfPlayerPath kGConfPlayerKeyboardPath "/", 1);
339 // If the user does not map fullscreen or quit
340 bool quit_mapped = false;
342 printf("Hgw: Using gconf key mappings\n");
343 // Thus ignoring config file key mappings
344 ZeroMemory(Config.joypad1Mapping, sizeof(Config.joypad1Mapping));
345 ZeroMemory(Config.action, sizeof(Config.action));
348 for (i = 0; buttons[i].gconf_key; i++) {
349 strcpy(relKey, buttons[i].gconf_key);
350 scancode = gconf_client_get_int(gcc, key, NULL);
352 if (scancode <= 0 || scancode > 255) continue;
354 if (buttons[i].is_action) {
355 Config.action[scancode] |= buttons[i].mask;
356 if (buttons[i].mask & (kActionQuit | kActionToggleFullscreen)) {
360 Config.joypad1Mapping[scancode] |= buttons[i].mask;
364 #if MAEMO && !CONF_EXIT_BUTTON
367 // Newbie user won't know how to quit game.
368 if (!Config.joypad1Mapping[72] && !Config.action[72]) {
369 // Fullscreen key is not mapped, map
370 Config.action[72] = kActionQuit;
373 if (!quit_mapped && !Config.joypad1Mapping[9] && !Config.action[9]) {
374 // Escape key is not mapped, map
375 // But only if we couldn't map quit to fullscreen. Some people
376 // actually want Quit not to be mapped.
377 Config.action[9] = kActionQuit;
381 // Force mapping of fullscreen to Quit if can't map anywhere else.
382 Config.joypad1Mapping[72] = 0;
383 Config.action[72] = kActionQuit;
387 // If task switch key is not mapped, map it to Quit by default.
388 if (!Config.action[71] && !Config.joypad1Mapping[71]) {
389 Config.action[71] = kActionQuit;