1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
|
11
|
|
12
|
|
13
|
|
14
|
|
15
|
|
16
|
|
17
|
|
18
|
|
19
|
|
20
|
#include "runtime.h"
|
21
|
|
22
|
#include <errno.h>
|
23
|
#include <locale.h>
|
24
|
#include <stdlib.h>
|
25
|
#include <string.h>
|
26
|
#include <sys/stat.h>
|
27
|
#include <unistd.h>
|
28
|
|
29
|
#include <new>
|
30
|
|
31
|
#ifdef _WIN32
|
32
|
#include <windows.h>
|
33
|
#endif
|
34
|
#ifdef __APPLE__
|
35
|
#include <mach-o/dyld.h>
|
36
|
#endif
|
37
|
|
38
|
#include <glib.h>
|
39
|
#include <libintl.h>
|
40
|
|
41
|
#include "audstrings.h"
|
42
|
#include "drct.h"
|
43
|
#include "hook.h"
|
44
|
#include "internal.h"
|
45
|
#include "mainloop.h"
|
46
|
#include "playlist-internal.h"
|
47
|
#include "plugins-internal.h"
|
48
|
#include "scanner.h"
|
49
|
|
50
|
#define AUTOSAVE_INTERVAL 300000
|
51
|
|
52
|
#ifdef WORDS_BIGENDIAN
|
53
|
#define UTF16_NATIVE "UTF-16BE"
|
54
|
#else
|
55
|
#define UTF16_NATIVE "UTF-16LE"
|
56
|
#endif
|
57
|
|
58
|
#ifdef S_IRGRP
|
59
|
#define DIRMODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
|
60
|
#else
|
61
|
#define DIRMODE (S_IRWXU)
|
62
|
#endif
|
63
|
|
64
|
size_t misc_bytes_allocated;
|
65
|
|
66
|
static bool headless_mode;
|
67
|
static bool MuteInLieuOfPause;
|
68
|
|
69
|
#if defined(USE_QT) && ! defined(USE_GTK)
|
70
|
static MainloopType mainloop_type = MainloopType::Qt;
|
71
|
#else
|
72
|
static MainloopType mainloop_type = MainloopType::GLib;
|
73
|
#endif
|
74
|
|
75
|
static aud::array<AudPath, String> aud_paths;
|
76
|
|
77
|
EXPORT void aud_set_headless_mode (bool headless)
|
78
|
{
|
79
|
headless_mode = headless;
|
80
|
}
|
81
|
|
82
|
EXPORT bool aud_get_headless_mode ()
|
83
|
{
|
84
|
return headless_mode;
|
85
|
}
|
86
|
|
87
|
EXPORT void aud_set_mainloop_type (MainloopType type)
|
88
|
{
|
89
|
mainloop_type = type;
|
90
|
}
|
91
|
|
92
|
EXPORT MainloopType aud_get_mainloop_type ()
|
93
|
{
|
94
|
return mainloop_type;
|
95
|
}
|
96
|
|
97
|
EXPORT void aud_set_pausemute_mode (bool pausemute_mode)
|
98
|
{
|
99
|
MuteInLieuOfPause = pausemute_mode;
|
100
|
}
|
101
|
|
102
|
EXPORT bool aud_get_pausemute_mode ()
|
103
|
{
|
104
|
return MuteInLieuOfPause;
|
105
|
}
|
106
|
|
107
|
static StringBuf get_path_to_self ()
|
108
|
{
|
109
|
#ifdef HAVE_PROC_SELF_EXE
|
110
|
|
111
|
StringBuf buf (-1);
|
112
|
int len = readlink ("/proc/self/exe", buf, buf.len ());
|
113
|
|
114
|
if (len < 0)
|
115
|
{
|
116
|
AUDERR ("Failed to read /proc/self/exe: %s\n", strerror (errno));
|
117
|
return StringBuf ();
|
118
|
}
|
119
|
|
120
|
if (len == buf.len ())
|
121
|
throw std::bad_alloc ();
|
122
|
|
123
|
buf.resize (len);
|
124
|
return buf;
|
125
|
|
126
|
#elif defined _WIN32
|
127
|
|
128
|
StringBuf buf (-1);
|
129
|
wchar_t * bufw = (wchar_t *) (char *) buf;
|
130
|
int sizew = buf.len () / sizeof (wchar_t);
|
131
|
int lenw = GetModuleFileNameW (nullptr, bufw, sizew);
|
132
|
|
133
|
if (! lenw)
|
134
|
{
|
135
|
AUDERR ("GetModuleFileName failed.\n");
|
136
|
return StringBuf ();
|
137
|
}
|
138
|
|
139
|
if (lenw == sizew)
|
140
|
throw std::bad_alloc ();
|
141
|
|
142
|
buf.resize (lenw * sizeof (wchar_t));
|
143
|
buf.steal (str_convert (buf, buf.len (), UTF16_NATIVE, "UTF-8"));
|
144
|
return buf;
|
145
|
|
146
|
#elif defined __APPLE__
|
147
|
|
148
|
StringBuf buf (-1);
|
149
|
uint32_t size = buf.len ();
|
150
|
|
151
|
if (_NSGetExecutablePath (buf, & size) < 0)
|
152
|
throw std::bad_alloc ();
|
153
|
|
154
|
buf.resize (strlen (buf));
|
155
|
return buf;
|
156
|
|
157
|
#else
|
158
|
|
159
|
return StringBuf ();
|
160
|
|
161
|
#endif
|
162
|
}
|
163
|
|
164
|
static String relocate_path (const char * path, const char * from, const char * to)
|
165
|
{
|
166
|
int oldlen = strlen (from);
|
167
|
int newlen = strlen (to);
|
168
|
|
169
|
if (oldlen && from[oldlen - 1] == G_DIR_SEPARATOR)
|
170
|
oldlen --;
|
171
|
if (newlen && to[newlen - 1] == G_DIR_SEPARATOR)
|
172
|
newlen --;
|
173
|
|
174
|
#ifdef _WIN32
|
175
|
if (strcmp_nocase (path, from, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR))
|
176
|
#else
|
177
|
if (strncmp (path, from, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR))
|
178
|
#endif
|
179
|
return String (path);
|
180
|
|
181
|
return String (str_printf ("%.*s%s", newlen, to, path + oldlen));
|
182
|
}
|
183
|
|
184
|
static void set_default_paths ()
|
185
|
{
|
186
|
aud_paths[AudPath::BinDir] = String (HARDCODE_BINDIR);
|
187
|
aud_paths[AudPath::DataDir] = String (HARDCODE_DATADIR);
|
188
|
aud_paths[AudPath::PluginDir] = String (HARDCODE_PLUGINDIR);
|
189
|
aud_paths[AudPath::LocaleDir] = String (HARDCODE_LOCALEDIR);
|
190
|
aud_paths[AudPath::DesktopFile] = String (HARDCODE_DESKTOPFILE);
|
191
|
aud_paths[AudPath::IconFile] = String (HARDCODE_ICONFILE);
|
192
|
}
|
193
|
|
194
|
static void relocate_all_paths ()
|
195
|
{
|
196
|
StringBuf bindir = filename_normalize (str_copy (HARDCODE_BINDIR));
|
197
|
StringBuf datadir = filename_normalize (str_copy (HARDCODE_DATADIR));
|
198
|
StringBuf plugindir = filename_normalize (str_copy (HARDCODE_PLUGINDIR));
|
199
|
StringBuf localedir = filename_normalize (str_copy (HARDCODE_LOCALEDIR));
|
200
|
StringBuf desktopfile = filename_normalize (str_copy (HARDCODE_DESKTOPFILE));
|
201
|
StringBuf iconfile = filename_normalize (str_copy (HARDCODE_ICONFILE));
|
202
|
|
203
|
StringBuf from = str_copy (bindir);
|
204
|
|
205
|
|
206
|
StringBuf to = get_path_to_self ();
|
207
|
|
208
|
if (! to)
|
209
|
{
|
210
|
set_default_paths ();
|
211
|
return;
|
212
|
}
|
213
|
|
214
|
to.steal (filename_normalize (std::move (to)));
|
215
|
|
216
|
const char * base = last_path_element (to);
|
217
|
|
218
|
if (! base)
|
219
|
{
|
220
|
set_default_paths ();
|
221
|
return;
|
222
|
}
|
223
|
|
224
|
cut_path_element (to, base - to);
|
225
|
|
226
|
|
227
|
|
228
|
const char * a, * b;
|
229
|
while ((a = last_path_element (from)) &&
|
230
|
(b = last_path_element (to)) &&
|
231
|
#ifdef _WIN32
|
232
|
! strcmp_nocase (a, b))
|
233
|
#else
|
234
|
! strcmp (a, b))
|
235
|
#endif
|
236
|
{
|
237
|
cut_path_element (from, a - from);
|
238
|
cut_path_element (to, b - to);
|
239
|
}
|
240
|
|
241
|
|
242
|
aud_paths[AudPath::BinDir] = relocate_path (bindir, from, to);
|
243
|
aud_paths[AudPath::DataDir] = relocate_path (datadir, from, to);
|
244
|
aud_paths[AudPath::PluginDir] = relocate_path (plugindir, from, to);
|
245
|
aud_paths[AudPath::LocaleDir] = relocate_path (localedir, from, to);
|
246
|
aud_paths[AudPath::DesktopFile] = relocate_path (desktopfile, from, to);
|
247
|
aud_paths[AudPath::IconFile] = relocate_path (iconfile, from, to);
|
248
|
}
|
249
|
|
250
|
EXPORT void aud_init_paths ()
|
251
|
{
|
252
|
relocate_all_paths ();
|
253
|
|
254
|
const char * xdg_config_home = g_get_user_config_dir ();
|
255
|
|
256
|
aud_paths[AudPath::UserDir] = String (filename_build ({xdg_config_home, "audacious"}));
|
257
|
aud_paths[AudPath::PlaylistDir] = String (filename_build
|
258
|
({aud_paths[AudPath::UserDir], "playlists"}));
|
259
|
|
260
|
|
261
|
if (g_mkdir_with_parents (aud_paths[AudPath::PlaylistDir], DIRMODE) < 0)
|
262
|
AUDERR ("Failed to create %s: %s\n",
|
263
|
(const char *) aud_paths[AudPath::PlaylistDir], strerror (errno));
|
264
|
|
265
|
#ifdef _WIN32
|
266
|
|
267
|
g_setenv ("HOME", g_get_home_dir (), true);
|
268
|
g_setenv ("XDG_CONFIG_HOME", xdg_config_home, true);
|
269
|
g_setenv ("XDG_DATA_HOME", g_get_user_data_dir (), true);
|
270
|
g_setenv ("XDG_CACHE_HOME", g_get_user_cache_dir (), true);
|
271
|
#endif
|
272
|
}
|
273
|
|
274
|
EXPORT void aud_cleanup_paths ()
|
275
|
{
|
276
|
for (String & path : aud_paths)
|
277
|
path = String ();
|
278
|
}
|
279
|
|
280
|
EXPORT const char * aud_get_path (AudPath id)
|
281
|
{
|
282
|
return aud_paths[id];
|
283
|
}
|
284
|
|
285
|
EXPORT void aud_init_i18n ()
|
286
|
{
|
287
|
const char * localedir = aud_get_path (AudPath::LocaleDir);
|
288
|
|
289
|
setlocale (LC_ALL, "");
|
290
|
bindtextdomain (PACKAGE, localedir);
|
291
|
bind_textdomain_codeset (PACKAGE, "UTF-8");
|
292
|
bindtextdomain (PACKAGE "-plugins", localedir);
|
293
|
bind_textdomain_codeset (PACKAGE "-plugins", "UTF-8");
|
294
|
textdomain (PACKAGE);
|
295
|
}
|
296
|
|
297
|
EXPORT void aud_init ()
|
298
|
{
|
299
|
g_thread_pool_set_max_idle_time (100);
|
300
|
|
301
|
config_load ();
|
302
|
|
303
|
art_init ();
|
304
|
chardet_init ();
|
305
|
eq_init ();
|
306
|
playlist_init ();
|
307
|
|
308
|
start_plugins_one ();
|
309
|
|
310
|
scanner_init ();
|
311
|
playlist_enable_scan (true);
|
312
|
|
313
|
load_playlists ();
|
314
|
}
|
315
|
|
316
|
static void do_autosave (void *)
|
317
|
{
|
318
|
hook_call ("config save", nullptr);
|
319
|
save_playlists (false);
|
320
|
config_save ();
|
321
|
}
|
322
|
|
323
|
EXPORT void aud_run ()
|
324
|
{
|
325
|
start_plugins_two ();
|
326
|
|
327
|
static QueuedFunc autosave;
|
328
|
autosave.start (AUTOSAVE_INTERVAL, do_autosave, nullptr);
|
329
|
|
330
|
|
331
|
interface_run ();
|
332
|
|
333
|
autosave.stop ();
|
334
|
|
335
|
stop_plugins_two ();
|
336
|
}
|
337
|
|
338
|
EXPORT void aud_cleanup ()
|
339
|
{
|
340
|
save_playlists (true);
|
341
|
|
342
|
aud_playlist_play (-1);
|
343
|
playback_stop (true);
|
344
|
|
345
|
adder_cleanup ();
|
346
|
playlist_enable_scan (false);
|
347
|
scanner_cleanup ();
|
348
|
|
349
|
stop_plugins_one ();
|
350
|
|
351
|
art_cleanup ();
|
352
|
chardet_cleanup ();
|
353
|
eq_cleanup ();
|
354
|
playlist_end ();
|
355
|
|
356
|
event_queue_cancel_all ();
|
357
|
hook_cleanup ();
|
358
|
|
359
|
config_save ();
|
360
|
config_cleanup ();
|
361
|
}
|
362
|
|
363
|
EXPORT void aud_leak_check ()
|
364
|
{
|
365
|
string_leak_check ();
|
366
|
|
367
|
if (misc_bytes_allocated)
|
368
|
AUDWARN ("Bytes allocated at exit: %zd\n", misc_bytes_allocated);
|
369
|
}
|