source: libcaca/trunk/src/io.c @ 331

Last change on this file since 331 was 331, checked in by Sam Hocevar, 17 years ago
  • test/event.c: + Do not refresh after each event, but only when there is no event

pending.

+ If the pressed key is a printable character, display it.

  • src/time.c: + Moved _caca_getticks() to this file.
  • src/caca.c: + Set the escape delay to a very low value in the ncurses driver,

because I don't want escape sequences to be entered manually.

  • src/io.c: + Autorepeat emulation in the ncurses and slang drivers: do not

immediately send the key release event.

  • configure.ac: + Check for usleep. + Improvements in the win32 platform detection.
  • Property svn:keywords set to Id
File size: 17.6 KB
Line 
1/*
2 *  libcaca       ASCII-Art library
3 *  Copyright (c) 2002, 2003 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 GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2 of the License, or (at your option) any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 *  02111-1307  USA
20 */
21
22/** \file io.c
23 *  \version \$Id: io.c 331 2004-01-09 09:51:53Z sam $
24 *  \author Sam Hocevar <sam@zoy.org>
25 *  \brief Event handling
26 *
27 *  This file contains event handling functions for keyboard and mouse input.
28 */
29
30#include "config.h"
31
32#if defined(USE_SLANG)
33#   if defined(HAVE_SLANG_SLANG_H)
34#       include <slang/slang.h>
35#   else
36#       include <slang.h>
37#   endif
38#endif
39#if defined(USE_NCURSES)
40#   include <curses.h>
41#endif
42#if defined(USE_CONIO)
43#   include <conio.h>
44#endif
45#if defined(USE_X11)
46#   include <X11/Xlib.h>
47#   include <X11/Xutil.h>
48#   include <X11/keysym.h>
49#endif
50
51#include <unistd.h>
52
53#include "caca.h"
54#include "caca_internals.h"
55
56static unsigned int _get_next_event(void);
57static unsigned int _lowlevel_event(void);
58static void _push_event(unsigned int);
59static unsigned int _pop_event(void);
60
61#if !defined(_DOXYGEN_SKIP_ME)
62#define EVENTBUF_LEN 10
63#endif
64static unsigned int eventbuf[EVENTBUF_LEN];
65static int events = 0;
66
67#if !defined(_DOXYGEN_SKIP_ME)
68/* If no new key was pressed after AUTOREPEAT_THRESHOLD usec, assume the
69 * key was released */
70#define AUTOREPEAT_THRESHOLD 200000
71/* Start repeating key after AUTOREPEAT_TRIGGER usec and send keypress
72 * events every AUTOREPEAT_RATE usec. */
73#define AUTOREPEAT_TRIGGER 300000
74#define AUTOREPEAT_RATE 100000
75#endif
76
77/** \brief Get the next mouse or keyboard input event.
78 *
79 *  This function polls the event queue for mouse or keyboard events matching
80 *  the event mask and returns the first matching event. Non-matching events
81 *  are discarded. It is non-blocking and returns zero if no more events are
82 *  pending in the queue. See also caca_wait_event() for a blocking version
83 *  of this function.
84 *
85 * \param event_mask Bitmask of requested events.
86 * \return The next matching event in the queue, or 0 if no event is pending.
87 */
88unsigned int caca_get_event(unsigned int event_mask)
89{
90    for( ; ; )
91    {
92        unsigned int event = _get_next_event();
93
94        if(!event || event & event_mask)
95            return event;
96    }
97}
98
99/** \brief Wait for the next mouse or keyboard input event.
100 *
101 *  This function returns the first mouse or keyboard event in the queue
102 *  that matches the event mask. If no event is pending, it blocks until a
103 *  matching event is received. See also caca_get_event() for a non-blocking
104 *  version of this function.
105 *
106 *  \param event_mask Bitmask of requested events.
107 *  \return The next event in the queue.
108 */
109unsigned int caca_wait_event(unsigned int event_mask)
110{
111    for( ; ; )
112    {
113        unsigned int event = _get_next_event();
114
115        if(event & event_mask)
116            return event;
117
118#if defined(HAVE_USLEEP)
119        usleep(10000);
120#elif defined(HAVE_SLEEP)
121        Sleep(10);
122#else
123        SLEEP
124#endif
125    }
126}
127
128/*
129 * XXX: The following functions are local.
130 */
131
132static unsigned int _get_next_event(void)
133{
134#if defined(USE_SLANG) || defined(USE_NCURSES)
135    static struct caca_timer key_timer = CACA_TIMER_INITIALIZER;
136    static unsigned int last_key_ticks = 0;
137    static unsigned int autorepeat_ticks = 0;
138    static unsigned int last_key = 0;
139    unsigned int ticks;
140#endif
141    unsigned int event = _lowlevel_event();
142
143#if defined(USE_SLANG)
144    if(_caca_driver != CACA_DRIVER_SLANG)
145#endif
146#if defined(USE_NCURSES)
147    if(_caca_driver != CACA_DRIVER_NCURSES)
148#endif
149    return event;
150
151#if defined(USE_SLANG) || defined(USE_NCURSES)
152    /* Simulate long keypresses using autorepeat features */
153    ticks = _caca_getticks(&key_timer);
154    last_key_ticks += ticks;
155    autorepeat_ticks += ticks;
156
157    /* Handle autorepeat */
158    if(last_key && autorepeat_ticks > AUTOREPEAT_TRIGGER
159                && autorepeat_ticks > AUTOREPEAT_THRESHOLD
160                && autorepeat_ticks > AUTOREPEAT_RATE)
161    {
162        _push_event(event);
163        autorepeat_ticks -= AUTOREPEAT_RATE;
164        return CACA_EVENT_KEY_PRESS | last_key;
165    }
166
167    /* We are in autorepeat mode and the same key was just pressed, ignore
168     * this event and return the next one by calling ourselves. */
169    if(event == (CACA_EVENT_KEY_PRESS | last_key))
170    {
171        last_key_ticks = 0;
172        return _get_next_event();
173    }
174
175    /* We are in autorepeat mode, but key has expired or a new key was
176     * pressed - store our event and return a key release event first */
177    if(last_key && (last_key_ticks > AUTOREPEAT_THRESHOLD
178                     || (event & CACA_EVENT_KEY_PRESS)))
179    {
180        _push_event(event);
181        event = CACA_EVENT_KEY_RELEASE | last_key;
182        last_key = 0;
183        return event;
184    }
185
186    /* A new key was pressed, enter autorepeat mode */
187    if(event & CACA_EVENT_KEY_PRESS)
188    {
189        last_key_ticks = 0;
190        autorepeat_ticks = 0;
191        last_key = event & 0x00ffffff;
192    }
193
194    return event;
195#endif
196}
197
198static unsigned int _lowlevel_event(void)
199{
200    unsigned int event = _pop_event();
201
202    if(event)
203        return event;
204
205#if defined(USE_X11)
206    /* The X11 event check routine */
207    if(_caca_driver == CACA_DRIVER_X11)
208    {
209        XEvent xevent;
210        static unsigned int x11_x = 0, x11_y = 0;
211        long int xevent_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
212                                | ButtonReleaseMask | PointerMotionMask;
213        char key;
214
215        while(XCheckWindowEvent(x11_dpy, x11_window, xevent_mask, &xevent)
216               == True)
217        {
218            KeySym keysym;
219
220            /* Check for mouse motion events */
221            if(xevent.type == MotionNotify)
222            {
223                unsigned int newx = xevent.xmotion.x / x11_font_width;
224                unsigned int newy = xevent.xmotion.y / x11_font_height;
225
226                if(newx >= _caca_width)
227                    newx = _caca_width - 1;
228                if(newy >= _caca_height)
229                    newy = _caca_height - 1;
230
231                if(x11_x == newx && x11_y == newy)
232                    continue;
233
234                x11_x = newx & 0xfff;
235                x11_y = newy & 0xfff;
236
237                return CACA_EVENT_MOUSE_MOTION | (newx << 12) | (newy << 0);
238            }
239
240            /* Check for mouse press and release events */
241            if(xevent.type == ButtonPress)
242                return CACA_EVENT_MOUSE_PRESS
243                        | ((XButtonEvent *)&xevent)->button;
244
245            if(xevent.type == ButtonRelease)
246                return CACA_EVENT_MOUSE_RELEASE
247                        | ((XButtonEvent *)&xevent)->button;
248
249            /* Check for key press and release events */
250            if(xevent.type == KeyPress)
251                event |= CACA_EVENT_KEY_PRESS;
252            else if(xevent.type == KeyRelease)
253                event |= CACA_EVENT_KEY_RELEASE;
254            else
255                continue;
256
257            if(XLookupString(&xevent.xkey, &key, 1, NULL, NULL))
258                return event | key;
259
260            keysym = XKeycodeToKeysym(x11_dpy, xevent.xkey.keycode, 0);
261            switch(keysym)
262            {
263            case XK_F1:    return event | CACA_KEY_F1;
264            case XK_F2:    return event | CACA_KEY_F2;
265            case XK_F3:    return event | CACA_KEY_F3;
266            case XK_F4:    return event | CACA_KEY_F4;
267            case XK_F5:    return event | CACA_KEY_F5;
268            case XK_F6:    return event | CACA_KEY_F6;
269            case XK_F7:    return event | CACA_KEY_F7;
270            case XK_F8:    return event | CACA_KEY_F8;
271            case XK_F9:    return event | CACA_KEY_F9;
272            case XK_F10:   return event | CACA_KEY_F10;
273            case XK_F11:   return event | CACA_KEY_F11;
274            case XK_F12:   return event | CACA_KEY_F12;
275            case XK_F13:   return event | CACA_KEY_F13;
276            case XK_F14:   return event | CACA_KEY_F14;
277            case XK_F15:   return event | CACA_KEY_F15;
278            case XK_Left:  return event | CACA_KEY_LEFT;
279            case XK_Right: return event | CACA_KEY_RIGHT;
280            case XK_Up:    return event | CACA_KEY_UP;
281            case XK_Down:  return event | CACA_KEY_DOWN;
282            default:       return 0;
283            }
284        }
285
286        return 0;
287    }
288    else
289#endif
290#if defined(USE_NCURSES)
291    if(_caca_driver == CACA_DRIVER_NCURSES)
292    {
293        int intkey = getch();
294        if(intkey == ERR)
295            return 0;
296
297        if(intkey < 0x100)
298        {
299            return CACA_EVENT_KEY_PRESS | intkey;
300        }
301
302        if(intkey == KEY_MOUSE)
303        {
304            MEVENT mevent;
305            getmouse(&mevent);
306
307            switch(mevent.bstate)
308            {
309                case BUTTON1_PRESSED:
310                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
311                    break;
312                case BUTTON1_RELEASED:
313                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
314                    break;
315                case BUTTON1_CLICKED:
316                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
317                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
318                    break;
319                case BUTTON1_DOUBLE_CLICKED:
320                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
321                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
322                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
323                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
324                    break;
325                case BUTTON1_TRIPLE_CLICKED:
326                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
327                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
328                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
329                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
330                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
331                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
332                    break;
333                case BUTTON1_RESERVED_EVENT:
334                    break;
335
336                case BUTTON2_PRESSED:
337                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
338                    break;
339                case BUTTON2_RELEASED:
340                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
341                    break;
342                case BUTTON2_CLICKED:
343                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
344                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
345                    break;
346                case BUTTON2_DOUBLE_CLICKED:
347                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
348                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
349                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
350                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
351                    break;
352                case BUTTON2_TRIPLE_CLICKED:
353                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
354                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
355                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
356                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
357                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
358                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
359                    break;
360                case BUTTON2_RESERVED_EVENT:
361                    break;
362
363                case BUTTON3_PRESSED:
364                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
365                    break;
366                case BUTTON3_RELEASED:
367                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
368                    break;
369                case BUTTON3_CLICKED:
370                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
371                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
372                    break;
373                case BUTTON3_DOUBLE_CLICKED:
374                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
375                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
376                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
377                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
378                    break;
379                case BUTTON3_TRIPLE_CLICKED:
380                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
381                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
382                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
383                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
384                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
385                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
386                    break;
387                case BUTTON3_RESERVED_EVENT:
388                    break;
389
390                case BUTTON4_PRESSED:
391                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
392                    break;
393                case BUTTON4_RELEASED:
394                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
395                    break;
396                case BUTTON4_CLICKED:
397                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
398                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
399                    break;
400                case BUTTON4_DOUBLE_CLICKED:
401                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
402                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
403                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
404                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
405                    break;
406                case BUTTON4_TRIPLE_CLICKED:
407                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
408                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
409                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
410                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
411                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
412                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
413                    break;
414                case BUTTON4_RESERVED_EVENT:
415                    break;
416
417                default:
418                    break;
419            }
420
421            return CACA_EVENT_MOUSE_MOTION | (mevent.x << 12) | mevent.y;
422        }
423
424        switch(intkey)
425        {
426            case KEY_UP: event = CACA_KEY_UP; break;
427            case KEY_DOWN: event = CACA_KEY_DOWN; break;
428            case KEY_LEFT: event = CACA_KEY_LEFT; break;
429            case KEY_RIGHT: event = CACA_KEY_RIGHT; break;
430
431            case KEY_F(1): event = CACA_KEY_F1; break;
432            case KEY_F(2): event = CACA_KEY_F2; break;
433            case KEY_F(3): event = CACA_KEY_F3; break;
434            case KEY_F(4): event = CACA_KEY_F4; break;
435            case KEY_F(5): event = CACA_KEY_F5; break;
436            case KEY_F(6): event = CACA_KEY_F6; break;
437            case KEY_F(7): event = CACA_KEY_F7; break;
438            case KEY_F(8): event = CACA_KEY_F8; break;
439            case KEY_F(9): event = CACA_KEY_F9; break;
440            case KEY_F(10): event = CACA_KEY_F10; break;
441            case KEY_F(11): event = CACA_KEY_F11; break;
442            case KEY_F(12): event = CACA_KEY_F12; break;
443        }
444
445        return CACA_EVENT_KEY_PRESS | event;
446    }
447    else
448#endif
449#if defined(USE_SLANG)
450    if(_caca_driver == CACA_DRIVER_SLANG)
451    {
452        int intkey;
453
454        if(!SLang_input_pending(0))
455            return 0;
456
457        intkey = SLkp_getkey();
458
459        if(intkey < 0x100)
460        {
461            return CACA_EVENT_KEY_PRESS | intkey;
462        }
463
464        if(intkey == 0x3e9)
465        {
466            int button = (SLang_getkey() - ' ' + 1) & 0xf;
467            int x = SLang_getkey() - '!';
468            int y = SLang_getkey() - '!';
469            _push_event(CACA_EVENT_MOUSE_PRESS | button);
470            _push_event(CACA_EVENT_MOUSE_RELEASE | button);
471            return CACA_EVENT_MOUSE_MOTION | (x << 12) | (y << 0);
472        }
473
474        switch(intkey)
475        {
476            case SL_KEY_UP: event = CACA_KEY_UP; break;
477            case SL_KEY_DOWN: event = CACA_KEY_DOWN; break;
478            case SL_KEY_LEFT: event = CACA_KEY_LEFT; break;
479            case SL_KEY_RIGHT: event = CACA_KEY_RIGHT; break;
480
481            case SL_KEY_F(1): event = CACA_KEY_F1; break;
482            case SL_KEY_F(2): event = CACA_KEY_F2; break;
483            case SL_KEY_F(3): event = CACA_KEY_F3; break;
484            case SL_KEY_F(4): event = CACA_KEY_F4; break;
485            case SL_KEY_F(5): event = CACA_KEY_F5; break;
486            case SL_KEY_F(6): event = CACA_KEY_F6; break;
487            case SL_KEY_F(7): event = CACA_KEY_F7; break;
488            case SL_KEY_F(8): event = CACA_KEY_F8; break;
489            case SL_KEY_F(9): event = CACA_KEY_F9; break;
490            case SL_KEY_F(10): event = CACA_KEY_F10; break;
491            case SL_KEY_F(11): event = CACA_KEY_F11; break;
492            case SL_KEY_F(12): event = CACA_KEY_F12; break;
493        }
494
495        return CACA_EVENT_KEY_PRESS | event;
496    }
497    else
498#endif
499#if defined(USE_CONIO)
500    if(_caca_driver == CACA_DRIVER_CONIO)
501    {
502        if(!_conio_kbhit())
503            return 0;
504
505        event = getch();
506        _push_event(CACA_EVENT_KEY_RELEASE | event);
507        return CACA_EVENT_KEY_PRESS | event;
508    }
509    else
510#endif
511    {
512        /* Dummy */
513    }
514
515    return 0;
516}
517
518static void _push_event(unsigned int event)
519{
520    if(events == EVENTBUF_LEN)
521        return;
522    eventbuf[events] = event;
523    events++;
524}
525
526static unsigned int _pop_event(void)
527{
528    int i;
529    unsigned int event;
530
531    if(events == 0)
532        return 0;
533
534    event = eventbuf[0];
535    for(i = 1; i < events; i++)
536        eventbuf[i - 1] = eventbuf[i];
537    events--;
538
539    return event;
540}
541
Note: See TracBrowser for help on using the repository browser.