summaryrefslogtreecommitdiff
path: root/Source/DirectFB/systems/x11/x11input.c
blob: 0b711e0e31b554f88a5a5ff92da9c2ed6c3aaf44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
/*
   (c) Copyright 2001-2009  The world wide DirectFB Open Source Community (directfb.org)
   (c) Copyright 2000-2004  Convergence (integrated media) GmbH

   All rights reserved.

   Written by Denis Oliver Kropp <dok@directfb.org>,
              Andreas Hundt <andi@fischlustig.de>,
              Sven Neumann <neo@directfb.org>,
              Ville Syrjälä <syrjala@sci.fi> and
              Claudio Ciccani <klan@users.sf.net>.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#include <config.h>

#include <fusion/types.h>

#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <directfb.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>


#include <core/coredefs.h>
#include <core/coretypes.h>
#include <core/input.h>
#include <core/layer_context.h>
#include <core/layer_control.h>
#include <core/layers_internal.h>
#include <core/system.h>

#include <direct/mem.h>
#include <direct/thread.h>

#include "xwindow.h"

#include "primary.h"
#include "x11.h"

#define DFB_INPUTDRIVER_HAS_SENSITIVITY

#include <core/input_driver.h>

D_DEBUG_DOMAIN( X11_Input, "X11/Input", "X11 Input/Key/Mouse handling" );

DFB_INPUT_DRIVER( x11input )


extern DFBX11 *dfb_x11;

/*
 * declaration of private data
 */
typedef struct {
     CoreInputDevice*    device;
     DirectThread*       thread;
     DFBX11*             x11;
     bool                stop;
} X11InputData;


static DFBInputEvent motionX = {
     .type    = DIET_UNKNOWN,
     .axisabs = 0,
};

static DFBInputEvent motionY = {
     .type    = DIET_UNKNOWN,
     .axisabs = 0,
};

static void
motion_compress( int x, int y, const XEvent *xEvent )
{
     if (motionX.axisabs != x) {
          motionX.type    = DIET_AXISMOTION;
          motionX.flags   = DIEF_AXISABS | DIEF_TIMESTAMP;
          motionX.axis    = DIAI_X;
          motionX.axisabs = x;

          motionX.timestamp.tv_sec  =  xEvent->xmotion.time / 1000;
          motionX.timestamp.tv_usec = (xEvent->xmotion.time % 1000) * 1000;
     }

     if (motionY.axisabs != y) {
          motionY.type    = DIET_AXISMOTION;
          motionY.flags   = DIEF_AXISABS | DIEF_TIMESTAMP;
          motionY.axis    = DIAI_Y;
          motionY.axisabs = y;

          motionY.timestamp.tv_sec  =  xEvent->xmotion.time / 1000;
          motionY.timestamp.tv_usec = (xEvent->xmotion.time % 1000) * 1000;
     }
}

static void
motion_realize( X11InputData *data )
{
     if (motionX.type != DIET_UNKNOWN) {
          if (motionY.type != DIET_UNKNOWN)
               motionX.flags |= DIEF_FOLLOW;

          if (dfb_x11->shared->xw) {
               motionX.flags |= DIEF_MIN | DIEF_MAX;
               motionX.min    = 0;
               motionX.max    = dfb_x11->shared->xw->width - 1;
          }

          dfb_input_dispatch( data->device, &motionX );

          motionX.type = DIET_UNKNOWN;
     }

     if (motionY.type != DIET_UNKNOWN) {
          if (dfb_x11->shared->xw) {
               motionY.flags |= DIEF_MIN | DIEF_MAX;
               motionY.min    = 0;
               motionY.max    = dfb_x11->shared->xw->height - 1;
          }

          dfb_input_dispatch( data->device, &motionY );

          motionY.type = DIET_UNKNOWN;
     }
}

static DFBInputDeviceKeyIdentifier
xsymbol_to_id( KeySym xKeySymbol )
{
     switch (xKeySymbol) {
          case XK_a                : return DIKI_A;     
          case XK_b                : return DIKI_B;     
          case XK_c                : return DIKI_C;     
          case XK_d                : return DIKI_D;     
          case XK_e                : return DIKI_E;     
          case XK_f                : return DIKI_F;     
          case XK_g                : return DIKI_G;     
          case XK_h                : return DIKI_H;     
          case XK_i                : return DIKI_I;     
          case XK_j                : return DIKI_J;     
          case XK_k                : return DIKI_K;     
          case XK_l                : return DIKI_L;     
          case XK_m                : return DIKI_M;     
          case XK_n                : return DIKI_N;     
          case XK_o                : return DIKI_O;     
          case XK_p                : return DIKI_P;     
          case XK_q                : return DIKI_Q;     
          case XK_r                : return DIKI_R;     
          case XK_s                : return DIKI_S;     
          case XK_t                : return DIKI_T;     
          case XK_u                : return DIKI_U;     
          case XK_v                : return DIKI_V;     
          case XK_w                : return DIKI_W;     
          case XK_x                : return DIKI_X;     
          case XK_y                : return DIKI_Y;     
          case XK_z                : return DIKI_Z;     
          case XK_0                : return DIKI_0;     
          case XK_1                : return DIKI_1;     
          case XK_2                : return DIKI_2;     
          case XK_3                : return DIKI_3;     
          case XK_4                : return DIKI_4;     
          case XK_5                : return DIKI_5;     
          case XK_6                : return DIKI_6;     
          case XK_7                : return DIKI_7;     
          case XK_8                : return DIKI_8;     
          case XK_9                : return DIKI_9;     
          case XK_F1               : return DIKI_F1;    
          case XK_F2               : return DIKI_F2;    
          case XK_F3               : return DIKI_F3;    
          case XK_F4               : return DIKI_F4;    
          case XK_F5               : return DIKI_F5;    
          case XK_F6               : return DIKI_F6;    
          case XK_F7               : return DIKI_F7;    
          case XK_F8               : return DIKI_F8;    
          case XK_F9               : return DIKI_F9;    
          case XK_F10              : return DIKI_F10;   
          case XK_F11              : return DIKI_F11;   
          case XK_F12              : return DIKI_F12;   

          case XK_Shift_L          : return DIKI_SHIFT_L;     
          case XK_Shift_R          : return DIKI_SHIFT_R;     
          case XK_Control_L        : return DIKI_CONTROL_L;   
          case XK_Control_R        : return DIKI_CONTROL_R;   
          case XK_Alt_L            : return DIKI_ALT_L;       
          case XK_Alt_R            : return DIKI_ALT_R;       
          case XK_Meta_L           : return DIKI_META_L;      
          case XK_Meta_R           : return DIKI_META_R;      
          case XK_Super_L          : return DIKI_SUPER_L;     
          case XK_Super_R          : return DIKI_SUPER_R;     
          case XK_Hyper_L          : return DIKI_HYPER_L;     
          case XK_Hyper_R          : return DIKI_HYPER_R;     
          case XK_Mode_switch      : return DIKI_ALT_R;

          case XK_Caps_Lock        : return DIKI_CAPS_LOCK;   
          case XK_Num_Lock         : return DIKI_NUM_LOCK;    
          case XK_Scroll_Lock      : return DIKI_SCROLL_LOCK; 

          case XK_Escape           : return DIKI_ESCAPE;      
          case XK_Left             : return DIKI_LEFT;        
          case XK_Right            : return DIKI_RIGHT;       
          case XK_Up               : return DIKI_UP;          
          case XK_Down             : return DIKI_DOWN;        

          case XK_Tab              : return DIKI_TAB;
          case XK_ISO_Left_Tab     : return DIKI_TAB;
          case XK_Return           : return DIKI_ENTER;       
          case XK_space            : return DIKI_SPACE;       
          case XK_BackSpace        : return DIKI_BACKSPACE;   
          case XK_Insert           : return DIKI_INSERT;      
          case XK_Delete           : return DIKI_DELETE;      
          case XK_Home             : return DIKI_HOME;       
          case XK_End              : return DIKI_END;        
          case XK_Page_Up          : return DIKI_PAGE_UP;    
          case XK_Page_Down        : return DIKI_PAGE_DOWN;  
          case XK_Print            : return DIKI_PRINT;
          case XK_Pause            : return DIKI_PAUSE;       

          /*  The labels on these keys depend on the type of keyboard.
           *  We've choosen the names from a US keyboard layout. The
           *  comments refer to the ISO 9995 terminology.
           */
          case XK_quoteleft        : return DIKI_QUOTE_LEFT;   /*  TLDE  */
          case XK_minus            : return DIKI_MINUS_SIGN;   /*  AE11  */
          case XK_equal            : return DIKI_EQUALS_SIGN;  /*  AE12  */
          case XK_bracketleft      : return DIKI_BRACKET_LEFT; /*  AD11  */
          case XK_bracketright     : return DIKI_BRACKET_RIGHT;/*  AD12  */
          case XK_backslash        : return DIKI_BACKSLASH;    /*  BKSL  */
          case XK_semicolon        : return DIKI_SEMICOLON;    /*  AC10  */
          case XK_quoteright       : return DIKI_QUOTE_RIGHT;  /*  AC11  */
          case XK_comma            : return DIKI_COMMA;        /*  AB08  */
          case XK_period           : return DIKI_PERIOD;       /*  AB09  */
          case XK_slash            : return DIKI_SLASH;        /*  AB10  */
          case XK_less             : return DIKI_LESS_SIGN;    /*  103rd  */

          case XK_KP_Divide        : return DIKI_KP_DIV;      
          case XK_KP_Multiply      : return DIKI_KP_MULT;     
          case XK_KP_Subtract      : return DIKI_KP_MINUS;    
          case XK_KP_Add           : return DIKI_KP_PLUS;     
          case XK_KP_Enter         : return DIKI_KP_ENTER;    
          case XK_KP_Space         : return DIKI_KP_SPACE;    
          case XK_KP_Tab           : return DIKI_KP_TAB;      
          case XK_KP_F1            : return DIKI_KP_F1;       
          case XK_KP_F2            : return DIKI_KP_F2;       
          case XK_KP_F3            : return DIKI_KP_F3;       
          case XK_KP_F4            : return DIKI_KP_F4;       
          case XK_KP_Equal         : return DIKI_KP_EQUAL;    
          case XK_KP_Separator     : return DIKI_KP_SEPARATOR;
                                   
          case XK_KP_Delete        : return DIKI_KP_DECIMAL;
          case XK_KP_Insert        : return DIKI_KP_0;      
          case XK_KP_End           : return DIKI_KP_1;      
          case XK_KP_Down          : return DIKI_KP_2;      
          case XK_KP_Page_Down     : return DIKI_KP_3;      
          case XK_KP_Left          : return DIKI_KP_4;      
          case XK_KP_Begin         : return DIKI_KP_5;      
          case XK_KP_Right         : return DIKI_KP_6;      
          case XK_KP_Home          : return DIKI_KP_7;      
          case XK_KP_Up            : return DIKI_KP_8;      
          case XK_KP_Page_Up       : return DIKI_KP_9;

          case XK_KP_Decimal       : return DIKI_KP_DECIMAL;
          case XK_KP_0             : return DIKI_KP_0;      
          case XK_KP_1             : return DIKI_KP_1;      
          case XK_KP_2             : return DIKI_KP_2;      
          case XK_KP_3             : return DIKI_KP_3;      
          case XK_KP_4             : return DIKI_KP_4;      
          case XK_KP_5             : return DIKI_KP_5;      
          case XK_KP_6             : return DIKI_KP_6;      
          case XK_KP_7             : return DIKI_KP_7;      
          case XK_KP_8             : return DIKI_KP_8;      
          case XK_KP_9             : return DIKI_KP_9;

          case 0                   : break;

          default:
               D_DEBUG_AT( X11_Input, "Unknown key symbol 0x%lx\n", xKeySymbol);
     }    

     return DIKI_UNKNOWN;
}

static DFBInputDeviceKeySymbol
xsymbol_to_symbol( KeySym xKeySymbol )
{
     if (xKeySymbol >= 0x20 && xKeySymbol <= 0xff)
          return xKeySymbol;

     if (xKeySymbol >= XK_F1 && xKeySymbol <= XK_F35)
          return DFB_FUNCTION_KEY( xKeySymbol - XK_F1 + 1 );

     switch (xKeySymbol) {
          case XK_Shift_L          : return DIKS_SHIFT;
          case XK_Shift_R          : return DIKS_SHIFT;     
          case XK_Control_L        : return DIKS_CONTROL;   
          case XK_Control_R        : return DIKS_CONTROL;   
          case XK_Alt_L            : return DIKS_ALT;       
          case XK_Alt_R            : return DIKS_ALT;       
          case XK_Meta_L           : return DIKS_META;      
          case XK_Meta_R           : return DIKS_META;      
          case XK_Super_L          : return DIKS_SUPER;     
          case XK_Super_R          : return DIKS_SUPER;     
          case XK_Hyper_L          : return DIKS_HYPER;     
          case XK_Hyper_R          : return DIKS_HYPER;     
          case XK_Mode_switch      : return DIKS_ALTGR;

          case XK_Caps_Lock        : return DIKS_CAPS_LOCK;   
          case XK_Num_Lock         : return DIKS_NUM_LOCK;    
          case XK_Scroll_Lock      : return DIKS_SCROLL_LOCK; 

          case XK_Escape           : return DIKS_ESCAPE;      
          case XK_Left             : return DIKS_CURSOR_LEFT;
          case XK_Right            : return DIKS_CURSOR_RIGHT;       
          case XK_Up               : return DIKS_CURSOR_UP;          
          case XK_Down             : return DIKS_CURSOR_DOWN;        

          case XK_Tab              : return DIKS_TAB;
          case XK_ISO_Left_Tab     : return DIKS_TAB;
          case XK_Return           : return DIKS_ENTER;       
          case XK_space            : return DIKS_SPACE;       
          case XK_BackSpace        : return DIKS_BACKSPACE;   
          case XK_Insert           : return DIKS_INSERT;      
          case XK_Delete           : return DIKS_DELETE;      
          case XK_Home             : return DIKS_HOME;       
          case XK_End              : return DIKS_END;        
          case XK_Page_Up          : return DIKS_PAGE_UP;    
          case XK_Page_Down        : return DIKS_PAGE_DOWN;  
          case XK_Print            : return DIKS_PRINT;
          case XK_Pause            : return DIKS_PAUSE;       

          case XK_KP_Divide        : return DIKS_SLASH;
          case XK_KP_Multiply      : return DIKS_ASTERISK;     
          case XK_KP_Subtract      : return DIKS_MINUS_SIGN;    
          case XK_KP_Add           : return DIKS_PLUS_SIGN;     
          case XK_KP_Enter         : return DIKS_ENTER;    
          case XK_KP_Space         : return DIKS_SPACE;    
          case XK_KP_Tab           : return DIKS_TAB;      
          case XK_KP_F1            : return DIKS_F1;       
          case XK_KP_F2            : return DIKS_F2;       
          case XK_KP_F3            : return DIKS_F3;       
          case XK_KP_F4            : return DIKS_F4;       
          case XK_KP_Equal         : return DIKS_EQUALS_SIGN;    
          case XK_KP_Separator     : return DIKS_COLON; /* FIXME: what is a separator */
                                   
          case XK_KP_Delete        : return DIKS_DELETE;
          case XK_KP_Insert        : return DIKS_INSERT;      
          case XK_KP_End           : return DIKS_END;      
          case XK_KP_Down          : return DIKS_CURSOR_DOWN;      
          case XK_KP_Page_Down     : return DIKS_PAGE_DOWN;      
          case XK_KP_Left          : return DIKS_CURSOR_LEFT;      
          case XK_KP_Begin         : return DIKS_BEGIN;      
          case XK_KP_Right         : return DIKS_CURSOR_RIGHT;      
          case XK_KP_Home          : return DIKS_HOME;      
          case XK_KP_Up            : return DIKS_CURSOR_UP;      
          case XK_KP_Page_Up       : return DIKS_PAGE_UP;

          case XK_KP_Decimal       : return DIKS_PERIOD;
          case XK_KP_0             : return DIKS_0;      
          case XK_KP_1             : return DIKS_1;      
          case XK_KP_2             : return DIKS_2;      
          case XK_KP_3             : return DIKS_3;      
          case XK_KP_4             : return DIKS_4;      
          case XK_KP_5             : return DIKS_5;      
          case XK_KP_6             : return DIKS_6;      
          case XK_KP_7             : return DIKS_7;      
          case XK_KP_8             : return DIKS_8;      
          case XK_KP_9             : return DIKS_9;

          case 0                   : break;

          default:
               D_DEBUG("X11: Unknown key symbol 0x%lx\n", xKeySymbol);
     }    

     return DIKS_NULL;
}



static void handleMouseEvent(XEvent* pXEvent, X11InputData* pData)
{
     static int          iMouseEventCount = 0;
     DFBInputEvent  dfbEvent;
     if (pXEvent->type == MotionNotify) {
          motion_compress( pXEvent->xmotion.x, pXEvent->xmotion.y, pXEvent );
          ++iMouseEventCount;
     }

     if ( pXEvent->type == ButtonPress || pXEvent->type == ButtonRelease ) {
          if ( pXEvent->type == ButtonPress )
               dfbEvent.type = DIET_BUTTONPRESS;
          else
               dfbEvent.type = DIET_BUTTONRELEASE;

          dfbEvent.flags = DIEF_TIMESTAMP;

          /* Get pressed button */
          switch ( pXEvent->xbutton.button ) {
               case 1:
                    dfbEvent.button = DIBI_LEFT;
                    break;
               case 2:
                    dfbEvent.button = DIBI_MIDDLE;
                    break;
               case 3:
                    dfbEvent.button = DIBI_RIGHT;
                    break;
                    //Wheel events
               case 4: /*up*/
               case 5: /*down*/
               case 6: /*left*/
               case 7: /*right*/
                    if (pXEvent->type == ButtonPress) {
                         dfbEvent.type = DIET_AXISMOTION;
                         dfbEvent.flags = DIEF_AXISREL;
                         dfbEvent.axis = DIAI_Z;
                         /*SCROLL UP*/
                         if ( pXEvent->xbutton.button == 4 ) {
                              dfbEvent.axisrel = -1;
                         }
                         /*SCROLL DOWN */
                         else if (pXEvent->xbutton.button == 5) {
                              dfbEvent.axisrel = 1;
                         }
                         /*SCROLL LEFT*/
                         else if (pXEvent->xbutton.button == 6) {
                              dfbEvent.axis = DIAI_X;
                              dfbEvent.axisrel = -1;
                         }
                         /*SCROLL RIGHT*/
                         else if (pXEvent->xbutton.button == 7 ) {
                              dfbEvent.axis = DIAI_X;
                              dfbEvent.axisrel = 1;
                         }
                    }
                    else
                         return;
                    break;
               default:
                    break;
          }

          dfbEvent.timestamp.tv_sec  =  pXEvent->xbutton.time / 1000;
          dfbEvent.timestamp.tv_usec = (pXEvent->xbutton.time % 1000) * 1000;

          dfb_input_dispatch( pData->device, &dfbEvent );
          ++iMouseEventCount;
     }
}

static void
handle_expose( const XExposeEvent *expose )
{
     CoreLayer               *layer = dfb_layer_at( DLID_PRIMARY );
     const DisplayLayerFuncs *funcs = layer->funcs;
     CoreLayerContext        *context;

     D_ASSERT( funcs != NULL );
     D_ASSERT( funcs->UpdateRegion != NULL );

     /* Get the currently active context. */
     if (dfb_layer_get_active_context( layer, &context ) == DFB_OK) {
          CoreLayerRegion *region;

          /* Get the first region. */
          if (dfb_layer_context_get_primary_region( context,
                                                    false, &region ) == DFB_OK)
          {
               /* Lock the region to avoid tearing due to concurrent updates. */
               dfb_layer_region_lock( region );

               /* Get the surface of the region. */
               if (region->surface && region->surface_lock.buffer) {
                    DFBRegion update = { expose->x, expose->y,
                                         expose->x + expose->width  - 1,
                                         expose->y + expose->height - 1 };

                    funcs->UpdateRegion( layer, layer->driver_data, layer->layer_data,
                                         region->region_data, region->surface, &update, &region->surface_lock );
               }

               /* Unlock the region. */
               dfb_layer_region_unlock( region );

               /* Release the region. */
               dfb_layer_region_unref( region );
          }

          /* Release the context. */
          dfb_layer_context_unref( context );
     }
}

/*
 * Input thread reading from device.
 * Generates events on incoming data.
 */
static void*
x11EventThread( DirectThread *thread, void *driver_data )
{
     X11InputData *data   = driver_data;
     DFBX11       *x11    = data->x11;
     DFBX11Shared *shared = x11->shared;

     while (!data->stop) {
          unsigned int  pull = 23;
          XEvent        xEvent; 
          DFBInputEvent dfbEvent;

          /* FIXME: Detect key repeats, we're receiving KeyPress, KeyRelease, KeyPress, KeyRelease... !!?? */

          if (!shared->xw || !shared->xw->window) {
               /* no window, so no event */
               usleep( 50000 );
               continue;
          }

          usleep( 10000 );

          XLockDisplay( x11->display );

          while (!data->stop && pull-- && XPending( x11->display )) {
               XNextEvent( x11->display, &xEvent );

               XUnlockDisplay( x11->display );

               D_DEBUG_AT( X11_Input, "Event received: %d\n", xEvent.type );

               switch (xEvent.type) {
                    case ButtonPress:
                    case ButtonRelease:
                         motion_realize( data );
                    case MotionNotify:
                         handleMouseEvent( &xEvent, data ); // crash ???
                         break;

                    case KeyPress:
                    case KeyRelease: {
                         motion_realize( data );

                         dfbEvent.type     = (xEvent.type == KeyPress) ? DIET_KEYPRESS : DIET_KEYRELEASE;
                         dfbEvent.flags    = DIEF_KEYCODE | DIEF_TIMESTAMP;
                         dfbEvent.key_code = xEvent.xkey.keycode;

                         dfbEvent.timestamp.tv_sec  =  xEvent.xkey.time / 1000;
                         dfbEvent.timestamp.tv_usec = (xEvent.xkey.time % 1000) * 1000;

                         dfb_input_dispatch( data->device, &dfbEvent );
                         break;
                    }

                    case Expose:
                         handle_expose( &xEvent.xexpose );
                         break;

                    case DestroyNotify:
                         /* this event is mainly to unblock XNextEvent. */
                         break;

                    default:
                         break;
               }

               XLockDisplay( x11->display );
          }

          XUnlockDisplay( x11->display );

          if (!data->stop)
               motion_realize( data );
     }

     return NULL;
}

/* exported symbols */

/*
 * Return the number of available devices.
 * Called once during initialization of DirectFB.
 */
static int
driver_get_available( void )
{
     return dfb_system_type() == CORE_X11;
}

/*
 * Fill out general information about this driver.
 * Called once during initialization of DirectFB.
 */
static void
driver_get_info( InputDriverInfo *info )
{
     /* fill driver info structure */
     snprintf ( info->name,   DFB_INPUT_DRIVER_INFO_NAME_LENGTH, "X11 Input Driver" );
     snprintf ( info->vendor, DFB_INPUT_DRIVER_INFO_VENDOR_LENGTH, "directfb.org" );

     info->version.major = 0;
     info->version.minor = 1;
}

/*
 * Open the device, fill out information about it,
 * allocate and fill private data, start input thread.
 * Called during initialization, resuming or taking over mastership.
 */
static DFBResult
driver_open_device( CoreInputDevice  *device,
                    unsigned int      number,
                    InputDeviceInfo  *info,
                    void            **driver_data )
{
     X11InputData *data;
     DFBX11       *x11    = dfb_system_data();
     DFBX11Shared *shared = x11->shared;

     D_DEBUG_AT( X11_Input, "%s()\n", __FUNCTION__ );

     fusion_skirmish_prevail( &shared->lock );

     fusion_skirmish_dismiss( &shared->lock );

     /* set device vendor and name */
     snprintf( info->desc.vendor, DFB_INPUT_DEVICE_DESC_VENDOR_LENGTH, "XServer" );
     snprintf( info->desc.name,   DFB_INPUT_DEVICE_DESC_NAME_LENGTH, "X11 Input" );

     /* set one of the primary input device IDs */
     info->prefered_id = DIDID_KEYBOARD;

     /* set type flags */
     info->desc.type   = DIDTF_JOYSTICK | DIDTF_KEYBOARD | DIDTF_MOUSE;

     /* set capabilities */
     info->desc.caps   = DICAPS_ALL;

     /* enable translation of fake raw hardware keycodes */
     info->desc.min_keycode = 8;
     info->desc.max_keycode = 255;


     /* allocate and fill private data */
     data = D_CALLOC( 1, sizeof(X11InputData) );

     data->device = device;
     data->x11    = x11;

     /* start input thread */
     data->thread = direct_thread_create( DTT_INPUT, x11EventThread, data, "X11 Input" );

     /* set private data pointer */
     *driver_data = data;

     return DFB_OK;
}

/*
 * Fetch one entry from the device's keymap if supported.
 * this does a fake mapping based on the orginal DFB code
 */
static DFBResult
driver_get_keymap_entry( CoreInputDevice           *device,
                         void                      *driver_data,
                         DFBInputDeviceKeymapEntry *entry )
{
     int           i;
     X11InputData *data = driver_data;
     DFBX11       *x11  = data->x11;

     XLockDisplay( x11->display );

     for (i=0; i<4; i++) {
          KeySym xSymbol = XKeycodeToKeysym( x11->display, entry->code, i );

          if (i == 0)
               entry->identifier = xsymbol_to_id( xSymbol );

          entry->symbols[i] = xsymbol_to_symbol( xSymbol );
     }

     XUnlockDisplay( x11->display );

     /* is CapsLock effective? */
     if (entry->identifier >= DIKI_A && entry->identifier <= DIKI_Z)
          entry->locks |= DILS_CAPS;

     /* is NumLock effective? */
     if (entry->identifier >= DIKI_KP_DECIMAL && entry->identifier <= DIKI_KP_9)
          entry->locks |= DILS_NUM;

     return DFB_OK;
}

/*
 * End thread, close device and free private data.
 */
static DFBResult
driver_set_sensitivity( CoreInputDevice           *device,
                        void                      *driver_data,
                        int                        sensitivity )
{
     D_DEBUG_AT( X11_Input, "%s( %d )\n", __FUNCTION__, sensitivity );

     return DFB_OK;
}

/*
 * End thread, close device and free private data.
 */
static void
driver_close_device( void *driver_data )
{
     X11InputData *data   = driver_data;
     DFBX11       *x11    = data->x11;
     DFBX11Shared *shared = x11->shared;

     D_DEBUG_AT( X11_Input, "%s()\n", __FUNCTION__ );

     /* stop input thread */
     data->stop = true;

     XLockDisplay( x11->display );

     if (shared->xw) {
          XWindow *xw = shared->xw;

          shared->xw = NULL;

          /* the window must generate an event, otherwise the input thread will not end */
          dfb_x11_close_window( x11, xw );
     }

     XSync( x11->display, False );

     XUnlockDisplay( x11->display );

     /* it is possible that this "close" function is called from the same
      * thread that the input device is actually running on.
      * This happens when you e.g. click the close box with your mouse.
      * As a fix, we check if we are this thread. */
     if (data->thread != direct_thread_self()) {
          direct_thread_join( data->thread );
          direct_thread_destroy( data->thread );
     }

     /* free private data */
     D_FREE ( data );
}