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

Last change on this file since 849 was 849, checked in by Sam Hocevar, 14 years ago
  • Allow the caca_event_t *ev argument for caca_get_event() to be NULL.
  • Property svn:keywords set to Id
File size: 7.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 849 2006-04-22 19:46:43Z sam $
7 *
8 *  This library is free software; you can redistribute it and/or
9 *  modify it under the terms of the Do What The Fuck You Want To
10 *  Public License, Version 2, as published by Sam Hocevar. See
11 *  http://sam.zoy.org/wtfpl/COPYING for more details.
12 */
13
14/*
15 *  This file contains event handling functions for keyboard and mouse input.
16 */
17
18#include "config.h"
19
20#if !defined(__KERNEL__)
21#   include <stdio.h>
22#endif
23
24#include "cucul.h"
25#include "cucul_internals.h"
26#include "caca.h"
27#include "caca_internals.h"
28
29static int _get_next_event(caca_display_t *, caca_event_t *);
30static int _lowlevel_event(caca_display_t *, caca_event_t *);
31
32#if !defined(_DOXYGEN_SKIP_ME)
33/* If no new key was pressed after AUTOREPEAT_THRESHOLD usec, assume the
34 * key was released */
35#define AUTOREPEAT_THRESHOLD 200000
36/* Start repeating key after AUTOREPEAT_TRIGGER usec and send keypress
37 * events every AUTOREPEAT_RATE usec. */
38#define AUTOREPEAT_TRIGGER 300000
39#define AUTOREPEAT_RATE 100000
40#endif
41
42/** \brief Get the next mouse or keyboard input event.
43 *
44 *  This function polls the event queue for mouse or keyboard events matching
45 *  the event mask and returns the first matching event. Non-matching events
46 *  are discarded. \c event_mask must have a non-zero value.
47 *
48 *  The timeout value tells how long this function needs to wait for an
49 *  event. A value of zero returns immediately and the function returns zero
50 *  if no more events are pending in the queue. A negative value causes the
51 *  function to wait indefinitely until a matching event is received.
52 *
53 *  If not null, \c ev will be filled with information about the event
54 *  received. If null, the function will return but no information about
55 *  the event will be sent.
56 *
57 *  \param dp The libcaca graphical context.
58 *  \param event_mask Bitmask of requested events.
59 *  \param timeout A timeout value in microseconds
60 *  \param ev A pointer to a caca_event structure, or NULL.
61 *  \return 1 if a matching event was received, or 0 if the wait timeouted.
62 */
63int caca_get_event(caca_display_t *dp, unsigned int event_mask,
64                   caca_event_t *ev, int timeout)
65{
66    caca_event_t dummy_event;
67    caca_timer_t timer;
68    int usec = 0;
69
70    if(!event_mask)
71        return 0;
72
73    if(timeout > 0)
74        _caca_getticks(&timer);
75
76    if(ev == NULL)
77        ev = &dummy_event;
78
79    for( ; ; )
80    {
81        int ret = _get_next_event(dp, ev);
82
83        /* If we got the event we wanted, return */
84        if(ev->type & event_mask)
85            return ret;
86
87        /* If there is no timeout, sleep and try again. */
88        if(timeout < 0)
89        {
90            _caca_sleep(10000);
91            continue;
92        }
93
94        /* If we timeouted, return an empty event */
95        if(usec >= timeout)
96        {
97            ev->type = CACA_EVENT_NONE;
98            return 0;
99        }
100
101        /* Otherwise sleep a bit. Our granularity is far too high for values
102         * below 10000 microseconds so we cheat a bit. */
103        if(usec > 10000)
104            _caca_sleep(10000);
105        else
106            _caca_sleep(1000);
107
108        usec += _caca_getticks(&timer);
109    }
110}
111
112/** \brief Return the X mouse coordinate.
113 *
114 *  This function returns the X coordinate of the mouse position last time
115 *  it was detected. This function is not reliable if the ncurses or S-Lang
116 *  drivers are being used, because mouse position is only detected when
117 *  the mouse is clicked. Other drivers such as X11 work well.
118 *
119 *  \param dp The libcaca graphical context.
120 *  \return The X mouse coordinate.
121 */
122unsigned int caca_get_mouse_x(caca_display_t *dp)
123{
124    if(dp->mouse.x >= dp->cv->width)
125        dp->mouse.x = dp->cv->width - 1;
126
127    return dp->mouse.x;
128}
129
130/** \brief Return the Y mouse coordinate.
131 *
132 *  This function returns the Y coordinate of the mouse position last time
133 *  it was detected. This function is not reliable if the ncurses or S-Lang
134 *  drivers are being used, because mouse position is only detected when
135 *  the mouse is clicked. Other drivers such as X11 work well.
136 *
137 *  \param dp The libcaca graphical context.
138 *  \return The Y mouse coordinate.
139 */
140unsigned int caca_get_mouse_y(caca_display_t *dp)
141{
142    if(dp->mouse.y >= dp->cv->height)
143        dp->mouse.y = dp->cv->height - 1;
144
145    return dp->mouse.y;
146}
147
148/*
149 * XXX: The following functions are local.
150 */
151
152static int _get_next_event(caca_display_t *dp, caca_event_t *ev)
153{
154#if defined(USE_SLANG) || defined(USE_NCURSES)
155    unsigned int ticks;
156#endif
157    int ret;
158
159    /* If we are about to return a resize event, acknowledge it */
160    if(dp->resize.resized)
161    {
162        dp->resize.resized = 0;
163        _caca_handle_resize(dp);
164        ev->type = CACA_EVENT_RESIZE;
165        ev->data.resize.w = dp->cv->width;
166        ev->data.resize.h = dp->cv->height;
167        return 1;
168    }
169
170    ret = _lowlevel_event(dp, ev);
171
172#if defined(USE_SLANG)
173    if(dp->drv.driver != CACA_DRIVER_SLANG)
174#endif
175#if defined(USE_NCURSES)
176    if(dp->drv.driver != CACA_DRIVER_NCURSES)
177#endif
178    return ret;
179
180#if defined(USE_SLANG) || defined(USE_NCURSES)
181    /* Simulate long keypresses using autorepeat features */
182    ticks = _caca_getticks(&dp->events.key_timer);
183    dp->events.last_key_ticks += ticks;
184    dp->events.autorepeat_ticks += ticks;
185
186    /* Handle autorepeat */
187    if(dp->events.last_key_event.type
188           && dp->events.autorepeat_ticks > AUTOREPEAT_TRIGGER
189           && dp->events.autorepeat_ticks > AUTOREPEAT_THRESHOLD
190           && dp->events.autorepeat_ticks > AUTOREPEAT_RATE)
191    {
192        _push_event(dp, ev);
193        dp->events.autorepeat_ticks -= AUTOREPEAT_RATE;
194        *ev = dp->events.last_key_event;
195        return 1;
196    }
197
198    /* We are in autorepeat mode and the same key was just pressed, ignore
199     * this event and return the next one by calling ourselves. */
200    if(ev->type == CACA_EVENT_KEY_PRESS
201        && dp->events.last_key_event.type
202        && ev->data.key.ch == dp->events.last_key_event.data.key.ch
203        && ev->data.key.ucs4 == dp->events.last_key_event.data.key.ucs4)
204    {
205        dp->events.last_key_ticks = 0;
206        return _get_next_event(dp, ev);
207    }
208
209    /* We are in autorepeat mode, but key has expired or a new key was
210     * pressed - store our event and return a key release event first */
211    if(dp->events.last_key_event.type
212          && (dp->events.last_key_ticks > AUTOREPEAT_THRESHOLD
213               || (ev->type & CACA_EVENT_KEY_PRESS)))
214    {
215        _push_event(dp, ev);
216        *ev = dp->events.last_key_event;
217        ev->type = CACA_EVENT_KEY_RELEASE;
218        dp->events.last_key_event.type = CACA_EVENT_NONE;
219        return 1;
220    }
221
222    /* A new key was pressed, enter autorepeat mode */
223    if(ev->type & CACA_EVENT_KEY_PRESS)
224    {
225        dp->events.last_key_ticks = 0;
226        dp->events.autorepeat_ticks = 0;
227        dp->events.last_key_event = *ev;
228    }
229
230    return ev->type ? 1 : 0;
231#endif
232}
233
234static int _lowlevel_event(caca_display_t *dp, caca_event_t *ev)
235{
236#if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO)
237    int ret = _pop_event(dp, ev);
238
239    if(ret)
240        return ret;
241#endif
242
243    return dp->drv.get_event(dp, ev);
244}
245
246#if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO) || defined(USE_GL)
247void _push_event(caca_display_t *dp, caca_event_t *ev)
248{
249    if(!ev->type || dp->events.queue == EVENTBUF_LEN)
250        return;
251    dp->events.buf[dp->events.queue] = *ev;
252    dp->events.queue++;
253}
254
255int _pop_event(caca_display_t *dp, caca_event_t *ev)
256{
257    int i;
258
259    if(dp->events.queue == 0)
260        return 0;
261
262    *ev = dp->events.buf[0];
263    for(i = 1; i < dp->events.queue; i++)
264        dp->events.buf[i - 1] = dp->events.buf[i];
265    dp->events.queue--;
266
267    return 1;
268}
269#endif
270
Note: See TracBrowser for help on using the repository browser.