341da455c5f8f7487613279510d42362bf7e30e9
[rocksndiamonds.git] / src / libgame / system.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2000 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * system.c                                                 *
12 ***********************************************************/
13
14 #include <string.h>
15 #include <signal.h>
16
17 #include "platform.h"
18
19 #if defined(PLATFORM_MSDOS)
20 #include <fcntl.h>
21 #endif
22
23 #include "system.h"
24 #include "sound.h"
25 #include "misc.h"
26
27
28 /* ========================================================================= */
29 /* exported variables                                                        */
30 /* ========================================================================= */
31
32 struct ProgramInfo      program;
33 struct OptionInfo       options;
34 struct VideoSystemInfo  video;
35 struct AudioSystemInfo  audio;
36 struct GfxInfo          gfx;
37
38 struct LevelDirInfo    *leveldir_first = NULL;
39 struct LevelDirInfo    *leveldir_current = NULL;
40
41 Display        *display = NULL;
42 Visual         *visual = NULL;
43 int             screen = 0;
44 Colormap        cmap = None;
45
46 DrawWindow     *window = NULL;
47 DrawBuffer     *backbuffer = NULL;
48 DrawBuffer     *drawto = NULL;
49
50 int             button_status = MB_NOT_PRESSED;
51 boolean         motion_status = FALSE;
52
53 int             redraw_mask = REDRAW_NONE;
54 int             redraw_tiles = 0;
55
56 int             FrameCounter = 0;
57
58
59 /* ========================================================================= */
60 /* init/close functions                                                      */
61 /* ========================================================================= */
62
63 void InitCommandName(char *argv0)
64 {
65   program.command_basename =
66     (strrchr(argv0, '/') ? strrchr(argv0, '/') + 1 : argv0);
67 }
68
69 void InitExitFunction(void (*exit_function)(int))
70 {
71   program.exit_function = exit_function;
72
73   /* set signal handlers to custom exit function */
74   signal(SIGINT, exit_function);
75   signal(SIGTERM, exit_function);
76
77 #if defined(TARGET_SDL)
78   /* set exit function to automatically cleanup SDL stuff after exit() */
79   atexit(SDL_Quit);
80 #endif
81 }
82
83 void InitPlatformDependantStuff(void)
84 {
85 #if defined(PLATFORM_MSDOS)
86   _fmode = O_BINARY;
87 #endif
88
89 #if !defined(PLATFORM_UNIX)
90   initErrorFile();
91 #endif
92
93 #if defined(TARGET_SDL)
94   if (SDL_Init(SDL_INIT_EVENTTHREAD | SDL_INIT_NOPARACHUTE) < 0)
95     Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError());
96 #endif
97 }
98
99 void ClosePlatformDependantStuff(void)
100 {
101 #if !defined(PLATFORM_UNIX)
102   dumpErrorFile();
103 #endif
104 }
105
106 void InitProgramInfo(char *unix_userdata_directory, char *program_title,
107                      char *window_title, char *icon_title,
108                      char *x11_icon_basename, char *x11_iconmask_basename,
109                      char *msdos_pointer_basename)
110 {
111   char *gfx_dir = getPath2(options.ro_base_directory, GRAPHICS_DIRECTORY);
112   char *x11_icon_filename = getPath2(gfx_dir, x11_icon_basename);
113   char *x11_iconmask_filename = getPath2(gfx_dir, x11_iconmask_basename);
114   char *msdos_pointer_filename = getPath2(gfx_dir, msdos_pointer_basename);
115
116   free(gfx_dir);
117
118 #if defined(PLATFORM_UNIX)
119   program.userdata_directory = unix_userdata_directory;
120 #else
121   program.userdata_directory = "userdata";
122 #endif
123
124   program.program_title = program_title;
125   program.window_title = window_title;
126   program.icon_title = icon_title;
127   program.x11_icon_filename = x11_icon_filename;
128   program.x11_iconmask_filename = x11_iconmask_filename;
129   program.msdos_pointer_filename = msdos_pointer_filename;
130 }
131
132 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
133                       int real_sx, int real_sy,
134                       int full_sxsize, int full_sysize)
135 {
136   gfx.sx = sx;
137   gfx.sy = sy;
138   gfx.sxsize = sxsize;
139   gfx.sysize = sysize;
140   gfx.real_sx = real_sx;
141   gfx.real_sy = real_sy;
142   gfx.full_sxsize = full_sxsize;
143   gfx.full_sysize = full_sysize;
144 }
145
146 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
147 {
148   gfx.dx = dx;
149   gfx.dy = dy;
150   gfx.dxsize = dxsize;
151   gfx.dysize = dysize;
152 }
153
154 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
155 {
156   gfx.vx = vx;
157   gfx.vy = vy;
158   gfx.vxsize = vxsize;
159   gfx.vysize = vysize;
160 }
161
162 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
163 {
164   /* currently only used by MSDOS code to alloc VRAM buffer, if available */
165   gfx.scrollbuffer_width = scrollbuffer_width;
166   gfx.scrollbuffer_height = scrollbuffer_height;
167 }
168
169
170 /* ========================================================================= */
171 /* video functions                                                           */
172 /* ========================================================================= */
173
174 inline static int GetRealDepth(int depth)
175 {
176   return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
177 }
178
179 inline void InitVideoDisplay(void)
180 {
181 #ifdef TARGET_SDL
182   SDLInitVideoDisplay();
183 #else
184   X11InitVideoDisplay();
185 #endif
186 }
187
188 inline void CloseVideoDisplay(void)
189 {
190 #if defined(TARGET_X11)
191   if (display)
192     XCloseDisplay(display);
193 #endif
194 }
195
196 inline void InitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window,
197                             int width, int height, int depth,
198                             boolean fullscreen)
199 {
200   video.width = width;
201   video.height = height;
202   video.depth = GetRealDepth(depth);
203   video.fullscreen_available = FULLSCREEN_STATUS;
204   video.fullscreen_enabled = FALSE;
205
206 #ifdef TARGET_SDL
207   SDLInitVideoBuffer(backbuffer, window, fullscreen);
208 #else
209   X11InitVideoBuffer(backbuffer, window);
210 #endif
211 }
212
213 inline Bitmap *CreateBitmapStruct(void)
214 {
215 #ifdef TARGET_SDL
216   return checked_calloc(sizeof(struct SDLSurfaceInfo));
217 #else
218   return checked_calloc(sizeof(struct X11DrawableInfo));
219 #endif
220 }
221
222 inline Bitmap *CreateBitmap(int width, int height, int depth)
223 {
224   Bitmap *new_bitmap = CreateBitmapStruct();
225   int real_depth = GetRealDepth(depth);
226
227 #ifdef TARGET_SDL
228   SDL_Surface *surface_tmp, *surface_native;
229
230   if ((surface_tmp = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height,
231                                           real_depth, 0, 0, 0, 0))
232       == NULL)
233     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
234
235   if ((surface_native = SDL_DisplayFormat(surface_tmp)) == NULL)
236     Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
237
238   SDL_FreeSurface(surface_tmp);
239
240   new_bitmap->surface = surface_native;
241 #else
242   Pixmap pixmap;
243
244   if ((pixmap = XCreatePixmap(display, window->drawable,
245                               width, height, real_depth))
246       == None)
247     Error(ERR_EXIT, "cannot create pixmap");
248
249   new_bitmap->drawable = pixmap;
250
251   if (window == NULL)
252     Error(ERR_EXIT, "Window GC needed for Bitmap -- create Window first");
253
254   new_bitmap->gc = window->gc;
255
256   new_bitmap->line_gc[0] = window->line_gc[0];
257   new_bitmap->line_gc[1] = window->line_gc[1];
258 #endif
259
260   return new_bitmap;
261 }
262
263 inline void FreeBitmap(Bitmap *bitmap)
264 {
265   if (bitmap == NULL)
266     return;
267
268 #ifdef TARGET_SDL
269   if (bitmap->surface)
270     SDL_FreeSurface(bitmap->surface);
271   if (bitmap->surface_masked)
272     SDL_FreeSurface(bitmap->surface_masked);
273 #else
274   if (bitmap->drawable)
275     XFreePixmap(display, bitmap->drawable);
276   if (bitmap->clip_mask)
277     XFreePixmap(display, bitmap->clip_mask);
278   if (bitmap->stored_clip_gc)
279     XFreeGC(display, bitmap->stored_clip_gc);
280 #endif
281
282   free(bitmap);
283 }
284
285 inline void CloseWindow(DrawWindow *window)
286 {
287 #ifdef TARGET_X11
288   if (window->drawable)
289   {
290     XUnmapWindow(display, window->drawable);
291     XDestroyWindow(display, window->drawable);
292   }
293   if (window->gc)
294     XFreeGC(display, window->gc);
295 #endif
296 }
297
298 inline void BlitBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap,
299                        int src_x, int src_y,
300                        int width, int height,
301                        int dst_x, int dst_y)
302 {
303 #ifdef TARGET_SDL
304   SDLCopyArea(src_bitmap, dst_bitmap,
305               src_x, src_y, width, height, dst_x, dst_y, SDLCOPYAREA_OPAQUE);
306 #else
307   XCopyArea(display, src_bitmap->drawable, dst_bitmap->drawable,
308             dst_bitmap->gc, src_x, src_y, width, height, dst_x, dst_y);
309 #endif
310 }
311
312 inline void ClearRectangle(Bitmap *bitmap, int x, int y, int width, int height)
313 {
314 #ifdef TARGET_SDL
315   SDLFillRectangle(bitmap, x, y, width, height, 0x000000);
316 #else
317   XFillRectangle(display, bitmap->drawable, bitmap->gc, x, y, width, height);
318 #endif
319 }
320
321 #if 0
322 #ifndef TARGET_SDL
323 static GC last_clip_gc = 0;     /* needed for XCopyArea() through clip mask */
324 #endif
325 #endif
326
327 inline void SetClipMask(Bitmap *bitmap, GC clip_gc, Pixmap clip_pixmap)
328 {
329 #ifdef TARGET_X11
330   if (clip_gc)
331   {
332     bitmap->clip_gc = clip_gc;
333     XSetClipMask(display, bitmap->clip_gc, clip_pixmap);
334   }
335 #if 0
336   last_clip_gc = clip_gc;
337 #endif
338 #endif
339 }
340
341 inline void SetClipOrigin(Bitmap *bitmap, GC clip_gc, int clip_x, int clip_y)
342 {
343 #ifdef TARGET_X11
344   if (clip_gc)
345   {
346     bitmap->clip_gc = clip_gc;
347     XSetClipOrigin(display, bitmap->clip_gc, clip_x, clip_y);
348   }
349 #if 0
350   last_clip_gc = clip_gc;
351 #endif
352 #endif
353 }
354
355 inline void BlitBitmapMasked(Bitmap *src_bitmap, Bitmap *dst_bitmap,
356                              int src_x, int src_y,
357                              int width, int height,
358                              int dst_x, int dst_y)
359 {
360 #ifdef TARGET_SDL
361   SDLCopyArea(src_bitmap, dst_bitmap,
362               src_x, src_y, width, height, dst_x, dst_y, SDLCOPYAREA_MASKED);
363 #else
364   XCopyArea(display, src_bitmap->drawable, dst_bitmap->drawable,
365             src_bitmap->clip_gc, src_x, src_y, width, height, dst_x, dst_y);
366 #endif
367 }
368
369 inline void DrawSimpleWhiteLine(Bitmap *bitmap, int from_x, int from_y,
370                                 int to_x, int to_y)
371 {
372 #ifdef TARGET_SDL
373   SDLDrawSimpleLine(bitmap->surface, from_x, from_y, to_x, to_y, 0xffffff);
374 #else
375   XSetForeground(display, bitmap->gc, WhitePixel(display, screen));
376   XDrawLine(display, bitmap->drawable, bitmap->gc, from_x, from_y, to_x, to_y);
377   XSetForeground(display, bitmap->gc, BlackPixel(display, screen));
378 #endif
379 }
380
381 #if !defined(TARGET_X11_NATIVE)
382 inline void DrawLine(Bitmap *bitmap, int from_x, int from_y,
383                      int to_x, int to_y, Pixel pixel, int line_width)
384 {
385   int x, y;
386
387   for (x=0; x<line_width; x++)
388   {
389     for (y=0; y<line_width; y++)
390     {
391       int dx = x - line_width / 2;
392       int dy = y - line_width / 2;
393
394       if ((x == 0 && y == 0) ||
395           (x == 0 && y == line_width - 1) ||
396           (x == line_width - 1 && y == 0) ||
397           (x == line_width - 1 && y == line_width - 1))
398         continue;
399
400 #if defined(TARGET_SDL)
401       sge_Line(bitmap->surface,
402                from_x + dx, from_y + dy, to_x + dx, to_y + dy, pixel);
403 #elif defined(TARGET_ALLEGRO)
404       AllegroDrawLine(bitmap->drawable, from_x + dx, from_y + dy,
405                       to_x + dx, to_y + dy, pixel);
406 #endif
407     }
408   }
409 }
410 #endif
411
412 inline void DrawLines(Bitmap *bitmap, struct XY *points, int num_points,
413                       Pixel pixel)
414 {
415 #if !defined(TARGET_X11_NATIVE)
416   int line_width = 4;
417   int i;
418
419   for (i=0; i<num_points - 1; i++)
420     DrawLine(bitmap, points[i].x, points[i].y,
421              points[i + 1].x, points[i + 1].y, pixel, line_width);
422
423   /*
424   SDLDrawLines(bitmap->surface, points, num_points, pixel);
425   */
426 #else
427   XSetForeground(display, bitmap->line_gc[1], pixel);
428   XDrawLines(display, bitmap->drawable, bitmap->line_gc[1],
429              (XPoint *)points, num_points, CoordModeOrigin);
430   /*
431   XSetForeground(display, gc, BlackPixel(display, screen));
432   */
433 #endif
434 }
435
436 inline Pixel GetPixelFromRGB(Bitmap *bitmap, unsigned int color_r,
437                              unsigned int color_g, unsigned int color_b)
438 {
439   Pixel pixel;
440
441 #if defined(TARGET_SDL)
442   pixel = SDL_MapRGB(bitmap->surface->format, color_r, color_g, color_b);
443 #elif defined(TARGET_X11_NATIVE)
444   XColor xcolor;
445
446   xcolor.flags = DoRed | DoGreen | DoBlue;
447   xcolor.red = (color_r << 8);
448   xcolor.green = (color_g << 8);
449   xcolor.blue = (color_b << 8);
450   XAllocColor(display, cmap, &xcolor);
451   pixel = xcolor.pixel;
452 #endif
453
454   return pixel;
455 }
456
457 inline Pixel GetPixelFromRGBcompact(Bitmap *bitmap, unsigned int color)
458 {
459   unsigned int color_r = (color >> 16) & 0xff;
460   unsigned int color_g = (color >>  8) & 0xff;
461   unsigned int color_b = (color >>  0) & 0xff;
462
463   return GetPixelFromRGB(bitmap, color_r, color_g, color_b);
464 }
465
466 /* execute all pending screen drawing operations */
467 inline void FlushDisplay(void)
468 {
469 #ifndef TARGET_SDL
470   XFlush(display);
471 #endif
472 }
473
474 /* execute and wait for all pending screen drawing operations */
475 inline void SyncDisplay(void)
476 {
477 #ifndef TARGET_SDL
478   XSync(display, FALSE);
479 #endif
480 }
481
482 inline void KeyboardAutoRepeatOn(void)
483 {
484 #ifdef TARGET_SDL
485   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
486                       SDL_DEFAULT_REPEAT_INTERVAL / 2);
487   SDL_EnableUNICODE(1);
488 #else
489   if (display)
490     XAutoRepeatOn(display);
491 #endif
492 }
493
494 inline void KeyboardAutoRepeatOff(void)
495 {
496 #ifdef TARGET_SDL
497   SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
498   SDL_EnableUNICODE(0);
499 #else
500   if (display)
501     XAutoRepeatOff(display);
502 #endif
503 }
504
505 inline boolean PointerInWindow(DrawWindow *window)
506 {
507 #ifdef TARGET_SDL
508   return TRUE;
509 #else
510   Window root, child;
511   int root_x, root_y;
512   unsigned int mask;
513   int win_x, win_y;
514
515   /* if XQueryPointer() returns False, the pointer
516      is not on the same screen as the specified window */
517   return XQueryPointer(display, window->drawable, &root, &child,
518                        &root_x, &root_y, &win_x, &win_y, &mask);
519 #endif
520 }
521
522 inline boolean SetVideoMode(boolean fullscreen)
523 {
524 #ifdef TARGET_SDL
525   return SDLSetVideoMode(&backbuffer, fullscreen);
526 #else
527   boolean success = TRUE;
528
529   if (fullscreen && video.fullscreen_available)
530   {
531     Error(ERR_WARN, "fullscreen not available in X11 version");
532
533     /* display error message only once */
534     video.fullscreen_available = FALSE;
535
536     success = FALSE;
537   }
538
539   return success;
540 #endif
541 }
542
543 inline boolean ChangeVideoModeIfNeeded(boolean fullscreen)
544 {
545 #ifdef TARGET_SDL
546   if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
547       (!fullscreen && video.fullscreen_enabled))
548     fullscreen = SetVideoMode(fullscreen);
549 #endif
550
551   return fullscreen;
552 }
553
554 Bitmap *LoadImage(char *basename)
555 {
556   Bitmap *new_bitmap;
557   char filename[256];
558
559   sprintf(filename, "%s/%s/%s",
560           options.ro_base_directory, GRAPHICS_DIRECTORY, basename);
561
562 #if defined(TARGET_SDL)
563   new_bitmap = SDLLoadImage(filename);
564 #else
565   new_bitmap = X11LoadImage(filename);
566 #endif
567
568   return new_bitmap;
569 }
570
571
572 /* ========================================================================= */
573 /* audio functions                                                           */
574 /* ========================================================================= */
575
576 inline boolean OpenAudio(struct AudioSystemInfo *audio)
577 {
578   audio->sound_available = FALSE;
579   audio->loops_available = FALSE;
580   audio->sound_enabled = FALSE;
581   audio->soundserver_pipe[0] = audio->soundserver_pipe[1] = 0;
582   audio->soundserver_pid = 0;
583   audio->device_name = NULL;
584   audio->device_fd = 0;
585
586 #if defined(TARGET_SDL)
587   if (SDLOpenAudio())
588   {
589     audio->sound_available = TRUE;
590     audio->loops_available = TRUE;
591     audio->sound_enabled = TRUE;
592   }
593 #elif defined(PLATFORM_MSDOS)
594   if (MSDOSOpenAudio())
595   {
596     audio->sound_available = TRUE;
597     audio->loops_available = TRUE;
598     audio->sound_enabled = TRUE;
599   }
600 #elif defined(PLATFORM_UNIX)
601   UnixOpenAudio(audio);
602 #endif
603
604   return audio->sound_available;
605 }
606
607 inline void CloseAudio(struct AudioSystemInfo *audio)
608 {
609 #if defined(TARGET_SDL)
610   SDLCloseAudio();
611 #elif defined(PLATFORM_MSDOS)
612   MSDOSCloseAudio();
613 #elif defined(PLATFORM_UNIX)
614   UnixCloseAudio(audio);
615 #endif
616
617   audio->sound_available = FALSE;
618   audio->loops_available = FALSE;
619   audio->sound_enabled = FALSE;
620 }
621
622 inline void SetAudioMode(boolean enabled)
623 {
624   if (!audio.sound_available)
625     return;
626
627   audio.sound_enabled = enabled;
628 }
629
630
631 /* ========================================================================= */
632 /* event functions                                                           */
633 /* ========================================================================= */
634
635 inline void InitEventFilter(EventFilter filter_function)
636 {
637 #ifdef TARGET_SDL
638   /* set event filter to filter out certain events */
639   SDL_SetEventFilter(filter_function);
640 #endif
641 }
642
643 inline boolean PendingEvent(void)
644 {
645 #ifdef TARGET_SDL
646   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
647 #else
648   return (XPending(display) ? TRUE : FALSE);
649 #endif
650 }
651
652 inline void NextEvent(Event *event)
653 {
654 #ifdef TARGET_SDL
655   SDL_WaitEvent(event);
656 #else
657   XNextEvent(display, event);
658 #endif
659 }
660
661 inline Key GetEventKey(KeyEvent *event, boolean with_modifiers)
662 {
663 #ifdef TARGET_SDL
664 #if 0
665   printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
666          (int)event->keysym.unicode,
667          (int)event->keysym.sym,
668          (int)SDL_GetModState());
669 #endif
670
671   if (with_modifiers && event->keysym.unicode != 0)
672     return event->keysym.unicode;
673   else
674     return event->keysym.sym;
675 #else
676 #if 0
677   printf("with modifiers == '0x%04x', without modifiers == '0x%04x'\n",
678          (int)XLookupKeysym(event, event->state),
679          (int)XLookupKeysym(event, 0));
680 #endif
681
682   if (with_modifiers)
683     return XLookupKeysym(event, event->state);
684   else
685     return XLookupKeysym(event, 0);
686 #endif
687 }
688
689 inline boolean CheckCloseWindowEvent(ClientMessageEvent *event)
690 {
691   if (event->type != EVENT_CLIENTMESSAGE)
692     return FALSE;
693
694 #if defined(TARGET_SDL)
695   return TRUE;          /* the only possible message here is SDL_QUIT */
696 #elif defined(PLATFORM_UNIX)
697   if ((event->window == window->drawable) &&
698       (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
699     return TRUE;
700 #endif
701
702   return FALSE;
703 }
704
705
706 inline void dummy(void)
707 {
708 #ifdef TARGET_SDL
709 #else
710 #endif
711 }