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 static volatile enum {
19 STARTUP_COMMAND_INVALID = -1,
20 STARTUP_COMMAND_UNKNOWN = 0,
22 STARTUP_COMMAND_CONTINUE,
23 STARTUP_COMMAND_RESTART,
27 static void createActionMappingsOnly();
28 static void parseGConfKeyMappings(GConfClient* gcc);
30 static gint ossoAppCallback(const gchar *interface, const gchar *method,
31 GArray *arguments, gpointer data, osso_rpc_t *retval)
33 retval->type = DBUS_TYPE_BOOLEAN;
35 if (startupCommand == STARTUP_COMMAND_UNKNOWN) {
36 // Only if we haven't received the startup command yet.
37 printf("Osso: Startup method is: %s\n", method);
39 if (strcmp(method, "game_run") == 0) {
40 startupCommand = STARTUP_COMMAND_RUN;
41 retval->value.b = TRUE;
42 } else if (strcmp(method, "game_continue") == 0) {
43 startupCommand = STARTUP_COMMAND_CONTINUE;
44 retval->value.b = TRUE;
45 } else if (strcmp(method, "game_restart") == 0) {
46 startupCommand = STARTUP_COMMAND_RESTART;
47 retval->value.b = TRUE;
48 } else if (strcmp(method, "game_close") == 0) {
49 // A bit weird, but could happen
50 startupCommand = STARTUP_COMMAND_QUIT;
51 retval->value.b = TRUE;
53 startupCommand = STARTUP_COMMAND_INVALID;
54 retval->value.b = FALSE;
57 if (strcmp(method, "game_close") == 0) {
58 printf("Osso: quitting because of D-Bus close message\n");
59 S9xDoAction(kActionQuit);
60 retval->value.b = TRUE;
62 retval->value.b = FALSE;
69 static gboolean ossoTimeoutCallback(gpointer data)
71 if (startupCommand == STARTUP_COMMAND_UNKNOWN) {
72 // Assume that after N seconds we're not going to get a startup reason.
73 startupCommand = STARTUP_COMMAND_INVALID;
76 return FALSE; // This is a timeout, don't call us ever again.
79 static void ossoHwCallback(osso_hw_state_t *state, gpointer data)
81 if (state->shutdown_ind) {
82 // Shutting down. Try to quit gracefully.
83 S9xDoAction(kActionQuit);
85 if (Config.saver && state->system_inactivity_ind) {
86 // Screen went off, and power saving is active.
87 S9xDoAction(kActionQuit);
91 /** Called from main(), initializes Glib & libosso stuff if needed. */
94 char *dbusLaunch = getenv("DRNOKSNES_DBUS");
96 if (!dbusLaunch || dbusLaunch[0] != 'y') {
97 // Not launched from GUI, so we don't assume GUI features.
103 g_set_prgname("drnoksnes");
104 g_set_application_name("DrNokSnes");
105 mainContext = g_main_context_default();
106 mainLoop = g_main_loop_new(mainContext, FALSE);
107 ossoContext = osso_initialize("com.javispedro.drnoksnes", "1", 0, 0);
110 fprintf(stderr, "Error initializing libosso\n");
114 // At this point, we still don't know what the startup command is
115 startupCommand = STARTUP_COMMAND_UNKNOWN;
118 ret = osso_rpc_set_default_cb_f(ossoContext, ossoAppCallback, 0);
119 g_warn_if_fail(ret == OSSO_OK);
121 osso_hw_state_t hwStateFlags = { FALSE };
122 hwStateFlags.shutdown_ind = TRUE;
123 hwStateFlags.system_inactivity_ind = TRUE;
124 ret = osso_hw_set_event_cb(ossoContext, &hwStateFlags, ossoHwCallback, 0);
125 g_warn_if_fail(ret == OSSO_OK);
127 printf("Osso: Initialized libosso\n");
130 static osso_return_t invokeLauncherMethod(const char *method, osso_rpc_t *retval)
132 // The launcher seems to assume there is at least one parameter,
133 // even if the method doesn't actually require one.
134 return osso_rpc_run(ossoContext, "com.javispedro.drnoksnes.startup",
135 "/com/javispedro/drnoksnes/startup", "com.javispedro.drnoksnes.startup",
136 method, retval, DBUS_TYPE_INVALID);
141 if (!OssoOk()) return;
143 // Send a goodbye message to the launcher
145 osso_rpc_t retval = { 0 };
146 if (Config.snapshotSave) {
147 // If we saved game state, notify launcher to enter "paused" status.
148 ret = invokeLauncherMethod("game_pause", &retval);
150 ret = invokeLauncherMethod("game_close", &retval);
152 if (ret != OSSO_OK) {
153 printf("Osso: failed to notify launcher\n");
155 osso_rpc_free_val(&retval);
157 osso_deinitialize(ossoContext);
158 g_main_loop_unref(mainLoop);
159 g_main_context_unref(mainContext);
164 /** Called after loading the config file, loads settings from gconf. */
167 if (!OssoOk()) return;
169 GConfClient *gcc = gconf_client_get_default();
171 // GUI only allows fullscreen
172 Config.fullscreen = true;
174 // Get ROM filename from Gconf
175 gchar *romFile = gconf_client_get_string(gcc, kGConfRomFile, 0);
176 if (romFile && strlen(romFile) > 0) {
177 S9xSetRomFile(romFile);
179 printf("Exiting gracefully because there's no ROM in Gconf\n");
184 // Read most of the non-player specific settings
185 Config.saver = gconf_client_get_bool(gcc, kGConfSaver, 0);
186 Config.enableAudio = gconf_client_get_bool(gcc, kGConfSound, 0);
187 Settings.TurboMode = gconf_client_get_bool(gcc, kGConfTurboMode, 0);
188 Settings.Transparency = gconf_client_get_bool(gcc, kGConfTransparency, 0);
189 Settings.DisplayFrameRate = gconf_client_get_bool(gcc, kGConfDisplayFramerate, 0);
191 int frameskip = gconf_client_get_int(gcc, kGConfFrameskip, 0);
192 Settings.SkipFrames = (frameskip > 0 ? frameskip : AUTO_FRAMERATE);
194 gchar *scaler = gconf_client_get_string(gcc, kGConfScaler, 0);
195 if (scaler && strlen(scaler) > 0) {
197 Config.scaler = strdup(scaler);
201 int speedhacks = gconf_client_get_int(gcc, kGConfSpeedhacks, 0);
202 if (speedhacks <= 0) {
203 Settings.HacksEnabled = FALSE;
204 Settings.HacksFilter = FALSE;
205 } else if (speedhacks == 1) {
206 Settings.HacksEnabled = TRUE;
207 Settings.HacksFilter = TRUE;
209 Settings.HacksEnabled = TRUE;
210 Settings.HacksFilter = FALSE;
213 if (Settings.HacksEnabled && !Config.hacksFile) {
214 // Provide a default speedhacks file
215 gchar *romDir = g_path_get_dirname(romFile);
216 if (asprintf(&Config.hacksFile, "%s/snesadvance.dat", romDir)
218 Config.hacksFile = 0; // malloc error.
225 // Read player 1 controls
226 gchar key[kGConfPlayerPathBufferLen];
227 gchar *relKey = key + sprintf(key, kGConfPlayerPath, 1);
229 strcpy(relKey, kGConfPlayerKeyboardEnable);
230 if (gconf_client_get_bool(gcc, key, NULL)) {
231 parseGConfKeyMappings(gcc);
233 createActionMappingsOnly();
236 // Time to read the startup command from D-Bus
238 // Timeout after 3 seconds, and assume we didn't receive any.
239 guint timeout = g_timeout_add_seconds(3, ossoTimeoutCallback, 0);
240 g_warn_if_fail(timeout > 0);
242 // Iterate the event loop since we want to catch the initial dbus messages
243 while (startupCommand == STARTUP_COMMAND_UNKNOWN) {
244 // This is not busylooping since we are blocking here
245 g_main_context_iteration(mainContext, TRUE);
248 // The command we received from the launcher will tell us if we have to
249 // load a snapshot file.
250 switch (startupCommand) {
251 case STARTUP_COMMAND_RUN:
252 case STARTUP_COMMAND_RESTART:
253 Config.snapshotLoad = false;
254 Config.snapshotSave = true;
256 case STARTUP_COMMAND_CONTINUE:
257 Config.snapshotLoad = true;
258 Config.snapshotSave = true;
260 case STARTUP_COMMAND_QUIT:
261 Config.snapshotLoad = false;
262 Config.snapshotSave = false;
263 Config.quitting = true;
266 Config.snapshotLoad = false;
267 Config.snapshotSave = false;
271 g_object_unref(G_OBJECT(gcc));
274 /** This is called periodically from the main loop.
275 Iterates the GLib loop to get D-Bus events.
277 void OssoPollEvents()
279 if (!OssoOk()) return;
281 g_main_context_iteration(mainContext, FALSE);
284 typedef struct ButtonEntry {
285 const char * gconf_key;
290 /** This arrays contains generic info about each of the mappable buttons the
292 static const ButtonEntry buttons[] = {
294 #define P(x) SNES_##x##_MASK
295 #define A(x) kAction##x
296 #define BUTTON(description, slug, actions, d, f) \
297 { G_STRINGIFY(slug), actions, false },
298 #define ACTION(description, slug, actions, d, f) \
299 { G_STRINGIFY(slug), actions, true },
302 #include "../gui/buttons.inc"
311 static void createActionMappingsOnly()
313 // Map quit to fullscreen, escape and task switch.
314 Config.action[72] = kActionQuit;
315 Config.action[9] = kActionQuit;
316 Config.action[71] = kActionQuit;
319 static void parseGConfKeyMappings(GConfClient* gcc)
321 // Build player 1 keyboard gconf key relative path
322 gchar key[kGConfPlayerPathBufferLen];
323 gchar *relKey = key + sprintf(key,
324 kGConfPlayerPath kGConfPlayerKeyboardPath "/", 1);
326 // If the user does not map fullscreen or quit
327 bool quit_mapped = false;
329 printf("Hgw: Using gconf key mappings\n");
330 // Thus ignoring config file key mappings
331 ZeroMemory(Config.joypad1Mapping, sizeof(Config.joypad1Mapping));
332 ZeroMemory(Config.action, sizeof(Config.action));
335 for (i = 0; buttons[i].gconf_key; i++) {
336 strcpy(relKey, buttons[i].gconf_key);
337 scancode = gconf_client_get_int(gcc, key, NULL);
339 if (scancode <= 0 || scancode > 255) continue;
341 if (buttons[i].is_action) {
342 Config.action[scancode] |= buttons[i].mask;
343 if (buttons[i].mask & (kActionQuit | kActionToggleFullscreen)) {
347 Config.joypad1Mapping[scancode] |= buttons[i].mask;
351 #if MAEMO && !CONF_EXIT_BUTTON
354 // Newbie user won't know how to quit game.
355 if (!Config.joypad1Mapping[72] && !Config.action[72]) {
356 // Fullscreen key is not mapped, map
357 Config.action[72] = kActionQuit;
360 if (!quit_mapped && !Config.joypad1Mapping[9] && !Config.action[9]) {
361 // Escape key is not mapped, map
362 // But only if we couldn't map quit to fullscreen. Some people
363 // actually want Quit not to be mapped.
364 Config.action[9] = kActionQuit;
368 // Force mapping of fullscreen to Quit if can't map anywhere else.
369 Config.joypad1Mapping[72] = 0;
370 Config.action[72] = kActionQuit;
374 // If task switch key is not mapped, map it to Quit by default.
375 if (!Config.action[71] && !Config.joypad1Mapping[71]) {
376 Config.action[71] = kActionQuit;