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

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

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