1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      New system driver.
12  *
13  *      By Elias Pschernig.
14  *
15  *      Modified by Trent Gamblin.
16  */
17 
18 /* Title: System routines
19  */
20 
21 #include "allegro5/allegro.h"
22 #include "allegro5/internal/aintern.h"
23 #ifdef ALLEGRO_CFG_SHADER_GLSL
24 #include "allegro5/allegro_opengl.h"
25 #include "allegro5/internal/aintern_opengl.h"
26 #endif
27 #include ALLEGRO_INTERNAL_HEADER
28 #include "allegro5/internal/aintern_bitmap.h"
29 #include "allegro5/internal/aintern_debug.h"
30 #include "allegro5/internal/aintern_dtor.h"
31 #include "allegro5/internal/aintern_exitfunc.h"
32 #include "allegro5/internal/aintern_pixels.h"
33 #include "allegro5/internal/aintern_system.h"
34 #include "allegro5/internal/aintern_thread.h"
35 #include "allegro5/internal/aintern_timer.h"
36 #include "allegro5/internal/aintern_tls.h"
37 #include "allegro5/internal/aintern_vector.h"
38 
39 ALLEGRO_DEBUG_CHANNEL("system")
40 
41 static ALLEGRO_SYSTEM *active_sysdrv = NULL;
42 static ALLEGRO_CONFIG *sys_config = NULL;
43 
44 _AL_VECTOR _al_system_interfaces;
45 static _AL_VECTOR _user_system_interfaces = _AL_VECTOR_INITIALIZER(ALLEGRO_SYSTEM_INTERFACE *);
46 
47 _AL_DTOR_LIST *_al_dtor_list = NULL;
48 
49 static bool atexit_virgin = true;
50 
51 static char _al_app_name[256] = "";
52 static char _al_org_name[256] = "";
53 
54 
55 
find_system(_AL_VECTOR * vector)56 static ALLEGRO_SYSTEM *find_system(_AL_VECTOR *vector)
57 {
58    ALLEGRO_SYSTEM_INTERFACE **sptr;
59    ALLEGRO_SYSTEM_INTERFACE *sys_interface;
60    ALLEGRO_SYSTEM *system;
61    unsigned int i;
62 
63    for (i = 0; i < vector->_size; i++) {
64       sptr = _al_vector_ref(vector, i);
65       sys_interface = *sptr;
66       if ((system = sys_interface->initialize(0)) != NULL)
67          return system;
68    }
69 
70    return NULL;
71 }
72 
73 
74 
shutdown_system_driver(void)75 static void shutdown_system_driver(void)
76 {
77    if (active_sysdrv) {
78       if (active_sysdrv->user_exe_path)
79          al_destroy_path(active_sysdrv->user_exe_path);
80       if (active_sysdrv->vt && active_sysdrv->vt->shutdown_system)
81          active_sysdrv->vt->shutdown_system();
82       active_sysdrv = NULL;
83 
84       while (!_al_vector_is_empty(&_al_system_interfaces))
85          _al_vector_delete_at(&_al_system_interfaces, _al_vector_size(&_al_system_interfaces)-1);
86       _al_vector_free(&_al_system_interfaces);
87       _al_vector_init(&_al_system_interfaces, sizeof(ALLEGRO_SYSTEM_INTERFACE *));
88    }
89    al_destroy_config(sys_config);
90    sys_config = NULL;
91 }
92 
93 
94 
95 /* al_get_standard_path() does not work before the system driver is
96  * initialised.  Before that, we need to call the underlying functions
97  * directly.
98  */
early_get_exename_path(void)99 static ALLEGRO_PATH *early_get_exename_path(void)
100 {
101 #if defined(ALLEGRO_WINDOWS)
102    return _al_win_get_path(ALLEGRO_EXENAME_PATH);
103 #elif defined(ALLEGRO_MACOSX)
104    return _al_osx_get_path(ALLEGRO_EXENAME_PATH);
105 #elif defined(ALLEGRO_IPHONE)
106    return _al_iphone_get_path(ALLEGRO_EXENAME_PATH);
107 #elif defined(ALLEGRO_UNIX)
108    return _al_unix_get_path(ALLEGRO_EXENAME_PATH);
109 #elif defined(ALLEGRO_ANDROID)
110    return _al_android_get_path(ALLEGRO_EXENAME_PATH);
111 #elif defined(ALLEGRO_SDL)
112    char* p = SDL_GetBasePath();
113    ALLEGRO_PATH *path = al_create_path_for_directory(p);
114    SDL_free(p);
115    return path;
116 #else
117    #error early_get_exename_path not implemented
118 #endif
119 }
120 
121 
122 
read_allegro_cfg(void)123 static void read_allegro_cfg(void)
124 {
125    /* We assume that the stdio file interface is in effect. */
126 
127    ALLEGRO_PATH *path;
128    ALLEGRO_CONFIG *temp;
129 
130    if (!sys_config)
131       sys_config = al_create_config();
132 
133 #if defined(ALLEGRO_UNIX) && !defined(ALLEGRO_IPHONE)
134    temp = al_load_config_file("/etc/allegro5rc");
135    if (temp) {
136       al_merge_config_into(sys_config, temp);
137       al_destroy_config(temp);
138    }
139 
140    path = _al_unix_get_path(ALLEGRO_USER_HOME_PATH);
141    if (path) {
142       al_set_path_filename(path, "allegro5rc");
143       temp = al_load_config_file(al_path_cstr(path, '/'));
144       if (temp) {
145          al_merge_config_into(sys_config, temp);
146          al_destroy_config(temp);
147       }
148       al_set_path_filename(path, ".allegro5rc");
149       temp = al_load_config_file(al_path_cstr(path, '/'));
150       if (temp) {
151          al_merge_config_into(sys_config, temp);
152          al_destroy_config(temp);
153       }
154       al_destroy_path(path);
155    }
156 #endif
157 
158    path = early_get_exename_path();
159    if (path) {
160       al_set_path_filename(path, "allegro5.cfg");
161       temp = al_load_config_file(al_path_cstr(path, ALLEGRO_NATIVE_PATH_SEP));
162       if (temp) {
163          al_merge_config_into(sys_config, temp);
164          al_destroy_config(temp);
165       }
166       al_destroy_path(path);
167    }
168    /* Reconfigure logging in case something changed. */
169    _al_configure_logging();
170 }
171 
172 
173 
174 /*
175  * Can a binary with version a use a library with version b?
176  *
177  * Let a = xa.ya.za.*
178  * Let b = xb.yb.zb.*
179  *
180  * When a has the unstable bit, a is compatible with b if xa.ya.za = xb.yb.zb.
181  * Otherwise, a is compatible with b if xa.ya = xb.yb and zb >= za.
182  *
183  * Otherwise a and b are incompatible.
184  */
compatible_versions(int a,int b)185 static bool compatible_versions(int a, int b)
186 {
187    int a_unstable = a & _ALLEGRO_UNSTABLE_BIT_SET;
188 
189    int a_major = (a & 0x7f000000) >> 24;
190    int a_sub   = (a & 0x00ff0000) >> 16;
191    int a_wip   = (a & 0x0000ff00) >> 8;
192 
193    int b_major = (b & 0x7f000000) >> 24;
194    int b_sub   = (b & 0x00ff0000) >> 16;
195    int b_wip   = (b & 0x0000ff00) >> 8;
196 
197    if (a_major != b_major) {
198       return false;
199    }
200    if (a_unstable && a_wip != b_wip) {
201       return false;
202    }
203    if (a_sub != b_sub) {
204       return false;
205    }
206    if (a_wip > b_wip) {
207       return false;
208    }
209    return true;
210 }
211 
212 
213 
214 /* Function: al_install_system
215  */
al_install_system(int version,int (* atexit_ptr)(void (*)(void)))216 bool al_install_system(int version, int (*atexit_ptr)(void (*)(void)))
217 {
218    ALLEGRO_SYSTEM bootstrap;
219    ALLEGRO_SYSTEM *real_system;
220    int library_version = al_get_allegro_version();
221 
222    if (active_sysdrv) {
223       return true;
224    }
225 
226    /* Note: We cannot call logging functions yet.
227     * TODO: Maybe we want to do the check after the "bootstrap" system
228     * is available at least?
229     */
230    if (!compatible_versions(version, library_version))
231       return false;
232 
233    _al_tls_init_once();
234    _al_reinitialize_tls_values();
235 
236    _al_vector_init(&_al_system_interfaces, sizeof(ALLEGRO_SYSTEM_INTERFACE *));
237 
238    /* Set up a bootstrap system so the calls expecting it don't freak out */
239    memset(&bootstrap, 0, sizeof(bootstrap));
240    active_sysdrv = &bootstrap;
241    read_allegro_cfg();
242 
243 #ifdef ALLEGRO_BCC32
244    /* This supresses exceptions on floating point divide by zero */
245    _control87(MCW_EM, MCW_EM);
246 #endif
247 
248    /* Register builtin system drivers */
249    _al_register_system_interfaces();
250 
251    /* Check for a user-defined system driver first */
252    real_system = find_system(&_user_system_interfaces);
253 
254    /* If a user-defined driver is not found, look for a builtin one */
255    if (real_system == NULL) {
256       real_system = find_system(&_al_system_interfaces);
257    }
258 
259    if (real_system == NULL) {
260       active_sysdrv = NULL;
261       return false;
262    }
263 
264    active_sysdrv = real_system;
265    active_sysdrv->mouse_wheel_precision = 1;
266 
267    const char *min_bitmap_size = al_get_config_value(
268       al_get_system_config(), "graphics", "min_bitmap_size");
269    active_sysdrv->min_bitmap_size = min_bitmap_size ? atoi(min_bitmap_size) : 16;
270 
271    ALLEGRO_INFO("Allegro version: %s\n", ALLEGRO_VERSION_STR);
272 
273    if (strcmp(al_get_app_name(), "") == 0) {
274       al_set_app_name(NULL);
275    }
276 
277    _al_add_exit_func(shutdown_system_driver, "shutdown_system_driver");
278 
279    _al_dtor_list = _al_init_destructors();
280 
281    _al_init_events();
282 
283    _al_init_iio_table();
284 
285    _al_init_convert_bitmap_list();
286 
287    _al_init_timers();
288 
289 #ifdef ALLEGRO_CFG_SHADER_GLSL
290    _al_glsl_init_shaders();
291 #endif
292 
293    if (active_sysdrv->vt->heartbeat_init)
294       active_sysdrv->vt->heartbeat_init();
295 
296    if (atexit_ptr && atexit_virgin) {
297 #ifndef ALLEGRO_ANDROID
298       atexit_ptr(al_uninstall_system);
299 #endif
300       atexit_virgin = false;
301    }
302 
303    /* Clear errnos set while searching for config files. */
304    al_set_errno(0);
305 
306    active_sysdrv->installed = true;
307 
308    _al_srand(time(NULL));
309 
310    return true;
311 }
312 
313 /* Function: al_uninstall_system
314  */
al_uninstall_system(void)315 void al_uninstall_system(void)
316 {
317    /* Note: al_uninstall_system may get called multiple times without an
318     * al_install_system in between. For example if the user manually
319     * calls it at the end of the program it is called right again
320     * because it's installed as an atexit function by al_init.
321     */
322 
323    _al_run_destructors(_al_dtor_list);
324    _al_run_exit_funcs();
325    _al_shutdown_destructors(_al_dtor_list);
326    _al_dtor_list = NULL;
327 
328 #ifdef ALLEGRO_CFG_SHADER_GLSL
329    _al_glsl_shutdown_shaders();
330 #endif
331 
332    _al_shutdown_logging();
333 
334    /* shutdown_system_driver is registered as an exit func so we don't need
335     * to do any more here.
336     */
337 
338    ASSERT(active_sysdrv == NULL);
339 }
340 
341 
342 
343 /* Function: al_is_system_installed
344  */
al_is_system_installed(void)345 bool al_is_system_installed(void)
346 {
347    return (active_sysdrv && active_sysdrv->installed) ? true : false;
348 }
349 
350 
351 /* Hidden function: al_get_system_driver
352  *  This was exported and documented in 5.0rc1 but probably shouldn't have been
353  *  as ALLEGRO_SYSTEM is not documented.
354  */
al_get_system_driver(void)355 ALLEGRO_SYSTEM *al_get_system_driver(void)
356 {
357    return active_sysdrv;
358 }
359 
360 /* Function: al_get_system_id
361  */
al_get_system_id(void)362 ALLEGRO_SYSTEM_ID al_get_system_id(void)
363 {
364    ASSERT(active_sysdrv);
365    return active_sysdrv->vt->id;
366 }
367 
368 /* Function: al_get_system_config
369  */
al_get_system_config(void)370 ALLEGRO_CONFIG *al_get_system_config(void)
371 {
372    if (!sys_config)
373       sys_config = al_create_config();
374    return sys_config;
375 }
376 
377 
378 /* Function: al_get_standard_path
379  */
al_get_standard_path(int id)380 ALLEGRO_PATH *al_get_standard_path(int id)
381 {
382    ASSERT(active_sysdrv);
383    ASSERT(active_sysdrv->vt);
384    ASSERT(active_sysdrv->vt->get_path);
385 
386    if (id == ALLEGRO_EXENAME_PATH && active_sysdrv->user_exe_path)
387       return al_clone_path(active_sysdrv->user_exe_path);
388 
389    if (id == ALLEGRO_RESOURCES_PATH && active_sysdrv->user_exe_path) {
390       ALLEGRO_PATH *exe_dir = al_clone_path(active_sysdrv->user_exe_path);
391       al_set_path_filename(exe_dir, NULL);
392       return exe_dir;
393    }
394 
395    if (active_sysdrv->vt->get_path)
396       return active_sysdrv->vt->get_path(id);
397 
398    return NULL;
399 }
400 
401 
402 /* Function: al_set_exe_name
403  */
al_set_exe_name(char const * path)404 void al_set_exe_name(char const *path)
405 {
406    ASSERT(active_sysdrv);
407    if (active_sysdrv->user_exe_path) {
408       al_destroy_path(active_sysdrv->user_exe_path);
409    }
410    active_sysdrv->user_exe_path = al_create_path(path);
411 }
412 
413 
414 /* Function: al_set_org_name
415  */
al_set_org_name(const char * org_name)416 void al_set_org_name(const char *org_name)
417 {
418    if (!org_name)
419       org_name = "";
420 
421    _al_sane_strncpy(_al_org_name, org_name, sizeof(_al_org_name));
422 }
423 
424 
425 /* Function: al_set_app_name
426  */
al_set_app_name(const char * app_name)427 void al_set_app_name(const char *app_name)
428 {
429    if (app_name) {
430       _al_sane_strncpy(_al_app_name, app_name, sizeof(_al_app_name));
431    }
432    else {
433       ALLEGRO_PATH *path;
434       path = al_get_standard_path(ALLEGRO_EXENAME_PATH);
435       _al_sane_strncpy(_al_app_name, al_get_path_filename(path), sizeof(_al_app_name));
436       al_destroy_path(path);
437    }
438 }
439 
440 
441 /* Function: al_get_org_name
442  */
al_get_org_name(void)443 const char *al_get_org_name(void)
444 {
445    return _al_org_name;
446 }
447 
448 
449 /* Function: al_get_app_name
450  */
al_get_app_name(void)451 const char *al_get_app_name(void)
452 {
453    return _al_app_name;
454 }
455 
456 
457 /* Function: al_inhibit_screensaver
458  */
al_inhibit_screensaver(bool inhibit)459 bool al_inhibit_screensaver(bool inhibit)
460 {
461    ASSERT(active_sysdrv);
462 
463    if (active_sysdrv->vt->inhibit_screensaver)
464       return active_sysdrv->vt->inhibit_screensaver(inhibit);
465    else
466       return false;
467 }
468 
469 
_al_open_library(const char * filename)470 void *_al_open_library(const char *filename)
471 {
472    ASSERT(active_sysdrv);
473 
474    if (active_sysdrv->vt->open_library)
475       return active_sysdrv->vt->open_library(filename);
476    else
477       return NULL;
478 }
479 
480 
_al_import_symbol(void * library,const char * symbol)481 void *_al_import_symbol(void *library, const char *symbol)
482 {
483    ASSERT(active_sysdrv);
484 
485    if (active_sysdrv->vt->import_symbol)
486       return active_sysdrv->vt->import_symbol(library, symbol);
487    else
488       return NULL;
489 }
490 
491 
_al_close_library(void * library)492 void _al_close_library(void *library)
493 {
494    ASSERT(active_sysdrv);
495 
496    if (active_sysdrv->vt->close_library)
497       active_sysdrv->vt->close_library(library);
498 }
499 
500 
501 /* Function: al_get_time
502  */
al_get_time(void)503 double al_get_time(void)
504 {
505    ASSERT(active_sysdrv);
506 
507    if (active_sysdrv->vt->get_time)
508       return active_sysdrv->vt->get_time();
509    return 0.0;
510 }
511 
512 
513 /* Function: al_rest
514  */
al_rest(double seconds)515 void al_rest(double seconds)
516 {
517    ASSERT(active_sysdrv);
518 
519    if (active_sysdrv->vt->rest)
520       active_sysdrv->vt->rest(seconds);
521 }
522 
523 
524 /* Function: al_init_timeout
525  */
al_init_timeout(ALLEGRO_TIMEOUT * timeout,double seconds)526 void al_init_timeout(ALLEGRO_TIMEOUT *timeout, double seconds)
527 {
528    ASSERT(active_sysdrv);
529 
530    if (active_sysdrv->vt->init_timeout)
531       active_sysdrv->vt->init_timeout(timeout, seconds);
532 }
533 
534 /* vim: set sts=3 sw=3 et: */
535