source: libcaca/branches/0.7/src/io.c @ 4104

Last change on this file since 4104 was 325, checked in by Sam Hocevar, 16 years ago
  • src/io.c: + Do not report mouse motions if the coordinates did not change.
  • src/bitmap.c: + More documentation.
  • Property svn:keywords set to Id
File size: 17.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 325 2004-01-07 18:15:10Z 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 void _push_key(unsigned int);
58static unsigned int _pop_key(void);
59static unsigned int _read_key(void);
60
61#if !defined(_DOXYGEN_SKIP_ME)
62#define KEY_BUFLEN 10
63#endif
64static unsigned int keybuf[KEY_BUFLEN + 1]; /* zero-terminated */
65static int keys = 0;
66
67/** \brief Get the next mouse or keyboard input event.
68 *
69 *  This function polls the event queue for mouse or keyboard events matching
70 *  the event mask and returns the first matching event. Non-matching events
71 *  are discarded. It is non-blocking and returns zero if no more events are
72 *  pending in the queue. See also caca_wait_event() for a blocking version
73 *  of this function.
74 *
75 * \param event_mask Bitmask of requested events.
76 * \return The next matching event in the queue, or 0 if no event is pending.
77 */
78unsigned int caca_get_event(unsigned int event_mask)
79{
80    for( ; ; )
81    {
82        unsigned int event = _get_next_event();
83
84        if(!event || event & event_mask)
85            return event;
86    }
87}
88
89/** \brief Wait for the next mouse or keyboard input event.
90 *
91 *  This function returns the first mouse or keyboard event in the queue
92 *  that matches the event mask. If no event is pending, it blocks until a
93 *  matching event is received. See also caca_get_event() for a non-blocking
94 *  version of this function.
95 *
96 *  \param event_mask Bitmask of requested events.
97 *  \return The next event in the queue.
98 */
99unsigned int caca_wait_event(unsigned int event_mask)
100{
101    for( ; ; )
102    {
103        unsigned int event = _get_next_event();
104
105        if(event & event_mask)
106            return event;
107
108        usleep(1000);
109    }
110}
111
112/*
113 * XXX: The following functions are local.
114 */
115
116static unsigned int _get_next_event(void)
117{
118    unsigned int event = 0;
119
120#if defined(USE_X11)
121    /* The X11 event check routine */
122    if(_caca_driver == CACA_DRIVER_X11)
123    {
124        XEvent xevent;
125        static unsigned int x11_x = 0, x11_y = 0;
126        long int xevent_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask
127                                | ButtonReleaseMask | PointerMotionMask;
128        char key;
129
130        while(XCheckWindowEvent(x11_dpy, x11_window, xevent_mask, &xevent)
131               == True)
132        {
133            KeySym keysym;
134
135            /* Check for mouse motion events */
136            if(xevent.type == MotionNotify)
137            {
138                unsigned int newx = xevent.xmotion.x / x11_font_width;
139                unsigned int newy = xevent.xmotion.y / x11_font_height;
140
141                if(newx >= _caca_width)
142                    newx = _caca_width - 1;
143                if(newy >= _caca_height)
144                    newy = _caca_height - 1;
145
146                if(x11_x == newx && x11_y == newy)
147                    continue;
148
149                x11_x = newx & 0xfff;
150                x11_y = newy & 0xfff;
151
152                return CACA_EVENT_MOUSE_MOTION | (newx << 12) | (newy << 0);
153            }
154
155            /* Check for mouse press and release events */
156            if(xevent.type == ButtonPress)
157                return CACA_EVENT_MOUSE_PRESS
158                        | ((XButtonEvent *)&xevent)->button;
159
160            if(xevent.type == ButtonRelease)
161                return CACA_EVENT_MOUSE_RELEASE
162                        | ((XButtonEvent *)&xevent)->button;
163
164            /* Check for key press and release events */
165            if(xevent.type == KeyPress)
166                event |= CACA_EVENT_KEY_PRESS;
167            else if(xevent.type == KeyRelease)
168                event |= CACA_EVENT_KEY_RELEASE;
169            else
170                continue;
171
172            if(XLookupString(&xevent.xkey, &key, 1, NULL, NULL))
173                return event | key;
174
175            keysym = XKeycodeToKeysym(x11_dpy, xevent.xkey.keycode, 0);
176            switch(keysym)
177            {
178            case XK_F1:    return event | CACA_KEY_F1;
179            case XK_F2:    return event | CACA_KEY_F2;
180            case XK_F3:    return event | CACA_KEY_F3;
181            case XK_F4:    return event | CACA_KEY_F4;
182            case XK_F5:    return event | CACA_KEY_F5;
183            case XK_F6:    return event | CACA_KEY_F6;
184            case XK_F7:    return event | CACA_KEY_F7;
185            case XK_F8:    return event | CACA_KEY_F8;
186            case XK_F9:    return event | CACA_KEY_F9;
187            case XK_F10:   return event | CACA_KEY_F10;
188            case XK_F11:   return event | CACA_KEY_F11;
189            case XK_F12:   return event | CACA_KEY_F12;
190            case XK_F13:   return event | CACA_KEY_F13;
191            case XK_F14:   return event | CACA_KEY_F14;
192            case XK_F15:   return event | CACA_KEY_F15;
193            case XK_Left:  return event | CACA_KEY_LEFT;
194            case XK_Right: return event | CACA_KEY_RIGHT;
195            case XK_Up:    return event | CACA_KEY_UP;
196            case XK_Down:  return event | CACA_KEY_DOWN;
197            default:       return 0;
198            }
199        }
200
201        return 0;
202    }
203#endif
204
205    /* Read all available key events and push them into the buffer */
206    while(keys < KEY_BUFLEN)
207    {
208        unsigned int key = _read_key();
209        if(!key)
210            break;
211        _push_key(key);
212    }
213
214    /* If no keys were read, return */
215    if(!keys)
216        return 0;
217
218#if defined(USE_NCURSES)
219    if(_caca_driver == CACA_DRIVER_NCURSES)
220    {
221        if(keybuf[0] == KEY_MOUSE)
222        {
223            MEVENT mevent;
224            _pop_key();
225            getmouse(&mevent);
226
227            switch(mevent.bstate)
228            {
229                case BUTTON1_PRESSED:
230                    _push_key(CACA_EVENT_MOUSE_PRESS | 1);
231                    break;
232                case BUTTON1_RELEASED:
233                    _push_key(CACA_EVENT_MOUSE_RELEASE | 1);
234                    break;
235                case BUTTON1_CLICKED:
236                    _push_key(CACA_EVENT_MOUSE_PRESS | 1);
237                    _push_key(CACA_EVENT_MOUSE_RELEASE | 1);
238                    break;
239                case BUTTON1_DOUBLE_CLICKED:
240                    _push_key(CACA_EVENT_MOUSE_PRESS | 1);
241                    _push_key(CACA_EVENT_MOUSE_RELEASE | 1);
242                    _push_key(CACA_EVENT_MOUSE_PRESS | 1);
243                    _push_key(CACA_EVENT_MOUSE_RELEASE | 1);
244                    break;
245                case BUTTON1_TRIPLE_CLICKED:
246                    _push_key(CACA_EVENT_MOUSE_PRESS | 1);
247                    _push_key(CACA_EVENT_MOUSE_RELEASE | 1);
248                    _push_key(CACA_EVENT_MOUSE_PRESS | 1);
249                    _push_key(CACA_EVENT_MOUSE_RELEASE | 1);
250                    _push_key(CACA_EVENT_MOUSE_PRESS | 1);
251                    _push_key(CACA_EVENT_MOUSE_RELEASE | 1);
252                    break;
253                case BUTTON1_RESERVED_EVENT:
254                    break;
255
256                case BUTTON2_PRESSED:
257                    _push_key(CACA_EVENT_MOUSE_PRESS | 2);
258                    break;
259                case BUTTON2_RELEASED:
260                    _push_key(CACA_EVENT_MOUSE_RELEASE | 2);
261                    break;
262                case BUTTON2_CLICKED:
263                    _push_key(CACA_EVENT_MOUSE_PRESS | 2);
264                    _push_key(CACA_EVENT_MOUSE_RELEASE | 2);
265                    break;
266                case BUTTON2_DOUBLE_CLICKED:
267                    _push_key(CACA_EVENT_MOUSE_PRESS | 2);
268                    _push_key(CACA_EVENT_MOUSE_RELEASE | 2);
269                    _push_key(CACA_EVENT_MOUSE_PRESS | 2);
270                    _push_key(CACA_EVENT_MOUSE_RELEASE | 2);
271                    break;
272                case BUTTON2_TRIPLE_CLICKED:
273                    _push_key(CACA_EVENT_MOUSE_PRESS | 2);
274                    _push_key(CACA_EVENT_MOUSE_RELEASE | 2);
275                    _push_key(CACA_EVENT_MOUSE_PRESS | 2);
276                    _push_key(CACA_EVENT_MOUSE_RELEASE | 2);
277                    _push_key(CACA_EVENT_MOUSE_PRESS | 2);
278                    _push_key(CACA_EVENT_MOUSE_RELEASE | 2);
279                    break;
280                case BUTTON2_RESERVED_EVENT:
281                    break;
282
283                case BUTTON3_PRESSED:
284                    _push_key(CACA_EVENT_MOUSE_PRESS | 3);
285                    break;
286                case BUTTON3_RELEASED:
287                    _push_key(CACA_EVENT_MOUSE_RELEASE | 3);
288                    break;
289                case BUTTON3_CLICKED:
290                    _push_key(CACA_EVENT_MOUSE_PRESS | 3);
291                    _push_key(CACA_EVENT_MOUSE_RELEASE | 3);
292                    break;
293                case BUTTON3_DOUBLE_CLICKED:
294                    _push_key(CACA_EVENT_MOUSE_PRESS | 3);
295                    _push_key(CACA_EVENT_MOUSE_RELEASE | 3);
296                    _push_key(CACA_EVENT_MOUSE_PRESS | 3);
297                    _push_key(CACA_EVENT_MOUSE_RELEASE | 3);
298                    break;
299                case BUTTON3_TRIPLE_CLICKED:
300                    _push_key(CACA_EVENT_MOUSE_PRESS | 3);
301                    _push_key(CACA_EVENT_MOUSE_RELEASE | 3);
302                    _push_key(CACA_EVENT_MOUSE_PRESS | 3);
303                    _push_key(CACA_EVENT_MOUSE_RELEASE | 3);
304                    _push_key(CACA_EVENT_MOUSE_PRESS | 3);
305                    _push_key(CACA_EVENT_MOUSE_RELEASE | 3);
306                    break;
307                case BUTTON3_RESERVED_EVENT:
308                    break;
309
310                case BUTTON4_PRESSED:
311                    _push_key(CACA_EVENT_MOUSE_PRESS | 4);
312                    break;
313                case BUTTON4_RELEASED:
314                    _push_key(CACA_EVENT_MOUSE_RELEASE | 4);
315                    break;
316                case BUTTON4_CLICKED:
317                    _push_key(CACA_EVENT_MOUSE_PRESS | 4);
318                    _push_key(CACA_EVENT_MOUSE_RELEASE | 4);
319                    break;
320                case BUTTON4_DOUBLE_CLICKED:
321                    _push_key(CACA_EVENT_MOUSE_PRESS | 4);
322                    _push_key(CACA_EVENT_MOUSE_RELEASE | 4);
323                    _push_key(CACA_EVENT_MOUSE_PRESS | 4);
324                    _push_key(CACA_EVENT_MOUSE_RELEASE | 4);
325                    break;
326                case BUTTON4_TRIPLE_CLICKED:
327                    _push_key(CACA_EVENT_MOUSE_PRESS | 4);
328                    _push_key(CACA_EVENT_MOUSE_RELEASE | 4);
329                    _push_key(CACA_EVENT_MOUSE_PRESS | 4);
330                    _push_key(CACA_EVENT_MOUSE_RELEASE | 4);
331                    _push_key(CACA_EVENT_MOUSE_PRESS | 4);
332                    _push_key(CACA_EVENT_MOUSE_RELEASE | 4);
333                    break;
334                case BUTTON4_RESERVED_EVENT:
335                    break;
336
337                default:
338                    break;
339            }
340
341            return CACA_EVENT_MOUSE_MOTION | (mevent.x << 12) | mevent.y;
342        }
343
344        switch(keybuf[0])
345        {
346            case KEY_UP: event = CACA_EVENT_KEY_PRESS | CACA_KEY_UP; break;
347            case KEY_DOWN: event = CACA_EVENT_KEY_PRESS | CACA_KEY_DOWN; break;
348            case KEY_LEFT: event = CACA_EVENT_KEY_PRESS | CACA_KEY_LEFT; break;
349            case KEY_RIGHT: event = CACA_EVENT_KEY_PRESS | CACA_KEY_RIGHT; break;
350
351            case KEY_F(1): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F1; break;
352            case KEY_F(2): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F2; break;
353            case KEY_F(3): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F3; break;
354            case KEY_F(4): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F4; break;
355            case KEY_F(5): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F5; break;
356            case KEY_F(6): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F6; break;
357            case KEY_F(7): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F7; break;
358            case KEY_F(8): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F8; break;
359            case KEY_F(9): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F9; break;
360            case KEY_F(10): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F10; break;
361            case KEY_F(11): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F11; break;
362            case KEY_F(12): event = CACA_EVENT_KEY_PRESS | CACA_KEY_F12; break;
363        }
364
365        if(event)
366        {
367            _pop_key();
368            if(event & CACA_EVENT_KEY_PRESS)
369                _push_key(CACA_EVENT_KEY_RELEASE
370                           | (event & ~CACA_EVENT_KEY_PRESS));
371            return event;
372        }
373    }
374#endif
375
376    /* If it's already a special event, return it */
377    if((keybuf[0] & ~0xff) != 0)
378        return _pop_key();
379
380    /* If it's not an escape sequence, return the key */
381    if(keybuf[0] != '\x1b')
382    {
383        event = _pop_key();
384        _push_key(CACA_EVENT_KEY_RELEASE | event);
385        return CACA_EVENT_KEY_PRESS | event;
386    }
387
388    /*
389     * Handle known escape sequences
390     */
391
392    _pop_key();
393
394    if(keybuf[0] == 'O' && keybuf[1] >= 'P' && keybuf[1] <= 'S')
395    {
396        /* ^[OP ^[OQ ^[OR ^[OS */
397        static unsigned int keylist[] =
398            { CACA_KEY_F1, CACA_KEY_F2, CACA_KEY_F3, CACA_KEY_F4 };
399        _pop_key();
400        event = keylist[_pop_key() - 'P'];
401        _push_key(CACA_EVENT_KEY_RELEASE | event);
402        return CACA_EVENT_KEY_PRESS | event;
403    }
404    else if(keybuf[0] == '[' && keybuf[1] >= 'A' && keybuf[1] <= 'D')
405    {
406        /* ^[[A ^[[B ^[[C ^[[D */
407        static unsigned int keylist[] =
408            { CACA_KEY_UP, CACA_KEY_DOWN, CACA_KEY_RIGHT, CACA_KEY_LEFT };
409        _pop_key();
410        event = keylist[_pop_key() - 'A'];
411        _push_key(CACA_EVENT_KEY_RELEASE | event);
412        return CACA_EVENT_KEY_PRESS | event;
413    }
414    else if(keybuf[0] == '[' && keybuf[1] == 'M' &&
415            keybuf[2] && keybuf[3] && keybuf[3])
416    {
417        int button;
418
419        /* ^[[Mxxx */
420        _pop_key();
421        _pop_key();
422        button = (1 + _pop_key() - ' ') & 0xf;
423        _push_key(CACA_EVENT_MOUSE_PRESS | button);
424        _push_key(CACA_EVENT_MOUSE_RELEASE | button);
425        return CACA_EVENT_MOUSE_MOTION
426                | ((_pop_key() - '!') << 12) | ((_pop_key() - '!') << 0);
427    }
428    else if(keybuf[0] == '[' && keybuf[1] == '1' && keybuf[3] == '~' &&
429            keybuf[2] >= '5' && keybuf[2] != '6' && keybuf[2] <= '9')
430    {
431        /* ^[[15~ ^[[17~ ^[[18~ ^[[19~ */
432        static unsigned int keylist[] =
433            { CACA_KEY_F5, 0, CACA_KEY_F6, CACA_KEY_F7, CACA_KEY_F8 };
434        _pop_key();
435        _pop_key();
436        event = keylist[_pop_key() - '5'];
437        _pop_key();
438        _push_key(CACA_EVENT_KEY_RELEASE | event);
439        return CACA_EVENT_KEY_PRESS | event;
440    }
441    else if(keybuf[0] == '[' && keybuf[1] == '2' && keybuf[3] == '~' &&
442            keybuf[2] >= '0' && keybuf[2] != '2' && keybuf[2] <= '4')
443    {
444        /* ^[[20~ ^[[21~ ^[[23~ ^[[24~ */
445        static unsigned int keylist[] =
446            { CACA_KEY_F9, CACA_KEY_F10, 0, CACA_KEY_F11, CACA_KEY_F12 };
447        _pop_key();
448        _pop_key();
449        event = keylist[_pop_key() - '0'];
450        _pop_key();
451        _push_key(CACA_EVENT_KEY_RELEASE | event);
452        return CACA_EVENT_KEY_PRESS | event;
453    }
454
455    /* Unknown escape sequence: return the ESC key */
456    _push_key(CACA_EVENT_KEY_RELEASE | '\x1b');
457    return CACA_EVENT_KEY_PRESS | '\x1b';
458}
459
460static void _push_key(unsigned int key)
461{
462    if(keys == KEY_BUFLEN)
463        return;
464    keybuf[keys] = key;
465    keys++;
466    keybuf[keys] = 0;
467}
468
469static unsigned int _pop_key(void)
470{
471    int i;
472    unsigned int key = keybuf[0];
473    keys--;
474    for(i = 0; i < keys; i++)
475        keybuf[i] = keybuf[i + 1];
476    keybuf[keys] = 0;
477
478    return key;
479}
480
481static unsigned int _read_key(void)
482{
483#if defined(USE_NCURSES)
484    int intkey;
485#endif
486#if defined(USE_X11)
487#endif
488
489    switch(_caca_driver)
490    {
491#if defined(USE_SLANG)
492    case CACA_DRIVER_SLANG:
493        return SLang_input_pending(0) ? SLang_getkey() : 0;
494#endif
495#if defined(USE_NCURSES)
496    case CACA_DRIVER_NCURSES:
497        intkey = getch();
498        return (intkey == ERR) ? 0 : intkey;
499#endif
500#if defined(USE_CONIO)
501    case CACA_DRIVER_CONIO:
502        return _conio_kbhit() ? getch() : 0;
503#endif
504#if defined(USE_X11)
505    case CACA_DRIVER_X11:
506#endif
507    default:
508        break;
509    }
510
511    return 0;
512}
513
Note: See TracBrowser for help on using the repository browser.