~ubuntu-branches/ubuntu/trusty/libsdl2/trusty-proposed

« back to all changes in this revision

Viewing changes to src/input/evdev/SDL_evdev.c

  • Committer: Package Import Robot
  • Author(s): Manuel A. Fernandez Montecelo
  • Date: 2013-12-28 12:31:19 UTC
  • mto: (7.1.3 sid)
  • mto: This revision was merged to the branch mainline in revision 7.
  • Revision ID: package-import@ubuntu.com-20131228123119-wehupm72qsjvh6vz
Tags: upstream-2.0.1+dfsg1
ImportĀ upstreamĀ versionĀ 2.0.1+dfsg1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  Simple DirectMedia Layer
 
3
  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
 
4
 
 
5
  This software is provided 'as-is', without any express or implied
 
6
  warranty.  In no event will the authors be held liable for any damages
 
7
  arising from the use of this software.
 
8
 
 
9
  Permission is granted to anyone to use this software for any purpose,
 
10
  including commercial applications, and to alter it and redistribute it
 
11
  freely, subject to the following restrictions:
 
12
 
 
13
  1. The origin of this software must not be misrepresented; you must not
 
14
     claim that you wrote the original software. If you use this software
 
15
     in a product, an acknowledgment in the product documentation would be
 
16
     appreciated but is not required.
 
17
  2. Altered source versions must be plainly marked as such, and must not be
 
18
     misrepresented as being the original software.
 
19
  3. This notice may not be removed or altered from any source distribution.
 
20
*/
 
21
#include "SDL_config.h"
 
22
 
 
23
#ifdef SDL_INPUT_LINUXEV
 
24
 
 
25
/* This is based on the linux joystick driver */
 
26
/* References: https://www.kernel.org/doc/Documentation/input/input.txt 
 
27
 *             https://www.kernel.org/doc/Documentation/input/event-codes.txt
 
28
 *             /usr/include/linux/input.h
 
29
 *             The evtest application is also useful to debug the protocol
 
30
 */
 
31
 
 
32
 
 
33
#include "SDL_evdev.h"
 
34
#define _THIS SDL_EVDEV_PrivateData *_this
 
35
static _THIS = NULL;
 
36
 
 
37
#include <sys/stat.h>
 
38
#include <unistd.h>
 
39
#include <fcntl.h>
 
40
#include <sys/ioctl.h>
 
41
#include <limits.h>             /* For the definition of PATH_MAX */
 
42
#include <linux/input.h>
 
43
#ifdef SDL_INPUT_LINUXKD
 
44
#include <linux/kd.h>
 
45
#include <linux/keyboard.h>
 
46
#endif
 
47
 
 
48
 
 
49
/* We need this to prevent keystrokes from appear in the console */
 
50
#ifndef KDSKBMUTE
 
51
#define KDSKBMUTE 0x4B51
 
52
#endif
 
53
#ifndef KDSKBMODE
 
54
#define KDSKBMODE 0x4B45
 
55
#endif
 
56
#ifndef K_OFF
 
57
#define K_OFF 0x04
 
58
#endif
 
59
 
 
60
#include "SDL.h"
 
61
#include "SDL_assert.h"
 
62
#include "SDL_endian.h"
 
63
#include "../../core/linux/SDL_udev.h"
 
64
#include "SDL_scancode.h"
 
65
#include "../../events/SDL_events_c.h"
 
66
 
 
67
/* This isn't defined in older Linux kernel headers */
 
68
#ifndef SYN_DROPPED
 
69
#define SYN_DROPPED 3
 
70
#endif
 
71
 
 
72
static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode);
 
73
static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item);
 
74
static int SDL_EVDEV_device_removed(const char *devpath);
 
75
 
 
76
#if SDL_USE_LIBUDEV
 
77
static int SDL_EVDEV_device_added(const char *devpath);
 
78
void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
 
79
#endif /* SDL_USE_LIBUDEV */
 
80
 
 
81
static SDL_Scancode EVDEV_Keycodes[] = {
 
82
    SDL_SCANCODE_UNKNOWN,       /*  KEY_RESERVED        0 */
 
83
    SDL_SCANCODE_ESCAPE,        /*  KEY_ESC         1 */
 
84
    SDL_SCANCODE_1,             /*  KEY_1           2 */
 
85
    SDL_SCANCODE_2,             /*  KEY_2           3 */
 
86
    SDL_SCANCODE_3,             /*  KEY_3           4 */
 
87
    SDL_SCANCODE_4,             /*  KEY_4           5 */
 
88
    SDL_SCANCODE_5,             /*  KEY_5           6 */
 
89
    SDL_SCANCODE_6,             /*  KEY_6           7 */
 
90
    SDL_SCANCODE_7,             /*  KEY_7           8 */
 
91
    SDL_SCANCODE_8,             /*  KEY_8           9 */
 
92
    SDL_SCANCODE_9,             /*  KEY_9           10 */
 
93
    SDL_SCANCODE_0,             /*  KEY_0           11 */
 
94
    SDL_SCANCODE_MINUS,         /*  KEY_MINUS       12 */
 
95
    SDL_SCANCODE_EQUALS,        /*  KEY_EQUAL       13 */
 
96
    SDL_SCANCODE_BACKSPACE,     /*  KEY_BACKSPACE       14 */
 
97
    SDL_SCANCODE_TAB,           /*  KEY_TAB         15 */
 
98
    SDL_SCANCODE_Q,             /*  KEY_Q           16 */
 
99
    SDL_SCANCODE_W,             /*  KEY_W           17 */
 
100
    SDL_SCANCODE_E,             /*  KEY_E           18 */
 
101
    SDL_SCANCODE_R,             /*  KEY_R           19 */
 
102
    SDL_SCANCODE_T,             /*  KEY_T           20 */
 
103
    SDL_SCANCODE_Y,             /*  KEY_Y           21 */
 
104
    SDL_SCANCODE_U,             /*  KEY_U           22 */
 
105
    SDL_SCANCODE_I,             /*  KEY_I           23 */
 
106
    SDL_SCANCODE_O,             /*  KEY_O           24 */
 
107
    SDL_SCANCODE_P,             /*  KEY_P           25 */
 
108
    SDL_SCANCODE_LEFTBRACKET,   /*  KEY_LEFTBRACE       26 */
 
109
    SDL_SCANCODE_RIGHTBRACKET,  /*  KEY_RIGHTBRACE      27 */
 
110
    SDL_SCANCODE_RETURN,        /*  KEY_ENTER       28 */
 
111
    SDL_SCANCODE_LCTRL,         /*  KEY_LEFTCTRL        29 */
 
112
    SDL_SCANCODE_A,             /*  KEY_A           30 */
 
113
    SDL_SCANCODE_S,             /*  KEY_S           31 */
 
114
    SDL_SCANCODE_D,             /*  KEY_D           32 */
 
115
    SDL_SCANCODE_F,             /*  KEY_F           33 */
 
116
    SDL_SCANCODE_G,             /*  KEY_G           34 */
 
117
    SDL_SCANCODE_H,             /*  KEY_H           35 */
 
118
    SDL_SCANCODE_J,             /*  KEY_J           36 */
 
119
    SDL_SCANCODE_K,             /*  KEY_K           37 */
 
120
    SDL_SCANCODE_L,             /*  KEY_L           38 */
 
121
    SDL_SCANCODE_SEMICOLON,     /*  KEY_SEMICOLON       39 */
 
122
    SDL_SCANCODE_APOSTROPHE,    /*  KEY_APOSTROPHE      40 */
 
123
    SDL_SCANCODE_GRAVE,         /*  KEY_GRAVE       41 */
 
124
    SDL_SCANCODE_LSHIFT,        /*  KEY_LEFTSHIFT       42 */
 
125
    SDL_SCANCODE_BACKSLASH,     /*  KEY_BACKSLASH       43 */
 
126
    SDL_SCANCODE_Z,             /*  KEY_Z           44 */
 
127
    SDL_SCANCODE_X,             /*  KEY_X           45 */
 
128
    SDL_SCANCODE_C,             /*  KEY_C           46 */
 
129
    SDL_SCANCODE_V,             /*  KEY_V           47 */
 
130
    SDL_SCANCODE_B,             /*  KEY_B           48 */
 
131
    SDL_SCANCODE_N,             /*  KEY_N           49 */
 
132
    SDL_SCANCODE_M,             /*  KEY_M           50 */
 
133
    SDL_SCANCODE_COMMA,         /*  KEY_COMMA       51 */
 
134
    SDL_SCANCODE_PERIOD,        /*  KEY_DOT         52 */
 
135
    SDL_SCANCODE_SLASH,         /*  KEY_SLASH       53 */
 
136
    SDL_SCANCODE_RSHIFT,        /*  KEY_RIGHTSHIFT      54 */
 
137
    SDL_SCANCODE_KP_MULTIPLY,   /*  KEY_KPASTERISK      55 */
 
138
    SDL_SCANCODE_LALT,          /*  KEY_LEFTALT     56 */
 
139
    SDL_SCANCODE_SPACE,         /*  KEY_SPACE       57 */
 
140
    SDL_SCANCODE_CAPSLOCK,      /*  KEY_CAPSLOCK        58 */
 
141
    SDL_SCANCODE_F1,            /*  KEY_F1          59 */
 
142
    SDL_SCANCODE_F2,            /*  KEY_F2          60 */
 
143
    SDL_SCANCODE_F3,            /*  KEY_F3          61 */
 
144
    SDL_SCANCODE_F4,            /*  KEY_F4          62 */
 
145
    SDL_SCANCODE_F5,            /*  KEY_F5          63 */
 
146
    SDL_SCANCODE_F6,            /*  KEY_F6          64 */
 
147
    SDL_SCANCODE_F7,            /*  KEY_F7          65 */
 
148
    SDL_SCANCODE_F8,            /*  KEY_F8          66 */
 
149
    SDL_SCANCODE_F9,            /*  KEY_F9          67 */
 
150
    SDL_SCANCODE_F10,           /*  KEY_F10         68 */
 
151
    SDL_SCANCODE_NUMLOCKCLEAR,  /*  KEY_NUMLOCK     69 */
 
152
    SDL_SCANCODE_SCROLLLOCK,    /*  KEY_SCROLLLOCK      70 */
 
153
    SDL_SCANCODE_KP_7,          /*  KEY_KP7         71 */
 
154
    SDL_SCANCODE_KP_8,          /*  KEY_KP8         72 */
 
155
    SDL_SCANCODE_KP_9,          /*  KEY_KP9         73 */
 
156
    SDL_SCANCODE_KP_MINUS,      /*  KEY_KPMINUS     74 */
 
157
    SDL_SCANCODE_KP_4,          /*  KEY_KP4         75 */
 
158
    SDL_SCANCODE_KP_5,          /*  KEY_KP5         76 */
 
159
    SDL_SCANCODE_KP_6,          /*  KEY_KP6         77 */
 
160
    SDL_SCANCODE_KP_PLUS,       /*  KEY_KPPLUS      78 */
 
161
    SDL_SCANCODE_KP_1,          /*  KEY_KP1         79 */
 
162
    SDL_SCANCODE_KP_2,          /*  KEY_KP2         80 */
 
163
    SDL_SCANCODE_KP_3,          /*  KEY_KP3         81 */
 
164
    SDL_SCANCODE_KP_0,          /*  KEY_KP0         82 */
 
165
    SDL_SCANCODE_KP_PERIOD,     /*  KEY_KPDOT       83 */
 
166
    SDL_SCANCODE_UNKNOWN,       /*  84 */
 
167
    SDL_SCANCODE_LANG5,         /*  KEY_ZENKAKUHANKAKU  85 */
 
168
    SDL_SCANCODE_UNKNOWN,       /*  KEY_102ND       86 */
 
169
    SDL_SCANCODE_F11,           /*  KEY_F11         87 */
 
170
    SDL_SCANCODE_F12,           /*  KEY_F12         88 */
 
171
    SDL_SCANCODE_UNKNOWN,       /*  KEY_RO          89 */
 
172
    SDL_SCANCODE_LANG3,         /*  KEY_KATAKANA        90 */
 
173
    SDL_SCANCODE_LANG4,         /*  KEY_HIRAGANA        91 */
 
174
    SDL_SCANCODE_UNKNOWN,       /*  KEY_HENKAN      92 */
 
175
    SDL_SCANCODE_LANG3,         /*  KEY_KATAKANAHIRAGANA    93 */
 
176
    SDL_SCANCODE_UNKNOWN,       /*  KEY_MUHENKAN        94 */
 
177
    SDL_SCANCODE_KP_COMMA,      /*  KEY_KPJPCOMMA       95 */
 
178
    SDL_SCANCODE_KP_ENTER,      /*  KEY_KPENTER     96 */
 
179
    SDL_SCANCODE_RCTRL,         /*  KEY_RIGHTCTRL       97 */
 
180
    SDL_SCANCODE_KP_DIVIDE,     /*  KEY_KPSLASH     98 */
 
181
    SDL_SCANCODE_SYSREQ,        /*  KEY_SYSRQ       99 */
 
182
    SDL_SCANCODE_RALT,          /*  KEY_RIGHTALT        100 */
 
183
    SDL_SCANCODE_UNKNOWN,       /*  KEY_LINEFEED        101 */
 
184
    SDL_SCANCODE_HOME,          /*  KEY_HOME        102 */
 
185
    SDL_SCANCODE_UP,            /*  KEY_UP          103 */
 
186
    SDL_SCANCODE_PAGEUP,        /*  KEY_PAGEUP      104 */
 
187
    SDL_SCANCODE_LEFT,          /*  KEY_LEFT        105 */
 
188
    SDL_SCANCODE_RIGHT,         /*  KEY_RIGHT       106 */
 
189
    SDL_SCANCODE_END,           /*  KEY_END         107 */
 
190
    SDL_SCANCODE_DOWN,          /*  KEY_DOWN        108 */
 
191
    SDL_SCANCODE_PAGEDOWN,      /*  KEY_PAGEDOWN        109 */
 
192
    SDL_SCANCODE_INSERT,        /*  KEY_INSERT      110 */
 
193
    SDL_SCANCODE_DELETE,        /*  KEY_DELETE      111 */
 
194
    SDL_SCANCODE_UNKNOWN,       /*  KEY_MACRO       112 */
 
195
    SDL_SCANCODE_MUTE,          /*  KEY_MUTE        113 */
 
196
    SDL_SCANCODE_VOLUMEDOWN,    /*  KEY_VOLUMEDOWN      114 */
 
197
    SDL_SCANCODE_VOLUMEUP,      /*  KEY_VOLUMEUP        115 */
 
198
    SDL_SCANCODE_POWER,         /*  KEY_POWER       116 SC System Power Down */
 
199
    SDL_SCANCODE_KP_EQUALS,     /*  KEY_KPEQUAL     117 */
 
200
    SDL_SCANCODE_KP_MINUS,      /*  KEY_KPPLUSMINUS     118 */
 
201
    SDL_SCANCODE_PAUSE,         /*  KEY_PAUSE       119 */
 
202
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SCALE       120 AL Compiz Scale (Expose) */
 
203
    SDL_SCANCODE_KP_COMMA,      /*  KEY_KPCOMMA     121 */
 
204
    SDL_SCANCODE_LANG1,         /*  KEY_HANGEUL,KEY_HANGUEL 122 */
 
205
    SDL_SCANCODE_LANG2,         /*  KEY_HANJA       123 */
 
206
    SDL_SCANCODE_INTERNATIONAL3,/*  KEY_YEN         124 */
 
207
    SDL_SCANCODE_LGUI,          /*  KEY_LEFTMETA        125 */
 
208
    SDL_SCANCODE_RGUI,          /*  KEY_RIGHTMETA       126 */
 
209
    SDL_SCANCODE_APPLICATION,   /*  KEY_COMPOSE     127 */
 
210
    SDL_SCANCODE_STOP,          /*  KEY_STOP        128 AC Stop */
 
211
    SDL_SCANCODE_AGAIN,         /*  KEY_AGAIN       129 */
 
212
    SDL_SCANCODE_UNKNOWN,       /*  KEY_PROPS       130 AC Properties */
 
213
    SDL_SCANCODE_UNDO,          /*  KEY_UNDO        131 AC Undo */
 
214
    SDL_SCANCODE_UNKNOWN,       /*  KEY_FRONT       132 */
 
215
    SDL_SCANCODE_COPY,          /*  KEY_COPY        133 AC Copy */
 
216
    SDL_SCANCODE_UNKNOWN,       /*  KEY_OPEN        134 AC Open */
 
217
    SDL_SCANCODE_PASTE,         /*  KEY_PASTE       135 AC Paste */
 
218
    SDL_SCANCODE_FIND,          /*  KEY_FIND        136 AC Search */
 
219
    SDL_SCANCODE_CUT,           /*  KEY_CUT         137 AC Cut */
 
220
    SDL_SCANCODE_HELP,          /*  KEY_HELP        138 AL Integrated Help Center */
 
221
    SDL_SCANCODE_MENU,          /*  KEY_MENU        139 Menu (show menu) */
 
222
    SDL_SCANCODE_CALCULATOR,    /*  KEY_CALC        140 AL Calculator */
 
223
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SETUP       141 */
 
224
    SDL_SCANCODE_SLEEP,         /*  KEY_SLEEP       142 SC System Sleep */
 
225
    SDL_SCANCODE_UNKNOWN,       /*  KEY_WAKEUP      143 System Wake Up */
 
226
    SDL_SCANCODE_UNKNOWN,       /*  KEY_FILE        144 AL Local Machine Browser */
 
227
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SENDFILE        145 */
 
228
    SDL_SCANCODE_UNKNOWN,       /*  KEY_DELETEFILE      146 */
 
229
    SDL_SCANCODE_UNKNOWN,       /*  KEY_XFER        147 */
 
230
    SDL_SCANCODE_APP1,          /*  KEY_PROG1       148 */
 
231
    SDL_SCANCODE_APP1,          /*  KEY_PROG2       149 */
 
232
    SDL_SCANCODE_WWW,           /*  KEY_WWW         150 AL Internet Browser */
 
233
    SDL_SCANCODE_UNKNOWN,       /*  KEY_MSDOS       151 */
 
234
    SDL_SCANCODE_UNKNOWN,       /*  KEY_COFFEE,KEY_SCREENLOCK      152 AL Terminal Lock/Screensaver */
 
235
    SDL_SCANCODE_UNKNOWN,       /*  KEY_DIRECTION       153 */
 
236
    SDL_SCANCODE_UNKNOWN,       /*  KEY_CYCLEWINDOWS    154 */
 
237
    SDL_SCANCODE_MAIL,          /*  KEY_MAIL        155 */
 
238
    SDL_SCANCODE_AC_BOOKMARKS,  /*  KEY_BOOKMARKS       156 AC Bookmarks */
 
239
    SDL_SCANCODE_COMPUTER,      /*  KEY_COMPUTER        157 */
 
240
    SDL_SCANCODE_AC_BACK,       /*  KEY_BACK        158 AC Back */
 
241
    SDL_SCANCODE_AC_FORWARD,    /*  KEY_FORWARD     159 AC Forward */
 
242
    SDL_SCANCODE_UNKNOWN,       /*  KEY_CLOSECD     160 */
 
243
    SDL_SCANCODE_EJECT,         /*  KEY_EJECTCD     161 */
 
244
    SDL_SCANCODE_UNKNOWN,       /*  KEY_EJECTCLOSECD    162 */
 
245
    SDL_SCANCODE_AUDIONEXT,     /*  KEY_NEXTSONG        163 */
 
246
    SDL_SCANCODE_AUDIOPLAY,     /*  KEY_PLAYPAUSE       164 */
 
247
    SDL_SCANCODE_AUDIOPREV,     /*  KEY_PREVIOUSSONG    165 */
 
248
    SDL_SCANCODE_AUDIOSTOP,     /*  KEY_STOPCD      166 */
 
249
    SDL_SCANCODE_UNKNOWN,       /*  KEY_RECORD      167 */
 
250
    SDL_SCANCODE_UNKNOWN,       /*  KEY_REWIND      168 */
 
251
    SDL_SCANCODE_UNKNOWN,       /*  KEY_PHONE       169 Media Select Telephone */
 
252
    SDL_SCANCODE_UNKNOWN,       /*  KEY_ISO         170 */
 
253
    SDL_SCANCODE_UNKNOWN,       /*  KEY_CONFIG      171 AL Consumer Control Configuration */
 
254
    SDL_SCANCODE_AC_HOME,       /*  KEY_HOMEPAGE        172 AC Home */
 
255
    SDL_SCANCODE_AC_REFRESH,    /*  KEY_REFRESH     173 AC Refresh */
 
256
    SDL_SCANCODE_UNKNOWN,       /*  KEY_EXIT        174 AC Exit */
 
257
    SDL_SCANCODE_UNKNOWN,       /*  KEY_MOVE        175 */
 
258
    SDL_SCANCODE_UNKNOWN,       /*  KEY_EDIT        176 */
 
259
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SCROLLUP        177 */
 
260
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SCROLLDOWN      178 */
 
261
    SDL_SCANCODE_KP_LEFTPAREN,  /*  KEY_KPLEFTPAREN     179 */
 
262
    SDL_SCANCODE_KP_RIGHTPAREN, /*  KEY_KPRIGHTPAREN    180 */
 
263
    SDL_SCANCODE_UNKNOWN,       /*  KEY_NEW         181 AC New */
 
264
    SDL_SCANCODE_AGAIN,         /*  KEY_REDO        182 AC Redo/Repeat */
 
265
    SDL_SCANCODE_F13,           /*  KEY_F13         183 */
 
266
    SDL_SCANCODE_F14,           /*  KEY_F14         184 */
 
267
    SDL_SCANCODE_F15,           /*  KEY_F15         185 */
 
268
    SDL_SCANCODE_F16,           /*  KEY_F16         186 */
 
269
    SDL_SCANCODE_F17,           /*  KEY_F17         187 */
 
270
    SDL_SCANCODE_F18,           /*  KEY_F18         188 */
 
271
    SDL_SCANCODE_F19,           /*  KEY_F19         189 */
 
272
    SDL_SCANCODE_F20,           /*  KEY_F20         190 */
 
273
    SDL_SCANCODE_F21,           /*  KEY_F21         191 */
 
274
    SDL_SCANCODE_F22,           /*  KEY_F22         192 */
 
275
    SDL_SCANCODE_F23,           /*  KEY_F23         193 */
 
276
    SDL_SCANCODE_F24,           /*  KEY_F24         194 */
 
277
    SDL_SCANCODE_UNKNOWN,       /*  195 */
 
278
    SDL_SCANCODE_UNKNOWN,       /*  196 */
 
279
    SDL_SCANCODE_UNKNOWN,       /*  197 */
 
280
    SDL_SCANCODE_UNKNOWN,       /*  198 */
 
281
    SDL_SCANCODE_UNKNOWN,       /*  199 */
 
282
    SDL_SCANCODE_UNKNOWN,       /*  KEY_PLAYCD      200 */
 
283
    SDL_SCANCODE_UNKNOWN,       /*  KEY_PAUSECD     201 */
 
284
    SDL_SCANCODE_UNKNOWN,       /*  KEY_PROG3       202 */
 
285
    SDL_SCANCODE_UNKNOWN,       /*  KEY_PROG4       203 */
 
286
    SDL_SCANCODE_UNKNOWN,       /*  KEY_DASHBOARD       204 AL Dashboard */
 
287
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SUSPEND     205 */
 
288
    SDL_SCANCODE_UNKNOWN,       /*  KEY_CLOSE       206 AC Close */
 
289
    SDL_SCANCODE_UNKNOWN,       /*  KEY_PLAY        207 */
 
290
    SDL_SCANCODE_UNKNOWN,       /*  KEY_FASTFORWARD     208 */
 
291
    SDL_SCANCODE_UNKNOWN,       /*  KEY_BASSBOOST       209 */
 
292
    SDL_SCANCODE_UNKNOWN,       /*  KEY_PRINT       210 AC Print */
 
293
    SDL_SCANCODE_UNKNOWN,       /*  KEY_HP          211 */
 
294
    SDL_SCANCODE_UNKNOWN,       /*  KEY_CAMERA      212 */
 
295
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SOUND       213 */
 
296
    SDL_SCANCODE_UNKNOWN,       /*  KEY_QUESTION        214 */
 
297
    SDL_SCANCODE_UNKNOWN,       /*  KEY_EMAIL       215 */
 
298
    SDL_SCANCODE_UNKNOWN,       /*  KEY_CHAT        216 */
 
299
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SEARCH      217 */
 
300
    SDL_SCANCODE_UNKNOWN,       /*  KEY_CONNECT     218 */
 
301
    SDL_SCANCODE_UNKNOWN,       /*  KEY_FINANCE     219 AL Checkbook/Finance */
 
302
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SPORT       220 */
 
303
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SHOP        221 */
 
304
    SDL_SCANCODE_UNKNOWN,       /*  KEY_ALTERASE        222 */
 
305
    SDL_SCANCODE_UNKNOWN,       /*  KEY_CANCEL      223 AC Cancel */
 
306
    SDL_SCANCODE_UNKNOWN,       /*  KEY_BRIGHTNESSDOWN  224 */
 
307
    SDL_SCANCODE_UNKNOWN,       /*  KEY_BRIGHTNESSUP    225 */
 
308
    SDL_SCANCODE_UNKNOWN,       /*  KEY_MEDIA       226 */
 
309
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SWITCHVIDEOMODE 227 Cycle between available video outputs (Monitor/LCD/TV-out/etc) */
 
310
    SDL_SCANCODE_UNKNOWN,       /*  KEY_KBDILLUMTOGGLE  228 */
 
311
    SDL_SCANCODE_UNKNOWN,       /*  KEY_KBDILLUMDOWN    229 */
 
312
    SDL_SCANCODE_UNKNOWN,       /*  KEY_KBDILLUMUP      230 */
 
313
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SEND        231 AC Send */
 
314
    SDL_SCANCODE_UNKNOWN,       /*  KEY_REPLY       232 AC Reply */
 
315
    SDL_SCANCODE_UNKNOWN,       /*  KEY_FORWARDMAIL     233 AC Forward Msg */
 
316
    SDL_SCANCODE_UNKNOWN,       /*  KEY_SAVE        234 AC Save */
 
317
    SDL_SCANCODE_UNKNOWN,       /*  KEY_DOCUMENTS       235 */
 
318
    SDL_SCANCODE_UNKNOWN,       /*  KEY_BATTERY     236  */
 
319
    SDL_SCANCODE_UNKNOWN,       /*  KEY_BLUETOOTH       237 */
 
320
    SDL_SCANCODE_UNKNOWN,       /*  KEY_WLAN        238 */
 
321
    SDL_SCANCODE_UNKNOWN,       /*  KEY_UWB         239 */
 
322
    SDL_SCANCODE_UNKNOWN,       /*  KEY_UNKNOWN     240 */
 
323
    SDL_SCANCODE_UNKNOWN,       /*  KEY_VIDEO_NEXT      241 drive next video source */
 
324
    SDL_SCANCODE_UNKNOWN,       /*  KEY_VIDEO_PREV      242 drive previous video source */
 
325
    SDL_SCANCODE_UNKNOWN,       /*  KEY_BRIGHTNESS_CYCLE    243 brightness up, after max is min */
 
326
    SDL_SCANCODE_UNKNOWN,       /*  KEY_BRIGHTNESS_ZERO 244 brightness off, use ambient */
 
327
    SDL_SCANCODE_UNKNOWN,       /*  KEY_DISPLAY_OFF     245 display device to off state */
 
328
    SDL_SCANCODE_UNKNOWN,       /*  KEY_WIMAX       246 */
 
329
    SDL_SCANCODE_UNKNOWN,       /*  KEY_RFKILL      247 Key that controls all radios */
 
330
    SDL_SCANCODE_UNKNOWN,       /*  KEY_MICMUTE     248 Mute / unmute the microphone */
 
331
};
 
332
 
 
333
static Uint8 EVDEV_MouseButtons[] = {
 
334
    SDL_BUTTON_LEFT,            /*  BTN_LEFT        0x110 */
 
335
    SDL_BUTTON_RIGHT,           /*  BTN_RIGHT       0x111 */
 
336
    SDL_BUTTON_MIDDLE,          /*  BTN_MIDDLE      0x112 */
 
337
    SDL_BUTTON_X1,              /*  BTN_SIDE        0x113 */
 
338
    SDL_BUTTON_X2,              /*  BTN_EXTRA       0x114 */
 
339
    SDL_BUTTON_X2 + 1,          /*  BTN_FORWARD     0x115 */
 
340
    SDL_BUTTON_X2 + 2,          /*  BTN_BACK        0x116 */
 
341
    SDL_BUTTON_X2 + 3           /*  BTN_TASK        0x117 */
 
342
};
 
343
 
 
344
static char* EVDEV_consoles[] = {
 
345
    "/proc/self/fd/0",
 
346
    "/dev/tty",
 
347
    "/dev/tty0",
 
348
    "/dev/tty1",
 
349
    "/dev/tty2",
 
350
    "/dev/tty3",
 
351
    "/dev/tty4",
 
352
    "/dev/tty5",
 
353
    "/dev/tty6",
 
354
    "/dev/vc/0",
 
355
    "/dev/console"
 
356
};
 
357
 
 
358
#define IS_CONSOLE(fd) isatty (fd) && ioctl(fd, KDGKBTYPE, &arg) == 0 && ((arg == KB_101) || (arg == KB_84))
 
359
 
 
360
static int SDL_EVDEV_get_console_fd(void)
 
361
{
 
362
    int fd, i;
 
363
    char arg = 0;
 
364
    
 
365
    /* Try a few consoles to see which one we have read access to */
 
366
    
 
367
    for( i = 0; i < SDL_arraysize(EVDEV_consoles); i++) {
 
368
        fd = open(EVDEV_consoles[i], O_RDONLY);
 
369
        if (fd >= 0) {
 
370
            if (IS_CONSOLE(fd)) return fd;
 
371
            close(fd);
 
372
        }
 
373
    }
 
374
    
 
375
    /* Try stdin, stdout, stderr */
 
376
    
 
377
    for( fd = 0; fd < 3; fd++) {
 
378
        if (IS_CONSOLE(fd)) return fd;
 
379
    }
 
380
    
 
381
    /* We won't be able to send SDL_TEXTINPUT events */
 
382
    return -1;
 
383
}
 
384
 
 
385
/* Prevent keystrokes from reaching the tty */
 
386
static int SDL_EVDEV_mute_keyboard(int tty, int *kb_mode)
 
387
{
 
388
    char arg;
 
389
    
 
390
    *kb_mode = 0; /* FIXME: Is this a sane default in case KDGKBMODE fails? */
 
391
    if (!IS_CONSOLE(tty)) {
 
392
        return SDL_SetError("Tried to mute an invalid tty");
 
393
    }
 
394
    ioctl(tty, KDGKBMODE, kb_mode); /* It's not fatal if this fails */
 
395
    if (ioctl(tty, KDSKBMUTE, 1) && ioctl(tty, KDSKBMODE, K_OFF)) {
 
396
        return SDL_SetError("EVDEV: Failed muting keyboard");
 
397
    }
 
398
    
 
399
    return 0;  
 
400
}
 
401
 
 
402
/* Restore the keyboard mode for given tty */
 
403
static void SDL_EVDEV_unmute_keyboard(int tty, int kb_mode)
 
404
{
 
405
    if (ioctl(tty, KDSKBMUTE, 0) && ioctl(tty, KDSKBMODE, kb_mode)) {
 
406
        SDL_Log("EVDEV: Failed restoring keyboard mode");
 
407
    }
 
408
}
 
409
 
 
410
/* Read /sys/class/tty/tty0/active and open the tty */
 
411
static int SDL_EVDEV_get_active_tty()
 
412
{
 
413
    int fd, len;
 
414
    char ttyname[NAME_MAX + 1];
 
415
    char ttypath[PATH_MAX+1] = "/dev/";
 
416
    char arg;
 
417
    
 
418
    fd = open("/sys/class/tty/tty0/active", O_RDONLY);
 
419
    if (fd < 0) {
 
420
        return SDL_SetError("Could not determine which tty is active");
 
421
    }
 
422
    
 
423
    len = read(fd, ttyname, NAME_MAX);
 
424
    close(fd);
 
425
    
 
426
    if (len <= 0) {
 
427
        return SDL_SetError("Could not read which tty is active");
 
428
    }
 
429
    
 
430
    if (ttyname[len-1] == '\n') {
 
431
        ttyname[len-1] = '\0';
 
432
    }
 
433
    else {
 
434
        ttyname[len] = '\0';
 
435
    }
 
436
    
 
437
    SDL_strlcat(ttypath, ttyname, PATH_MAX);
 
438
    fd = open(ttypath, O_RDWR | O_NOCTTY);
 
439
    if (fd < 0) {
 
440
        return SDL_SetError("Could not open tty: %s", ttypath);
 
441
    }
 
442
    
 
443
    if (!IS_CONSOLE(fd)) {
 
444
        close(fd);
 
445
        return SDL_SetError("Invalid tty obtained: %s", ttypath);
 
446
    }
 
447
 
 
448
    return fd;  
 
449
}
 
450
 
 
451
int
 
452
SDL_EVDEV_Init(void)
 
453
{
 
454
    int retval = 0;
 
455
    
 
456
    if (_this == NULL) {
 
457
        
 
458
        _this = (SDL_EVDEV_PrivateData *) SDL_calloc(1, sizeof(*_this));
 
459
        if(_this == NULL) {
 
460
            return SDL_OutOfMemory();
 
461
        }
 
462
 
 
463
#if SDL_USE_LIBUDEV
 
464
        if (SDL_UDEV_Init() < 0) {
 
465
            SDL_free(_this);
 
466
            _this = NULL;
 
467
            return -1;
 
468
        }
 
469
 
 
470
        /* Set up the udev callback */
 
471
        if ( SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback) < 0) {
 
472
            SDL_EVDEV_Quit();
 
473
            return -1;
 
474
        }
 
475
        
 
476
        /* Force a scan to build the initial device list */
 
477
        SDL_UDEV_Scan();
 
478
#else
 
479
        /* TODO: Scan the devices manually, like a caveman */
 
480
#endif /* SDL_USE_LIBUDEV */
 
481
        
 
482
        /* We need a physical terminal (not PTS) to be able to translate key code to symbols via the kernel tables */
 
483
        _this->console_fd = SDL_EVDEV_get_console_fd();
 
484
        
 
485
        /* Mute the keyboard so keystrokes only generate evdev events and do not leak through to the console */
 
486
        _this->tty = STDIN_FILENO;
 
487
        if (SDL_EVDEV_mute_keyboard(_this->tty, &_this->kb_mode) < 0) {
 
488
            /* stdin is not a tty, probably we were launched remotely, so we try to disable the active tty */
 
489
            _this->tty = SDL_EVDEV_get_active_tty();
 
490
            if (_this->tty >= 0) {
 
491
                if (SDL_EVDEV_mute_keyboard(_this->tty, &_this->kb_mode) < 0) {
 
492
                    close(_this->tty);
 
493
                    _this->tty = -1;
 
494
                }
 
495
            }
 
496
        }
 
497
    }
 
498
    
 
499
    _this->ref_count += 1;
 
500
    
 
501
    return retval;
 
502
}
 
503
 
 
504
void
 
505
SDL_EVDEV_Quit(void)
 
506
{
 
507
    if (_this == NULL) {
 
508
        return;
 
509
    }
 
510
    
 
511
    _this->ref_count -= 1;
 
512
    
 
513
    if (_this->ref_count < 1) {
 
514
        
 
515
#if SDL_USE_LIBUDEV
 
516
        SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback);
 
517
        SDL_UDEV_Quit();
 
518
#endif /* SDL_USE_LIBUDEV */
 
519
       
 
520
        if (_this->console_fd >= 0) {
 
521
            close(_this->console_fd);
 
522
        }
 
523
        
 
524
        if (_this->tty >= 0) {
 
525
            SDL_EVDEV_unmute_keyboard(_this->tty, _this->kb_mode);
 
526
            close(_this->tty);
 
527
        }
 
528
        
 
529
        /* Remove existing devices */
 
530
        while(_this->first != NULL) {
 
531
            SDL_EVDEV_device_removed(_this->first->path);
 
532
        }
 
533
        
 
534
        SDL_assert(_this->first == NULL);
 
535
        SDL_assert(_this->last == NULL);
 
536
        SDL_assert(_this->numdevices == 0);
 
537
        
 
538
        SDL_free(_this);
 
539
        _this = NULL;
 
540
    }
 
541
}
 
542
 
 
543
#if SDL_USE_LIBUDEV
 
544
void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
 
545
{
 
546
    if (devpath == NULL) {
 
547
        return;
 
548
    }
 
549
    
 
550
    if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE|SDL_UDEV_DEVICE_KEYBOARD))) {
 
551
        return;
 
552
    }
 
553
 
 
554
    switch( udev_type ) {
 
555
    case SDL_UDEV_DEVICEADDED:
 
556
        SDL_EVDEV_device_added(devpath);
 
557
        break;
 
558
            
 
559
    case SDL_UDEV_DEVICEREMOVED:
 
560
        SDL_EVDEV_device_removed(devpath);
 
561
        break;
 
562
            
 
563
    default:
 
564
        break;
 
565
    }
 
566
}
 
567
 
 
568
#endif /* SDL_USE_LIBUDEV */
 
569
 
 
570
void 
 
571
SDL_EVDEV_Poll(void)
 
572
{
 
573
    struct input_event events[32];
 
574
    int i, len;
 
575
    SDL_evdevlist_item *item;
 
576
    SDL_Scancode scan_code;
 
577
    int mouse_button;
 
578
    SDL_Mouse *mouse;
 
579
#ifdef SDL_INPUT_LINUXKD
 
580
    Uint16 modstate;
 
581
    struct kbentry kbe;
 
582
    static char keysym[8];
 
583
    char *end;
 
584
    Uint32 kval;
 
585
#endif
 
586
 
 
587
#if SDL_USE_LIBUDEV
 
588
    SDL_UDEV_Poll();
 
589
#endif
 
590
 
 
591
    mouse = SDL_GetMouse();
 
592
 
 
593
    for (item = _this->first; item != NULL; item = item->next) {
 
594
        while ((len = read(item->fd, events, (sizeof events))) > 0) {
 
595
            len /= sizeof(events[0]);
 
596
            for (i = 0; i < len; ++i) {
 
597
                switch (events[i].type) {
 
598
                case EV_KEY:
 
599
                    if (events[i].code >= BTN_MOUSE && events[i].code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
 
600
                        mouse_button = events[i].code - BTN_MOUSE;
 
601
                        if (events[i].value == 0) {
 
602
                            SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
 
603
                        } else if (events[i].value == 1) {
 
604
                            SDL_SendMouseButton(mouse->focus, mouse->mouseID, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
 
605
                        }
 
606
                        break;
 
607
                    }
 
608
 
 
609
                    /* Probably keyboard */
 
610
                    scan_code = SDL_EVDEV_translate_keycode(events[i].code);
 
611
                    if (scan_code != SDL_SCANCODE_UNKNOWN) {
 
612
                        if (events[i].value == 0) {
 
613
                            SDL_SendKeyboardKey(SDL_RELEASED, scan_code);
 
614
                        } else if (events[i].value == 1 || events[i].value == 2 /* Key repeated */ ) {
 
615
                            SDL_SendKeyboardKey(SDL_PRESSED, scan_code);
 
616
#ifdef SDL_INPUT_LINUXKD
 
617
                            if (_this->console_fd >= 0) {
 
618
                                kbe.kb_index = events[i].code;
 
619
                                /* Convert the key to an UTF-8 char */
 
620
                                /* Ref: http://www.linuxjournal.com/article/2783 */
 
621
                                modstate = SDL_GetModState();
 
622
                                kbe.kb_table = 0;
 
623
                                
 
624
                                /* Ref: http://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching */
 
625
                                kbe.kb_table |= -( (modstate & KMOD_LCTRL) != 0) & (1 << KG_CTRLL | 1 << KG_CTRL);
 
626
                                kbe.kb_table |= -( (modstate & KMOD_RCTRL) != 0) & (1 << KG_CTRLR | 1 << KG_CTRL);
 
627
                                kbe.kb_table |= -( (modstate & KMOD_LSHIFT) != 0) & (1 << KG_SHIFTL | 1 << KG_SHIFT);
 
628
                                kbe.kb_table |= -( (modstate & KMOD_RSHIFT) != 0) & (1 << KG_SHIFTR | 1 << KG_SHIFT);
 
629
                                kbe.kb_table |= -( (modstate & KMOD_LALT) != 0) & (1 << KG_ALT);
 
630
                                kbe.kb_table |= -( (modstate & KMOD_RALT) != 0) & (1 << KG_ALTGR);
 
631
 
 
632
                                if (ioctl(_this->console_fd, KDGKBENT, (unsigned long)&kbe) == 0 && 
 
633
                                    ((KTYP(kbe.kb_value) == KT_LATIN) || (KTYP(kbe.kb_value) == KT_ASCII) || (KTYP(kbe.kb_value) == KT_LETTER))) 
 
634
                                {
 
635
                                    kval = KVAL(kbe.kb_value);
 
636
                                    
 
637
                                    /* While there's a KG_CAPSSHIFT symbol, it's not useful to build the table index with it
 
638
                                     * because 1 << KG_CAPSSHIFT overflows the 8 bits of kb_table 
 
639
                                     * So, we do the CAPS LOCK logic here. Note that isalpha depends on the locale!
 
640
                                     */
 
641
                                    if ( modstate & KMOD_CAPS && isalpha(kval) ) {
 
642
                                        if ( isupper(kval) ) {
 
643
                                            kval = tolower(kval);
 
644
                                        } else {
 
645
                                            kval = toupper(kval);
 
646
                                        }
 
647
                                    }
 
648
                                     
 
649
                                    /* Convert to UTF-8 and send */
 
650
                                    end = SDL_UCS4ToUTF8( kval, keysym);
 
651
                                    *end = '\0';
 
652
                                    SDL_SendKeyboardText(keysym);
 
653
                                }
 
654
                            }
 
655
#endif /* SDL_INPUT_LINUXKD */
 
656
                        }
 
657
                    }
 
658
                    break;
 
659
                case EV_ABS:
 
660
                    switch(events[i].code) {
 
661
                    case ABS_X:
 
662
                        SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, events[i].value, mouse->y);
 
663
                        break;
 
664
                    case ABS_Y:
 
665
                        SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_FALSE, mouse->x, events[i].value);
 
666
                        break;
 
667
                    default:
 
668
                        break;
 
669
                    }
 
670
                    break;
 
671
                case EV_REL:
 
672
                    switch(events[i].code) {
 
673
                    case REL_X:
 
674
                        SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, events[i].value, 0);
 
675
                        break;
 
676
                    case REL_Y:
 
677
                        SDL_SendMouseMotion(mouse->focus, mouse->mouseID, SDL_TRUE, 0, events[i].value);
 
678
                        break;
 
679
                    case REL_WHEEL:
 
680
                        SDL_SendMouseWheel(mouse->focus, mouse->mouseID, 0, events[i].value);
 
681
                        break;
 
682
                    case REL_HWHEEL:
 
683
                        SDL_SendMouseWheel(mouse->focus, mouse->mouseID, events[i].value, 0);
 
684
                        break;
 
685
                    default:
 
686
                        break;
 
687
                    }
 
688
                    break;
 
689
                case EV_SYN:
 
690
                    switch (events[i].code) {
 
691
                    case SYN_DROPPED:
 
692
                        SDL_EVDEV_sync_device(item);
 
693
                        break;
 
694
                    default:
 
695
                        break;
 
696
                    }
 
697
                    break;
 
698
                }
 
699
            }
 
700
        }    
 
701
    }
 
702
}
 
703
 
 
704
static SDL_Scancode
 
705
SDL_EVDEV_translate_keycode(int keycode)
 
706
{
 
707
    SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
 
708
 
 
709
    if (keycode < SDL_arraysize(EVDEV_Keycodes)) {
 
710
        scancode = EVDEV_Keycodes[keycode];
 
711
    }
 
712
    if (scancode == SDL_SCANCODE_UNKNOWN) {
 
713
        SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL mailing list <sdl@libsdl.org> EVDEV KeyCode %d \n", keycode);
 
714
    }
 
715
    return scancode;
 
716
}
 
717
 
 
718
static void
 
719
SDL_EVDEV_sync_device(SDL_evdevlist_item *item) 
 
720
{
 
721
    /* TODO: get full state of device and report whatever is required */
 
722
}
 
723
 
 
724
#if SDL_USE_LIBUDEV
 
725
static int
 
726
SDL_EVDEV_device_added(const char *devpath)
 
727
{
 
728
    SDL_evdevlist_item *item;
 
729
 
 
730
    /* Check to make sure it's not already in list. */
 
731
    for (item = _this->first; item != NULL; item = item->next) {
 
732
        if (strcmp(devpath, item->path) == 0) {
 
733
            return -1;  /* already have this one */
 
734
        }
 
735
    }
 
736
    
 
737
    item = (SDL_evdevlist_item *) SDL_calloc(1, sizeof (SDL_evdevlist_item));
 
738
    if (item == NULL) {
 
739
        return SDL_OutOfMemory();
 
740
    }
 
741
 
 
742
    item->fd = open(devpath, O_RDONLY, 0);
 
743
    if (item->fd < 0) {
 
744
        SDL_free(item);
 
745
        return SDL_SetError("Unable to open %s", devpath);
 
746
    }
 
747
    
 
748
    item->path = SDL_strdup(devpath);
 
749
    if (item->path == NULL) {
 
750
        close(item->fd);
 
751
        SDL_free(item);
 
752
        return SDL_OutOfMemory();
 
753
    }
 
754
    
 
755
    /* Non blocking read mode */
 
756
    fcntl(item->fd, F_SETFL, O_NONBLOCK);
 
757
    
 
758
    if (_this->last == NULL) {
 
759
        _this->first = _this->last = item;
 
760
    } else {
 
761
        _this->last->next = item;
 
762
        _this->last = item;
 
763
    }
 
764
    
 
765
    SDL_EVDEV_sync_device(item);
 
766
    
 
767
    return _this->numdevices++;
 
768
}
 
769
#endif /* SDL_USE_LIBUDEV */
 
770
 
 
771
static int
 
772
SDL_EVDEV_device_removed(const char *devpath)
 
773
{
 
774
    SDL_evdevlist_item *item;
 
775
    SDL_evdevlist_item *prev = NULL;
 
776
 
 
777
    for (item = _this->first; item != NULL; item = item->next) {
 
778
        /* found it, remove it. */
 
779
        if ( strcmp(devpath, item->path) ==0 ) {
 
780
            if (prev != NULL) {
 
781
                prev->next = item->next;
 
782
            } else {
 
783
                SDL_assert(_this->first == item);
 
784
                _this->first = item->next;
 
785
            }
 
786
            if (item == _this->last) {
 
787
                _this->last = prev;
 
788
            }
 
789
            close(item->fd);
 
790
            SDL_free(item->path);
 
791
            SDL_free(item);
 
792
            _this->numdevices--;
 
793
            return 0;
 
794
        }
 
795
        prev = item;
 
796
    }
 
797
 
 
798
    return -1;
 
799
}
 
800
 
 
801
 
 
802
#endif /* SDL_INPUT_LINUXEV */
 
803
 
 
804
/* vi: set ts=4 sw=4 expandtab: */
 
805