source: libcaca/trunk/caca/event.c @ 2140

Last change on this file since 2140 was 2140, checked in by Sam Hocevar, 12 years ago
  • Fix a tiny use of uninitialised memory in caca_get_event() that may have lead to deadlocks.
  • Property svn:keywords set to Id
File size: 13.7 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: event.c 2140 2007-12-16 11:54:25Z 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 event handling functions for keyboard and mouse input.
17 */
18
19#include "config.h"
20#include "common.h"
21
22#if !defined(__KERNEL__)
23#   include <stdio.h>
24#   include <string.h>
25#endif
26
27#include "cucul.h"
28#include "caca.h"
29#include "caca_internals.h"
30
31static int _get_next_event(caca_display_t *, caca_privevent_t *);
32static int _lowlevel_event(caca_display_t *, caca_privevent_t *);
33
34#if !defined(_DOXYGEN_SKIP_ME)
35/* If no new key was pressed after AUTOREPEAT_THRESHOLD usec, assume the
36 * key was released */
37#define AUTOREPEAT_THRESHOLD 100000
38/* Start repeating key after AUTOREPEAT_TRIGGER usec and send keypress
39 * events every AUTOREPEAT_RATE usec. */
40#define AUTOREPEAT_TRIGGER 300000
41#define AUTOREPEAT_RATE 100000
42#endif
43
44/** \brief Get the next mouse or keyboard input event.
45 *
46 *  Poll the event queue for mouse or keyboard events matching the event
47 *  mask and return the first matching event. Non-matching events are
48 *  discarded. If \c event_mask is zero, the function returns immediately.
49 *
50 *  The timeout value tells how long this function needs to wait for an
51 *  event. A value of zero returns immediately and the function returns zero
52 *  if no more events are pending in the queue. A negative value causes the
53 *  function to wait indefinitely until a matching event is received.
54 *
55 *  If not null, \c ev will be filled with information about the event
56 *  received. If null, the function will return but no information about
57 *  the event will be sent.
58 *
59 *  This function never fails.
60 *
61 *  \param dp The libcaca graphical context.
62 *  \param event_mask Bitmask of requested events.
63 *  \param timeout A timeout value in microseconds, -1 for blocking behaviour
64 *  \param ev A pointer to a caca_event structure, or NULL.
65 *  \return 1 if a matching event was received, or 0 if the wait timeouted.
66 */
67int caca_get_event(caca_display_t *dp, unsigned int event_mask,
68                   caca_event_t *ev, int timeout)
69{
70    caca_privevent_t privevent;
71    caca_timer_t timer = {0, 0};
72    int usec = 0;
73
74    if(!event_mask)
75        return 0;
76
77    if(timeout > 0)
78        _caca_getticks(&timer);
79
80    for( ; ; )
81    {
82        int ret = _get_next_event(dp, &privevent);
83
84        /* If we got the event we wanted, return */
85        if(privevent.type & event_mask)
86        {
87            if(ev)
88                memcpy(ev, &privevent, sizeof(privevent));
89            return ret;
90        }
91
92        /* If there is no timeout, sleep and try again. */
93        if(timeout < 0)
94        {
95            _caca_sleep(10000);
96            continue;
97        }
98
99        /* If we timeouted, return an empty event */
100        if(usec >= timeout)
101        {
102            privevent.type = CACA_EVENT_NONE;
103            if(ev)
104                memcpy(ev, &privevent, sizeof(privevent));
105            return 0;
106        }
107
108        /* Otherwise sleep a bit. Our granularity is far too high for values
109         * below 10000 microseconds so we cheat a bit. */
110        if(usec > 10000)
111            _caca_sleep(10000);
112        else
113            _caca_sleep(1000);
114
115        usec += _caca_getticks(&timer);
116    }
117}
118
119/** \brief Return the X mouse coordinate.
120 *
121 *  Return the X coordinate of the mouse position last time
122 *  it was detected. This function is not reliable if the ncurses or S-Lang
123 *  drivers are being used, because mouse position is only detected when
124 *  the mouse is clicked. Other drivers such as X11 work well.
125 *
126 *  This function never fails.
127 *
128 *  \param dp The libcaca graphical context.
129 *  \return The X mouse coordinate.
130 */
131unsigned int caca_get_mouse_x(caca_display_t const *dp)
132{
133    unsigned int width = cucul_get_canvas_width(dp->cv);
134
135    if(dp->mouse.x >= width)
136        return width - 1;
137
138    return dp->mouse.x;
139}
140
141/** \brief Return the Y mouse coordinate.
142 *
143 *  Return the Y coordinate of the mouse position last time
144 *  it was detected. This function is not reliable if the ncurses or S-Lang
145 *  drivers are being used, because mouse position is only detected when
146 *  the mouse is clicked. Other drivers such as X11 work well.
147 *
148 *  This function never fails.
149 *
150 *  \param dp The libcaca graphical context.
151 *  \return The Y mouse coordinate.
152 */
153unsigned int caca_get_mouse_y(caca_display_t const *dp)
154{
155    unsigned int height = cucul_get_canvas_height(dp->cv);
156
157    if(dp->mouse.y >= height)
158        return height - 1;
159
160    return dp->mouse.y;
161}
162
163/** \brief Return an event's type.
164 *
165 *  Return the type of an event. This function may always be called on an
166 *  event after caca_get_event() was called, and its return value indicates
167 *  which other functions may be called:
168 *  - \c CACA_EVENT_NONE: no other function may be called.
169 *  - \c CACA_EVENT_KEY_PRESS, \c CACA_EVENT_KEY_RELEASE:
170 *  caca_get_event_key_ch(), caca_get_event_key_utf32() and
171 *  caca_get_event_key_utf8() may be called.
172 *  - \c CACA_EVENT_MOUSE_PRESS, \c CACA_EVENT_MOUSE_RELEASE:
173 *  caca_get_event_mouse_button() may be called.
174 *  - \c CACA_EVENT_MOUSE_MOTION: caca_get_event_mouse_x() and
175 *  caca_get_event_mouse_y() may be called.
176 *  - \c CACA_EVENT_RESIZE: caca_get_event_resize_width() and
177 *  caca_get_event_resize_height() may be called.
178 *  - \c CACA_EVENT_QUIT: no other function may be called.
179 *
180 *  This function never fails.
181 *
182 *  \param ev The libcaca event.
183 *  \return The event's type.
184 */
185enum caca_event_type caca_get_event_type(caca_event_t const *ev)
186{
187    return ((caca_privevent_t const *)ev)->type;
188}
189
190/** \brief Return a key press or key release event's value
191 *
192 *  Return either the ASCII value for an event's key, or if the key is not
193 *  an ASCII character, an appropriate \e enum \e caca_key value.
194 *
195 *  This function never fails, but must only be called with a valid event of
196 *  type \c CACA_EVENT_KEY_PRESS or \c CACA_EVENT_KEY_RELEASE, or the results
197 *  will be undefined. See caca_get_event_type() for more information.
198 *
199 *  \param ev The libcaca event.
200 *  \return The key value.
201 */
202unsigned int caca_get_event_key_ch(caca_event_t const *ev)
203{
204    return ((caca_privevent_t const *)ev)->data.key.ch;
205}
206
207/** \brief Return a key press or key release event's Unicode value
208 *
209 *  Return the UTF-32/UCS-4 value for an event's key if it resolves to a
210 *  printable character.
211 *
212 *  This function never fails, but must only be called with a valid event of
213 *  type \c CACA_EVENT_KEY_PRESS or \c CACA_EVENT_KEY_RELEASE, or the results
214 *  will be undefined. See caca_get_event_type() for more information.
215 *
216 *  \param ev The libcaca event.
217 *  \return The key's Unicode value.
218 */
219unsigned long int caca_get_event_key_utf32(caca_event_t const *ev)
220{
221    return ((caca_privevent_t const *)ev)->data.key.utf32;
222}
223
224/** \brief Return a key press or key release event's UTF-8 value
225 *
226 *  Write the UTF-8 value for an event's key if it resolves to a printable
227 *  character. Up to 6 UTF-8 bytes and a null termination are written.
228 *
229 *  This function never fails, but must only be called with a valid event of
230 *  type \c CACA_EVENT_KEY_PRESS or \c CACA_EVENT_KEY_RELEASE, or the results
231 *  will be undefined. See caca_get_event_type() for more information.
232 *
233 *  \param ev The libcaca event.
234 *  \return This function always returns 0.
235 */
236int caca_get_event_key_utf8(caca_event_t const *ev, char *utf8)
237{
238    memcpy(utf8, ((caca_privevent_t const *)ev)->data.key.utf8, 8);
239    return 0;
240}
241
242/** \brief Return a mouse press or mouse release event's button
243 *
244 *  Return the mouse button index for an event.
245 *
246 *  This function never fails, but must only be called with a valid event of
247 *  type \c CACA_EVENT_MOUSE_PRESS or \c CACA_EVENT_MOUSE_RELEASE, or the
248 *  results will be undefined. See caca_get_event_type() for more information.
249 *
250 *  \param ev The libcaca event.
251 *  \return The event's mouse button.
252 */
253unsigned int caca_get_event_mouse_button(caca_event_t const *ev)
254{
255    return ((caca_privevent_t const *)ev)->data.mouse.button;
256}
257
258/** \brief Return a mouse motion event's X coordinate.
259 *
260 *  Return the X coordinate for a mouse motion event.
261 *
262 *  This function never fails, but must only be called with a valid event of
263 *  type \c CACA_EVENT_MOUSE_MOTION, or the results will be undefined. See
264 *  caca_get_event_type() for more information.
265 *
266 *  \param ev The libcaca event.
267 *  \return The event's X mouse coordinate.
268 */
269unsigned int caca_get_event_mouse_x(caca_event_t const *ev)
270{
271    return ((caca_privevent_t const *)ev)->data.mouse.x;
272}
273
274/** \brief Return a mouse motion event's Y coordinate.
275 *
276 *  Return the Y coordinate for a mouse motion event.
277 *
278 *  This function never fails, but must only be called with a valid event of
279 *  type \c CACA_EVENT_MOUSE_MOTION, or the results will be undefined. See
280 *  caca_get_event_type() for more information.
281 *
282 *  \param ev The libcaca event.
283 *  \return The event's Y mouse coordinate.
284 */
285unsigned int caca_get_event_mouse_y(caca_event_t const *ev)
286{
287    return ((caca_privevent_t const *)ev)->data.mouse.y;
288}
289
290/** \brief Return a resize event's display width value.
291 *
292 *  Return the width value for a display resize event.
293 *
294 *  This function never fails, but must only be called with a valid event of
295 *  type \c CACA_EVENT_RESIZE, or the results will be undefined. See
296 *  caca_get_event_type() for more information.
297 *
298 *  \param ev The libcaca event.
299 *  \return The event's new display width value.
300 */
301unsigned int caca_get_event_resize_width(caca_event_t const *ev)
302{
303    return ((caca_privevent_t const *)ev)->data.resize.w;
304}
305
306/** \brief Return a resize event's display height value.
307 *
308 *  Return the height value for a display resize event.
309 *
310 *  This function never fails, but must only be called with a valid event of
311 *  type \c CACA_EVENT_RESIZE, or the results will be undefined. See
312 *  caca_get_event_type() for more information.
313 *
314 *  \param ev The libcaca event.
315 *  \return The event's new display height value.
316 */
317unsigned int caca_get_event_resize_height(caca_event_t const *ev)
318{
319    return ((caca_privevent_t const *)ev)->data.resize.h;
320}
321
322/*
323 * XXX: The following functions are local.
324 */
325
326static int _get_next_event(caca_display_t *dp, caca_privevent_t *ev)
327{
328#if defined(USE_SLANG) || defined(USE_NCURSES)
329    unsigned int ticks;
330#endif
331    int ret;
332
333    /* If we are about to return a resize event, acknowledge it */
334    if(dp->resize.resized)
335    {
336        dp->resize.resized = 0;
337        _caca_handle_resize(dp);
338        ev->type = CACA_EVENT_RESIZE;
339        ev->data.resize.w = cucul_get_canvas_width(dp->cv);
340        ev->data.resize.h = cucul_get_canvas_height(dp->cv);
341        return 1;
342    }
343
344    ret = _lowlevel_event(dp, ev);
345
346#if defined(USE_SLANG)
347    if(dp->drv.id != CACA_DRIVER_SLANG)
348#endif
349#if defined(USE_NCURSES)
350    if(dp->drv.id != CACA_DRIVER_NCURSES)
351#endif
352    return ret;
353
354#if defined(USE_SLANG) || defined(USE_NCURSES)
355    /* Simulate long keypresses using autorepeat features */
356    ticks = _caca_getticks(&dp->events.key_timer);
357    dp->events.last_key_ticks += ticks;
358    dp->events.autorepeat_ticks += ticks;
359
360    /* Handle autorepeat */
361    if(dp->events.last_key_event.type
362           && dp->events.autorepeat_ticks > AUTOREPEAT_TRIGGER
363           && dp->events.autorepeat_ticks > AUTOREPEAT_THRESHOLD
364           && dp->events.autorepeat_ticks > AUTOREPEAT_RATE)
365    {
366        _push_event(dp, ev);
367        dp->events.autorepeat_ticks -= AUTOREPEAT_RATE;
368        *ev = dp->events.last_key_event;
369        return 1;
370    }
371
372    /* We are in autorepeat mode and the same key was just pressed, ignore
373     * this event and return the next one by calling ourselves. */
374    if(ev->type == CACA_EVENT_KEY_PRESS
375        && dp->events.last_key_event.type
376        && ev->data.key.ch == dp->events.last_key_event.data.key.ch
377        && ev->data.key.utf32 == dp->events.last_key_event.data.key.utf32)
378    {
379        dp->events.last_key_ticks = 0;
380        return _get_next_event(dp, ev);
381    }
382
383    /* We are in autorepeat mode, but key has expired or a new key was
384     * pressed - store our event and return a key release event first */
385    if(dp->events.last_key_event.type
386          && (dp->events.last_key_ticks > AUTOREPEAT_THRESHOLD
387               || (ev->type & CACA_EVENT_KEY_PRESS)))
388    {
389        _push_event(dp, ev);
390        *ev = dp->events.last_key_event;
391        ev->type = CACA_EVENT_KEY_RELEASE;
392        dp->events.last_key_event.type = CACA_EVENT_NONE;
393        return 1;
394    }
395
396    /* A new key was pressed, enter autorepeat mode */
397    if(ev->type & CACA_EVENT_KEY_PRESS)
398    {
399        dp->events.last_key_ticks = 0;
400        dp->events.autorepeat_ticks = 0;
401        dp->events.last_key_event = *ev;
402    }
403
404    return ev->type ? 1 : 0;
405#endif
406}
407
408static int _lowlevel_event(caca_display_t *dp, caca_privevent_t *ev)
409{
410#if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO)
411    int ret = _pop_event(dp, ev);
412
413    if(ret)
414        return ret;
415#endif
416
417    return dp->drv.get_event(dp, ev);
418}
419
420#if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO) || defined(USE_GL)
421void _push_event(caca_display_t *dp, caca_privevent_t *ev)
422{
423    if(!ev->type || dp->events.queue == EVENTBUF_LEN)
424        return;
425    dp->events.buf[dp->events.queue] = *ev;
426    dp->events.queue++;
427}
428
429int _pop_event(caca_display_t *dp, caca_privevent_t *ev)
430{
431    int i;
432
433    if(dp->events.queue == 0)
434        return 0;
435
436    *ev = dp->events.buf[0];
437    for(i = 1; i < dp->events.queue; i++)
438        dp->events.buf[i - 1] = dp->events.buf[i];
439    dp->events.queue--;
440
441    return 1;
442}
443#endif
444
Note: See TracBrowser for help on using the repository browser.