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

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