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

Last change on this file since 2305 was 2305, checked in by Sam Hocevar, 12 years ago
  • Remove all unsigned ints from exported functions. Signed arithmetic is far better for error checking.
  • Property svn:keywords set to Id
File size: 13.5 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 2305 2008-04-19 19:25:52Z 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
21#if !defined(__KERNEL__)
22#   include <stdio.h>
23#   include <string.h>
24#endif
25
26#include "cucul.h"
27#include "caca.h"
28#include "caca_internals.h"
29
30static int _get_next_event(caca_display_t *, caca_privevent_t *);
31static int _lowlevel_event(caca_display_t *, caca_privevent_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 100000
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 *  Poll the event queue for mouse or keyboard events matching the event
46 *  mask and return the first matching event. Non-matching events are
47 *  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, int event_mask,
67                   caca_event_t *ev, int timeout)
68{
69    caca_privevent_t privevent;
70    caca_timer_t timer = {0, 0};
71    int usec = 0;
72
73    if(!event_mask)
74        return 0;
75
76    if(timeout > 0)
77        _caca_getticks(&timer);
78
79    for( ; ; )
80    {
81        int ret = _get_next_event(dp, &privevent);
82
83        /* If we got the event we wanted, return */
84        if(privevent.type & event_mask)
85        {
86            if(ev)
87                memcpy(ev, &privevent, sizeof(privevent));
88            return ret;
89        }
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            privevent.type = CACA_EVENT_NONE;
102            if(ev)
103                memcpy(ev, &privevent, sizeof(privevent));
104            return 0;
105        }
106
107        /* Otherwise sleep a bit. Our granularity is far too high for values
108         * below 10000 microseconds so we cheat a bit. */
109        if(usec > 10000)
110            _caca_sleep(10000);
111        else
112            _caca_sleep(1000);
113
114        usec += _caca_getticks(&timer);
115    }
116}
117
118/** \brief Return the X mouse coordinate.
119 *
120 *  Return the X coordinate of the mouse position last time
121 *  it was detected. This function is not reliable if the ncurses or S-Lang
122 *  drivers are being used, because mouse position is only detected when
123 *  the mouse is clicked. Other drivers such as X11 work well.
124 *
125 *  This function never fails.
126 *
127 *  \param dp The libcaca graphical context.
128 *  \return The X mouse coordinate.
129 */
130int caca_get_mouse_x(caca_display_t const *dp)
131{
132    int width = cucul_get_canvas_width(dp->cv);
133
134    if(dp->mouse.x >= width)
135        return width - 1;
136
137    return dp->mouse.x;
138}
139
140/** \brief Return the Y mouse coordinate.
141 *
142 *  Return the Y coordinate of the mouse position last time
143 *  it was detected. This function is not reliable if the ncurses or S-Lang
144 *  drivers are being used, because mouse position is only detected when
145 *  the mouse is clicked. Other drivers such as X11 work well.
146 *
147 *  This function never fails.
148 *
149 *  \param dp The libcaca graphical context.
150 *  \return The Y mouse coordinate.
151 */
152int caca_get_mouse_y(caca_display_t const *dp)
153{
154    int height = cucul_get_canvas_height(dp->cv);
155
156    if(dp->mouse.y >= height)
157        return height - 1;
158
159    return dp->mouse.y;
160}
161
162/** \brief Return an event's type.
163 *
164 *  Return the type of an event. This function may always be called on an
165 *  event after caca_get_event() was called, and its return value indicates
166 *  which other functions may be called:
167 *  - \c CACA_EVENT_NONE: no other function may be called.
168 *  - \c CACA_EVENT_KEY_PRESS, \c CACA_EVENT_KEY_RELEASE:
169 *  caca_get_event_key_ch(), caca_get_event_key_utf32() and
170 *  caca_get_event_key_utf8() may be called.
171 *  - \c CACA_EVENT_MOUSE_PRESS, \c CACA_EVENT_MOUSE_RELEASE:
172 *  caca_get_event_mouse_button() may be called.
173 *  - \c CACA_EVENT_MOUSE_MOTION: caca_get_event_mouse_x() and
174 *  caca_get_event_mouse_y() may be called.
175 *  - \c CACA_EVENT_RESIZE: caca_get_event_resize_width() and
176 *  caca_get_event_resize_height() may be called.
177 *  - \c CACA_EVENT_QUIT: no other function may be called.
178 *
179 *  This function never fails.
180 *
181 *  \param ev The libcaca event.
182 *  \return The event's type.
183 */
184enum caca_event_type caca_get_event_type(caca_event_t const *ev)
185{
186    return ((caca_privevent_t const *)ev)->type;
187}
188
189/** \brief Return a key press or key release event's value
190 *
191 *  Return either the ASCII value for an event's key, or if the key is not
192 *  an ASCII character, an appropriate \e enum \e caca_key value.
193 *
194 *  This function never fails, but must only be called with a valid event of
195 *  type \c CACA_EVENT_KEY_PRESS or \c CACA_EVENT_KEY_RELEASE, or the results
196 *  will be undefined. See caca_get_event_type() for more information.
197 *
198 *  \param ev The libcaca event.
199 *  \return The key value.
200 */
201int caca_get_event_key_ch(caca_event_t const *ev)
202{
203    return ((caca_privevent_t const *)ev)->data.key.ch;
204}
205
206/** \brief Return a key press or key release event's Unicode value
207 *
208 *  Return the UTF-32/UCS-4 value for an event's key if it resolves to a
209 *  printable character.
210 *
211 *  This function never fails, but must only be called with a valid event of
212 *  type \c CACA_EVENT_KEY_PRESS or \c CACA_EVENT_KEY_RELEASE, or the results
213 *  will be undefined. See caca_get_event_type() for more information.
214 *
215 *  \param ev The libcaca event.
216 *  \return The key's Unicode value.
217 */
218uint32_t caca_get_event_key_utf32(caca_event_t const *ev)
219{
220    return ((caca_privevent_t const *)ev)->data.key.utf32;
221}
222
223/** \brief Return a key press or key release event's UTF-8 value
224 *
225 *  Write the UTF-8 value for an event's key if it resolves to a printable
226 *  character. Up to 6 UTF-8 bytes and a null termination are written.
227 *
228 *  This function never fails, but must only be called with a valid event of
229 *  type \c CACA_EVENT_KEY_PRESS or \c CACA_EVENT_KEY_RELEASE, or the results
230 *  will be undefined. See caca_get_event_type() for more information.
231 *
232 *  \param ev The libcaca event.
233 *  \return This function always returns 0.
234 */
235int caca_get_event_key_utf8(caca_event_t const *ev, char *utf8)
236{
237    memcpy(utf8, ((caca_privevent_t const *)ev)->data.key.utf8, 8);
238    return 0;
239}
240
241/** \brief Return a mouse press or mouse release event's button
242 *
243 *  Return the mouse button index for an event.
244 *
245 *  This function never fails, but must only be called with a valid event of
246 *  type \c CACA_EVENT_MOUSE_PRESS or \c CACA_EVENT_MOUSE_RELEASE, or the
247 *  results will be undefined. See caca_get_event_type() for more information.
248 *
249 *  \param ev The libcaca event.
250 *  \return The event's mouse button.
251 */
252int caca_get_event_mouse_button(caca_event_t const *ev)
253{
254    return ((caca_privevent_t const *)ev)->data.mouse.button;
255}
256
257/** \brief Return a mouse motion event's X coordinate.
258 *
259 *  Return the X coordinate for a mouse motion event.
260 *
261 *  This function never fails, but must only be called with a valid event of
262 *  type \c CACA_EVENT_MOUSE_MOTION, or the results will be undefined. See
263 *  caca_get_event_type() for more information.
264 *
265 *  \param ev The libcaca event.
266 *  \return The event's X mouse coordinate.
267 */
268int caca_get_event_mouse_x(caca_event_t const *ev)
269{
270    return ((caca_privevent_t const *)ev)->data.mouse.x;
271}
272
273/** \brief Return a mouse motion event's Y coordinate.
274 *
275 *  Return the Y coordinate for a mouse motion event.
276 *
277 *  This function never fails, but must only be called with a valid event of
278 *  type \c CACA_EVENT_MOUSE_MOTION, or the results will be undefined. See
279 *  caca_get_event_type() for more information.
280 *
281 *  \param ev The libcaca event.
282 *  \return The event's Y mouse coordinate.
283 */
284int caca_get_event_mouse_y(caca_event_t const *ev)
285{
286    return ((caca_privevent_t const *)ev)->data.mouse.y;
287}
288
289/** \brief Return a resize event's display width value.
290 *
291 *  Return the width value for a display resize event.
292 *
293 *  This function never fails, but must only be called with a valid event of
294 *  type \c CACA_EVENT_RESIZE, or the results will be undefined. See
295 *  caca_get_event_type() for more information.
296 *
297 *  \param ev The libcaca event.
298 *  \return The event's new display width value.
299 */
300int caca_get_event_resize_width(caca_event_t const *ev)
301{
302    return ((caca_privevent_t const *)ev)->data.resize.w;
303}
304
305/** \brief Return a resize event's display height value.
306 *
307 *  Return the height value for a display resize event.
308 *
309 *  This function never fails, but must only be called with a valid event of
310 *  type \c CACA_EVENT_RESIZE, or the results will be undefined. See
311 *  caca_get_event_type() for more information.
312 *
313 *  \param ev The libcaca event.
314 *  \return The event's new display height value.
315 */
316int caca_get_event_resize_height(caca_event_t const *ev)
317{
318    return ((caca_privevent_t const *)ev)->data.resize.h;
319}
320
321/*
322 * XXX: The following functions are local.
323 */
324
325static int _get_next_event(caca_display_t *dp, caca_privevent_t *ev)
326{
327#if defined(USE_SLANG) || defined(USE_NCURSES)
328    int ticks;
329#endif
330    int ret;
331
332    /* If we are about to return a resize event, acknowledge it */
333    if(dp->resize.resized)
334    {
335        dp->resize.resized = 0;
336        _caca_handle_resize(dp);
337        ev->type = CACA_EVENT_RESIZE;
338        ev->data.resize.w = cucul_get_canvas_width(dp->cv);
339        ev->data.resize.h = cucul_get_canvas_height(dp->cv);
340        return 1;
341    }
342
343    ret = _lowlevel_event(dp, ev);
344
345#if defined(USE_SLANG)
346    if(dp->drv.id != CACA_DRIVER_SLANG)
347#endif
348#if defined(USE_NCURSES)
349    if(dp->drv.id != CACA_DRIVER_NCURSES)
350#endif
351    return ret;
352
353#if defined(USE_SLANG) || defined(USE_NCURSES)
354    /* Simulate long keypresses using autorepeat features */
355    ticks = _caca_getticks(&dp->events.key_timer);
356    dp->events.last_key_ticks += ticks;
357    dp->events.autorepeat_ticks += ticks;
358
359    /* Handle autorepeat */
360    if(dp->events.last_key_event.type
361           && dp->events.autorepeat_ticks > AUTOREPEAT_TRIGGER
362           && dp->events.autorepeat_ticks > AUTOREPEAT_THRESHOLD
363           && dp->events.autorepeat_ticks > AUTOREPEAT_RATE)
364    {
365        _push_event(dp, ev);
366        dp->events.autorepeat_ticks -= AUTOREPEAT_RATE;
367        *ev = dp->events.last_key_event;
368        return 1;
369    }
370
371    /* We are in autorepeat mode and the same key was just pressed, ignore
372     * this event and return the next one by calling ourselves. */
373    if(ev->type == CACA_EVENT_KEY_PRESS
374        && dp->events.last_key_event.type
375        && ev->data.key.ch == dp->events.last_key_event.data.key.ch
376        && ev->data.key.utf32 == dp->events.last_key_event.data.key.utf32)
377    {
378        dp->events.last_key_ticks = 0;
379        return _get_next_event(dp, ev);
380    }
381
382    /* We are in autorepeat mode, but key has expired or a new key was
383     * pressed - store our event and return a key release event first */
384    if(dp->events.last_key_event.type
385          && (dp->events.last_key_ticks > AUTOREPEAT_THRESHOLD
386               || (ev->type & CACA_EVENT_KEY_PRESS)))
387    {
388        _push_event(dp, ev);
389        *ev = dp->events.last_key_event;
390        ev->type = CACA_EVENT_KEY_RELEASE;
391        dp->events.last_key_event.type = CACA_EVENT_NONE;
392        return 1;
393    }
394
395    /* A new key was pressed, enter autorepeat mode */
396    if(ev->type & CACA_EVENT_KEY_PRESS)
397    {
398        dp->events.last_key_ticks = 0;
399        dp->events.autorepeat_ticks = 0;
400        dp->events.last_key_event = *ev;
401    }
402
403    return ev->type ? 1 : 0;
404#endif
405}
406
407static int _lowlevel_event(caca_display_t *dp, caca_privevent_t *ev)
408{
409#if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO)
410    int ret = _pop_event(dp, ev);
411
412    if(ret)
413        return ret;
414#endif
415
416    return dp->drv.get_event(dp, ev);
417}
418
419#if defined(USE_SLANG) || defined(USE_NCURSES) || defined(USE_CONIO) || defined(USE_GL)
420void _push_event(caca_display_t *dp, caca_privevent_t *ev)
421{
422    if(!ev->type || dp->events.queue == EVENTBUF_LEN)
423        return;
424    dp->events.buf[dp->events.queue] = *ev;
425    dp->events.queue++;
426}
427
428int _pop_event(caca_display_t *dp, caca_privevent_t *ev)
429{
430    int i;
431
432    if(dp->events.queue == 0)
433        return 0;
434
435    *ev = dp->events.buf[0];
436    for(i = 1; i < dp->events.queue; i++)
437        dp->events.buf[i - 1] = dp->events.buf[i];
438    dp->events.queue--;
439
440    return 1;
441}
442#endif
443
Note: See TracBrowser for help on using the repository browser.