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