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

Last change on this file since 2043 was 2043, checked in by Sam Hocevar, 12 years ago
  • Added as many "const" qualifiers as possible to the public API.
  • Property svn:keywords set to Id
File size: 7.9 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 2043 2007-11-24 11:08:21Z 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#endif
25
26#include "cucul.h"
27#include "cucul_internals.h"
28#include "caca.h"
29#include "caca_internals.h"
30
31static int _get_next_event(caca_display_t *, caca_event_t *);
32static int _lowlevel_event(caca_display_t *, caca_event_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_event_t dummy_event;
71    caca_timer_t timer;
72    int usec = 0;
73
74    if(!event_mask)
75        return 0;
76
77    if(timeout > 0)
78        _caca_getticks(&timer);
79
80    if(ev == NULL)
81        ev = &dummy_event;
82
83    for( ; ; )
84    {
85        int ret = _get_next_event(dp, ev);
86
87        /* If we got the event we wanted, return */
88        if(ev->type & event_mask)
89            return ret;
90
91        /* If there is no timeout, sleep and try again. */
92        if(timeout < 0)
93        {
94            _caca_sleep(10000);
95            continue;
96        }
97
98        /* If we timeouted, return an empty event */
99        if(usec >= timeout)
100        {
101            ev->type = CACA_EVENT_NONE;
102            return 0;
103        }
104
105        /* Otherwise sleep a bit. Our granularity is far too high for values
106         * below 10000 microseconds so we cheat a bit. */
107        if(usec > 10000)
108            _caca_sleep(10000);
109        else
110            _caca_sleep(1000);
111
112        usec += _caca_getticks(&timer);
113    }
114}
115
116/** \brief Return the X mouse coordinate.
117 *
118 *  Return the X coordinate of the mouse position last time
119 *  it was detected. This function is not reliable if the ncurses or S-Lang
120 *  drivers are being used, because mouse position is only detected when
121 *  the mouse is clicked. Other drivers such as X11 work well.
122 *
123 *  This function never fails.
124 *
125 *  \param dp The libcaca graphical context.
126 *  \return The X mouse coordinate.
127 */
128unsigned int caca_get_mouse_x(caca_display_t const *dp)
129{
130    if(dp->mouse.x >= dp->cv->width)
131        return dp->cv->width - 1;
132
133    return dp->mouse.x;
134}
135
136/** \brief Return the Y mouse coordinate.
137 *
138 *  Return the Y coordinate of the mouse position last time
139 *  it was detected. This function is not reliable if the ncurses or S-Lang
140 *  drivers are being used, because mouse position is only detected when
141 *  the mouse is clicked. Other drivers such as X11 work well.
142 *
143 *  This function never fails.
144 *
145 *  \param dp The libcaca graphical context.
146 *  \return The Y mouse coordinate.
147 */
148unsigned int caca_get_mouse_y(caca_display_t const *dp)
149{
150    if(dp->mouse.y >= dp->cv->height)
151        return dp->cv->height - 1;
152
153    return dp->mouse.y;
154}
155
156/*
157 * XXX: The following functions are local.
158 */
159
160static int _get_next_event(caca_display_t *dp, caca_event_t *ev)
161{
162#if defined(USE_SLANG) || defined(USE_NCURSES)
163    unsigned int ticks;
164#endif
165    int ret;
166
167    /* If we are about to return a resize event, acknowledge it */
168    if(dp->resize.resized)
169    {
170        dp->resize.resized = 0;
171        _caca_handle_resize(dp);
172        ev->type = CACA_EVENT_RESIZE;
173        ev->data.resize.w = dp->cv->width;
174        ev->data.resize.h = dp->cv->height;
175        return 1;
176    }
177
178    ret = _lowlevel_event(dp, ev);
179
180#if defined(USE_SLANG)
181    if(dp->drv.driver != CACA_DRIVER_SLANG)
182#endif
183#if defined(USE_NCURSES)
184    if(dp->drv.driver != CACA_DRIVER_NCURSES)
185#endif
186    return ret;
187
188#if defined(USE_SLANG) || defined(USE_NCURSES)
189    /* Simulate long keypresses using autorepeat features */
190    ticks = _caca_getticks(&dp->events.key_timer);
191    dp->events.last_key_ticks += ticks;
192    dp->events.autorepeat_ticks += ticks;
193
194    /* Handle autorepeat */
195    if(dp->events.last_key_event.type
196           && dp->events.autorepeat_ticks > AUTOREPEAT_TRIGGER
197           && dp->events.autorepeat_ticks > AUTOREPEAT_THRESHOLD
198           && dp->events.autorepeat_ticks > AUTOREPEAT_RATE)
199    {
200        _push_event(dp, ev);
201        dp->events.autorepeat_ticks -= AUTOREPEAT_RATE;
202        *ev = dp->events.last_key_event;
203        return 1;
204    }
205
206    /* We are in autorepeat mode and the same key was just pressed, ignore
207     * this event and return the next one by calling ourselves. */
208    if(ev->type == CACA_EVENT_KEY_PRESS
209        && dp->events.last_key_event.type
210        && ev->data.key.ch == dp->events.last_key_event.data.key.ch
211        && ev->data.key.utf32 == dp->events.last_key_event.data.key.utf32)
212    {
213        dp->events.last_key_ticks = 0;
214        return _get_next_event(dp, ev);
215    }
216
217    /* We are in autorepeat mode, but key has expired or a new key was
218     * pressed - store our event and return a key release event first */
219    if(dp->events.last_key_event.type
220          && (dp->events.last_key_ticks > AUTOREPEAT_THRESHOLD
221               || (ev->type & CACA_EVENT_KEY_PRESS)))
222    {
223        _push_event(dp, ev);
224        *ev = dp->events.last_key_event;
225        ev->type = CACA_EVENT_KEY_RELEASE;
226        dp->events.last_key_event.type = CACA_EVENT_NONE;
227        return 1;
228    }
229
230    /* A new key was pressed, enter autorepeat mode */
231    if(ev->type & CACA_EVENT_KEY_PRESS)
232    {
233        dp->events.last_key_ticks = 0;
234        dp->events.autorepeat_ticks = 0;
235        dp->events.last_key_event = *ev;
236    }
237
238    return ev->type ? 1 : 0;
239#endif
240}
241
242static int _lowlevel_event(caca_display_t *dp, caca_event_t *ev)
243{
244#if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO)
245    int ret = _pop_event(dp, ev);
246
247    if(ret)
248        return ret;
249#endif
250
251    return dp->drv.get_event(dp, ev);
252}
253
254#if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO) || defined(USE_GL)
255void _push_event(caca_display_t *dp, caca_event_t *ev)
256{
257    if(!ev->type || dp->events.queue == EVENTBUF_LEN)
258        return;
259    dp->events.buf[dp->events.queue] = *ev;
260    dp->events.queue++;
261}
262
263int _pop_event(caca_display_t *dp, caca_event_t *ev)
264{
265    int i;
266
267    if(dp->events.queue == 0)
268        return 0;
269
270    *ev = dp->events.buf[0];
271    for(i = 1; i < dp->events.queue; i++)
272        dp->events.buf[i - 1] = dp->events.buf[i];
273    dp->events.queue--;
274
275    return 1;
276}
277#endif
278
Note: See TracBrowser for help on using the repository browser.