Merge branch 'master' of https://git.maemo.org/projects/drnoksnes
[drnoksnes] / platform / osso.cpp
1 #include <stdio.h>
2
3 #include "snes9x.h"
4
5 #include <glib.h>
6 #include <libosso.h>
7 #include <gconf/gconf.h>
8 #include <gconf/gconf-client.h>
9
10 #include "platform.h"
11 #include "osso.h"
12 #include "../gui/gconf.h"
13
14 static GMainContext *mainContext;
15 static GMainLoop *mainLoop;
16 osso_context_t *ossoContext;
17
18 static volatile enum {
19         STARTUP_COMMAND_INVALID = -1,
20         STARTUP_COMMAND_UNKNOWN = 0,
21         STARTUP_COMMAND_RUN,
22         STARTUP_COMMAND_CONTINUE,
23         STARTUP_COMMAND_RESTART,
24         STARTUP_COMMAND_QUIT
25 } startupCommand;
26
27 static void createActionMappingsOnly();
28 static void parseGConfKeyMappings(GConfClient* gcc);
29
30 static gint ossoAppCallback(const gchar *interface, const gchar *method,
31   GArray *arguments, gpointer data, osso_rpc_t *retval)
32 {
33         retval->type = DBUS_TYPE_BOOLEAN;
34
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);
38
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;
52                 } else {
53                         startupCommand = STARTUP_COMMAND_INVALID;
54                         retval->value.b = FALSE;
55                 }
56         } else {
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;
61                 } else {
62                         retval->value.b = FALSE;
63                 }
64         }
65
66         return OSSO_OK;
67 }
68
69 /** Called from main(), initializes Glib & libosso stuff if needed. */
70 void OssoInit()
71 {
72         char *dbusLaunch = getenv("DRNOKSNES_DBUS");
73
74         if (!dbusLaunch || dbusLaunch[0] != 'y') {
75                 // Not launched from GUI, so we don't assume GUI features.
76                 ossoContext = 0;
77                 return;
78         }
79
80         g_type_init();
81         mainContext = g_main_context_default();
82         mainLoop = g_main_loop_new(mainContext, FALSE);
83         ossoContext = osso_initialize("com.javispedro.drnoksnes", "1", 0, 0);
84
85         if (!ossoContext) {
86                 fprintf(stderr, "Error initializing libosso\n");
87                 exit(2);
88         }
89
90         // At this point, we still don't know what the startup command is
91         startupCommand = STARTUP_COMMAND_UNKNOWN;
92
93         osso_return_t ret;
94         ret = osso_rpc_set_default_cb_f(ossoContext, ossoAppCallback, 0);
95         g_assert(ret == OSSO_OK);
96
97         printf("Osso: Initialized libosso\n");
98 }
99
100 static osso_return_t invokeLauncherMethod(const char *method, osso_rpc_t *retval)
101 {
102         // The launcher seems to assume there is at least one parameter,
103         // even if the method doesn't actually require one.
104         return osso_rpc_run(ossoContext, "com.javispedro.drnoksnes.startup",
105                 "/com/javispedro/drnoksnes/startup", "com.javispedro.drnoksnes.startup",
106                 method, retval, DBUS_TYPE_INVALID);
107 }
108
109 void OssoDeinit()
110 {
111         if (!OssoOk()) return;
112
113         // Send a goodbye message to the launcher
114         osso_return_t ret;
115         osso_rpc_t retval = { 0 };
116         if (Config.snapshotSave) {
117                 // If we saved game state, notify launcher to enter "paused" status.
118                 ret = invokeLauncherMethod("game_pause", &retval);
119         } else {
120                 ret = invokeLauncherMethod("game_close", &retval);
121         }
122         if (ret != OSSO_OK) {
123                 printf("Osso: failed to notify launcher\n");
124         }
125         osso_rpc_free_val(&retval);
126
127         osso_deinitialize(ossoContext);
128         g_main_loop_unref(mainLoop);
129         g_main_context_unref(mainContext);
130
131         ossoContext = 0;
132 }
133
134 /** Called after loading the config file, loads settings from gconf. */
135 void OssoConfig()
136 {
137         if (!OssoOk()) return;
138
139         GConfClient *gcc = gconf_client_get_default();
140
141         // GUI only allows fullscreen
142         Config.fullscreen = true;
143
144         // Get ROM filename from Gconf
145         gchar *romFile = gconf_client_get_string(gcc, kGConfRomFile, 0);
146         if (romFile && strlen(romFile) > 0) {
147                 S9xSetRomFile(romFile);
148         } else {
149                 printf("Exiting gracefully because there's no ROM in Gconf\n");
150                 OssoDeinit();
151                 exit(0);
152         }
153
154         // Read most of the non-player specific settings
155         Config.enableAudio = gconf_client_get_bool(gcc, kGConfSound, 0);
156         Settings.TurboMode = gconf_client_get_bool(gcc, kGConfTurboMode, 0);
157         Settings.Transparency = gconf_client_get_bool(gcc, kGConfTransparency, 0);
158         Settings.DisplayFrameRate = gconf_client_get_bool(gcc, kGConfDisplayFramerate, 0);
159
160         int frameskip = gconf_client_get_int(gcc, kGConfFrameskip, 0);
161         Settings.SkipFrames = (frameskip > 0 ? frameskip : AUTO_FRAMERATE);
162
163         gchar *scaler = gconf_client_get_string(gcc, kGConfScaler, 0);
164         if (scaler && strlen(scaler) > 0) {
165                 free(Config.scaler);
166                 Config.scaler = strdup(scaler);
167         }
168         g_free(scaler);
169
170         int speedhacks = gconf_client_get_int(gcc, kGConfSpeedhacks, 0);
171         if (speedhacks <= 0) {
172                 Settings.HacksEnabled = FALSE;
173                 Settings.HacksFilter = FALSE;
174         } else if (speedhacks == 1) {
175                 Settings.HacksEnabled = TRUE;
176                 Settings.HacksFilter = TRUE;
177         } else {
178                 Settings.HacksEnabled = TRUE;
179                 Settings.HacksFilter = FALSE;
180         }
181
182         if (Settings.HacksEnabled && !Config.hacksFile) {
183                 // Provide a default speedhacks file
184                 gchar *romDir = g_path_get_dirname(romFile);
185                 if (asprintf(&Config.hacksFile, "%s/snesadvance.dat", romDir)
186                                 < 0) {
187                         Config.hacksFile = 0; // malloc error.
188                 }
189                 g_free(romDir);
190         }
191
192         g_free(romFile);
193
194         gchar key[kGConfPlayerPathBufferLen];
195         gchar *relKey = key + sprintf(key, kGConfPlayerPath, 1);
196
197         strcpy(relKey, kGConfPlayerKeyboardEnable);
198         if (gconf_client_get_bool(gcc, key, NULL)) {
199                 parseGConfKeyMappings(gcc);
200         } else {
201                 createActionMappingsOnly();
202         }
203
204         // Iterate the event loop since we want to catch the initial dbus messages
205         while (startupCommand == STARTUP_COMMAND_UNKNOWN) {
206                 // This is not busylooping since we are blocking here
207                 g_main_context_iteration(mainContext, TRUE);
208         }
209
210         switch (startupCommand) {
211                 case STARTUP_COMMAND_RUN:
212                 case STARTUP_COMMAND_RESTART:
213                         Config.snapshotLoad = false;
214                         Config.snapshotSave = true;
215                         break;
216                 case STARTUP_COMMAND_CONTINUE:
217                         Config.snapshotLoad = true;
218                         Config.snapshotSave = true;
219                         break;
220                 case STARTUP_COMMAND_QUIT:
221                         Config.snapshotLoad = false;
222                         Config.snapshotSave = false;
223                         Config.quitting = true;
224                         break;
225                 default:
226                         Config.snapshotLoad = false;
227                         Config.snapshotSave = false;
228                         break;
229         }
230
231         g_object_unref(G_OBJECT(gcc));
232 }
233
234 void OssoPollEvents()
235 {
236         if (!OssoOk()) return;
237
238         //g_main_context_iteration(mainContext, FALSE);
239 }
240
241 typedef struct ButtonEntry {
242         const char * gconf_key;
243         unsigned long mask;
244         bool is_action;
245 } ButtonEntry;
246
247 /** This arrays contains generic info about each of the mappable buttons the
248         GUI shows */
249 static const ButtonEntry buttons[] = {
250 #define HELP(...)
251 #define P(x) SNES_##x##_MASK
252 #define A(x) kAction##x
253 #define BUTTON(description, slug, actions, d, f) \
254         { G_STRINGIFY(slug), actions, false },
255 #define ACTION(description, slug, actions, d, f) \
256         { G_STRINGIFY(slug), actions, true },
257 #define LAST \
258         { 0 }
259 #include "../gui/buttons.inc"
260 #undef HELP
261 #undef P
262 #undef A
263 #undef BUTTON
264 #undef ACTION
265 #undef LAST
266 };
267
268 static void createActionMappingsOnly()
269 {
270         // Map quit to fullscreen, escape and task switch.
271         Config.action[72] = kActionQuit;
272         Config.action[9] = kActionQuit;
273         Config.action[71] = kActionQuit;
274 }
275
276 static void parseGConfKeyMappings(GConfClient* gcc)
277 {
278         // Build player 1 keyboard gconf key relative path
279         gchar key[kGConfPlayerPathBufferLen];
280         gchar *relKey = key + sprintf(key,
281                 kGConfPlayerPath kGConfPlayerKeyboardPath "/", 1);
282
283         // If the user does not map fullscreen or quit
284         bool quit_mapped = false;
285
286         printf("Hgw: Using gconf key mappings\n");
287         // Thus ignoring config file key mappings
288         ZeroMemory(Config.joypad1Mapping, sizeof(Config.joypad1Mapping));
289         ZeroMemory(Config.action, sizeof(Config.action));
290
291         int i, scancode;
292         for (i = 0; buttons[i].gconf_key; i++) {
293                 strcpy(relKey, buttons[i].gconf_key);
294                 scancode = gconf_client_get_int(gcc, key, NULL);
295
296                 if (scancode <= 0 || scancode > 255) continue;
297
298                 if (buttons[i].is_action) {
299                         Config.action[scancode] |= buttons[i].mask;
300                         if (buttons[i].mask & (kActionQuit | kActionToggleFullscreen)) {
301                                 quit_mapped = true;
302                         }
303                 } else {
304                         Config.joypad1Mapping[scancode] |= buttons[i].mask;
305                 }
306         }
307
308 #if MAEMO && !CONF_EXIT_BUTTON
309         // Safeguards
310         if (!quit_mapped) {
311                 // Newbie user won't know how to quit game.
312                 if (!Config.joypad1Mapping[72] && !Config.action[72]) {
313                         // Fullscreen key is not mapped, map
314                         Config.action[72] = kActionQuit;
315                         quit_mapped = true;
316                 }
317                 if (!quit_mapped && !Config.joypad1Mapping[9] && !Config.action[9]) {
318                         // Escape key is not mapped, map
319                         // But only if we couldn't map quit to fullscreen. Some people
320                         // actually want Quit not to be mapped.
321                         Config.action[9] = kActionQuit;
322                         quit_mapped = true;
323                 }
324                 if (!quit_mapped) {
325                         // Force mapping of fullscreen to Quit if can't map anywhere else.
326                         Config.joypad1Mapping[72] = 0;
327                         Config.action[72] = kActionQuit;
328                 }
329         }
330
331         // If task switch key is not mapped, map it to Quit by default.
332         if (!Config.action[71] && !Config.joypad1Mapping[71]) {
333                 Config.action[71] = kActionQuit;
334         }
335 #endif
336 }
337