rnd-20001202-1-src
[rocksndiamonds.git] / src / libgame / misc.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  misc.c                                                  *
12 ***********************************************************/
13
14 #include <time.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <stdarg.h>
18 #include <ctype.h>
19
20 #if !defined(PLATFORM_WIN32)
21 #include <pwd.h>
22 #include <sys/param.h>
23 #endif
24
25 #include "libgame.h"
26
27 #include "main_TMP.h"
28
29 #include "misc.h"
30
31 #if 0
32 #include "joystick_TMP.h"
33 #endif
34
35 #if defined(PLATFORM_MSDOS)
36 volatile unsigned long counter = 0;
37
38 void increment_counter()
39 {
40   counter++;
41 }
42
43 END_OF_FUNCTION(increment_counter);
44 #endif
45
46
47 /* maximal allowed length of a command line option */
48 #define MAX_OPTION_LEN          256
49
50 #ifdef TARGET_SDL
51 static unsigned long mainCounter(int mode)
52 {
53   static unsigned long base_ms = 0;
54   unsigned long current_ms;
55   unsigned long counter_ms;
56
57   current_ms = SDL_GetTicks();
58
59   /* reset base time in case of counter initializing or wrap-around */
60   if (mode == INIT_COUNTER || current_ms < base_ms)
61     base_ms = current_ms;
62
63   counter_ms = current_ms - base_ms;
64
65   return counter_ms;            /* return milliseconds since last init */
66 }
67
68 #else /* !TARGET_SDL */
69
70 #if defined(PLATFORM_UNIX)
71 static unsigned long mainCounter(int mode)
72 {
73   static struct timeval base_time = { 0, 0 };
74   struct timeval current_time;
75   unsigned long counter_ms;
76
77   gettimeofday(&current_time, NULL);
78
79   /* reset base time in case of counter initializing or wrap-around */
80   if (mode == INIT_COUNTER || current_time.tv_sec < base_time.tv_sec)
81     base_time = current_time;
82
83   counter_ms = (current_time.tv_sec  - base_time.tv_sec)  * 1000
84              + (current_time.tv_usec - base_time.tv_usec) / 1000;
85
86   return counter_ms;            /* return milliseconds since last init */
87 }
88 #endif /* PLATFORM_UNIX */
89 #endif /* !TARGET_SDL */
90
91 void InitCounter()              /* set counter back to zero */
92 {
93 #if !defined(PLATFORM_MSDOS)
94   mainCounter(INIT_COUNTER);
95 #else
96   LOCK_VARIABLE(counter);
97   LOCK_FUNCTION(increment_counter);
98   install_int_ex(increment_counter, BPS_TO_TIMER(100));
99 #endif
100 }
101
102 unsigned long Counter() /* get milliseconds since last call of InitCounter() */
103 {
104 #if !defined(PLATFORM_MSDOS)
105   return mainCounter(READ_COUNTER);
106 #else
107   return (counter * 10);
108 #endif
109 }
110
111 static void sleep_milliseconds(unsigned long milliseconds_delay)
112 {
113   boolean do_busy_waiting = (milliseconds_delay < 5 ? TRUE : FALSE);
114
115 #if defined(PLATFORM_MSDOS)
116   /* don't use select() to perform waiting operations under DOS/Windows
117      environment; always use a busy loop for waiting instead */
118   do_busy_waiting = TRUE;
119 #endif
120
121   if (do_busy_waiting)
122   {
123     /* we want to wait only a few ms -- if we assume that we have a
124        kernel timer resolution of 10 ms, we would wait far to long;
125        therefore it's better to do a short interval of busy waiting
126        to get our sleeping time more accurate */
127
128     unsigned long base_counter = Counter(), actual_counter = Counter();
129
130     while (actual_counter < base_counter + milliseconds_delay &&
131            actual_counter >= base_counter)
132       actual_counter = Counter();
133   }
134   else
135   {
136 #if defined(TARGET_SDL)
137     SDL_Delay(milliseconds_delay);
138 #else
139     struct timeval delay;
140
141     delay.tv_sec  = milliseconds_delay / 1000;
142     delay.tv_usec = 1000 * (milliseconds_delay % 1000);
143
144     if (select(0, NULL, NULL, NULL, &delay) != 0)
145       Error(ERR_WARN, "sleep_milliseconds(): select() failed");
146 #endif
147   }
148 }
149
150 void Delay(unsigned long delay) /* Sleep specified number of milliseconds */
151 {
152   sleep_milliseconds(delay);
153 }
154
155 boolean FrameReached(unsigned long *frame_counter_var,
156                      unsigned long frame_delay)
157 {
158   unsigned long actual_frame_counter = FrameCounter;
159
160   if (actual_frame_counter < *frame_counter_var+frame_delay &&
161       actual_frame_counter >= *frame_counter_var)
162     return(FALSE);
163
164   *frame_counter_var = actual_frame_counter;
165   return(TRUE);
166 }
167
168 boolean DelayReached(unsigned long *counter_var,
169                      unsigned long delay)
170 {
171   unsigned long actual_counter = Counter();
172
173   if (actual_counter < *counter_var + delay &&
174       actual_counter >= *counter_var)
175     return(FALSE);
176
177   *counter_var = actual_counter;
178   return(TRUE);
179 }
180
181 void WaitUntilDelayReached(unsigned long *counter_var, unsigned long delay)
182 {
183   unsigned long actual_counter;
184
185   while(1)
186   {
187     actual_counter = Counter();
188
189     if (actual_counter < *counter_var + delay &&
190         actual_counter >= *counter_var)
191       sleep_milliseconds((*counter_var + delay - actual_counter) / 2);
192     else
193       break;
194   }
195
196   *counter_var = actual_counter;
197 }
198
199 /* int2str() returns a number converted to a string;
200    the used memory is static, but will be overwritten by later calls,
201    so if you want to save the result, copy it to a private string buffer;
202    there can be 10 local calls of int2str() without buffering the result --
203    the 11th call will then destroy the result from the first call and so on.
204 */
205
206 char *int2str(int number, int size)
207 {
208   static char shift_array[10][40];
209   static int shift_counter = 0;
210   char *s = shift_array[shift_counter];
211
212   shift_counter = (shift_counter + 1) % 10;
213
214   if (size > 20)
215     size = 20;
216
217   if (size)
218   {
219     sprintf(s, "                    %09d", number);
220     return &s[strlen(s) - size];
221   }
222   else
223   {
224     sprintf(s, "%d", number);
225     return s;
226   }
227 }
228
229 unsigned int SimpleRND(unsigned int max)
230 {
231 #if defined(TARGET_SDL)
232   static unsigned long root = 654321;
233   unsigned long current_ms;
234
235   current_ms = SDL_GetTicks();
236   root = root * 4253261 + current_ms;
237   return (root % max);
238 #else
239   static unsigned long root = 654321;
240   struct timeval current_time;
241
242   gettimeofday(&current_time, NULL);
243   root = root * 4253261 + current_time.tv_sec + current_time.tv_usec;
244   return (root % max);
245 #endif
246 }
247
248 #ifdef DEBUG
249 static unsigned int last_RND_value = 0;
250
251 unsigned int last_RND()
252 {
253   return last_RND_value;
254 }
255 #endif
256
257 unsigned int RND(unsigned int max)
258 {
259 #ifdef DEBUG
260   return (last_RND_value = random_linux_libc() % max);
261 #else
262   return (random_linux_libc() % max);
263 #endif
264 }
265
266 unsigned int InitRND(long seed)
267 {
268 #if defined(TARGET_SDL)
269   unsigned long current_ms;
270
271   if (seed == NEW_RANDOMIZE)
272   {
273     current_ms = SDL_GetTicks();
274     srandom_linux_libc((unsigned int) current_ms);
275     return (unsigned int) current_ms;
276   }
277   else
278   {
279     srandom_linux_libc((unsigned int) seed);
280     return (unsigned int) seed;
281   }
282 #else
283   struct timeval current_time;
284
285   if (seed == NEW_RANDOMIZE)
286   {
287     gettimeofday(&current_time, NULL);
288     srandom_linux_libc((unsigned int) current_time.tv_usec);
289     return (unsigned int) current_time.tv_usec;
290   }
291   else
292   {
293     srandom_linux_libc((unsigned int) seed);
294     return (unsigned int) seed;
295   }
296 #endif
297 }
298
299 char *getLoginName()
300 {
301 #if defined(PLATFORM_WIN32)
302   return ANONYMOUS_NAME;
303 #else
304   struct passwd *pwd;
305
306   if ((pwd = getpwuid(getuid())) == NULL)
307     return ANONYMOUS_NAME;
308   else
309     return pwd->pw_name;
310 #endif
311 }
312
313 char *getRealName()
314 {
315 #if defined(PLATFORM_UNIX)
316   struct passwd *pwd;
317
318   if ((pwd = getpwuid(getuid())) == NULL || strlen(pwd->pw_gecos) == 0)
319     return ANONYMOUS_NAME;
320   else
321   {
322     static char real_name[1024];
323     char *from_ptr = pwd->pw_gecos, *to_ptr = real_name;
324
325     if (strchr(pwd->pw_gecos, 'ß') == NULL)
326       return pwd->pw_gecos;
327
328     /* the user's real name contains a 'ß' character (german sharp s),
329        which has no equivalent in upper case letters (which our fonts use) */
330     while (*from_ptr != '\0' && (long)(to_ptr - real_name) < 1024 - 2)
331     {
332       if (*from_ptr != 'ß')
333         *to_ptr++ = *from_ptr++;
334       else
335       {
336         from_ptr++;
337         *to_ptr++ = 's';
338         *to_ptr++ = 's';
339       }
340     }
341     *to_ptr = '\0';
342
343     return real_name;
344   }
345 #else /* !PLATFORM_UNIX */
346   return ANONYMOUS_NAME;
347 #endif
348 }
349
350 char *getHomeDir()
351 {
352 #if defined(PLATFORM_UNIX)
353   static char *home_dir = NULL;
354
355   if (!home_dir)
356   {
357     if (!(home_dir = getenv("HOME")))
358     {
359       struct passwd *pwd;
360
361       if ((pwd = getpwuid(getuid())))
362         home_dir = pwd->pw_dir;
363       else
364         home_dir = ".";
365     }
366   }
367
368   return home_dir;
369 #else
370   return ".";
371 #endif
372 }
373
374 char *getPath2(char *path1, char *path2)
375 {
376   char *complete_path = checked_malloc(strlen(path1) + 1 +
377                                        strlen(path2) + 1);
378
379   sprintf(complete_path, "%s/%s", path1, path2);
380   return complete_path;
381 }
382
383 char *getPath3(char *path1, char *path2, char *path3)
384 {
385   char *complete_path = checked_malloc(strlen(path1) + 1 +
386                                        strlen(path2) + 1 +
387                                        strlen(path3) + 1);
388
389   sprintf(complete_path, "%s/%s/%s", path1, path2, path3);
390   return complete_path;
391 }
392
393 char *getStringCopy(char *s)
394 {
395   char *s_copy;
396
397   if (s == NULL)
398     return NULL;
399
400   s_copy = checked_malloc(strlen(s) + 1);
401
402   strcpy(s_copy, s);
403   return s_copy;
404 }
405
406 char *getStringToLower(char *s)
407 {
408   char *s_copy = checked_malloc(strlen(s) + 1);
409   char *s_ptr = s_copy;
410
411   while (*s)
412     *s_ptr++ = tolower(*s++);
413   *s_ptr = '\0';
414
415   return s_copy;
416 }
417
418 void MarkTileDirty(int x, int y)
419 {
420   int xx = redraw_x1 + x;
421   int yy = redraw_y1 + y;
422
423   if (!redraw[xx][yy])
424     redraw_tiles++;
425
426   redraw[xx][yy] = TRUE;
427   redraw_mask |= REDRAW_TILES;
428 }
429
430 void SetBorderElement()
431 {
432   int x, y;
433
434   BorderElement = EL_LEERRAUM;
435
436   for(y=0; y<lev_fieldy && BorderElement == EL_LEERRAUM; y++)
437   {
438     for(x=0; x<lev_fieldx; x++)
439     {
440       if (!IS_MASSIVE(Feld[x][y]))
441         BorderElement = EL_BETON;
442
443       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
444         x = lev_fieldx - 2;
445     }
446   }
447 }
448
449 void GetOptions(char *argv[])
450 {
451   char **options_left = &argv[1];
452
453   /* initialize global program options */
454   options.display_name = NULL;
455   options.server_host = NULL;
456   options.server_port = 0;
457   options.ro_base_directory = RO_BASE_PATH;
458   options.rw_base_directory = RW_BASE_PATH;
459   options.level_directory = RO_BASE_PATH "/" LEVELS_DIRECTORY;
460   options.serveronly = FALSE;
461   options.network = FALSE;
462   options.verbose = FALSE;
463   options.debug = FALSE;
464
465   /* initialize some more global variables */
466   global.frames_per_second = 0;
467   global.fps_slowdown = FALSE;
468   global.fps_slowdown_factor = 1;
469
470   while (*options_left)
471   {
472     char option_str[MAX_OPTION_LEN];
473     char *option = options_left[0];
474     char *next_option = options_left[1];
475     char *option_arg = NULL;
476     int option_len = strlen(option);
477
478     if (option_len >= MAX_OPTION_LEN)
479       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
480
481     strcpy(option_str, option);                 /* copy argument into buffer */
482     option = option_str;
483
484     if (strcmp(option, "--") == 0)              /* stop scanning arguments */
485       break;
486
487     if (strncmp(option, "--", 2) == 0)          /* treat '--' like '-' */
488       option++;
489
490     option_arg = strchr(option, '=');
491     if (option_arg == NULL)                     /* no '=' in option */
492       option_arg = next_option;
493     else
494     {
495       *option_arg++ = '\0';                     /* cut argument from option */
496       if (*option_arg == '\0')                  /* no argument after '=' */
497         Error(ERR_EXIT_HELP, "option '%s' has invalid argument", option_str);
498     }
499
500     option_len = strlen(option);
501
502     if (strcmp(option, "-") == 0)
503       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option);
504     else if (strncmp(option, "-help", option_len) == 0)
505     {
506       printf("Usage: %s [options] [server.name [port]]\n"
507              "Options:\n"
508              "  -d, --display machine:0       X server display\n"
509              "  -b, --basepath directory      alternative base directory\n"
510              "  -l, --level directory         alternative level directory\n"
511              "  -s, --serveronly              only start network server\n"
512              "  -n, --network                 network multiplayer game\n"
513              "  -v, --verbose                 verbose mode\n",
514              program_name);
515       exit(0);
516     }
517     else if (strncmp(option, "-display", option_len) == 0)
518     {
519       if (option_arg == NULL)
520         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
521
522       options.display_name = option_arg;
523       if (option_arg == next_option)
524         options_left++;
525     }
526     else if (strncmp(option, "-basepath", option_len) == 0)
527     {
528       if (option_arg == NULL)
529         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
530
531       /* this should be extended to separate options for ro and rw data */
532       options.ro_base_directory = option_arg;
533       options.rw_base_directory = option_arg;
534       if (option_arg == next_option)
535         options_left++;
536
537       /* adjust path for level directory accordingly */
538       options.level_directory =
539         getPath2(options.ro_base_directory, LEVELS_DIRECTORY);
540     }
541     else if (strncmp(option, "-levels", option_len) == 0)
542     {
543       if (option_arg == NULL)
544         Error(ERR_EXIT_HELP, "option '%s' requires an argument", option_str);
545
546       options.level_directory = option_arg;
547       if (option_arg == next_option)
548         options_left++;
549     }
550     else if (strncmp(option, "-network", option_len) == 0)
551     {
552       options.network = TRUE;
553     }
554     else if (strncmp(option, "-serveronly", option_len) == 0)
555     {
556       options.serveronly = TRUE;
557     }
558     else if (strncmp(option, "-verbose", option_len) == 0)
559     {
560       options.verbose = TRUE;
561     }
562     else if (strncmp(option, "-debug", option_len) == 0)
563     {
564       options.debug = TRUE;
565     }
566     else if (*option == '-')
567     {
568       Error(ERR_EXIT_HELP, "unrecognized option '%s'", option_str);
569     }
570     else if (options.server_host == NULL)
571     {
572       options.server_host = *options_left;
573     }
574     else if (options.server_port == 0)
575     {
576       options.server_port = atoi(*options_left);
577       if (options.server_port < 1024)
578         Error(ERR_EXIT_HELP, "bad port number '%d'", options.server_port);
579     }
580     else
581       Error(ERR_EXIT_HELP, "too many arguments");
582
583     options_left++;
584   }
585 }
586
587 void Error(int mode, char *format, ...)
588 {
589   char *process_name = "";
590   FILE *error = stderr;
591
592   /* display warnings only when running in verbose mode */
593   if (mode & ERR_WARN && !options.verbose)
594     return;
595
596 #if !defined(PLATFORM_UNIX)
597   if ((error = openErrorFile()) == NULL)
598   {
599     printf("Cannot write to error output file!\n");
600     CloseAllAndExit(1);
601   }
602 #endif
603
604   if (mode & ERR_SOUND_SERVER)
605     process_name = " sound server";
606   else if (mode & ERR_NETWORK_SERVER)
607     process_name = " network server";
608   else if (mode & ERR_NETWORK_CLIENT)
609     process_name = " network client **";
610
611   if (format)
612   {
613     va_list ap;
614
615     fprintf(error, "%s%s: ", program_name, process_name);
616
617     if (mode & ERR_WARN)
618       fprintf(error, "warning: ");
619
620     va_start(ap, format);
621     vfprintf(error, format, ap);
622     va_end(ap);
623   
624     fprintf(error, "\n");
625   }
626   
627   if (mode & ERR_HELP)
628     fprintf(error, "%s: Try option '--help' for more information.\n",
629             program_name);
630
631   if (mode & ERR_EXIT)
632     fprintf(error, "%s%s: aborting\n", program_name, process_name);
633
634   if (error != stderr)
635     fclose(error);
636
637   if (mode & ERR_EXIT)
638   {
639     if (mode & ERR_FROM_SERVER)
640       exit(1);                          /* child process: normal exit */
641     else
642       CloseAllAndExit(1);               /* main process: clean up stuff */
643   }
644 }
645
646 void *checked_malloc(unsigned long size)
647 {
648   void *ptr;
649
650   ptr = malloc(size);
651
652   if (ptr == NULL)
653     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
654
655   return ptr;
656 }
657
658 void *checked_calloc(unsigned long size)
659 {
660   void *ptr;
661
662   ptr = calloc(1, size);
663
664   if (ptr == NULL)
665     Error(ERR_EXIT, "cannot allocate %d bytes -- out of memory", size);
666
667   return ptr;
668 }
669
670 short getFile16BitInteger(FILE *file, int byte_order)
671 {
672   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
673     return ((fgetc(file) <<  8) |
674             (fgetc(file) <<  0));
675   else           /* BYTE_ORDER_LITTLE_ENDIAN */
676     return ((fgetc(file) <<  0) |
677             (fgetc(file) <<  8));
678 }
679
680 void putFile16BitInteger(FILE *file, short value, int byte_order)
681 {
682   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
683   {
684     fputc((value >>  8) & 0xff, file);
685     fputc((value >>  0) & 0xff, file);
686   }
687   else           /* BYTE_ORDER_LITTLE_ENDIAN */
688   {
689     fputc((value >>  0) & 0xff, file);
690     fputc((value >>  8) & 0xff, file);
691   }
692 }
693
694 int getFile32BitInteger(FILE *file, int byte_order)
695 {
696   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
697     return ((fgetc(file) << 24) |
698             (fgetc(file) << 16) |
699             (fgetc(file) <<  8) |
700             (fgetc(file) <<  0));
701   else           /* BYTE_ORDER_LITTLE_ENDIAN */
702     return ((fgetc(file) <<  0) |
703             (fgetc(file) <<  8) |
704             (fgetc(file) << 16) |
705             (fgetc(file) << 24));
706 }
707
708 void putFile32BitInteger(FILE *file, int value, int byte_order)
709 {
710   if (byte_order == BYTE_ORDER_BIG_ENDIAN)
711   {
712     fputc((value >> 24) & 0xff, file);
713     fputc((value >> 16) & 0xff, file);
714     fputc((value >>  8) & 0xff, file);
715     fputc((value >>  0) & 0xff, file);
716   }
717   else           /* BYTE_ORDER_LITTLE_ENDIAN */
718   {
719     fputc((value >>  0) & 0xff, file);
720     fputc((value >>  8) & 0xff, file);
721     fputc((value >> 16) & 0xff, file);
722     fputc((value >> 24) & 0xff, file);
723   }
724 }
725
726 void getFileChunk(FILE *file, char *chunk_buffer, int *chunk_length,
727                   int byte_order)
728 {
729   const int chunk_identifier_length = 4;
730
731   /* read chunk identifier */
732   fgets(chunk_buffer, chunk_identifier_length + 1, file);
733
734   /* read chunk length */
735   *chunk_length = getFile32BitInteger(file, byte_order);
736 }
737
738 void putFileChunk(FILE *file, char *chunk_name, int chunk_length,
739                   int byte_order)
740 {
741   /* write chunk identifier */
742   fputs(chunk_name, file);
743
744   /* write chunk length */
745   putFile32BitInteger(file, chunk_length, byte_order);
746 }
747
748 #define TRANSLATE_KEYSYM_TO_KEYNAME     0
749 #define TRANSLATE_KEYSYM_TO_X11KEYNAME  1
750 #define TRANSLATE_X11KEYNAME_TO_KEYSYM  2
751
752 void translate_keyname(Key *keysym, char **x11name, char **name, int mode)
753 {
754   static struct
755   {
756     Key key;
757     char *x11name;
758     char *name;
759   } translate_key[] =
760   {
761     /* normal cursor keys */
762     { KSYM_Left,        "XK_Left",              "cursor left" },
763     { KSYM_Right,       "XK_Right",             "cursor right" },
764     { KSYM_Up,          "XK_Up",                "cursor up" },
765     { KSYM_Down,        "XK_Down",              "cursor down" },
766
767     /* keypad cursor keys */
768 #ifdef KSYM_KP_Left
769     { KSYM_KP_Left,     "XK_KP_Left",           "keypad left" },
770     { KSYM_KP_Right,    "XK_KP_Right",          "keypad right" },
771     { KSYM_KP_Up,       "XK_KP_Up",             "keypad up" },
772     { KSYM_KP_Down,     "XK_KP_Down",           "keypad down" },
773 #endif
774
775     /* other keypad keys */
776 #ifdef KSYM_KP_Enter
777     { KSYM_KP_Enter,    "XK_KP_Enter",          "keypad enter" },
778     { KSYM_KP_Add,      "XK_KP_Add",            "keypad +" },
779     { KSYM_KP_Subtract, "XK_KP_Subtract",       "keypad -" },
780     { KSYM_KP_Multiply, "XK_KP_Multiply",       "keypad mltply" },
781     { KSYM_KP_Divide,   "XK_KP_Divide",         "keypad /" },
782     { KSYM_KP_Separator,"XK_KP_Separator",      "keypad ," },
783 #endif
784
785     /* modifier keys */
786     { KSYM_Shift_L,     "XK_Shift_L",           "left shift" },
787     { KSYM_Shift_R,     "XK_Shift_R",           "right shift" },
788     { KSYM_Control_L,   "XK_Control_L",         "left control" },
789     { KSYM_Control_R,   "XK_Control_R",         "right control" },
790     { KSYM_Meta_L,      "XK_Meta_L",            "left meta" },
791     { KSYM_Meta_R,      "XK_Meta_R",            "right meta" },
792     { KSYM_Alt_L,       "XK_Alt_L",             "left alt" },
793     { KSYM_Alt_R,       "XK_Alt_R",             "right alt" },
794     { KSYM_Super_L,     "XK_Super_L",           "left super" },  /* Win-L */
795     { KSYM_Super_R,     "XK_Super_R",           "right super" }, /* Win-R */
796     { KSYM_Mode_switch, "XK_Mode_switch",       "mode switch" }, /* Alt-R */
797     { KSYM_Multi_key,   "XK_Multi_key",         "multi key" },   /* Ctrl-R */
798
799     /* some special keys */
800     { KSYM_BackSpace,   "XK_BackSpace",         "backspace" },
801     { KSYM_Delete,      "XK_Delete",            "delete" },
802     { KSYM_Insert,      "XK_Insert",            "insert" },
803     { KSYM_Tab,         "XK_Tab",               "tab" },
804     { KSYM_Home,        "XK_Home",              "home" },
805     { KSYM_End,         "XK_End",               "end" },
806     { KSYM_Page_Up,     "XK_Page_Up",           "page up" },
807     { KSYM_Page_Down,   "XK_Page_Down",         "page down" },
808     { KSYM_Menu,        "XK_Menu",              "menu" },        /* Win-Menu */
809
810     /* ASCII 0x20 to 0x40 keys (except numbers) */
811     { KSYM_space,       "XK_space",             "space" },
812     { KSYM_exclam,      "XK_exclam",            "!" },
813     { KSYM_quotedbl,    "XK_quotedbl",          "\"" },
814     { KSYM_numbersign,  "XK_numbersign",        "#" },
815     { KSYM_dollar,      "XK_dollar",            "$" },
816     { KSYM_percent,     "XK_percent",           "%" },
817     { KSYM_ampersand,   "XK_ampersand",         "&" },
818     { KSYM_apostrophe,  "XK_apostrophe",        "'" },
819     { KSYM_parenleft,   "XK_parenleft",         "(" },
820     { KSYM_parenright,  "XK_parenright",        ")" },
821     { KSYM_asterisk,    "XK_asterisk",          "*" },
822     { KSYM_plus,        "XK_plus",              "+" },
823     { KSYM_comma,       "XK_comma",             "," },
824     { KSYM_minus,       "XK_minus",             "-" },
825     { KSYM_period,      "XK_period",            "." },
826     { KSYM_slash,       "XK_slash",             "/" },
827     { KSYM_colon,       "XK_colon",             ":" },
828     { KSYM_semicolon,   "XK_semicolon",         ";" },
829     { KSYM_less,        "XK_less",              "<" },
830     { KSYM_equal,       "XK_equal",             "=" },
831     { KSYM_greater,     "XK_greater",           ">" },
832     { KSYM_question,    "XK_question",          "?" },
833     { KSYM_at,          "XK_at",                "@" },
834
835     /* more ASCII keys */
836     { KSYM_bracketleft, "XK_bracketleft",       "[" },
837     { KSYM_backslash,   "XK_backslash",         "backslash" },
838     { KSYM_bracketright,"XK_bracketright",      "]" },
839     { KSYM_asciicircum, "XK_asciicircum",       "circumflex" },
840     { KSYM_underscore,  "XK_underscore",        "_" },
841     { KSYM_grave,       "XK_grave",             "grave" },
842     { KSYM_quoteleft,   "XK_quoteleft",         "quote left" },
843     { KSYM_braceleft,   "XK_braceleft",         "brace left" },
844     { KSYM_bar,         "XK_bar",               "bar" },
845     { KSYM_braceright,  "XK_braceright",        "brace right" },
846     { KSYM_asciitilde,  "XK_asciitilde",        "ascii tilde" },
847
848     /* special (non-ASCII) keys */
849     { KSYM_Adiaeresis,  "XK_Adiaeresis",        "Ä" },
850     { KSYM_Odiaeresis,  "XK_Odiaeresis",        "Ö" },
851     { KSYM_Udiaeresis,  "XK_Udiaeresis",        "Ãœ" },
852     { KSYM_adiaeresis,  "XK_adiaeresis",        "ä" },
853     { KSYM_odiaeresis,  "XK_odiaeresis",        "ö" },
854     { KSYM_udiaeresis,  "XK_udiaeresis",        "ü" },
855     { KSYM_ssharp,      "XK_ssharp",            "sharp s" },
856
857     /* end-of-array identifier */
858     { 0,                NULL,                   NULL }
859   };
860
861   int i;
862
863   if (mode == TRANSLATE_KEYSYM_TO_KEYNAME)
864   {
865     static char name_buffer[30];
866     Key key = *keysym;
867
868     if (key >= KSYM_A && key <= KSYM_Z)
869       sprintf(name_buffer, "%c", 'A' + (char)(key - KSYM_A));
870     else if (key >= KSYM_a && key <= KSYM_z)
871       sprintf(name_buffer, "%c", 'a' + (char)(key - KSYM_a));
872     else if (key >= KSYM_0 && key <= KSYM_9)
873       sprintf(name_buffer, "%c", '0' + (char)(key - KSYM_0));
874     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
875       sprintf(name_buffer, "keypad %c", '0' + (char)(key - KSYM_KP_0));
876     else if (key >= KSYM_F1 && key <= KSYM_F24)
877       sprintf(name_buffer, "function F%d", (int)(key - KSYM_F1 + 1));
878     else if (key == KSYM_UNDEFINED)
879       strcpy(name_buffer, "(undefined)");
880     else
881     {
882       i = 0;
883
884       do
885       {
886         if (key == translate_key[i].key)
887         {
888           strcpy(name_buffer, translate_key[i].name);
889           break;
890         }
891       }
892       while (translate_key[++i].name);
893
894       if (!translate_key[i].name)
895         strcpy(name_buffer, "(unknown)");
896     }
897
898     *name = name_buffer;
899   }
900   else if (mode == TRANSLATE_KEYSYM_TO_X11KEYNAME)
901   {
902     static char name_buffer[30];
903     Key key = *keysym;
904
905     if (key >= KSYM_A && key <= KSYM_Z)
906       sprintf(name_buffer, "XK_%c", 'A' + (char)(key - KSYM_A));
907     else if (key >= KSYM_a && key <= KSYM_z)
908       sprintf(name_buffer, "XK_%c", 'a' + (char)(key - KSYM_a));
909     else if (key >= KSYM_0 && key <= KSYM_9)
910       sprintf(name_buffer, "XK_%c", '0' + (char)(key - KSYM_0));
911     else if (key >= KSYM_KP_0 && key <= KSYM_KP_9)
912       sprintf(name_buffer, "XK_KP_%c", '0' + (char)(key - KSYM_KP_0));
913     else if (key >= KSYM_F1 && key <= KSYM_F24)
914       sprintf(name_buffer, "XK_F%d", (int)(key - KSYM_F1 + 1));
915     else if (key == KSYM_UNDEFINED)
916       strcpy(name_buffer, "[undefined]");
917     else
918     {
919       i = 0;
920
921       do
922       {
923         if (key == translate_key[i].key)
924         {
925           strcpy(name_buffer, translate_key[i].x11name);
926           break;
927         }
928       }
929       while (translate_key[++i].x11name);
930
931       if (!translate_key[i].x11name)
932         sprintf(name_buffer, "0x%04lx", (unsigned long)key);
933     }
934
935     *x11name = name_buffer;
936   }
937   else if (mode == TRANSLATE_X11KEYNAME_TO_KEYSYM)
938   {
939     Key key = KSYM_UNDEFINED;
940     char *name_ptr = *x11name;
941
942     if (strncmp(name_ptr, "XK_", 3) == 0 && strlen(name_ptr) == 4)
943     {
944       char c = name_ptr[3];
945
946       if (c >= 'A' && c <= 'Z')
947         key = KSYM_A + (Key)(c - 'A');
948       else if (c >= 'a' && c <= 'z')
949         key = KSYM_a + (Key)(c - 'a');
950       else if (c >= '0' && c <= '9')
951         key = KSYM_0 + (Key)(c - '0');
952     }
953     else if (strncmp(name_ptr, "XK_KP_", 6) == 0 && strlen(name_ptr) == 7)
954     {
955       char c = name_ptr[6];
956
957       if (c >= '0' && c <= '9')
958         key = KSYM_0 + (Key)(c - '0');
959     }
960     else if (strncmp(name_ptr, "XK_F", 4) == 0 && strlen(name_ptr) <= 6)
961     {
962       char c1 = name_ptr[4];
963       char c2 = name_ptr[5];
964       int d = 0;
965
966       if ((c1 >= '0' && c1 <= '9') &&
967           ((c2 >= '0' && c1 <= '9') || c2 == '\0'))
968         d = atoi(&name_ptr[4]);
969
970       if (d >=1 && d <= 24)
971         key = KSYM_F1 + (Key)(d - 1);
972     }
973     else if (strncmp(name_ptr, "XK_", 3) == 0)
974     {
975       i = 0;
976
977       do
978       {
979         if (strcmp(name_ptr, translate_key[i].x11name) == 0)
980         {
981           key = translate_key[i].key;
982           break;
983         }
984       }
985       while (translate_key[++i].x11name);
986     }
987     else if (strncmp(name_ptr, "0x", 2) == 0)
988     {
989       unsigned long value = 0;
990
991       name_ptr += 2;
992
993       while (name_ptr)
994       {
995         char c = *name_ptr++;
996         int d = -1;
997
998         if (c >= '0' && c <= '9')
999           d = (int)(c - '0');
1000         else if (c >= 'a' && c <= 'f')
1001           d = (int)(c - 'a' + 10);
1002         else if (c >= 'A' && c <= 'F')
1003           d = (int)(c - 'A' + 10);
1004
1005         if (d == -1)
1006         {
1007           value = -1;
1008           break;
1009         }
1010
1011         value = value * 16 + d;
1012       }
1013
1014       if (value != -1)
1015         key = (Key)value;
1016     }
1017
1018     *keysym = key;
1019   }
1020 }
1021
1022 char *getKeyNameFromKey(Key key)
1023 {
1024   char *name;
1025
1026   translate_keyname(&key, NULL, &name, TRANSLATE_KEYSYM_TO_KEYNAME);
1027   return name;
1028 }
1029
1030 char *getX11KeyNameFromKey(Key key)
1031 {
1032   char *x11name;
1033
1034   translate_keyname(&key, &x11name, NULL, TRANSLATE_KEYSYM_TO_X11KEYNAME);
1035   return x11name;
1036 }
1037
1038 Key getKeyFromX11KeyName(char *x11name)
1039 {
1040   Key key;
1041
1042   translate_keyname(&key, &x11name, NULL, TRANSLATE_X11KEYNAME_TO_KEYSYM);
1043   return key;
1044 }
1045
1046 char getCharFromKey(Key key)
1047 {
1048   char *keyname = getKeyNameFromKey(key);
1049   char letter = 0;
1050
1051   if (strlen(keyname) == 1)
1052     letter = keyname[0];
1053   else if (strcmp(keyname, "space") == 0)
1054     letter = ' ';
1055   else if (strcmp(keyname, "circumflex") == 0)
1056     letter = '^';
1057
1058   return letter;
1059 }
1060
1061 /* ------------------------------------------------------------------------- */
1062 /* some functions to handle lists of level directories                       */
1063 /* ------------------------------------------------------------------------- */
1064
1065 struct LevelDirInfo *newLevelDirInfo()
1066 {
1067   return checked_calloc(sizeof(struct LevelDirInfo));
1068 }
1069
1070 void pushLevelDirInfo(struct LevelDirInfo **node_first,
1071                       struct LevelDirInfo *node_new)
1072 {
1073   node_new->next = *node_first;
1074   *node_first = node_new;
1075 }
1076
1077 int numLevelDirInfo(struct LevelDirInfo *node)
1078 {
1079   int num = 0;
1080
1081   while (node)
1082   {
1083     num++;
1084     node = node->next;
1085   }
1086
1087   return num;
1088 }
1089
1090 boolean validLevelSeries(struct LevelDirInfo *node)
1091 {
1092   return (node != NULL && !node->node_group && !node->parent_link);
1093 }
1094
1095 struct LevelDirInfo *getFirstValidLevelSeries(struct LevelDirInfo *node)
1096 {
1097   if (node == NULL)
1098   {
1099     if (leveldir_first)         /* start with first level directory entry */
1100       return getFirstValidLevelSeries(leveldir_first);
1101     else
1102       return NULL;
1103   }
1104   else if (node->node_group)    /* enter level group (step down into tree) */
1105     return getFirstValidLevelSeries(node->node_group);
1106   else if (node->parent_link)   /* skip start entry of level group */
1107   {
1108     if (node->next)             /* get first real level series entry */
1109       return getFirstValidLevelSeries(node->next);
1110     else                        /* leave empty level group and go on */
1111       return getFirstValidLevelSeries(node->node_parent->next);
1112   }
1113   else                          /* this seems to be a regular level series */
1114     return node;
1115 }
1116
1117 struct LevelDirInfo *getLevelDirInfoFirstGroupEntry(struct LevelDirInfo *node)
1118 {
1119   if (node == NULL)
1120     return NULL;
1121
1122   if (node->node_parent == NULL)                /* top level group */
1123     return leveldir_first;
1124   else                                          /* sub level group */
1125     return node->node_parent->node_group;
1126 }
1127
1128 int numLevelDirInfoInGroup(struct LevelDirInfo *node)
1129 {
1130   return numLevelDirInfo(getLevelDirInfoFirstGroupEntry(node));
1131 }
1132
1133 int posLevelDirInfo(struct LevelDirInfo *node)
1134 {
1135   struct LevelDirInfo *node_cmp = getLevelDirInfoFirstGroupEntry(node);
1136   int pos = 0;
1137
1138   while (node_cmp)
1139   {
1140     if (node_cmp == node)
1141       return pos;
1142
1143     pos++;
1144     node_cmp = node_cmp->next;
1145   }
1146
1147   return 0;
1148 }
1149
1150 struct LevelDirInfo *getLevelDirInfoFromPos(struct LevelDirInfo *node, int pos)
1151 {
1152   struct LevelDirInfo *node_default = node;
1153   int pos_cmp = 0;
1154
1155   while (node)
1156   {
1157     if (pos_cmp == pos)
1158       return node;
1159
1160     pos_cmp++;
1161     node = node->next;
1162   }
1163
1164   return node_default;
1165 }
1166
1167 struct LevelDirInfo *getLevelDirInfoFromFilenameExt(struct LevelDirInfo *node,
1168                                                     char *filename)
1169 {
1170   if (filename == NULL)
1171     return NULL;
1172
1173   while (node)
1174   {
1175     if (node->node_group)
1176     {
1177       struct LevelDirInfo *node_group;
1178
1179       node_group = getLevelDirInfoFromFilenameExt(node->node_group, filename);
1180
1181       if (node_group)
1182         return node_group;
1183     }
1184     else if (!node->parent_link)
1185     {
1186       if (strcmp(filename, node->filename) == 0)
1187         return node;
1188     }
1189
1190     node = node->next;
1191   }
1192
1193   return NULL;
1194 }
1195
1196 struct LevelDirInfo *getLevelDirInfoFromFilename(char *filename)
1197 {
1198   return getLevelDirInfoFromFilenameExt(leveldir_first, filename);
1199 }
1200
1201 void dumpLevelDirInfo(struct LevelDirInfo *node, int depth)
1202 {
1203   int i;
1204
1205   while (node)
1206   {
1207     for (i=0; i<depth * 3; i++)
1208       printf(" ");
1209
1210     printf("filename == '%s'\n", node->filename);
1211
1212     if (node->node_group != NULL)
1213       dumpLevelDirInfo(node->node_group, depth + 1);
1214
1215     node = node->next;
1216   }
1217 }
1218
1219 void sortLevelDirInfo(struct LevelDirInfo **node_first,
1220                       int (*compare_function)(const void *, const void *))
1221 {
1222   int num_nodes = numLevelDirInfo(*node_first);
1223   struct LevelDirInfo **sort_array;
1224   struct LevelDirInfo *node = *node_first;
1225   int i = 0;
1226
1227   if (num_nodes == 0)
1228     return;
1229
1230   /* allocate array for sorting structure pointers */
1231   sort_array = checked_calloc(num_nodes * sizeof(struct LevelDirInfo *));
1232
1233   /* writing structure pointers to sorting array */
1234   while (i < num_nodes && node)         /* double boundary check... */
1235   {
1236     sort_array[i] = node;
1237
1238     i++;
1239     node = node->next;
1240   }
1241
1242   /* sorting the structure pointers in the sorting array */
1243   qsort(sort_array, num_nodes, sizeof(struct LevelDirInfo *),
1244         compare_function);
1245
1246   /* update the linkage of list elements with the sorted node array */
1247   for (i=0; i<num_nodes - 1; i++)
1248     sort_array[i]->next = sort_array[i + 1];
1249   sort_array[num_nodes - 1]->next = NULL;
1250
1251   /* update the linkage of the main list anchor pointer */
1252   *node_first = sort_array[0];
1253
1254   free(sort_array);
1255
1256   /* now recursively sort the level group structures */
1257   node = *node_first;
1258   while (node)
1259   {
1260     if (node->node_group != NULL)
1261       sortLevelDirInfo(&node->node_group, compare_function);
1262
1263     node = node->next;
1264   }
1265 }
1266
1267 inline void swap_numbers(int *i1, int *i2)
1268 {
1269   int help = *i1;
1270
1271   *i1 = *i2;
1272   *i2 = help;
1273 }
1274
1275 inline void swap_number_pairs(int *x1, int *y1, int *x2, int *y2)
1276 {
1277   int help_x = *x1;
1278   int help_y = *y1;
1279
1280   *x1 = *x2;
1281   *x2 = help_x;
1282
1283   *y1 = *y2;
1284   *y2 = help_y;
1285 }
1286
1287
1288 /* ------------------------------------------------------------------------- */
1289 /* the following is only for debugging purpose and normally not used         */
1290 /* ------------------------------------------------------------------------- */
1291
1292 #define DEBUG_NUM_TIMESTAMPS    3
1293
1294 void debug_print_timestamp(int counter_nr, char *message)
1295 {
1296   static long counter[DEBUG_NUM_TIMESTAMPS][2];
1297
1298   if (counter_nr >= DEBUG_NUM_TIMESTAMPS)
1299     Error(ERR_EXIT, "debugging: increase DEBUG_NUM_TIMESTAMPS in misc.c");
1300
1301   counter[counter_nr][0] = Counter();
1302
1303   if (message)
1304     printf("%s %.2f seconds\n", message,
1305            (float)(counter[counter_nr][0] - counter[counter_nr][1]) / 1000);
1306
1307   counter[counter_nr][1] = Counter();
1308 }