30c3bc5cdcbbb024e4420650001ea5a29e5688c6
[rocksndiamonds.git] / src / libgame / system.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  ©1995 Artsoft Development                               *
5 *        Holger Schemel                                    *
6 *        33659 Bielefeld-Senne                             *
7 *        Telefon: (0521) 493245                            *
8 *        eMail: aeglos@valinor.owl.de                      *
9 *               aeglos@uni-paderborn.de                    *
10 *               q99492@pbhrzx.uni-paderborn.de             *
11 *----------------------------------------------------------*
12 *  system.c                                                *
13 ***********************************************************/
14
15 #include <string.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 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
74 void InitPlatformDependantStuff(void)
75 {
76 #if defined(PLATFORM_MSDOS)
77   _fmode = O_BINARY;
78 #endif
79 }
80
81 void InitProgramInfo(char *unix_userdata_directory, char *program_title,
82                      char *window_title, char *icon_title,
83                      char *x11_icon_basename, char *x11_iconmask_basename,
84                      char *msdos_pointer_basename)
85 {
86   char *gfx_dir = getPath2(options.ro_base_directory, GRAPHICS_DIRECTORY);
87   char *x11_icon_filename = getPath2(gfx_dir, x11_icon_basename);
88   char *x11_iconmask_filename = getPath2(gfx_dir, x11_iconmask_basename);
89   char *msdos_pointer_filename = getPath2(gfx_dir, msdos_pointer_basename);
90
91   free(gfx_dir);
92
93 #if defined(PLATFORM_UNIX)
94   program.userdata_directory = unix_userdata_directory;
95 #else
96   program.userdata_directory = "userdata";
97 #endif
98
99   program.program_title = program_title;
100   program.window_title = window_title;
101   program.icon_title = icon_title;
102   program.x11_icon_filename = x11_icon_filename;
103   program.x11_iconmask_filename = x11_iconmask_filename;
104   program.msdos_pointer_filename = msdos_pointer_filename;
105 }
106
107 void InitGfxFieldInfo(int sx, int sy, int sxsize, int sysize,
108                       int real_sx, int real_sy,
109                       int full_sxsize, int full_sysize)
110 {
111   gfx.sx = sx;
112   gfx.sy = sy;
113   gfx.sxsize = sxsize;
114   gfx.sysize = sysize;
115   gfx.real_sx = real_sx;
116   gfx.real_sy = real_sy;
117   gfx.full_sxsize = full_sxsize;
118   gfx.full_sysize = full_sysize;
119 }
120
121 void InitGfxDoor1Info(int dx, int dy, int dxsize, int dysize)
122 {
123   gfx.dx = dx;
124   gfx.dy = dy;
125   gfx.dxsize = dxsize;
126   gfx.dysize = dysize;
127 }
128
129 void InitGfxDoor2Info(int vx, int vy, int vxsize, int vysize)
130 {
131   gfx.vx = vx;
132   gfx.vy = vy;
133   gfx.vxsize = vxsize;
134   gfx.vysize = vysize;
135 }
136
137 void InitGfxScrollbufferInfo(int scrollbuffer_width, int scrollbuffer_height)
138 {
139   /* currently only used by MSDOS code to alloc VRAM buffer, if available */
140   gfx.scrollbuffer_width = scrollbuffer_width;
141   gfx.scrollbuffer_height = scrollbuffer_height;
142 }
143
144
145 /* ========================================================================= */
146 /* video functions                                                           */
147 /* ========================================================================= */
148
149 inline static int GetRealDepth(int depth)
150 {
151   return (depth == DEFAULT_DEPTH ? video.default_depth : depth);
152 }
153
154 inline void InitVideoDisplay(void)
155 {
156 #ifdef TARGET_SDL
157   SDLInitVideoDisplay();
158 #else
159   X11InitVideoDisplay();
160 #endif
161 }
162
163 inline void InitVideoBuffer(DrawBuffer *backbuffer, DrawWindow *window,
164                             int width, int height, int depth,
165                             boolean fullscreen)
166 {
167   video.width = width;
168   video.height = height;
169   video.depth = GetRealDepth(depth);
170   video.fullscreen_available = FULLSCREEN_STATUS;
171   video.fullscreen_enabled = FALSE;
172
173 #ifdef TARGET_SDL
174   SDLInitVideoBuffer(backbuffer, window, fullscreen);
175 #else
176   X11InitVideoBuffer(backbuffer, window);
177 #endif
178 }
179
180 inline Bitmap CreateBitmapStruct(void)
181 {
182 #ifdef TARGET_SDL
183   return checked_calloc(sizeof(struct SDLSurfaceInfo));
184 #else
185   return checked_calloc(sizeof(struct X11DrawableInfo));
186 #endif
187 }
188
189 inline Bitmap CreateBitmap(int width, int height, int depth)
190 {
191   Bitmap new_bitmap = CreateBitmapStruct();
192   int real_depth = GetRealDepth(depth);
193
194 #ifdef TARGET_SDL
195   SDL_Surface *surface_tmp, *surface_native;
196
197   if ((surface_tmp = SDL_CreateRGBSurface(SURFACE_FLAGS, width, height,
198                                           real_depth, 0, 0, 0, 0))
199       == NULL)
200     Error(ERR_EXIT, "SDL_CreateRGBSurface() failed: %s", SDL_GetError());
201
202   if ((surface_native = SDL_DisplayFormat(surface_tmp)) == NULL)
203     Error(ERR_EXIT, "SDL_DisplayFormat() failed: %s", SDL_GetError());
204
205   SDL_FreeSurface(surface_tmp);
206
207   new_bitmap->surface = surface_native;
208 #else
209   Pixmap pixmap;
210
211   if (!(pixmap = XCreatePixmap(display, window->drawable,
212                                width, height, real_depth)))
213     Error(ERR_EXIT, "cannot create pixmap");
214   new_bitmap->drawable = pixmap;
215
216   if (window == NULL)
217     Error(ERR_EXIT, "Window GC needed for Bitmap -- create Window first");
218   new_bitmap->gc = window->gc;
219
220 #endif
221
222   return new_bitmap;
223 }
224
225 inline void FreeBitmap(Bitmap bitmap)
226 {
227   if (bitmap == NULL)
228     return;
229
230 #ifdef TARGET_SDL
231   if (bitmap->surface)
232     SDL_FreeSurface(bitmap->surface);
233   if (bitmap->surface_masked)
234     SDL_FreeSurface(bitmap->surface_masked);
235 #else
236   if (bitmap->drawable)
237     XFreePixmap(display, bitmap->drawable);
238   if (bitmap->clip_mask)
239     XFreePixmap(display, bitmap->clip_mask);
240   if (bitmap->stored_clip_gc)
241     XFreeGC(display, bitmap->stored_clip_gc);
242 #endif
243
244   free(bitmap);
245 }
246
247 inline void CloseWindow(DrawWindow window)
248 {
249 #ifdef TARGET_X11
250   if (window->drawable)
251   {
252     XUnmapWindow(display, window->drawable);
253     XDestroyWindow(display, window->drawable);
254   }
255   if (window->gc)
256     XFreeGC(display, window->gc);
257 #endif
258 }
259
260 inline void BlitBitmap(Bitmap src_bitmap, Bitmap dst_bitmap,
261                        int src_x, int src_y,
262                        int width, int height,
263                        int dst_x, int dst_y)
264 {
265 #ifdef TARGET_SDL
266   SDLCopyArea(src_bitmap, dst_bitmap,
267               src_x, src_y, width, height, dst_x, dst_y, SDLCOPYAREA_OPAQUE);
268 #else
269   XCopyArea(display, src_bitmap->drawable, dst_bitmap->drawable,
270             dst_bitmap->gc, src_x, src_y, width, height, dst_x, dst_y);
271 #endif
272 }
273
274 inline void ClearRectangle(Bitmap bitmap, int x, int y, int width, int height)
275 {
276 #ifdef TARGET_SDL
277   SDLFillRectangle(bitmap, x, y, width, height, 0x000000);
278 #else
279   XFillRectangle(display, bitmap->drawable, bitmap->gc, x, y, width, height);
280 #endif
281 }
282
283 #if 0
284 #ifndef TARGET_SDL
285 static GC last_clip_gc = 0;     /* needed for XCopyArea() through clip mask */
286 #endif
287 #endif
288
289 inline void SetClipMask(Bitmap bitmap, GC clip_gc, Pixmap clip_pixmap)
290 {
291 #ifdef TARGET_X11
292   if (clip_gc)
293   {
294     bitmap->clip_gc = clip_gc;
295     XSetClipMask(display, bitmap->clip_gc, clip_pixmap);
296   }
297 #if 0
298   last_clip_gc = clip_gc;
299 #endif
300 #endif
301 }
302
303 inline void SetClipOrigin(Bitmap bitmap, GC clip_gc, int clip_x, int clip_y)
304 {
305 #ifdef TARGET_X11
306   if (clip_gc)
307   {
308     bitmap->clip_gc = clip_gc;
309     XSetClipOrigin(display, bitmap->clip_gc, clip_x, clip_y);
310   }
311 #if 0
312   last_clip_gc = clip_gc;
313 #endif
314 #endif
315 }
316
317 inline void BlitBitmapMasked(Bitmap src_bitmap, Bitmap dst_bitmap,
318                              int src_x, int src_y,
319                              int width, int height,
320                              int dst_x, int dst_y)
321 {
322 #ifdef TARGET_SDL
323   SDLCopyArea(src_bitmap, dst_bitmap,
324               src_x, src_y, width, height, dst_x, dst_y, SDLCOPYAREA_MASKED);
325 #else
326   XCopyArea(display, src_bitmap->drawable, dst_bitmap->drawable,
327             src_bitmap->clip_gc, src_x, src_y, width, height, dst_x, dst_y);
328 #endif
329 }
330
331 inline void DrawSimpleWhiteLine(Bitmap bitmap, int from_x, int from_y,
332                                 int to_x, int to_y)
333 {
334 #ifdef TARGET_SDL
335   SDLDrawSimpleLine(bitmap->surface, from_x, from_y, to_x, to_y, 0xffffff);
336 #else
337   XSetForeground(display, bitmap->gc, WhitePixel(display, screen));
338   XDrawLine(display, bitmap->drawable, bitmap->gc, from_x, from_y, to_x, to_y);
339   XSetForeground(display, bitmap->gc, BlackPixel(display, screen));
340 #endif
341 }
342
343 /* execute all pending screen drawing operations */
344 inline void FlushDisplay(void)
345 {
346 #ifndef TARGET_SDL
347   XFlush(display);
348 #endif
349 }
350
351 /* execute and wait for all pending screen drawing operations */
352 inline void SyncDisplay(void)
353 {
354 #ifndef TARGET_SDL
355   XSync(display, FALSE);
356 #endif
357 }
358
359 inline void KeyboardAutoRepeatOn(void)
360 {
361 #ifdef TARGET_SDL
362   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2,
363                       SDL_DEFAULT_REPEAT_INTERVAL / 2);
364   SDL_EnableUNICODE(1);
365 #else
366   XAutoRepeatOn(display);
367 #endif
368 }
369
370 inline void KeyboardAutoRepeatOff(void)
371 {
372 #ifdef TARGET_SDL
373   SDL_EnableKeyRepeat(0, SDL_DEFAULT_REPEAT_INTERVAL);
374   SDL_EnableUNICODE(0);
375 #else
376   XAutoRepeatOff(display);
377 #endif
378 }
379
380 inline boolean PointerInWindow(DrawWindow window)
381 {
382 #ifdef TARGET_SDL
383   return TRUE;
384 #else
385   Window root, child;
386   int root_x, root_y;
387   unsigned int mask;
388   int win_x, win_y;
389
390   /* if XQueryPointer() returns False, the pointer
391      is not on the same screen as the specified window */
392   return XQueryPointer(display, window->drawable, &root, &child,
393                        &root_x, &root_y, &win_x, &win_y, &mask);
394 #endif
395 }
396
397 inline boolean SetVideoMode(boolean fullscreen)
398 {
399 #ifdef TARGET_SDL
400   return SDLSetVideoMode(&backbuffer, fullscreen);
401 #else
402   boolean success = TRUE;
403
404   if (fullscreen && video.fullscreen_available)
405   {
406     Error(ERR_WARN, "fullscreen not available in X11 version");
407
408     /* display error message only once */
409     video.fullscreen_available = FALSE;
410
411     success = FALSE;
412   }
413
414   return success;
415 #endif
416 }
417
418 inline boolean ChangeVideoModeIfNeeded(boolean fullscreen)
419 {
420 #ifdef TARGET_SDL
421   if ((fullscreen && !video.fullscreen_enabled && video.fullscreen_available)||
422       (!fullscreen && video.fullscreen_enabled))
423     fullscreen = SetVideoMode(fullscreen);
424 #endif
425
426   return fullscreen;
427 }
428
429
430 /* ========================================================================= */
431 /* audio functions                                                           */
432 /* ========================================================================= */
433
434 inline boolean OpenAudio(struct AudioSystemInfo *audio)
435 {
436   audio->sound_available = FALSE;
437   audio->loops_available = FALSE;
438   audio->sound_enabled = FALSE;
439   audio->soundserver_pipe[0] = audio->soundserver_pipe[1] = 0;
440   audio->soundserver_pid = 0;
441   audio->device_name = NULL;
442   audio->device_fd = 0;
443
444 #if defined(TARGET_SDL)
445   if (SDLOpenAudio())
446   {
447     audio->sound_available = TRUE;
448     audio->loops_available = TRUE;
449     audio->sound_enabled = TRUE;
450   }
451 #elif defined(PLATFORM_MSDOS)
452   if (MSDOSOpenAudio())
453   {
454     audio->sound_available = TRUE;
455     audio->loops_available = TRUE;
456     audio->sound_enabled = TRUE;
457   }
458 #elif defined(PLATFORM_UNIX)
459   UnixOpenAudio(audio);
460 #endif
461
462   return audio->sound_available;
463 }
464
465 inline void CloseAudio(struct AudioSystemInfo *audio)
466 {
467 #if defined(TARGET_SDL)
468   SDLCloseAudio();
469 #elif defined(PLATFORM_MSDOS)
470   MSDOSCloseAudio();
471 #elif defined(PLATFORM_UNIX)
472   UnixCloseAudio(audio);
473 #endif
474
475   audio->sound_available = FALSE;
476   audio->loops_available = FALSE;
477   audio->sound_enabled = FALSE;
478 }
479
480 inline void SetAudioMode(boolean enabled)
481 {
482   if (!audio.sound_available)
483     return;
484
485   audio.sound_enabled = enabled;
486 }
487
488
489 /* ========================================================================= */
490 /* event functions                                                           */
491 /* ========================================================================= */
492
493 inline void InitEventFilter(EventFilter filter_function)
494 {
495 #ifdef TARGET_SDL
496   /* set event filter to filter out certain events */
497   SDL_SetEventFilter(filter_function);
498 #endif
499 }
500
501 inline boolean PendingEvent(void)
502 {
503 #ifdef TARGET_SDL
504   return (SDL_PollEvent(NULL) ? TRUE : FALSE);
505 #else
506   return (XPending(display) ? TRUE : FALSE);
507 #endif
508 }
509
510 inline void NextEvent(Event *event)
511 {
512 #ifdef TARGET_SDL
513   SDL_WaitEvent(event);
514 #else
515   XNextEvent(display, event);
516 #endif
517 }
518
519 inline Key GetEventKey(KeyEvent *event, boolean with_modifiers)
520 {
521 #ifdef TARGET_SDL
522 #if 0
523   printf("unicode == '%d', sym == '%d', mod == '0x%04x'\n",
524          (int)event->keysym.unicode,
525          (int)event->keysym.sym,
526          (int)SDL_GetModState());
527 #endif
528
529   if (with_modifiers && event->keysym.unicode != 0)
530     return event->keysym.unicode;
531   else
532     return event->keysym.sym;
533 #else
534 #if 0
535   printf("with modifiers == '0x%04x', without modifiers == '0x%04x'\n",
536          (int)XLookupKeysym(event, event->state),
537          (int)XLookupKeysym(event, 0));
538 #endif
539
540   if (with_modifiers)
541     return XLookupKeysym(event, event->state);
542   else
543     return XLookupKeysym(event, 0);
544 #endif
545 }
546
547 inline boolean CheckCloseWindowEvent(ClientMessageEvent *event)
548 {
549   if (event->type != EVENT_CLIENTMESSAGE)
550     return FALSE;
551
552 #if defined(TARGET_SDL)
553   return TRUE;          /* the only possible message here is SDL_QUIT */
554 #elif defined(PLATFORM_UNIX)
555   if ((event->window == window->drawable) &&
556       (event->data.l[0] == XInternAtom(display, "WM_DELETE_WINDOW", FALSE)))
557     return TRUE;
558 #endif
559
560   return FALSE;
561 }
562
563
564 inline void dummy(void)
565 {
566 #ifdef TARGET_SDL
567 #else
568 #endif
569 }