1 /*
2  *  libcaca       Colour ASCII-Art library
3  *  Copyright (c) 2002-2012 Sam Hocevar <sam@hocevar.net>
4  *                All Rights Reserved
5  *
6  *  This library is free software. It comes without any warranty, to
7  *  the extent permitted by applicable law. You can redistribute it
8  *  and/or modify it under the terms of the Do What the Fuck You Want
9  *  to Public License, Version 2, as published by Sam Hocevar. See
10  *  http://www.wtfpl.net/ for more details.
11  */
12 
13 /*
14  *  This file contains character and string drawing functions.
15  */
16 
17 #include "config.h"
18 
19 #if !defined(__KERNEL__)
20 #   include <stdio.h>
21 #   include <stdlib.h>
22 #   include <string.h>
23 #endif
24 
25 #include "caca.h"
26 #include "caca_internals.h"
27 
28 /** \brief Set the display title.
29  *
30  *  If libcaca runs in a window, try to change its title. This works with
31  *  the ncurses, S-Lang, OpenGL, X11 and Win32 drivers.
32  *
33  *  If an error occurs, -1 is returned and \b errno is set accordingly:
34  *  - \c ENOSYS Display driver does not support setting the window title.
35  *
36  *  \param dp The libcaca display context.
37  *  \param title The desired display title.
38  *  \return 0 upon success, -1 if an error occurred.
39  */
caca_set_display_title(caca_display_t * dp,char const * title)40 int caca_set_display_title(caca_display_t *dp, char const *title)
41 {
42     int ret = dp->drv.set_display_title(dp, title);
43 
44     if(ret)
45         seterrno(ENOSYS);
46 
47     return ret;
48 }
49 
50 /** \brief Get the display width.
51  *
52  *  If libcaca runs in a window, get the usable window width. This value can
53  *  be used for aspect ratio calculation. If libcaca does not run in a window
54  *  or if there is no way to know the font size, most drivers will assume a
55  *  6x10 font is being used. Note that the units are not necessarily pixels.
56  *
57  *  This function never fails.
58  *
59  *  \param dp The libcaca display context.
60  *  \return The display width.
61  */
caca_get_display_width(caca_display_t const * dp)62 int caca_get_display_width(caca_display_t const *dp)
63 {
64     return dp->drv.get_display_width(dp);
65 }
66 
67 /** \brief Get the display height.
68  *
69  *  If libcaca runs in a window, get the usable window height. This value can
70  *  be used for aspect ratio calculation. If libcaca does not run in a window
71  *  or if there is no way to know the font size, assume a 6x10 font is being
72  *  used. Note that the units are not necessarily pixels.
73  *
74  *  This function never fails.
75  *
76  *  \param dp The libcaca display context.
77  *  \return The display height.
78  */
caca_get_display_height(caca_display_t const * dp)79 int caca_get_display_height(caca_display_t const *dp)
80 {
81     return dp->drv.get_display_height(dp);
82 }
83 
84 /** \brief Set the refresh delay.
85  *
86  *  Set the refresh delay in microseconds. The refresh delay is used by
87  *  caca_refresh_display() to achieve constant framerate. See the
88  *  caca_refresh_display() documentation for more details.
89  *
90  *  If the argument is zero, constant framerate is disabled. This is the
91  *  default behaviour.
92  *
93  *  If an error occurs, -1 is returned and \b errno is set accordingly:
94  *  - \c EINVAL Refresh delay value is invalid.
95  *
96  *  \param dp The libcaca display context.
97  *  \param usec The refresh delay in microseconds.
98  *  \return 0 upon success, -1 if an error occurred.
99  */
caca_set_display_time(caca_display_t * dp,int usec)100 int caca_set_display_time(caca_display_t *dp, int usec)
101 {
102     if(usec < 0)
103     {
104         seterrno(EINVAL);
105         return -1;
106     }
107 
108     dp->delay = usec;
109     return 0;
110 }
111 
112 /** \brief Get the display's average rendering time.
113  *
114  *  Get the average rendering time, which is the average measured time
115  *  between two caca_refresh_display() calls, in microseconds. If constant
116  *  framerate was activated by calling caca_set_display_time(), the average
117  *  rendering time will be close to the requested delay even if the real
118  *  rendering time was shorter.
119  *
120  *  This function never fails.
121  *
122  *  \param dp The libcaca display context.
123  *  \return The render time in microseconds.
124  */
caca_get_display_time(caca_display_t const * dp)125 int caca_get_display_time(caca_display_t const *dp)
126 {
127     return dp->rendertime;
128 }
129 
130 /** \brief Flush pending changes and redraw the screen.
131  *
132  *  Flush all graphical operations and print them to the display device.
133  *  Nothing will show on the screen until this function is called.
134  *
135  *  If caca_set_display_time() was called with a non-zero value,
136  *  caca_refresh_display() will use that value to achieve constant
137  *  framerate: if two consecutive calls to caca_refresh_display() are within
138  *  a time range shorter than the value set with caca_set_display_time(),
139  *  the second call will be delayed before performing the screen refresh.
140  *
141  *  This function never fails.
142  *
143  *  \param dp The libcaca display context.
144  *  \return This function always returns 0.
145  */
caca_refresh_display(caca_display_t * dp)146 int caca_refresh_display(caca_display_t *dp)
147 {
148 #if defined PROF
149     caca_timer_t proftimer = { 0, 0 };
150 #endif
151 #if !defined(_DOXYGEN_SKIP_ME)
152 #   define IDLE_USEC 5000
153 #endif
154     int ticks = dp->lastticks + _caca_getticks(&dp->timer);
155 
156 #if defined PROF
157     _caca_getticks(&proftimer);
158 #endif
159     dp->drv.display(dp);
160 #if defined PROF
161     STAT_IADD(&dp->display_stat, _caca_getticks(&proftimer));
162 #endif
163 
164     /* Invalidate the dirty rectangle */
165     caca_clear_dirty_rect_list(dp->cv);
166 
167     /* Once the display is finished, we can ack resizes */
168     if(dp->resize.resized)
169     {
170         dp->resize.resized = 0;
171         _caca_handle_resize(dp);
172     }
173 
174 #if defined PROF
175     _caca_getticks(&proftimer);
176 #endif
177     /* Wait until dp->delay + time of last call */
178     ticks += _caca_getticks(&dp->timer);
179     for(ticks += _caca_getticks(&dp->timer);
180         ticks + IDLE_USEC < (int)dp->delay;
181         ticks += _caca_getticks(&dp->timer))
182     {
183         _caca_sleep(IDLE_USEC);
184     }
185 #if defined PROF
186     STAT_IADD(&dp->wait_stat, _caca_getticks(&proftimer));
187 #endif
188 
189     /* Update the render time */
190     dp->rendertime = ticks;
191 
192     dp->lastticks = ticks - dp->delay;
193 
194     /* If we drifted too much, it's bad, bad, bad. */
195     if(dp->lastticks > (int)dp->delay)
196         dp->lastticks = 0;
197 
198 #if defined PROF
199     _caca_dump_stats();
200 #endif
201 
202     return 0;
203 }
204 
205 /** \brief Show or hide the cursor.
206  *
207  *  Show or hide the cursor, for devices that support such a feature.
208  *
209  *  If an error occurs, -1 is returned and \b errno is set accordingly:
210  *  - \c ENOSYS Display driver does not support showing the cursor.
211  *
212  *  \param dp The libcaca display context.
213  *  \param flag 0 hides the cursor, 1 shows the system's default cursor
214  *              (usually a white rectangle). Other values are reserved for
215  *              future use.
216  *  \return 0 upon success, -1 if an error occurred.
217  */
caca_set_cursor(caca_display_t * dp,int flag)218 int caca_set_cursor(caca_display_t *dp, int flag)
219 {
220     if(!dp->drv.set_cursor)
221     {
222         seterrno(ENOSYS);
223         return -1;
224     }
225 
226     dp->drv.set_cursor(dp, flag);
227     return 0;
228 }
229 
230 /** \brief Show or hide the mouse pointer.
231  *
232  *  Show or hide the mouse pointer. This function works with the ncurses,
233  *  S-Lang and X11 drivers.
234  *
235  *  If an error occurs, -1 is returned and \b errno is set accordingly:
236  *  - \c ENOSYS Display driver does not support hiding the mouse pointer.
237  *
238  *  \param dp The libcaca display context.
239  *  \param flag 0 hides the pointer, 1 shows the system's default pointer
240  *              (usually an arrow). Other values are reserved for future use.
241  *  \return 0 upon success, -1 if an error occurred.
242  */
caca_set_mouse(caca_display_t * dp,int flag)243 int caca_set_mouse(caca_display_t *dp, int flag)
244 {
245     if(!dp->drv.set_mouse)
246     {
247         seterrno(ENOSYS);
248         return -1;
249     }
250 
251     dp->drv.set_mouse(dp, flag);
252     return 0;
253 }
254 
255 /*
256  * XXX: following functions are local
257  */
258 
_caca_handle_resize(caca_display_t * dp)259 void _caca_handle_resize(caca_display_t *dp)
260 {
261     dp->drv.handle_resize(dp);
262 
263     /* Tell libcaca we changed size */
264     if(dp->resize.w != caca_get_canvas_width(dp->cv)
265         || dp->resize.h != caca_get_canvas_height(dp->cv))
266     {
267         dp->resize.allow = 1;
268         caca_set_canvas_size(dp->cv, dp->resize.w, dp->resize.h);
269         dp->resize.allow = 0;
270     }
271 }
272 
_caca_set_term_title(char const * str)273 void _caca_set_term_title(char const *str)
274 {
275 #if defined(HAVE_GETENV)
276     char *term;
277 
278     term = getenv("TERM");
279 
280     if(!term || !strcmp(term, "linux"))
281         return;
282 #endif
283 
284     fprintf(stdout, "\033]0;%s\007", str);
285     fflush(stdout);
286 }
287 
288