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

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