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
RevLine 
[35]1/*
[672]2 *  libcaca       Colour ASCII-Art library
[699]3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
[268]4 *                All Rights Reserved
[35]5 *
[769]6 *  $Id: caca.c 2139 2007-12-16 11:54:02Z sam $
7 *
[1462]8 *  This library is free software. It comes without any warranty, to
[1452]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
[522]12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
[35]13 */
[17]14
[769]15/*
[268]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.
[205]19 */
20
[63]21#include "config.h"
[859]22#include "common.h"
[63]23
[565]24#if !defined(__KERNEL__)
25#   include <stdlib.h>
26#   include <string.h>
[1208]27#   include <stdio.h>
28#   if defined(USE_PLUGINS)
29#       if defined(HAVE_DLFCN_H)
30#           include <dlfcn.h>
31#       endif
32#   endif
[565]33#endif
[17]34
[524]35#include "cucul.h"
[185]36#include "caca.h"
37#include "caca_internals.h"
[17]38
[1208]39#if defined(USE_PLUGINS)
[2138]40#   define gl_install(p) caca_plugin_install(p, "gl")
41#   define x11_install(p) caca_plugin_install(p, "x11")
[1208]42#endif
[227]43
[2055]44static int caca_can_resize(caca_display_t *);
[2139]45static int caca_install_driver(caca_display_t *, char const *);
46static int caca_uninstall_driver(caca_display_t *);
[2138]47static int caca_select_driver(caca_display_t *, char const *);
[1208]48#if defined(USE_PLUGINS)
49static int caca_plugin_install(char const *, caca_display_t *);
50#endif
51
[810]52/** \brief Attach a caca graphical context to a cucul canvas.
[540]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 *
[2061]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 *
[2138]63 *  See also caca_create_display_with_driver().
64 *
[1006]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 *
[2138]69 *  \param cv The cucul canvas or NULL to create a canvas automatically.
[540]70 *  \return The caca graphical context or NULL if an error occurred.
71 */
[2055]72caca_display_t * caca_create_display(cucul_canvas_t *cv)
[17]73{
[2138]74    return caca_create_display_with_driver(cv, NULL);
75}
76
[2139]77/** \brief Attach a specific caca graphical context to a cucul canvas.
[2138]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 *
[2139]88 *  If no driver name is provided, \e libcaca will try to autodetect the best
89 *  output driver it can.
90 *
[2138]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{
[811]105    caca_display_t *dp = malloc(sizeof(caca_display_t));
[524]106
[1006]107    if(!dp)
108    {
[1362]109        seterrno(ENOMEM);
[1006]110        return NULL;
111    }
112
[2061]113    if((dp->autorelease = (cv == NULL)))
114    {
115        cv = cucul_create_canvas(0, 0);
116    }
117
118    dp->cv = cv;
119
[2055]120    if(cucul_manage_canvas(cv, (int (*)(void *))caca_can_resize, (void *)dp))
121    {
[2061]122        if(dp->autorelease)
123            cucul_free_canvas(dp->cv);
[2055]124        free(dp);
125        seterrno(EBUSY);
126        return NULL;
127    }
128
[2139]129    if(caca_install_driver(dp, driver))
[539]130    {
[2055]131        cucul_unmanage_canvas(cv, (int (*)(void *))caca_can_resize, (void *)dp);
[2061]132        if(dp->autorelease)
133            cucul_free_canvas(dp->cv);
[811]134        free(dp);
[1362]135        seterrno(ENODEV);
[524]136        return NULL;
[265]137    }
[79]138
[2139]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[] =
[335]157    {
[2139]158#if defined(USE_COCOA)
159        "cocoa", "Mac OS X Cocoa",
[1208]160#endif
[2139]161#if defined(USE_WIN32)
162        "win32", "Windows console",
[540]163#endif
[2139]164#if defined(USE_CONIO)
165        "conio", "MS-DOS conio",
[593]166#endif
[2139]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    };
[527]187
[2139]188    return list;
[17]189}
190
[2139]191/** \brief Return a caca graphical context's current output driver.
[2138]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
[2139]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
[540]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 *
[2061]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 *
[1006]237 *  This function never fails.
238 *
[811]239 *  \param dp The libcaca graphical context.
[1006]240 *  \return This function always returns 0.
[540]241 */
[1006]242int caca_free_display(caca_display_t *dp)
[17]243{
[2139]244    caca_uninstall_driver(dp);
[2055]245    cucul_unmanage_canvas(dp->cv, (int (*)(void *))caca_can_resize, (void *)dp);
[2061]246    if(dp->autorelease)
247        cucul_free_canvas(dp->cv);
[811]248    free(dp);
[1006]249
250    return 0;
[17]251}
252
[2061]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
[2074]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
[2139]281/*
282 * XXX: The following functions are local.
[2135]283 */
[2139]284
285static int caca_can_resize(caca_display_t *dp)
[2135]286{
[2139]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))
[2135]297    {
[2139]298#if defined(USE_PLUGINS)
299        if(dp->plugin)
300            dlclose(dp->plugin);
[2135]301#endif
[2139]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);
[2135]310#endif
[2139]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;
[2135]325#endif
[2139]326#if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO) || defined(USE_GL)
327    dp->events.queue = 0;
[2135]328#endif
329
[2139]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;
[2135]343}
344
[2139]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
[268]352
[2139]353    return 0;
[2055]354}
355
[2138]356static int caca_select_driver(caca_display_t *dp, char const *driver)
[265]357{
[2138]358    char const *var = driver;
359#if defined(HAVE_GETENV)
360    if(!var)
361        var = getenv("CACA_DRIVER");
362#endif
[1396]363
[2138]364#if defined(HAVE_STRCASECMP)
[265]365    /* If the environment variable was set, use it */
366    if(var && *var)
[623]367    {
[1396]368#if defined(USE_COCOA)
369        if(!strcasecmp(var, "cocoa")) return cocoa_install(dp);
370#endif
[335]371#if defined(USE_WIN32)
[811]372        if(!strcasecmp(var, "win32")) return win32_install(dp);
[335]373#endif
[265]374#if defined(USE_CONIO)
[811]375        if(!strcasecmp(var, "conio")) return conio_install(dp);
[265]376#endif
[285]377#if defined(USE_X11)
[811]378        if(!strcasecmp(var, "x11")) return x11_install(dp);
[265]379#endif
[483]380#if defined(USE_GL)
[811]381        if(!strcasecmp(var, "gl")) return gl_install(dp);
[483]382#endif
[1054]383#if !defined(__KERNEL__)
[811]384        if(!strcasecmp(var, "raw")) return raw_install(dp);
[1054]385#endif
[265]386#if defined(USE_SLANG)
[811]387        if(!strcasecmp(var, "slang")) return slang_install(dp);
[265]388#endif
[285]389#if defined(USE_NCURSES)
[811]390        if(!strcasecmp(var, "ncurses")) return ncurses_install(dp);
[265]391#endif
[565]392#if defined(USE_VGA)
[811]393        if(!strcasecmp(var, "vga")) return vga_install(dp);
[565]394#endif
[684]395        return -1;
[265]396    }
397#endif
398
[1396]399#if defined(USE_COCOA)
400    if(cocoa_install(dp) == 0) return 0;
401#endif
[335]402#if defined(USE_WIN32)
[811]403    if(win32_install(dp) == 0) return 0;
[335]404#endif
[265]405#if defined(USE_CONIO)
[811]406    if(conio_install(dp) == 0) return 0;
[265]407#endif
[565]408#if defined(USE_VGA)
[811]409    if(vga_install(dp) == 0) return 0;
[565]410#endif
[285]411#if defined(USE_X11)
[811]412    if(x11_install(dp) == 0) return 0;
[265]413#endif
[483]414#if defined(USE_GL)
[811]415    if(gl_install(dp) == 0) return 0;
[483]416#endif
[984]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
[265]422#if defined(USE_SLANG)
[811]423    if(slang_install(dp) == 0) return 0;
[265]424#endif
[487]425
[539]426    return -1;
[265]427}
428
[1208]429#if defined(USE_PLUGINS)
[2138]430static int caca_plugin_install(caca_display_t *dp, char const *driver)
[1208]431{
432    char buf[512];
433    int (*sym) (caca_display_t *);
434
[2138]435    sprintf(buf, "%s/lib%s_plugin.so", PLUGINDIR, driver);
[1208]436    dp->plugin = dlopen(buf, RTLD_NOW);
437    if(!dp->plugin)
438    {
[2138]439        sprintf(buf, "lib%s_plugin.so", driver);
[1208]440        dp->plugin = dlopen(buf, RTLD_NOW);
441        if(!dp->plugin)
442            return -1;
443    }
444
[2138]445    sprintf(buf, "%s_install", driver);
[1208]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.