source: libcaca/trunk/caca/caca.c @ 2139

Last change on this file since 2139 was 2139, checked in by Sam Hocevar, 12 years ago
  • Implemented caca_set_display_driver() to change driver at runtime.
  • Property svn:keywords set to Id
File size: 11.7 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: caca.c 2139 2007-12-16 11:54:02Z sam $
7 *
8 *  This library is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  This file contains the main functions used by \e libcaca applications to
17 *  initialise the library, get the screen properties, set the framerate and
18 *  so on.
19 */
20
21#include "config.h"
22#include "common.h"
23
24#if !defined(__KERNEL__)
25#   include <stdlib.h>
26#   include <string.h>
27#   include <stdio.h>
28#   if defined(USE_PLUGINS)
29#       if defined(HAVE_DLFCN_H)
30#           include <dlfcn.h>
31#       endif
32#   endif
33#endif
34
35#include "cucul.h"
36#include "caca.h"
37#include "caca_internals.h"
38
39#if defined(USE_PLUGINS)
40#   define gl_install(p) caca_plugin_install(p, "gl")
41#   define x11_install(p) caca_plugin_install(p, "x11")
42#endif
43
44static int caca_can_resize(caca_display_t *);
45static int caca_install_driver(caca_display_t *, char const *);
46static int caca_uninstall_driver(caca_display_t *);
47static int caca_select_driver(caca_display_t *, char const *);
48#if defined(USE_PLUGINS)
49static int caca_plugin_install(char const *, caca_display_t *);
50#endif
51
52/** \brief Attach a caca graphical context to a cucul canvas.
53 *
54 *  Create a graphical context using device-dependent features (ncurses for
55 *  terminals, an X11 window, a DOS command window...) that attaches to a
56 *  libcucul canvas. Everything that gets drawn in the libcucul canvas can
57 *  then be displayed by the libcaca driver.
58 *
59 *  If no cucul canvas is provided, a new one is created. Its handle can be
60 *  retrieved using caca_get_canvas() and it is automatically destroyed when
61 *  caca_free_display() is called.
62 *
63 *  See also caca_create_display_with_driver().
64 *
65 *  If an error occurs, NULL is returned and \b errno is set accordingly:
66 *  - \c ENOMEM Not enough memory.
67 *  - \c ENODEV Graphical device could not be initialised.
68 *
69 *  \param cv The cucul canvas or NULL to create a canvas automatically.
70 *  \return The caca graphical context or NULL if an error occurred.
71 */
72caca_display_t * caca_create_display(cucul_canvas_t *cv)
73{
74    return caca_create_display_with_driver(cv, NULL);
75}
76
77/** \brief Attach a specific caca graphical context to a cucul canvas.
78 *
79 *  Create a graphical context using device-dependent features (ncurses for
80 *  terminals, an X11 window, a DOS command window...) that attaches to a
81 *  libcucul canvas. Everything that gets drawn in the libcucul canvas can
82 *  then be displayed by the libcaca driver.
83 *
84 *  If no cucul canvas is provided, a new one is created. Its handle can be
85 *  retrieved using caca_get_canvas() and it is automatically destroyed when
86 *  caca_free_display() is called.
87 *
88 *  If no driver name is provided, \e libcaca will try to autodetect the best
89 *  output driver it can.
90 *
91 *  See also caca_create_display().
92 *
93 *  If an error occurs, NULL is returned and \b errno is set accordingly:
94 *  - \c ENOMEM Not enough memory.
95 *  - \c ENODEV Graphical device could not be initialised.
96 *
97 *  \param cv The cucul canvas or NULL to create a canvas automatically.
98 *  \param driver A string describing the desired output driver or NULL to
99 *                choose the best driver automatically.
100 *  \return The caca graphical context or NULL if an error occurred.
101 */
102caca_display_t * caca_create_display_with_driver(cucul_canvas_t *cv,
103                                                 char const *driver)
104{
105    caca_display_t *dp = malloc(sizeof(caca_display_t));
106
107    if(!dp)
108    {
109        seterrno(ENOMEM);
110        return NULL;
111    }
112
113    if((dp->autorelease = (cv == NULL)))
114    {
115        cv = cucul_create_canvas(0, 0);
116    }
117
118    dp->cv = cv;
119
120    if(cucul_manage_canvas(cv, (int (*)(void *))caca_can_resize, (void *)dp))
121    {
122        if(dp->autorelease)
123            cucul_free_canvas(dp->cv);
124        free(dp);
125        seterrno(EBUSY);
126        return NULL;
127    }
128
129    if(caca_install_driver(dp, driver))
130    {
131        cucul_unmanage_canvas(cv, (int (*)(void *))caca_can_resize, (void *)dp);
132        if(dp->autorelease)
133            cucul_free_canvas(dp->cv);
134        free(dp);
135        seterrno(ENODEV);
136        return NULL;
137    }
138
139    return dp;
140}
141
142/** \brief Get available display drivers
143 *
144 *  Return a list of available display drivers. The list is a NULL-terminated
145 *  array of strings, interleaving a string containing the internal value for
146 *  the display driver, and a string containing the natural language
147 *  description for that driver.
148 *
149 *  This function never fails.
150 *
151 *  \param dp Display object.
152 *  \return An array of strings.
153 */
154char const * const * caca_get_display_driver_list(void)
155{
156    static char const * const list[] =
157    {
158#if defined(USE_COCOA)
159        "cocoa", "Mac OS X Cocoa",
160#endif
161#if defined(USE_WIN32)
162        "win32", "Windows console",
163#endif
164#if defined(USE_CONIO)
165        "conio", "MS-DOS conio",
166#endif
167#if defined(USE_X11)
168        "x11", "X11 graphical window",
169#endif
170#if defined(USE_GL)
171        "gl", "OpenGL window",
172#endif
173#if defined(USE_SLANG)
174        "slang", "S-Lang console library",
175#endif
176#if defined(USE_NCURSES)
177        "ncurses", "ncurses console library",
178#endif
179#if defined(USE_VGA)
180        "vga", "direct VGA memory",
181#endif
182#if !defined(__KERNEL__)
183        "raw", "raw libcaca output",
184#endif
185        NULL, NULL
186    };
187
188    return list;
189}
190
191/** \brief Return a caca graphical context's current output driver.
192 *
193 *  Return the given display's current output driver.
194 *
195 *  This function never fails.
196 *
197 *  \param dp The caca display.
198 *  \return A static string.
199 */
200char const * caca_get_display_driver(caca_display_t *dp)
201{
202    return dp->drv.driver;
203}
204
205/** \brief Set the output driver.
206 *
207 *  Dynamically change the given display's output driver.
208 *
209 *  FIXME: decide what to do in case of failure
210 *
211 *  \param dp The caca display.
212 *  \param driver A string describing the desired output driver or NULL to
213 *                choose the best driver automatically.
214 *  \return 0 in case of success, -1 if an error occurred.
215 */
216int caca_set_display_driver(caca_display_t *dp, char const *driver)
217{
218    caca_uninstall_driver(dp);
219    if(caca_install_driver(dp, driver))
220    {
221        seterrno(ENODEV);
222        return -1;
223    }
224
225    return 0;
226}
227
228/** \brief Detach a caca graphical context from a cucul backend context.
229 *
230 *  Detach a graphical context from its cucul backend and destroy it. The
231 *  libcucul canvas continues to exist and other graphical contexts can be
232 *  attached to it afterwards.
233 *
234 *  If the cucul canvas was automatically created by caca_create_display(),
235 *  it is automatically destroyed and any handle to it becomes invalid.
236 *
237 *  This function never fails.
238 *
239 *  \param dp The libcaca graphical context.
240 *  \return This function always returns 0.
241 */
242int caca_free_display(caca_display_t *dp)
243{
244    caca_uninstall_driver(dp);
245    cucul_unmanage_canvas(dp->cv, (int (*)(void *))caca_can_resize, (void *)dp);
246    if(dp->autorelease)
247        cucul_free_canvas(dp->cv);
248    free(dp);
249
250    return 0;
251}
252
253/** \brief Get the canvas attached to a caca graphical context.
254 *
255 *  Return a handle on the \e cucul_canvas_t object that was either attached
256 *  or created by caca_create_display().
257 *
258 *  This function never fails.
259 *
260 *  \param dp The libcaca graphical context.
261 *  \return The libcucul canvas.
262 */
263cucul_canvas_t * caca_get_canvas(caca_display_t *dp)
264{
265    return dp->cv;
266}
267
268/** \brief Return the \e libcaca version.
269 *
270 *  Return a read-only string with the \e libcaca version information.
271 *
272 *  This function never fails.
273 *
274 *  \return The \e libcaca version information.
275 */
276char const * caca_get_version(void)
277{
278    return VERSION;
279}
280
281/*
282 * XXX: The following functions are local.
283 */
284
285static int caca_can_resize(caca_display_t *dp)
286{
287    return dp->resize.allow;
288}
289
290static int caca_install_driver(caca_display_t *dp, char const *driver)
291{
292#if defined(USE_PLUGINS)
293    dp->plugin = NULL;
294#endif
295
296    if(caca_select_driver(dp, driver))
297    {
298#if defined(USE_PLUGINS)
299        if(dp->plugin)
300            dlclose(dp->plugin);
301#endif
302        return -1;
303    }
304
305    if(dp->drv.init_graphics(dp))
306    {
307#if defined(USE_PLUGINS)
308        if(dp->plugin)
309            dlclose(dp->plugin);
310#endif
311        return -1;
312    }
313
314    /* Graphics stuff */
315    dp->delay = 0;
316    dp->rendertime = 0;
317
318    /* Events stuff */
319#if defined(USE_SLANG) || defined(USE_NCURSES)
320    dp->events.key_timer.last_sec = 0;
321    dp->events.key_timer.last_usec = 0;
322    dp->events.last_key_ticks = 0;
323    dp->events.autorepeat_ticks = 0;
324    dp->events.last_key_event.type = CACA_EVENT_NONE;
325#endif
326#if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO) || defined(USE_GL)
327    dp->events.queue = 0;
328#endif
329
330    dp->timer.last_sec = 0;
331    dp->timer.last_usec = 0;
332    dp->lastticks = 0;
333
334    /* Mouse position */
335    dp->mouse.x = cucul_get_canvas_width(dp->cv) / 2;
336    dp->mouse.y = cucul_get_canvas_height(dp->cv) / 2;
337
338    /* Resize events */
339    dp->resize.resized = 0;
340    dp->resize.allow = 0;
341
342    return 0;
343}
344
345static int caca_uninstall_driver(caca_display_t *dp)
346{
347    dp->drv.end_graphics(dp);
348#if defined(USE_PLUGINS)
349    if(dp->plugin)
350        dlclose(dp->plugin);
351#endif
352
353    return 0;
354}
355
356static int caca_select_driver(caca_display_t *dp, char const *driver)
357{
358    char const *var = driver;
359#if defined(HAVE_GETENV)
360    if(!var)
361        var = getenv("CACA_DRIVER");
362#endif
363
364#if defined(HAVE_STRCASECMP)
365    /* If the environment variable was set, use it */
366    if(var && *var)
367    {
368#if defined(USE_COCOA)
369        if(!strcasecmp(var, "cocoa")) return cocoa_install(dp);
370#endif
371#if defined(USE_WIN32)
372        if(!strcasecmp(var, "win32")) return win32_install(dp);
373#endif
374#if defined(USE_CONIO)
375        if(!strcasecmp(var, "conio")) return conio_install(dp);
376#endif
377#if defined(USE_X11)
378        if(!strcasecmp(var, "x11")) return x11_install(dp);
379#endif
380#if defined(USE_GL)
381        if(!strcasecmp(var, "gl")) return gl_install(dp);
382#endif
383#if !defined(__KERNEL__)
384        if(!strcasecmp(var, "raw")) return raw_install(dp);
385#endif
386#if defined(USE_SLANG)
387        if(!strcasecmp(var, "slang")) return slang_install(dp);
388#endif
389#if defined(USE_NCURSES)
390        if(!strcasecmp(var, "ncurses")) return ncurses_install(dp);
391#endif
392#if defined(USE_VGA)
393        if(!strcasecmp(var, "vga")) return vga_install(dp);
394#endif
395        return -1;
396    }
397#endif
398
399#if defined(USE_COCOA)
400    if(cocoa_install(dp) == 0) return 0;
401#endif
402#if defined(USE_WIN32)
403    if(win32_install(dp) == 0) return 0;
404#endif
405#if defined(USE_CONIO)
406    if(conio_install(dp) == 0) return 0;
407#endif
408#if defined(USE_VGA)
409    if(vga_install(dp) == 0) return 0;
410#endif
411#if defined(USE_X11)
412    if(x11_install(dp) == 0) return 0;
413#endif
414#if defined(USE_GL)
415    if(gl_install(dp) == 0) return 0;
416#endif
417    /* ncurses has a higher priority than slang because it has better colour
418     * support across terminal types, despite being slightly slower. */
419#if defined(USE_NCURSES)
420    if(ncurses_install(dp) == 0) return 0;
421#endif
422#if defined(USE_SLANG)
423    if(slang_install(dp) == 0) return 0;
424#endif
425
426    return -1;
427}
428
429#if defined(USE_PLUGINS)
430static int caca_plugin_install(caca_display_t *dp, char const *driver)
431{
432    char buf[512];
433    int (*sym) (caca_display_t *);
434
435    sprintf(buf, "%s/lib%s_plugin.so", PLUGINDIR, driver);
436    dp->plugin = dlopen(buf, RTLD_NOW);
437    if(!dp->plugin)
438    {
439        sprintf(buf, "lib%s_plugin.so", driver);
440        dp->plugin = dlopen(buf, RTLD_NOW);
441        if(!dp->plugin)
442            return -1;
443    }
444
445    sprintf(buf, "%s_install", driver);
446    sym = dlsym(dp->plugin, buf);
447    if(!sym)
448    {
449        dlclose(dp->plugin);
450        return -1;
451    }
452
453    return sym(dp);
454}
455#endif
456
Note: See TracBrowser for help on using the repository browser.