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

Last change on this file since 335 was 335, checked in by Sam Hocevar, 17 years ago
  • configure.ac: + Check for gettimeofday().
  • src/time.c: + Created _caca_time(). + Ported _caca_getticks() to the Win32 API.
  • src/caca.c: + Properly builds on Win32.
  • test/event.c: + Added <stdlib.h> because we use malloc().
  • Property svn:keywords set to Id
File size: 19.1 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 335 2004-01-11 01:45:57Z 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#if defined(USE_WIN32)
51#   include <windows.h>
52#endif
53
54#include "caca.h"
55#include "caca_internals.h"
56
57static unsigned int _get_next_event(void);
58static unsigned int _lowlevel_event(void);
59static void _push_event(unsigned int);
60static unsigned int _pop_event(void);
61
62#if !defined(_DOXYGEN_SKIP_ME)
63#define EVENTBUF_LEN 10
64#endif
65static unsigned int eventbuf[EVENTBUF_LEN];
66static int events = 0;
67
68#if !defined(_DOXYGEN_SKIP_ME)
69/* If no new key was pressed after AUTOREPEAT_THRESHOLD usec, assume the
70 * key was released */
71#define AUTOREPEAT_THRESHOLD 200000
72/* Start repeating key after AUTOREPEAT_TRIGGER usec and send keypress
73 * events every AUTOREPEAT_RATE usec. */
74#define AUTOREPEAT_TRIGGER 300000
75#define AUTOREPEAT_RATE 100000
76#endif
77
78/** \brief Get the next mouse or keyboard input event.
79 *
80 *  This function polls the event queue for mouse or keyboard events matching
81 *  the event mask and returns the first matching event. Non-matching events
82 *  are discarded. \c event_mask must have a non-zero value. This function is
83 *  non-blocking and returns zero if no more events are pending in the queue.
84 *  See also caca_wait_event() for a blocking version of this function.
85 *
86 * \param event_mask Bitmask of requested events.
87 * \return The next matching event in the queue, or 0 if no event is pending.
88 */
89unsigned int caca_get_event(unsigned int event_mask)
90{
91    if(!event_mask)
92        return CACA_EVENT_NONE;
93
94    for( ; ; )
95    {
96        unsigned int event = _get_next_event();
97
98        if(!event || event & event_mask)
99            return event;
100    }
101}
102
103/** \brief Wait for the next mouse or keyboard input event.
104 *
105 *  This function returns the first mouse or keyboard event in the queue
106 *  that matches the event mask. If no event is pending, it blocks until a
107 *  matching event is received. \c event_mask must have a non-zero value.
108 *  See also caca_get_event() for a non-blocking version of this function.
109 *
110 *  \param event_mask Bitmask of requested events.
111 *  \return The next event in the queue.
112 */
113unsigned int caca_wait_event(unsigned int event_mask)
114{
115    if(!event_mask)
116        return CACA_EVENT_NONE;
117
118    for( ; ; )
119    {
120        unsigned int event = _get_next_event();
121
122        if(event & event_mask)
123            return event;
124
125        _caca_sleep(10000);
126    }
127}
128
129/*
130 * XXX: The following functions are local.
131 */
132
133static unsigned int _get_next_event(void)
134{
135#if defined(USE_SLANG) || defined(USE_NCURSES)
136    static struct caca_timer key_timer = CACA_TIMER_INITIALIZER;
137    static unsigned int last_key_ticks = 0;
138    static unsigned int autorepeat_ticks = 0;
139    static unsigned int last_key = 0;
140    unsigned int ticks;
141#endif
142    unsigned int event = _lowlevel_event();
143
144#if defined(USE_SLANG)
145    if(_caca_driver != CACA_DRIVER_SLANG)
146#endif
147#if defined(USE_NCURSES)
148    if(_caca_driver != CACA_DRIVER_NCURSES)
149#endif
150    return event;
151
152#if defined(USE_SLANG) || defined(USE_NCURSES)
153    /* Simulate long keypresses using autorepeat features */
154    ticks = _caca_getticks(&key_timer);
155    last_key_ticks += ticks;
156    autorepeat_ticks += ticks;
157
158    /* Handle autorepeat */
159    if(last_key && autorepeat_ticks > AUTOREPEAT_TRIGGER
160                && autorepeat_ticks > AUTOREPEAT_THRESHOLD
161                && autorepeat_ticks > AUTOREPEAT_RATE)
162    {
163        _push_event(event);
164        autorepeat_ticks -= AUTOREPEAT_RATE;
165        return CACA_EVENT_KEY_PRESS | last_key;
166    }
167
168    /* We are in autorepeat mode and the same key was just pressed, ignore
169     * this event and return the next one by calling ourselves. */
170    if(event == (CACA_EVENT_KEY_PRESS | last_key))
171    {
172        last_key_ticks = 0;
173        return _get_next_event();
174    }
175
176    /* We are in autorepeat mode, but key has expired or a new key was
177     * pressed - store our event and return a key release event first */
178    if(last_key && (last_key_ticks > AUTOREPEAT_THRESHOLD
179                     || (event & CACA_EVENT_KEY_PRESS)))
180    {
181        _push_event(event);
182        event = CACA_EVENT_KEY_RELEASE | last_key;
183        last_key = 0;
184        return event;
185    }
186
187    /* A new key was pressed, enter autorepeat mode */
188    if(event & CACA_EVENT_KEY_PRESS)
189    {
190        last_key_ticks = 0;
191        autorepeat_ticks = 0;
192        last_key = event & 0x00ffffff;
193    }
194
195    return event;
196#endif
197}
198
199static unsigned int _lowlevel_event(void)
200{
201    unsigned int event = _pop_event();
202
203    if(event)
204        return event;
205
206#if defined(USE_X11)
207    /* The X11 event check routine */
208    if(_caca_driver == CACA_DRIVER_X11)
209    {
210        XEvent xevent;
211        static unsigned int x11_x = 0, x11_y = 0;
212        long int xevent_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
213                                | ButtonReleaseMask | PointerMotionMask;
214        char key;
215
216        while(XCheckWindowEvent(x11_dpy, x11_window, xevent_mask, &xevent)
217               == True)
218        {
219            KeySym keysym;
220
221            /* Check for mouse motion events */
222            if(xevent.type == MotionNotify)
223            {
224                unsigned int newx = xevent.xmotion.x / x11_font_width;
225                unsigned int newy = xevent.xmotion.y / x11_font_height;
226
227                if(newx >= _caca_width)
228                    newx = _caca_width - 1;
229                if(newy >= _caca_height)
230                    newy = _caca_height - 1;
231
232                if(x11_x == newx && x11_y == newy)
233                    continue;
234
235                x11_x = newx & 0xfff;
236                x11_y = newy & 0xfff;
237
238                return CACA_EVENT_MOUSE_MOTION | (newx << 12) | (newy << 0);
239            }
240
241            /* Check for mouse press and release events */
242            if(xevent.type == ButtonPress)
243                return CACA_EVENT_MOUSE_PRESS
244                        | ((XButtonEvent *)&xevent)->button;
245
246            if(xevent.type == ButtonRelease)
247                return CACA_EVENT_MOUSE_RELEASE
248                        | ((XButtonEvent *)&xevent)->button;
249
250            /* Check for key press and release events */
251            if(xevent.type == KeyPress)
252                event |= CACA_EVENT_KEY_PRESS;
253            else if(xevent.type == KeyRelease)
254                event |= CACA_EVENT_KEY_RELEASE;
255            else
256                continue;
257
258            if(XLookupString(&xevent.xkey, &key, 1, NULL, NULL))
259                return event | key;
260
261            keysym = XKeycodeToKeysym(x11_dpy, xevent.xkey.keycode, 0);
262            switch(keysym)
263            {
264            case XK_F1:    return event | CACA_KEY_F1;
265            case XK_F2:    return event | CACA_KEY_F2;
266            case XK_F3:    return event | CACA_KEY_F3;
267            case XK_F4:    return event | CACA_KEY_F4;
268            case XK_F5:    return event | CACA_KEY_F5;
269            case XK_F6:    return event | CACA_KEY_F6;
270            case XK_F7:    return event | CACA_KEY_F7;
271            case XK_F8:    return event | CACA_KEY_F8;
272            case XK_F9:    return event | CACA_KEY_F9;
273            case XK_F10:   return event | CACA_KEY_F10;
274            case XK_F11:   return event | CACA_KEY_F11;
275            case XK_F12:   return event | CACA_KEY_F12;
276            case XK_F13:   return event | CACA_KEY_F13;
277            case XK_F14:   return event | CACA_KEY_F14;
278            case XK_F15:   return event | CACA_KEY_F15;
279            case XK_Left:  return event | CACA_KEY_LEFT;
280            case XK_Right: return event | CACA_KEY_RIGHT;
281            case XK_Up:    return event | CACA_KEY_UP;
282            case XK_Down:  return event | CACA_KEY_DOWN;
283            default:       return CACA_EVENT_NONE;
284            }
285        }
286
287        return CACA_EVENT_NONE;
288    }
289    else
290#endif
291#if defined(USE_NCURSES)
292    if(_caca_driver == CACA_DRIVER_NCURSES)
293    {
294        int intkey = getch();
295        if(intkey == ERR)
296            return CACA_EVENT_NONE;
297
298        if(intkey < 0x100)
299        {
300            return CACA_EVENT_KEY_PRESS | intkey;
301        }
302
303        if(intkey == KEY_MOUSE)
304        {
305            MEVENT mevent;
306            getmouse(&mevent);
307
308            switch(mevent.bstate)
309            {
310                case BUTTON1_PRESSED:
311                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
312                    break;
313                case BUTTON1_RELEASED:
314                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
315                    break;
316                case BUTTON1_CLICKED:
317                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
318                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
319                    break;
320                case BUTTON1_DOUBLE_CLICKED:
321                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
322                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
323                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
324                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
325                    break;
326                case BUTTON1_TRIPLE_CLICKED:
327                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
328                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
329                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
330                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
331                    _push_event(CACA_EVENT_MOUSE_PRESS | 1);
332                    _push_event(CACA_EVENT_MOUSE_RELEASE | 1);
333                    break;
334                case BUTTON1_RESERVED_EVENT:
335                    break;
336
337                case BUTTON2_PRESSED:
338                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
339                    break;
340                case BUTTON2_RELEASED:
341                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
342                    break;
343                case BUTTON2_CLICKED:
344                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
345                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
346                    break;
347                case BUTTON2_DOUBLE_CLICKED:
348                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
349                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
350                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
351                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
352                    break;
353                case BUTTON2_TRIPLE_CLICKED:
354                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
355                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
356                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
357                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
358                    _push_event(CACA_EVENT_MOUSE_PRESS | 2);
359                    _push_event(CACA_EVENT_MOUSE_RELEASE | 2);
360                    break;
361                case BUTTON2_RESERVED_EVENT:
362                    break;
363
364                case BUTTON3_PRESSED:
365                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
366                    break;
367                case BUTTON3_RELEASED:
368                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
369                    break;
370                case BUTTON3_CLICKED:
371                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
372                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
373                    break;
374                case BUTTON3_DOUBLE_CLICKED:
375                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
376                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
377                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
378                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
379                    break;
380                case BUTTON3_TRIPLE_CLICKED:
381                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
382                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
383                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
384                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
385                    _push_event(CACA_EVENT_MOUSE_PRESS | 3);
386                    _push_event(CACA_EVENT_MOUSE_RELEASE | 3);
387                    break;
388                case BUTTON3_RESERVED_EVENT:
389                    break;
390
391                case BUTTON4_PRESSED:
392                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
393                    break;
394                case BUTTON4_RELEASED:
395                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
396                    break;
397                case BUTTON4_CLICKED:
398                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
399                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
400                    break;
401                case BUTTON4_DOUBLE_CLICKED:
402                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
403                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
404                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
405                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
406                    break;
407                case BUTTON4_TRIPLE_CLICKED:
408                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
409                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
410                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
411                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
412                    _push_event(CACA_EVENT_MOUSE_PRESS | 4);
413                    _push_event(CACA_EVENT_MOUSE_RELEASE | 4);
414                    break;
415                case BUTTON4_RESERVED_EVENT:
416                    break;
417
418                default:
419                    break;
420            }
421
422            return CACA_EVENT_MOUSE_MOTION | (mevent.x << 12) | mevent.y;
423        }
424
425        event = CACA_EVENT_KEY_PRESS;
426
427        switch(intkey)
428        {
429            case KEY_UP: return event | CACA_KEY_UP;
430            case KEY_DOWN: return event | CACA_KEY_DOWN;
431            case KEY_LEFT: return event | CACA_KEY_LEFT;
432            case KEY_RIGHT: return event | CACA_KEY_RIGHT;
433
434            case KEY_IC: return event | CACA_KEY_INSERT;
435            case KEY_DC: return event | CACA_KEY_DELETE;
436            case KEY_HOME: return event | CACA_KEY_HOME;
437            case KEY_END: return event | CACA_KEY_END;
438            case KEY_PPAGE: return event | CACA_KEY_PAGEUP;
439            case KEY_NPAGE: return event | CACA_KEY_PAGEDOWN;
440
441            case KEY_F(1): return event | CACA_KEY_F1;
442            case KEY_F(2): return event | CACA_KEY_F2;
443            case KEY_F(3): return event | CACA_KEY_F3;
444            case KEY_F(4): return event | CACA_KEY_F4;
445            case KEY_F(5): return event | CACA_KEY_F5;
446            case KEY_F(6): return event | CACA_KEY_F6;
447            case KEY_F(7): return event | CACA_KEY_F7;
448            case KEY_F(8): return event | CACA_KEY_F8;
449            case KEY_F(9): return event | CACA_KEY_F9;
450            case KEY_F(10): return event | CACA_KEY_F10;
451            case KEY_F(11): return event | CACA_KEY_F11;
452            case KEY_F(12): return event | CACA_KEY_F12;
453        }
454
455        return CACA_EVENT_NONE;
456    }
457    else
458#endif
459#if defined(USE_SLANG)
460    if(_caca_driver == CACA_DRIVER_SLANG)
461    {
462        int intkey;
463
464        if(!SLang_input_pending(0))
465            return CACA_EVENT_NONE;
466
467        /* We first use SLang_getkey() to see whether Esc was pressed
468         * alone, then (if it wasn't) we unget the key and use SLkp_getkey()
469         * instead, so that escape sequences are interpreted. */
470        intkey = SLang_getkey();
471
472        if(intkey != 0x1b /* Esc */ || SLang_input_pending(0))
473        {
474            SLang_ungetkey(intkey);
475            intkey = SLkp_getkey();
476        }
477
478        /* If the key was ASCII, return it immediately */
479        if(intkey < 0x100)
480        {
481            return CACA_EVENT_KEY_PRESS | intkey;
482        }
483
484        if(intkey == 0x3e9)
485        {
486            int button = (SLang_getkey() - ' ' + 1) & 0xf;
487            int x = SLang_getkey() - '!';
488            int y = SLang_getkey() - '!';
489            _push_event(CACA_EVENT_MOUSE_PRESS | button);
490            _push_event(CACA_EVENT_MOUSE_RELEASE | button);
491            return CACA_EVENT_MOUSE_MOTION | (x << 12) | (y << 0);
492        }
493
494        event = CACA_EVENT_KEY_PRESS;
495
496        switch(intkey)
497        {
498            case SL_KEY_UP: return event | CACA_KEY_UP;
499            case SL_KEY_DOWN: return event | CACA_KEY_DOWN;
500            case SL_KEY_LEFT: return event | CACA_KEY_LEFT;
501            case SL_KEY_RIGHT: return event | CACA_KEY_RIGHT;
502
503            case SL_KEY_IC: return event | CACA_KEY_INSERT;
504            case SL_KEY_DELETE: return event | CACA_KEY_DELETE;
505            case SL_KEY_HOME: return event | CACA_KEY_HOME;
506            case SL_KEY_END: return event | CACA_KEY_END;
507            case SL_KEY_PPAGE: return event | CACA_KEY_PAGEUP;
508            case SL_KEY_NPAGE: return event | CACA_KEY_PAGEDOWN;
509
510            case SL_KEY_F(1): return event | CACA_KEY_F1;
511            case SL_KEY_F(2): return event | CACA_KEY_F2;
512            case SL_KEY_F(3): return event | CACA_KEY_F3;
513            case SL_KEY_F(4): return event | CACA_KEY_F4;
514            case SL_KEY_F(5): return event | CACA_KEY_F5;
515            case SL_KEY_F(6): return event | CACA_KEY_F6;
516            case SL_KEY_F(7): return event | CACA_KEY_F7;
517            case SL_KEY_F(8): return event | CACA_KEY_F8;
518            case SL_KEY_F(9): return event | CACA_KEY_F9;
519            case SL_KEY_F(10): return event | CACA_KEY_F10;
520            case SL_KEY_F(11): return event | CACA_KEY_F11;
521            case SL_KEY_F(12): return event | CACA_KEY_F12;
522        }
523
524        return CACA_EVENT_NONE;
525    }
526    else
527#endif
528#if defined(USE_CONIO)
529    if(_caca_driver == CACA_DRIVER_CONIO)
530    {
531        if(!_conio_kbhit())
532            return CACA_EVENT_NONE;
533
534        event = getch();
535        _push_event(CACA_EVENT_KEY_RELEASE | event);
536        return CACA_EVENT_KEY_PRESS | event;
537    }
538    else
539#endif
540#if defined(USE_WIN32)
541    if(_caca_driver == CACA_DRIVER_WIN32)
542    {
543        return CACA_EVENT_NONE;
544    }
545    else
546#endif
547    {
548        /* Dummy */
549    }
550
551    return CACA_EVENT_NONE;
552}
553
554static void _push_event(unsigned int event)
555{
556    if(events == EVENTBUF_LEN)
557        return;
558    eventbuf[events] = event;
559    events++;
560}
561
562static unsigned int _pop_event(void)
563{
564    int i;
565    unsigned int event;
566
567    if(events == 0)
568        return CACA_EVENT_NONE;
569
570    event = eventbuf[0];
571    for(i = 1; i < events; i++)
572        eventbuf[i - 1] = eventbuf[i];
573    events--;
574
575    return event;
576}
577
Note: See TracBrowser for help on using the repository browser.