Outdated hints corrected.
[cake.git] / arch / all-hosted / hidd / x11 / x11kbd.c
blob058363b30c22d4d7f3a2f19b0de8af3b0cd23316
1 /*
2 Copyright © 1995-2009, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: X11 hidd handling keypresses.
6 Lang: English.
7 */
8 #define DEBUG 0
10 #define __OOP_NOATTRBASES__
12 #include <dos/dos.h>
14 #include <proto/dos.h>
15 #include <proto/utility.h>
16 #include <proto/oop.h>
17 #include <oop/oop.h>
19 #define timeval sys_timeval
20 #include <X11/Xlib.h>
21 #include <X11/keysym.h>
22 #include <X11/Xutil.h>
24 #include <stdio.h>
25 #undef timeval
27 #include <hidd/hidd.h>
28 #include <hidd/keyboard.h>
30 #include <devices/inputevent.h>
32 #include <aros/symbolsets.h>
34 //#define DEBUG 1
35 #include <aros/debug.h>
37 #include LC_LIBDEFS_FILE
39 #include "x11.h"
41 /****************************************************************************************/
43 static UBYTE keycode2rawkey[256];
44 static BOOL havetable;
46 long xkey2hidd (XKeyEvent *xk, struct x11_staticdata *xsd);
48 #if X11_LOAD_KEYMAPTABLE
49 static void LoadKeyCode2RawKeyTable(struct x11_staticdata *xsd);
50 #endif
52 static OOP_AttrBase HiddKbdAB;
54 static struct OOP_ABDescr attrbases[] =
56 { IID_Hidd_Kbd , &HiddKbdAB },
57 { NULL , NULL }
60 /****************************************************************************************/
62 static struct _keytable
64 KeySym keysym;
65 WORD hiddcode;
67 keytable[] =
69 {XK_Return, 0x44 },
70 {XK_Right, 0x4e },
71 {XK_Up, 0x4c },
72 {XK_Left, 0x4f },
73 {XK_Down, 0x4d },
74 {XK_Help, 0x5f },
76 {XK_BackSpace, 0x41 },
77 {XK_Delete, 0x46 },
78 {XK_space, 0x40 },
79 {XK_Shift_L, 0x60 },
80 {XK_Shift_R, 0x61 },
81 {XK_Alt_L, 0x64 },
82 {XK_Alt_R, 0x65 },
83 {XK_Escape, 0x45 },
84 {XK_Tab, 0x42 },
86 {XK_F1, 0x50 },
87 {XK_F2, 0x51 },
88 {XK_F3, 0x52 },
89 {XK_F4, 0x53 },
90 {XK_F5, 0x54 },
91 {XK_F6, 0x55 },
92 {XK_F7, 0x56 },
93 {XK_F8, 0x57 },
94 {XK_F9, 0x58 },
95 {XK_F10, 0x59 },
97 {XK_F11, 0x4B },
98 {XK_F12, 0x5f }, /* HELP, F12 would be 0x6F */
99 {XK_Home, 0x70 },
100 {XK_End, 0x71 },
101 {XK_Insert, 0x47 },
102 {XK_Prior, 0x48 }, /* PageUP */
103 {XK_Next, 0x49 }, /* PageDown */
104 {XK_Pause, 0x6e },
106 {XK_KP_Enter, 0x43 },
107 {XK_KP_Subtract, 0x4a },
108 {XK_KP_Decimal, 0x3c },
109 {XK_KP_Separator, 0x3c },
110 {XK_KP_Delete, 0x3c },
111 {XK_KP_Add, 0x5e },
112 {XK_KP_Subtract, 0x4a },
113 {XK_KP_Multiply, 0x5d },
114 {XK_KP_Divide, 0x5c },
116 {XK_KP_0, 0x0f },
117 {XK_KP_Insert, 0x0f },
118 {XK_KP_1, 0x1d },
119 {XK_KP_End, 0x1d },
120 {XK_KP_2, 0x1e },
121 {XK_KP_Down, 0x1e },
122 {XK_KP_3, 0x1f },
123 {XK_KP_Page_Down, 0x1f },
124 {XK_KP_4, 0x2d },
125 {XK_KP_Left, 0x2d },
126 {XK_KP_5, 0x2e },
127 {XK_KP_Begin, 0x2e },
128 {XK_KP_6, 0x2f },
129 {XK_KP_Right, 0x2f },
130 {XK_KP_7, 0x3d },
131 {XK_KP_Home, 0x3d },
132 {XK_KP_8, 0x3e },
133 {XK_KP_Up, 0x3e },
134 {XK_KP_9, 0x3f },
135 {XK_KP_Page_Up, 0x3f },
137 {XK_E, 0x12 },
138 {XK_e, 0x12 },
139 {XK_R, 0x13 },
140 {XK_r, 0x13 },
141 {XK_T, 0x14 },
142 {XK_t, 0x14 },
143 {XK_U, 0x16 },
144 {XK_u, 0x16 },
145 {XK_I, 0x17 },
146 {XK_i, 0x17 },
147 {XK_O, 0x18 },
148 {XK_o, 0x18 },
149 {XK_P, 0x19 },
150 {XK_p, 0x19 },
152 {XK_S, 0x21 },
153 {XK_s, 0x21 },
154 {XK_D, 0x22 },
155 {XK_d, 0x22 },
156 {XK_F, 0x23 },
157 {XK_f, 0x23 },
158 {XK_G, 0x24 },
159 {XK_g, 0x24 },
160 {XK_H, 0x25 },
161 {XK_h, 0x25 },
162 {XK_J, 0x26 },
163 {XK_j, 0x26 },
164 {XK_K, 0x27 },
165 {XK_k, 0x27 },
166 {XK_L, 0x28 },
167 {XK_l, 0x28 },
169 {XK_X, 0x32 },
170 {XK_x, 0x32 },
171 {XK_c, 0x33 },
172 {XK_C, 0x33 },
173 {XK_V, 0x34 },
174 {XK_v, 0x34 },
175 {XK_B, 0x35 },
176 {XK_b, 0x35 },
177 {XK_N, 0x36 },
178 {XK_n, 0x36 },
180 {XK_1, 0x01 },
181 {XK_2, 0x02 },
182 {XK_3, 0x03 },
183 {XK_4, 0x04 },
184 {XK_5, 0x05 },
185 {XK_6, 0x06 },
186 {XK_7, 0x07 },
187 {XK_8, 0x08 },
188 {XK_9, 0x09 },
189 {XK_0, 0x0A },
190 {0, - 1 }
193 /****************************************************************************************/
195 /* English keyboard */
196 static struct _keytable english_keytable[] =
198 {XK_Control_L, 0x63 }, /* left control = control */
199 {XK_Multi_key, 0x63 }, /* right control = control */
200 {XK_Super_L, 0x66 }, /* left win = LAMIGA */
201 {XK_Super_R, 0x67 }, /* right win = RAMIGA */
202 {XK_Meta_L, 0x64 }, /* left Alt = LALT */
203 {XK_Mode_switch, 0x65 }, /* right Alt = RALT */
205 /* Key left of S */
206 {XK_A, 0x20 },
207 {XK_a, 0x20 },
209 /* Key right of N */
210 {XK_M, 0x37 },
211 {XK_m, 0x37 },
213 /* Key right of TAB */
214 {XK_Q, 0x10 },
215 {XK_q, 0x10 },
217 /* Key between T and U */
218 {XK_y, 0x15 },
219 {XK_Y, 0x15 },
221 /* Key left of E */
222 {XK_W, 0x11 },
223 {XK_w, 0x11 },
225 /* Key left of X */
226 {XK_z, 0x31 },
227 {XK_Z, 0x31 },
230 /* Key left of 1 */
231 {XK_grave, 0x00 },
233 /* Keys right of 0 */
234 {XK_minus, 0x0B },
235 {XK_equal, 0x0C },
237 /* Keys right of P */
238 {XK_bracketleft, 0x1A },
239 {XK_bracketright, 0x1B },
241 /* Keys right of L */
242 {XK_semicolon, 0x29 },
243 {XK_apostrophe, 0x2A },
244 {XK_backslash, 0x2B }, /* Third key right of L might not be present */
246 /* Key right of shift and 2nd left of X (might not be present) */
247 {XK_less, 0x30 },
249 /* Keys 2nd right of N (= usually right of M) */
250 {XK_comma, 0x38 },
251 {XK_period, 0x39 },
252 {XK_slash, 0x3A },
254 {0, -1 }
257 /****************************************************************************************/
259 /* German keyboard */
260 static struct _keytable german_keytable[] =
262 {XK_Control_L, 0x63 }, /* linke STRG = control */
263 {XK_Multi_key, 0x63 }, /* rechte STRG = control */
264 {XK_Super_L, 0x66 }, /* Linke Win = LAMIGA */
265 {XK_Super_R, 0x67 }, /* Rechte Win = RAMIGA */
266 {XK_Meta_L, 0x64 }, /* Linke Alt = LALT */
267 {XK_Mode_switch, 0x65 }, /* Alt Gr = RALT */
269 /* Key left of S */
270 {XK_A, 0x20 },
271 {XK_a, 0x20 },
273 /* Key right of N */
274 {XK_M, 0x37 },
275 {XK_m, 0x37 },
277 /* Key right of TAB */
278 {XK_Q, 0x10 },
279 {XK_q, 0x10 },
281 /* Key between T and U */
282 {XK_Z, 0x15 },
283 {XK_z, 0x15 },
285 /* Key left of E */
286 {XK_W, 0x11 },
287 {XK_w, 0x11 },
289 /* Key left of X */
290 {XK_y, 0x31 },
291 {XK_Y, 0x31 },
293 /* Key left of 1 */
294 {XK_asciicircum, 0x00 }, /* Akzent links neben 1 Taste */
296 /* Keys right of 0 */
297 {XK_equal, 0x0A }, /* = */
298 {XK_ssharp, 0x0B }, /* scharfes s */
299 {XK_acute, 0x0C }, /* Akzent rechts von scharfem s */
301 /* Keys right of P */
302 {XK_udiaeresis, 0x1A }, /* Umlaut u */
303 {XK_Udiaeresis, 0x1A },
304 {XK_plus, 0x1B }, /* + */
306 /* Keys right of L */
307 {XK_odiaeresis, 0x29 }, /* Umlaut o */
308 {XK_Odiaeresis, 0x29 },
309 {XK_adiaeresis, 0x2A }, /* Umlaut a */
310 {XK_Adiaeresis, 0x2A },
311 {XK_numbersign, 0x2B }, /* # */
313 /* Key right of shift and 2nd left of X (might not be present) */
314 {XK_less, 0x30 }, /* < */
316 /* Keys 2nd right of N (= usually right of M) */
317 {XK_comma, 0x38 }, /* Komma */
318 {XK_period, 0x39 }, /* Punkt */
319 {XK_minus, 0x3A }, /* Minus */
321 {0, -1 }
324 /* Itialian keyboard */
325 static struct _keytable italian_keytable[] =
327 {XK_Control_L, 0x63 }, /* left CTRL = control */
328 {XK_Multi_key, 0x63 }, /* right CTRL = control */
329 {XK_Super_L, 0x66 }, /* left win = LAMIGA */
330 {XK_Super_R, 0x67 }, /* right win = RAMIGA */
331 {XK_Meta_L, 0x64 }, /* left alt = LALT */
332 {XK_Mode_switch, 0x65 }, /* right alt = RALT */
335 /* Key left of S */
336 {XK_A, 0x20 },
337 {XK_a, 0x20 },
339 /* Key right of N */
340 {XK_M, 0x37 },
341 {XK_m, 0x37 },
343 /* Key right of TAB */
344 {XK_Q, 0x10 },
345 {XK_q, 0x10 },
347 /* Key between T and U */
348 {XK_y, 0x15 },
349 {XK_Y, 0x15 },
351 /* Key left of E */
352 {XK_W, 0x11 },
353 {XK_w, 0x11 },
355 /* Key left of X */
356 {XK_z, 0x31 },
357 {XK_Z, 0x31 },
360 /* Key left of 1 */
361 {XK_backslash, 0x00 },
363 /* Keys right of 0 */
364 {XK_apostrophe, 0x0B },
365 {XK_Igrave, 0x0C },
366 {XK_igrave, 0x0C },
368 /* Keys right of P */
369 {XK_Egrave, 0x1A },
370 {XK_egrave, 0x1A },
371 {XK_plus, 0x1B }, /* + */
373 /* Keys right of L */
374 {XK_Ograve, 0x29 },
375 {XK_ograve, 0x29 },
376 {XK_Agrave, 0x2A },
377 {XK_agrave, 0x2A },
378 {XK_Ugrave, 0x2B }, /* Third key right of L might not be present */
379 {XK_ugrave, 0x2B },
381 /* Key right of shift and 2nd left of X (might not be present) */
382 {XK_less, 0x30 }, /* < */
384 /* Keys 2nd right of N (= usually right of M) */
385 {XK_comma, 0x38 },
386 {XK_period, 0x39 },
387 {XK_minus, 0x3A },
389 {0, -1 }
392 /****************************************************************************************/
394 #if 0
396 /* Use this template to create a keytable for your language:
398 Do not touch the right values (rawkey numbers). Only change
399 the XK's at the left side. To find out the XK_ names (keysym)
400 start "xev" and press the key the comment describes (for
401 example "Key left of S" in the xev window. In the Shell
402 window you will see output like this:
404 KeyPress event, serial 30, synthetic NO, window 0x5000001,
405 root 0x47, subw 0x5000002, time 3410089115, (24,45), root:(28,69),
406 state 0x0, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
407 XLookupString gives 0 characters: "" |
409 This is the keysym name _______________/
411 So in this case you would have to write "XK_Shift_L"
413 Check all keys, not just the ones with "XK_????"!!!
416 static struct _keytable template_keytable[] =
418 {XK_Control_L, 0x63 }, /* left control = control */
419 {XK_Multi_key, 0x63 }, /* right control = control */
420 {XK_Super_L, 0x66 }, /* left win = LAMIGA */
421 {XK_Super_R, 0x67 }, /* right win = RAMIGA */
422 {XK_Meta_L, 0x64 }, /* left Alt = LALT */
423 {XK_Mode_switch, 0x65 }, /* right Alt = RALT */
425 /* Key left of S */
426 {XK_A, 0x20 },
427 {XK_a, 0x20 },
429 /* Key right of N */
430 {XK_M, 0x37 },
431 {XK_m, 0x37 },
433 /* Key right of TAB */
434 {XK_Q, 0x10 },
435 {XK_q, 0x10 },
437 /* Key between T and U */
438 {XK_????, 0x15 },
439 {XK_????, 0x15 },
441 /* Key left of E */
442 {XK_W, 0x11 },
443 {XK_w, 0x11 },
445 /* Key left of X */
446 {XK_????, 0x31 },
447 {XK_????, 0x31 },
450 /* Key left of 1 */
451 {XK_????, 0x00 },
453 /* Keys right of 0 */
454 {XK_????, 0x0B },
455 {XK_????, 0x0C },
457 /* Keys right of P */
458 {XK_????, 0x1A },
459 {XK_????, 0x1B },
461 /* Keys right of L */
462 {XK_????, 0x29 },
463 {XK_????, 0x2A },
464 {XK_????, 0x2B }, /* Third key right of L might not be present */
466 /* Key right of shift and 2nd left of X (might not be present) */
467 {XK_less, 0x30 },
469 /* Keys 2nd right of N (= usually right of M) */
470 {XK_comma, 0x38 },
471 {XK_period, 0x39 },
472 {XK_slash, 0x3A },
474 {0, -1 }
477 #endif
479 /****************************************************************************************/
481 OOP_Object * X11Kbd__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg)
483 BOOL has_kbd_hidd = FALSE;
484 struct Task *me;
485 struct TagItem *tag, *tstate;
486 APTR callback = NULL;
487 APTR callbackdata = NULL;
489 EnterFunc(bug("X11Kbd::New()\n"));
491 ObtainSemaphoreShared( &XSD(cl)->sema);
493 if (XSD(cl)->kbdhidd)
494 has_kbd_hidd = TRUE;
496 ReleaseSemaphore( &XSD(cl)->sema);
498 if (has_kbd_hidd) { /* Cannot open twice */
499 D(bug("[X11Kbd] Attempt to create second instance\n"));
500 ReturnPtr("X11Kbd::New", OOP_Object *, NULL); /* Should have some error code here */
503 #if X11_LOAD_KEYMAPTABLE
504 /* During bootmenu initialization we are still task, not a process,
505 so we can't call DOS at that moment. Anyway there are no mounted
506 devices yet. */
507 me = FindTask(NULL);
508 if (me->tc_Node.ln_Type == NT_PROCESS) {
509 D(bug("[X11Kbd] Trying to load X keymap\n"));
510 LoadKeyCode2RawKeyTable(XSD(cl));
512 D(else bug("[X11Kbd] Early init, don't try to load X keymap\n");)
513 #endif
515 tstate = msg->attrList;
516 D(bug("tstate: %p, tag=%x\n", tstate, tstate->ti_Tag));
518 while ((tag = NextTagItem((const struct TagItem **)&tstate)))
520 ULONG idx;
522 D(bug("Got tag %d, data %x\n", tag->ti_Tag, tag->ti_Data));
524 if (IS_HIDDKBD_ATTR(tag->ti_Tag, idx))
526 D(bug("Kbd hidd tag\n"));
527 switch (idx)
529 case aoHidd_Kbd_IrqHandler:
530 callback = (APTR)tag->ti_Data;
531 D(bug("Got callback %p\n", (APTR)tag->ti_Data));
532 break;
534 case aoHidd_Kbd_IrqHandlerData:
535 callbackdata = (APTR)tag->ti_Data;
536 D(bug("Got data %p\n", (APTR)tag->ti_Data));
537 break;
541 } /* while (tags to process) */
543 if (NULL == callback)
544 ReturnPtr("X11Kbd::New", OOP_Object *, NULL); /* Should have some error code here */
546 o = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
547 if (o)
549 struct x11kbd_data *data = OOP_INST_DATA(cl, o);
551 data->kbd_callback = (VOID (*)(APTR, UWORD))callback;
552 data->callbackdata = callbackdata;
553 data->prev_keycode = 0xFFFF;
555 ObtainSemaphore( &XSD(cl)->sema);
556 XSD(cl)->kbdhidd = o;
557 ReleaseSemaphore( &XSD(cl)->sema);
560 ReturnPtr("X11Kbd::New", OOP_Object *, o);
563 /****************************************************************************************/
565 VOID X11Kbd__Root__Dispose(OOP_Class *cl, OOP_Object *o, struct pRoot_Dispose *msg)
567 EnterFunc(bug("[X11Kbd] Dispose()\n"));
569 ObtainSemaphore( &XSD(cl)->sema);
570 XSD(cl)->kbdhidd = NULL;
571 ReleaseSemaphore( &XSD(cl)->sema);
572 OOP_DoSuperMethod(cl, o, msg);
575 /****************************************************************************************/
577 VOID X11Kbd__Hidd_X11Kbd__HandleEvent(OOP_Class *cl, OOP_Object *o, struct pHidd_X11Kbd_HandleEvent *msg)
579 struct x11kbd_data *data;
580 XKeyEvent *xk;
581 UWORD keycode;
583 EnterFunc(bug("x11kbd_handleevent()\n"));
585 data = OOP_INST_DATA(cl, o);
586 xk = &(msg->event->xkey);
588 keycode = (UWORD)xkey2hidd(xk, XSD(cl));
590 if (msg->event->type == KeyRelease)
592 keycode |= IECODE_UP_PREFIX;
595 if (keycode != data->prev_keycode)
597 data->kbd_callback(data->callbackdata, keycode);
598 data->prev_keycode = keycode;
601 ReturnVoid("X11Kbd::HandleEvent");
604 /****************************************************************************************/
606 #undef XSD
607 #define XSD(cl) xsd
609 /****************************************************************************************/
611 WORD lookup_keytable(KeySym *ks, struct _keytable *keytable)
613 short t;
614 WORD result = -1;
616 for (t = 0; keytable[t].hiddcode != -1; t++)
618 if (*ks == keytable[t].keysym)
620 D(bug("xktac: found in key table\n"));
621 result = keytable[t].hiddcode;
622 break;
626 return result;
629 /****************************************************************************************/
631 long xkey2hidd (XKeyEvent *xk, struct x11_staticdata *xsd)
633 char buffer[10];
634 KeySym ks;
635 int count;
636 long result;
638 D(bug("xkey2hidd\n"));
640 if (havetable)
642 result = -1;
643 if ((xk->keycode >= 0) && (xk->keycode < 256))
645 result = keycode2rawkey[xk->keycode];
646 if (result == 255) result = -1;
649 return result;
652 LOCK_X11
653 xk->state = 0;
654 count = XCALL(XLookupString, xk, buffer, 10, &ks, NULL);
655 UNLOCK_X11
657 D(bug("xk2h: Code %d (0x%x). Event was decoded into %d chars: %d (0x%x)\n",xk->keycode, xk->keycode, count,ks,ks));
659 result = lookup_keytable(&ks, keytable);
660 if (result == -1) result = lookup_keytable(&ks, english_keytable);
662 if (result != -1)
664 ReturnInt ("xk2h", long, result);
667 D(bug("xk2h: Passing X keycode\n", xk->keycode & 0xffff));
669 result = xk->keycode & 0xffff;
671 ReturnInt ("xk2h", long, result);
673 } /* XKeyToAmigaCode */
675 /****************************************************************************************/
677 #if X11_LOAD_KEYMAPTABLE
679 /****************************************************************************************/
681 static void LoadKeyCode2RawKeyTable(struct x11_staticdata *xsd)
683 char *filename = "DEVS:Keymaps/X11/keycode2rawkey.table";
684 BPTR fh;
685 struct DosLibrary *DOSBase;
687 DOSBase = (struct DosLibrary *)OpenLibrary(DOSNAME, 37);
688 D(bug("[X11Kbd] DOSBase is %p\n", DOSBase));
689 if (DOSBase == NULL)
691 bug("LoadKeyCode2RawKeyTable: Opening %s failed\n", DOSNAME);
692 return;
695 if ((fh = Open(filename, MODE_OLDFILE)))
697 D(bug("[X11Kbd] X keymap file handle: %p\n", fh));
698 if ((256 == Read(fh, keycode2rawkey, 256)))
700 D(bug("LoadKeyCode2RawKeyTable: keycode2rawkey.table successfully loaded!\n"));
701 havetable = TRUE;
703 else
705 bug("LoadKeyCode2RawKeyTable: Reading from \"%s\" failed!\n", filename);
707 Close(fh);
709 else
711 bug("\nLoadKeyCode2RawKeyTable: Loading \"%s\" failed!\n"
712 "\n"
713 "This means that many/most/all keys on your keyboard won't work as you\n"
714 "would expect in AROS. Therefore you should create this table by either\n"
715 "using the default table:\n"
716 "\n"
717 " make default-x11keymaptable\n"
718 "\n"
719 "or generating your own one:\n"
720 "\n"
721 " make change-x11keymaptable\n"
722 "\n"
723 "The default keymaptable probably works with most PCs having a 105 key\n"
724 "keyboard if you are using XFree86 as X Server (might also work with\n"
725 "others). So try that one first!\n"
726 "\n"
727 "Since the keymap table will be deleted when you do a \"make clean\" you\n"
728 "might want to make a backup of it. Then you will be able to restor it later:\n"
729 "\n"
730 " make backup-x11keymaptable\n"
731 " make restore-x11keymaptable\n"
732 "\n"
733 "The keymap table will be backuped in your HOME directory.\n"
734 "\n"
735 "Note that the keymaptable only makes sure that your keyboard looks as\n"
736 "much as possible like an Amiga keyboard to AROS. So with the keymaptable\n"
737 "alone the keyboard will behave like an Amiga keyboard with American layout\n."
738 "For other layouts you must activate the correct keymap just like in AmigaOS.\n",
739 filename);
742 CloseLibrary((struct Library *)DOSBase);
745 /****************************************************************************************/
747 #endif
749 /****************************************************************************************/
751 #undef XSD
752 #define XSD(cl) (&LIBBASE->xsd)
754 /****************************************************************************************/
756 static int kbd_init(LIBBASETYPEPTR LIBBASE)
759 return OOP_ObtainAttrBases(attrbases);
762 /****************************************************************************************/
764 static int kbd_expunge(LIBBASETYPEPTR LIBBASE)
766 OOP_ReleaseAttrBases(attrbases);
767 return TRUE;
770 /****************************************************************************************/
772 ADD2INITLIB(kbd_init, 0);
773 ADD2EXPUNGELIB(kbd_expunge, 0);
775 /****************************************************************************************/