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

Last change on this file since 4096 was 4094, checked in by Sam Hocevar, 11 years ago

Gather profiling information in caca_refresh_display() and caca_get_event().

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