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

Last change on this file since 332 was 332, checked in by Sam Hocevar, 19 years ago
  • src/graphics: + Initialise the char buffers in the X11 driver.
  • src/io.c: + caca_get_event() and caca_wait_event() immediately return zero if

event_mask is zero.

+ Added support for the Home, End, PgUp? and PgDown? keys.
+ In the S-Lang driver, added a call to SLang_getkey() before the call to

SLkp_getkey(), otherwise a single Escape call will not be interpreted.

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