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

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