rnd-20020324-1-src
[rocksndiamonds.git] / src / libgame / msdos.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2001 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * msdos.c                                                  *
12 ***********************************************************/
13
14 #include "system.h"
15
16
17 #if defined(PLATFORM_MSDOS)
18
19 #include "sound.h"
20 #include "joystick.h"
21 #include "misc.h"
22 #include "pcx.h"
23
24 #define AllegroDefaultScreen() (display->screens[display->default_screen])
25
26 /* allegro driver declarations */
27 DECLARE_GFX_DRIVER_LIST(GFX_DRIVER_VBEAF GFX_DRIVER_VESA2L GFX_DRIVER_VESA1)
28 DECLARE_COLOR_DEPTH_LIST(COLOR_DEPTH_8)
29 DECLARE_DIGI_DRIVER_LIST(DIGI_DRIVER_SB)
30 DECLARE_MIDI_DRIVER_LIST()
31 DECLARE_JOYSTICK_DRIVER_LIST(JOYSTICK_DRIVER_STANDARD)
32
33 /* allegro global variables */
34 extern volatile int key_shifts;
35 extern int num_joysticks;
36 extern JOYSTICK_INFO joy[];
37 extern int i_love_bill;
38
39 /* internal variables of msdos.c */
40 static boolean keyboard_auto_repeat = TRUE;
41 static int key_press_state[MAX_SCANCODES];
42 static XEvent event_buffer[MAX_EVENT_BUFFER];
43 static int pending_events;
44 static boolean joystick_event;
45 static boolean mouse_installed = FALSE;
46 static int last_mouse_pos;
47 static int last_mouse_b;
48 static int last_joystick_state;
49 static BITMAP* video_bitmap;
50
51 static RGB global_colormap[MAX_COLORS];
52 static int global_colormap_entries_used = 0;
53
54 boolean wait_for_vsync;
55
56 /*
57 extern int playing_sounds;
58 extern struct SoundControl playlist[MAX_SOUNDS_PLAYING];
59 extern struct SoundControl emptySoundControl;
60 */
61
62 static BITMAP *Read_PCX_to_AllegroBitmap(char *);
63
64 static void allegro_init_drivers()
65 {
66   int i;
67
68   for (i=0; i<MAX_EVENT_BUFFER; i++)
69     event_buffer[i].type = 0;
70
71   for (i=0; i<MAX_SCANCODES; i++)
72     key_press_state[i] = KeyReleaseMask;
73
74   last_mouse_pos = mouse_pos;
75   last_mouse_b = 0;
76
77   pending_events = 0;
78   clear_keybuf();
79
80   /* enable Windows friendly timer mode (already default under Windows) */
81   i_love_bill = TRUE;
82
83   install_keyboard();
84   install_timer();
85   if (install_mouse() > 0)
86     mouse_installed = TRUE;
87
88   last_joystick_state = 0;
89   joystick_event = FALSE;
90 }
91
92 static boolean allegro_init_audio()
93 {
94   reserve_voices(MAX_SOUNDS_PLAYING, 0);
95
96   if (install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL) == -1)
97     if (install_sound(DIGI_SB, MIDI_NONE, NULL) == -1)
98       return FALSE;
99
100   return TRUE;
101 }
102
103 static boolean hide_mouse(Display *display, int x, int y,
104                           unsigned int width, unsigned int height)
105 {
106   if (mouse_x + display->mouse_ptr->w < x || mouse_x > x + width)
107     return FALSE;
108   if (mouse_y + display->mouse_ptr->h < y || mouse_y > y + height)
109     return FALSE;
110
111   show_mouse(NULL);
112
113   return TRUE;
114 }
115
116 static void unhide_mouse(Display *display)
117 {
118   if (mouse_installed)
119     show_mouse(video_bitmap);
120 }
121
122 static KeySym ScancodeToKeySym(byte scancode)
123 {
124   switch(scancode)
125   {
126     case KEY_ESC:               return XK_Escape;
127     case KEY_1:                 return XK_1;
128     case KEY_2:                 return XK_2;
129     case KEY_3:                 return XK_3;
130     case KEY_4:                 return XK_4;
131     case KEY_5:                 return XK_5;
132     case KEY_6:                 return XK_6;
133     case KEY_7:                 return XK_7;
134     case KEY_8:                 return XK_8;
135     case KEY_9:                 return XK_9;
136     case KEY_0:                 return XK_0;
137     case KEY_MINUS:             return XK_minus;
138     case KEY_EQUALS:            return XK_equal;
139     case KEY_BACKSPACE:         return XK_BackSpace;
140     case KEY_TAB:               return XK_Tab;
141     case KEY_Q:                 return XK_q;
142     case KEY_W:                 return XK_w;
143     case KEY_E:                 return XK_e;
144     case KEY_R:                 return XK_r;
145     case KEY_T:                 return XK_t;
146     case KEY_Y:                 return XK_y;
147     case KEY_U:                 return XK_u;
148     case KEY_I:                 return XK_i;
149     case KEY_O:                 return XK_o;
150     case KEY_P:                 return XK_p;
151     case KEY_OPENBRACE:         return XK_braceleft;
152     case KEY_CLOSEBRACE:        return XK_braceright;
153     case KEY_ENTER:             return XK_Return;
154     case KEY_LCONTROL:          return XK_Control_L;
155     case KEY_A:                 return XK_a;
156     case KEY_S:                 return XK_s;
157     case KEY_D:                 return XK_d;
158     case KEY_F:                 return XK_f;
159     case KEY_G:                 return XK_g;
160     case KEY_H:                 return XK_h;
161     case KEY_J:                 return XK_j;
162     case KEY_K:                 return XK_k;
163     case KEY_L:                 return XK_l;
164     case KEY_COLON:             return XK_colon;
165     case KEY_QUOTE:             return XK_apostrophe;
166     case KEY_TILDE:             return XK_asciitilde;
167     case KEY_LSHIFT:            return XK_Shift_L;
168     case KEY_BACKSLASH:         return XK_backslash;
169     case KEY_Z:                 return XK_z;
170     case KEY_X:                 return XK_x;
171     case KEY_C:                 return XK_c;
172     case KEY_V:                 return XK_v;
173     case KEY_B:                 return XK_b;
174     case KEY_N:                 return XK_n;
175     case KEY_M:                 return XK_m;
176     case KEY_COMMA:             return XK_comma;
177     case KEY_STOP:              return XK_period;
178     case KEY_SLASH:             return XK_slash;
179     case KEY_RSHIFT:            return XK_Shift_R;
180     case KEY_ASTERISK:          return XK_KP_Multiply;
181     case KEY_ALT:               return XK_Alt_L;
182     case KEY_SPACE:             return XK_space;
183     case KEY_CAPSLOCK:          return XK_Caps_Lock;
184     case KEY_F1:                return XK_F1;
185     case KEY_F2:                return XK_F2;
186     case KEY_F3:                return XK_F3;
187     case KEY_F4:                return XK_F4;
188     case KEY_F5:                return XK_F5;
189     case KEY_F6:                return XK_F6;
190     case KEY_F7:                return XK_F7;
191     case KEY_F8:                return XK_F8;
192     case KEY_F9:                return XK_F9;
193     case KEY_F10:               return XK_F10;
194     case KEY_NUMLOCK:           return XK_Num_Lock;
195     case KEY_SCRLOCK:           return XK_Scroll_Lock;
196     case KEY_HOME:              return XK_Home;
197     case KEY_UP:                return XK_Up;
198     case KEY_PGUP:              return XK_Page_Up;
199     case KEY_MINUS_PAD:         return XK_KP_Subtract;
200     case KEY_LEFT:              return XK_Left;
201     case KEY_5_PAD:             return XK_KP_5;
202     case KEY_RIGHT:             return XK_Right;
203     case KEY_PLUS_PAD:          return XK_KP_Add;
204     case KEY_END:               return XK_End;
205     case KEY_DOWN:              return XK_Down;
206     case KEY_PGDN:              return XK_Page_Down;
207     case KEY_INSERT:            return XK_Insert;
208     case KEY_DEL:               return XK_Delete;
209     case KEY_PRTSCR:            return XK_Print;
210     case KEY_F11:               return XK_F11;
211     case KEY_F12:               return XK_F12;
212     case KEY_LWIN:              return XK_Meta_L;
213     case KEY_RWIN:              return XK_Meta_R;
214     case KEY_MENU:              return XK_Menu;
215     case KEY_PAD:               return XK_VoidSymbol;
216     case KEY_RCONTROL:          return XK_Control_R;
217     case KEY_ALTGR:             return XK_Alt_R;
218     case KEY_SLASH2:            return XK_KP_Divide;
219     case KEY_PAUSE:             return XK_Pause;
220
221     case NEW_KEY_BACKSLASH:     return XK_backslash;
222     case NEW_KEY_1_PAD:         return XK_KP_1;
223     case NEW_KEY_2_PAD:         return XK_KP_2;
224     case NEW_KEY_3_PAD:         return XK_KP_3;
225     case NEW_KEY_4_PAD:         return XK_KP_4;
226     case NEW_KEY_5_PAD:         return XK_KP_5;
227     case NEW_KEY_6_PAD:         return XK_KP_6;
228     case NEW_KEY_7_PAD:         return XK_KP_7;
229     case NEW_KEY_8_PAD:         return XK_KP_8;
230     case NEW_KEY_9_PAD:         return XK_KP_9;
231     case NEW_KEY_0_PAD:         return XK_KP_0;
232     case NEW_KEY_STOP_PAD:      return XK_KP_Separator;
233     case NEW_KEY_EQUALS_PAD:    return XK_KP_Equal;
234     case NEW_KEY_SLASH_PAD:     return XK_KP_Divide;
235     case NEW_KEY_ASTERISK_PAD:  return XK_KP_Multiply;
236     case NEW_KEY_ENTER_PAD:     return XK_KP_Enter;
237
238     default:                    return XK_VoidSymbol;
239   }
240 }
241
242 Pixel AllegroAllocColorCell(int r, int g, int b)
243 {
244   byte pixel_mapping = 0;
245   int i;
246
247   r >>= 10;
248   g >>= 10;
249   b >>= 10;
250
251   /* try to use existing colors from the global colormap */
252   for (i=0; i<global_colormap_entries_used; i++)
253   {
254     if (r == global_colormap[i].r &&
255         g == global_colormap[i].g &&
256         b == global_colormap[i].b)              /* color found */
257     {
258       pixel_mapping = i;
259       break;
260     }
261   }
262
263   if (i == global_colormap_entries_used)        /* color not found */
264   {
265     if (global_colormap_entries_used < MAX_COLORS)
266       global_colormap_entries_used++;
267
268     i = global_colormap_entries_used - 1;
269
270     global_colormap[i].r = r;
271     global_colormap[i].g = g;
272     global_colormap[i].b = b;
273
274     set_palette(global_colormap);
275
276     pixel_mapping = i;
277   }
278
279   return pixel_mapping;
280 }
281
282 void XMapWindow(Display *display, Window window)
283 {
284   int x, y;
285   unsigned int width, height;
286   boolean mouse_off;
287
288   x = AllegroDefaultScreen().x;
289   y = AllegroDefaultScreen().y;
290   width = AllegroDefaultScreen().width;
291   height = AllegroDefaultScreen().height;
292
293   mouse_off = hide_mouse(display, x, y, width, height);
294   blit((BITMAP *)window, video_bitmap, 0, 0, x, y, width, height);
295
296   if (mouse_off)
297     unhide_mouse(display);
298 }
299
300 Display *XOpenDisplay(char *display_name)
301 {
302   Screen *screen;
303   Display *display;
304   BITMAP *mouse_bitmap = NULL;
305
306   mouse_bitmap = Read_PCX_to_AllegroBitmap(program.msdos_pointer_filename);
307   if (mouse_bitmap == NULL)
308     return NULL;
309
310   screen = malloc(sizeof(Screen));
311   display = malloc(sizeof(Display));
312
313   screen[0].cmap = 0;
314   screen[0].root = 0;
315   screen[0].white_pixel = AllegroAllocColorCell(0xFFFF, 0xFFFF, 0xFFFF);
316   screen[0].black_pixel = AllegroAllocColorCell(0x0000, 0x0000, 0x0000);
317   screen[0].video_bitmap = NULL;
318
319   display->default_screen = 0;
320   display->screens = screen;
321   display->mouse_ptr = mouse_bitmap;
322
323   allegro_init();
324   allegro_init_drivers();
325   set_color_depth(8);
326
327   /* force Windows 95 to switch to fullscreen mode */
328   set_gfx_mode(GFX_AUTODETECT, 320, 200, 0, 0);
329   rest(200);
330   set_gfx_mode(GFX_AUTODETECT, XRES, YRES, 0, 0);
331
332   return display;
333 }
334
335 Window XCreateSimpleWindow(Display *display, Window parent, int x, int y,
336                            unsigned int width, unsigned int height,
337                            unsigned int border_width, unsigned long border,
338                            unsigned long background)
339 {
340   video_bitmap = create_video_bitmap(XRES, YRES);
341   clear_to_color(video_bitmap, background);
342
343   AllegroDefaultScreen().video_bitmap = video_bitmap;
344   AllegroDefaultScreen().x = x;
345   AllegroDefaultScreen().y = y;
346   AllegroDefaultScreen().width = XRES;
347   AllegroDefaultScreen().height = YRES;
348
349   set_mouse_sprite(display->mouse_ptr);
350
351 #if 0
352   set_mouse_sprite_focus(1, 1);
353 #endif
354
355   set_mouse_speed(1, 1);
356   set_mouse_range(AllegroDefaultScreen().x + 1,
357                   AllegroDefaultScreen().y + 1,
358                   AllegroDefaultScreen().x + video.width + 1,
359                   AllegroDefaultScreen().y + video.height + 1);
360
361   show_video_bitmap(video_bitmap);
362
363   return (Window)video_bitmap;
364 }
365
366 Status XStringListToTextProperty(char **list, int count,
367                                  XTextProperty *text_prop_return)
368 {
369   char *string;
370
371   if (count >= 1)
372   {
373     string = malloc(strlen(list[0] + 1));
374     strcpy(string, list[0]);
375     text_prop_return->value = (unsigned char *)string;
376     return 1;
377   }
378   else
379     text_prop_return = NULL;
380
381   return 0;
382 }
383
384 void XFree(void *data)
385 {
386   if (data)
387     free(data);
388 }
389
390 GC XCreateGC(Display *display, Drawable d, unsigned long value_mask,
391              XGCValues *values)
392 {
393   XGCValues *gcv;
394   gcv = malloc(sizeof(XGCValues));
395   gcv->foreground = values->foreground;
396   gcv->background = values->background;
397   gcv->graphics_exposures = values->graphics_exposures;
398   gcv->clip_mask = values->clip_mask;
399   gcv->clip_x_origin = values->clip_x_origin;
400   gcv->clip_y_origin = values->clip_y_origin;
401   gcv->value_mask = value_mask;
402   return (GC)gcv;
403 }
404
405 void XSetClipMask(Display *display, GC gc, Pixmap pixmap)
406 {
407   XGCValues *gcv = (XGCValues *)gc;
408
409   gcv->clip_mask = pixmap;
410   gcv->value_mask |= GCClipMask;
411 }
412
413 void XSetClipOrigin(Display *display, GC gc, int x, int y)
414 {
415   XGCValues *gcv = (XGCValues *)gc;
416
417   gcv->clip_x_origin = x;
418   gcv->clip_x_origin = y;
419 }
420
421 void XFillRectangle(Display *display, Drawable d, GC gc, int x, int y,
422                     unsigned int width, unsigned int height)
423 {
424   boolean mouse_off = FALSE;
425
426   if ((BITMAP *)d == video_bitmap)
427   {
428     x += AllegroDefaultScreen().x;
429     y += AllegroDefaultScreen().y;
430     freeze_mouse_flag = TRUE;
431     mouse_off = hide_mouse(display, x, y, width, height);
432   }
433
434   rectfill((BITMAP *)d, x, y, x + width - 1, y + height - 1,
435            ((XGCValues *)gc)->foreground);
436
437   if (mouse_off)
438     unhide_mouse(display);
439
440   freeze_mouse_flag = FALSE;
441 }
442
443 Pixmap XCreatePixmap(Display *display, Drawable d, unsigned int width,
444                      unsigned int height, unsigned int depth)
445 {
446   BITMAP *bitmap = NULL;
447
448   if (gfx_capabilities & GFX_HW_VRAM_BLIT &&
449       width  == gfx.scrollbuffer_width && height == gfx.scrollbuffer_height)
450     bitmap = create_video_bitmap(width, height);
451
452   if (bitmap == NULL)
453     bitmap = create_bitmap(width, height);
454
455   return (Pixmap)bitmap;
456 }
457
458 void XSync(Display *display, Bool discard_events)
459 {
460   wait_for_vsync = TRUE;
461 }
462
463 inline void XCopyArea(Display *display, Drawable src, Drawable dest, GC gc,
464                       int src_x, int src_y,
465                       unsigned int width, unsigned int height,
466                       int dest_x, int dest_y)
467 {
468   boolean mouse_off = FALSE;
469
470   if ((BITMAP *)src == video_bitmap)
471   {
472     src_x += AllegroDefaultScreen().x;
473     src_y += AllegroDefaultScreen().y;
474   }
475
476   if ((BITMAP *)dest == video_bitmap)
477   {
478     dest_x += AllegroDefaultScreen().x;
479     dest_y += AllegroDefaultScreen().y;
480     freeze_mouse_flag = TRUE;
481     mouse_off = hide_mouse(display, dest_x, dest_y, width, height);
482   }
483
484   if (wait_for_vsync)
485   {
486     wait_for_vsync = FALSE;
487     vsync();
488   }
489
490   if (((XGCValues *)gc)->value_mask & GCClipMask)
491     masked_blit((BITMAP *)src, (BITMAP *)dest, src_x, src_y, dest_x, dest_y,
492                 width, height);
493   else
494     blit((BITMAP *)src, (BITMAP *)dest, src_x, src_y, dest_x, dest_y,
495          width, height);
496
497   if (mouse_off)
498     unhide_mouse(display);
499
500   freeze_mouse_flag = FALSE;
501 }
502
503 static BITMAP *Image_to_AllegroBitmap(Image *image)
504 {
505   BITMAP *bitmap;
506   byte *src_ptr = image->data;
507   byte pixel_mapping[MAX_COLORS];
508   unsigned int depth = 8;
509   int i, x, y;
510
511   if (image->type == IMAGETYPE_TRUECOLOR && depth == 8)
512     Error(ERR_EXIT, "cannot handle true-color images on 8-bit display");
513
514   /* allocate new allegro bitmap structure */
515   if ((bitmap = create_bitmap_ex(depth, image->width, image->height)) == NULL)
516   {
517     errno_pcx = PCX_NoMemory;
518     return NULL;
519   }
520
521   clear(bitmap);
522
523   /* try to use existing colors from the global colormap */
524   for (i=0; i<MAX_COLORS; i++)
525   {
526     if (!image->rgb.color_used[i])
527       continue;
528
529     pixel_mapping[i] = AllegroAllocColorCell(image->rgb.red[i],
530                                              image->rgb.green[i],
531                                              image->rgb.blue[i]);
532   }
533
534   /* copy bitmap data */
535   for (y=0; y<image->height; y++)
536     for (x=0; x<image->width; x++)
537       putpixel(bitmap, x, y, pixel_mapping[*src_ptr++]);
538
539   return bitmap;
540 }
541
542 static BITMAP *Read_PCX_to_AllegroBitmap(char *filename)
543 {
544   BITMAP *bitmap;
545   Image *image;
546
547   /* read the graphic file in PCX format to internal image structure */
548   if ((image = Read_PCX_to_Image(filename)) == NULL)
549     return NULL;
550
551   /* convert internal image structure to allegro bitmap structure */
552   if ((bitmap = Image_to_AllegroBitmap(image)) == NULL)
553     return NULL;
554
555   set_palette(global_colormap);
556
557   return bitmap;
558 }
559
560 int Read_PCX_to_Pixmap(Display *display, Window window, GC gc, char *filename,
561                        Pixmap *pixmap, Pixmap *pixmap_mask)
562 {
563   BITMAP *bitmap;
564
565   if ((bitmap = Read_PCX_to_AllegroBitmap(filename)) == NULL)
566     return errno_pcx;
567
568   *pixmap = (Pixmap)bitmap;
569
570   /* pixmap_mask will never be used in Allegro (which uses masked_blit()),
571      so use non-NULL dummy pointer to empty Pixmap */
572   *pixmap_mask = (Pixmap)DUMMY_MASK;
573
574   return PCX_Success;
575 }
576
577 int XReadBitmapFile(Display *display, Drawable d, char *filename,
578                     unsigned int *width_return, unsigned int *height_return,
579                     Pixmap *bitmap_return,
580                     int *x_hot_return, int *y_hot_return)
581 {
582   BITMAP *bitmap;
583
584   if ((bitmap = Read_PCX_to_AllegroBitmap(filename)) == NULL)
585     return BitmapOpenFailed;
586
587   *width_return = bitmap->w;
588   *height_return = bitmap->h;
589   *x_hot_return = -1;
590   *y_hot_return = -1;
591   *bitmap_return = (Pixmap)bitmap;
592
593   return BitmapSuccess;
594 }
595
596 void XFreePixmap(Display *display, Pixmap pixmap)
597 {
598   if (pixmap != DUMMY_MASK &&
599       (is_memory_bitmap((BITMAP *)pixmap) ||
600        is_screen_bitmap((BITMAP *)pixmap)))
601     destroy_bitmap((BITMAP *)pixmap);
602 }
603
604 void XFreeGC(Display *display, GC gc)
605 {
606   XGCValues *gcv;
607
608   gcv = (XGCValues *)gc;
609   if (gcv)
610     free(gcv);
611 }
612
613 void XUnmapWindow(Display *display, Window window)
614 {
615 }
616
617 void XCloseDisplay(Display *display)
618 {
619   BITMAP *bitmap = video_bitmap;
620
621   if (is_screen_bitmap(bitmap))
622     destroy_bitmap(bitmap);
623
624   if (display->screens)
625     free(display->screens);
626
627   if (display)
628     free(display);
629
630   /* return to text mode (or DOS box on Windows screen) */
631   set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
632 }
633
634 void XNextEvent(Display *display, XEvent *event_return)
635 {
636   while (!pending_events)
637     XPending(display);
638
639   memcpy(event_return, &event_buffer[pending_events], sizeof(XEvent));
640   pending_events--;
641 }
642
643 static void NewKeyEvent(int key_press_state, KeySym keysym)
644 {
645   XKeyEvent *xkey;
646
647   if (pending_events >= MAX_EVENT_BUFFER)
648     return;
649
650   pending_events++;
651   xkey = (XKeyEvent *)&event_buffer[pending_events];
652   xkey->type = key_press_state;
653   xkey->state = (unsigned int)keysym;
654 }
655
656 #define HANDLE_RAW_KB_ALL_KEYS                  0
657 #define HANDLE_RAW_KB_MODIFIER_KEYS_ONLY        1
658
659 static int modifier_scancode[] =
660 {
661   KEY_LSHIFT,
662   KEY_RSHIFT,
663   KEY_LCONTROL,
664   KEY_RCONTROL,
665   KEY_ALT,
666   KEY_ALTGR,
667   KEY_LWIN,
668   KEY_RWIN,
669   KEY_CAPSLOCK,
670   KEY_NUMLOCK,
671   KEY_SCRLOCK,
672   -1
673 };
674
675 static void HandleKeyboardRaw(int mode)
676 {
677   int i;
678
679   for (i=0; i<MAX_SCANCODES; i++)
680   {
681     int scancode, new_state, event_type;
682     char key_pressed;
683
684     if (mode == HANDLE_RAW_KB_MODIFIER_KEYS_ONLY)
685     {
686       if ((scancode = modifier_scancode[i]) == -1)
687         return;
688     }
689     else
690       scancode = i;
691
692     key_pressed = key[scancode];
693     new_state = (key_pressed ? KeyPressMask : KeyReleaseMask);
694     event_type = (key_pressed ? KeyPress : KeyRelease);
695
696     if (key_press_state[i] == new_state)        /* state not changed */
697       continue;
698
699     key_press_state[i] = new_state;
700
701     NewKeyEvent(event_type, ScancodeToKeySym(scancode));
702   }
703 }
704
705 static void HandleKeyboardEvent()
706 {
707   if (keypressed())
708   {
709     int key_info = readkey();
710     int scancode = (key_info >> 8);
711     int ascii = (key_info & 0xff);
712     KeySym keysym = ScancodeToKeySym(scancode);
713
714     if (scancode == KEY_PAD)
715     {
716       /* keys on the numeric keypad return just scancode 'KEY_PAD'
717          for some reason, so we must handle them separately */
718
719       if (ascii >= '0' && ascii <= '9')
720         keysym = XK_KP_0 + (KeySym)(ascii - '0');
721       else if (ascii == '.')
722         keysym = XK_KP_Separator;
723     }
724     else if (ascii >= ' ' && ascii <= 'Z')
725       keysym = XK_space + (KeySym)(ascii - ' ');
726     else if (ascii == '^')
727       keysym = XK_asciicircum;
728     else if (ascii == '_')
729       keysym = XK_underscore;
730     else if (ascii == 'Ä')
731       keysym = XK_Adiaeresis;
732     else if (ascii == 'Ö')
733       keysym = XK_Odiaeresis;
734     else if (ascii == 'Ãœ')
735       keysym = XK_Udiaeresis;
736     else if (ascii == 'ä')
737       keysym = XK_adiaeresis;
738     else if (ascii == 'ö')
739       keysym = XK_odiaeresis;
740     else if (ascii == 'ü')
741       keysym = XK_udiaeresis;
742     else if (ascii == 'ß')
743       keysym = XK_ssharp;
744
745     NewKeyEvent(KeyPress, keysym);
746   }
747   else if (key_shifts & (KB_SHIFT_FLAG | KB_CTRL_FLAG | KB_ALT_FLAG))
748   {
749     /* the allegro function keypressed() does not give us single pressed
750        modifier keys, so we must detect them with the internal global
751        allegro variable 'key_shifts' and then handle them separately */
752
753     HandleKeyboardRaw(HANDLE_RAW_KB_MODIFIER_KEYS_ONLY);
754   }
755 }
756
757 int XPending(Display *display)
758 {
759   XButtonEvent *xbutton;
760   XMotionEvent *xmotion;
761   int i;
762
763   /* When using 'HandleKeyboardRaw()', keyboard input is also stored in
764      the allegro keyboard input buffer and would be available a second
765      time by calling 'HandleKeyboardEvent()'. To avoid double keyboard
766      events, the allegro function 'clear_keybuf()' must be called each
767      time when switching from calling 'HandleKeyboardRaw()' to calling
768      'HandleKeyboardEvent()' to get keyboard input, which is actually
769      done by 'XAutoRepeatOn()' which sets keyboard_auto_repeat to TRUE. */
770
771   /* keyboard event */
772   if (keyboard_auto_repeat)
773     HandleKeyboardEvent();
774   else
775     HandleKeyboardRaw(HANDLE_RAW_KB_ALL_KEYS);
776
777   /* mouse motion event */
778   if (mouse_pos != last_mouse_pos)
779   {
780     last_mouse_pos = mouse_pos;
781     pending_events++;
782     xmotion = (XMotionEvent *)&event_buffer[pending_events];
783     xmotion->type = MotionNotify;
784     xmotion->x = mouse_x - AllegroDefaultScreen().x;
785     xmotion->y = mouse_y - AllegroDefaultScreen().y;
786   }
787
788   /* mouse button event */
789   if (mouse_b != last_mouse_b)
790   {
791     for (i=0; i<3; i++)         /* check all three mouse buttons */
792     {
793       int bitmask = (1 << i);
794
795       if ((last_mouse_b & bitmask) != (mouse_b & bitmask))
796       {
797         int mapping[3] = { 1, 3, 2 };
798
799         pending_events++;
800         xbutton = (XButtonEvent *)&event_buffer[pending_events];
801         xbutton->type = (mouse_b & bitmask ? ButtonPress : ButtonRelease);
802         xbutton->button = mapping[i];
803         xbutton->x = mouse_x - AllegroDefaultScreen().x;
804         xbutton->y = mouse_y - AllegroDefaultScreen().y;
805       }
806     }
807     last_mouse_b = mouse_b;
808   }
809
810   return pending_events;
811 }
812
813 KeySym XLookupKeysym(XKeyEvent *key_event, int index)
814 {
815   return key_event->state;
816 }
817
818 int XLookupString(XKeyEvent *key_event, char *buffer, int buffer_size,
819                   KeySym *key, XComposeStatus *compose)
820 {
821   *key = key_event->state;
822   return 0;
823 }
824
825 void XSetForeground(Display *display, GC gc, unsigned long pixel)
826 {
827   XGCValues *gcv = (XGCValues *)gc;
828
829   gcv->foreground = pixel;
830 }
831
832 void XDrawLine(Display *display, Drawable d, GC gc,
833                int x1, int y1, int x2, int y2)
834 {
835   XGCValues *gcv = (XGCValues *)gc;
836   boolean mouse_off = FALSE;
837
838   if ((BITMAP *)d == video_bitmap)
839   {
840     x1 += AllegroDefaultScreen().x;
841     y1 += AllegroDefaultScreen().y;
842     x2 += AllegroDefaultScreen().x;
843     y2 += AllegroDefaultScreen().y;
844     freeze_mouse_flag = TRUE;
845     mouse_off = hide_mouse(display, MIN(x1, x2), MIN(y1, y2),
846                            MAX(x1, x2) - MIN(x1, x2),
847                            MAX(y1, y2) - MIN(y1, y2));
848   }
849
850   line((BITMAP *)d, x1, y1, x2, y2, gcv->foreground);
851
852   if (mouse_off)
853     unhide_mouse(display);
854
855   freeze_mouse_flag = FALSE;
856 }
857
858 void XDestroyImage(XImage *ximage)
859 {
860 }
861
862 void XDestroyWindow(Display *display, Window window)
863 {
864 }
865
866 Bool XQueryPointer(Display *display, Window window,
867                    Window *root, Window *child, int *root_x, int *root_y,
868                    int *win_x, int *win_y, unsigned int *mask)
869 {
870   *win_x = mouse_x - AllegroDefaultScreen().x;
871   *win_y = mouse_y - AllegroDefaultScreen().y;
872
873   return True;
874 }
875
876 void XAutoRepeatOn(Display *display)
877 {
878   keyboard_auto_repeat = TRUE;
879   clear_keybuf();
880 }
881
882 void XAutoRepeatOff(Display *display)
883 {
884   keyboard_auto_repeat = FALSE;
885 }
886
887 void AllegroDrawLine(Drawable d, int from_x, int from_y, int to_x, int to_y,
888                      Pixel color)
889 {
890   boolean mouse_off = FALSE;
891
892   if ((BITMAP *)d == video_bitmap)
893   {
894     int dx = AllegroDefaultScreen().x;
895     int dy = AllegroDefaultScreen().y;
896     int x1, y1, x2, y2;
897
898     from_x += dx;
899     from_y += dy;
900     to_x += dx;
901     to_y += dy;
902
903     x1 = (from_x < to_x ? from_x : to_x);
904     y1 = (from_y < to_y ? from_y : to_y);
905     x2 = (from_x < to_x ? to_x : from_x);
906     y2 = (from_y < to_y ? to_y : from_y);
907
908     freeze_mouse_flag = TRUE;
909     mouse_off = hide_mouse(display, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
910   }
911
912   line((BITMAP *)d, from_x, from_y, to_x, to_y, color);
913
914   if (mouse_off)
915     unhide_mouse(display);
916
917   freeze_mouse_flag = FALSE;
918 }
919
920 Pixel AllegroGetPixel(Drawable d, int x, int y)
921 {
922   return getpixel((BITMAP *)d, x, y);
923 }
924
925 void MSDOSOpenAudio(void)
926 {
927   if (allegro_init_audio())
928   {
929     audio.sound_available = TRUE;
930     audio.music_available = TRUE;
931     audio.loops_available = TRUE;
932     audio.sound_enabled = TRUE;
933   }
934
935   InitPlaylist();
936 }
937
938 void MSDOSCloseAudio(void)
939 {
940   /* nothing to be done here */
941 }
942
943 void NetworkServer(int port, int serveronly)
944 {
945   Error(ERR_WARN, "networking not supported in DOS version");
946 }
947
948
949 /* ========================================================================= */
950 /* joystick functions                                                        */
951 /* ========================================================================= */
952
953 void MSDOSInitJoysticks()
954 {
955   int i;
956
957   /* start from scratch */
958   remove_joystick();
959
960   /* try to access two joysticks; if that fails, try to access just one */
961   if (install_joystick(JOY_TYPE_2PADS) == 0 ||
962       install_joystick(JOY_TYPE_AUTODETECT) == 0)
963     joystick.status = JOYSTICK_ACTIVATED;
964
965   for (i=0; i<MAX_PLAYERS; i++)
966   {
967     char *device_name = setup.input[i].joy.device_name;
968     int joystick_nr = getJoystickNrFromDeviceName(device_name);
969
970     if (joystick_nr >= num_joysticks)
971       joystick_nr = -1;
972
973     /* misuse joystick file descriptor variable to store joystick number */
974     joystick.fd[i] = joystick_nr;
975   }
976 }
977
978 boolean MSDOSReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2)
979 {
980   /* the allegro global variable 'num_joysticks' contains the number
981      of joysticks found at initialization under MS-DOS / Windows */
982
983   if (nr < 0 || nr >= num_joysticks)
984     return FALSE;
985
986   poll_joystick();
987
988   if (x != NULL)
989     *x = joy[nr].stick[0].axis[0].pos;
990   if (y != NULL)
991     *y = joy[nr].stick[0].axis[1].pos;
992
993   if (b1 != NULL)
994     *b1 = joy[nr].button[0].b;
995   if (b2 != NULL)
996     *b2 = joy[nr].button[1].b;
997
998   return TRUE;
999 }
1000
1001 #endif /* PLATFORM_MSDOS */