diff -bcrN xglk-virgin/Makefile xglk/Makefile
*** xglk-virgin/Makefile	Sat Apr 15 14:13:28 2000
--- xglk/Makefile	Sun Aug 19 10:25:33 2001
***************
*** 37,44 ****
  # definitions for SGI / Irix
  #SYSTEMFLAGS =
  
! # definitions for Linux
! #SYSTEMFLAGS =
  
  # --------------------
  
--- 37,45 ----
  # definitions for SGI / Irix
  #SYSTEMFLAGS =
  
! # definitions for Linux. _BSD_SOURCE may be necessary for struct timezone.
! # At least that is my experience with Debian and glibc 2.2.3.
! SYSTEMFLAGS = -D_BSD_SOURCE
  
  # --------------------
  
***************
*** 54,66 ****
  # directories could be just about anywhere. Sigh.
  
  # for Debian Linux
- #XINCLUDE = -I/usr/X11R6/include/X11
- #XLIB = -L/usr/X11R6/lib -lX11
- 
- # for Red Hat Linux
  XINCLUDE = -I/usr/X11R6/include/X11
  XLIB = -L/usr/X11R6/lib -lX11
  
  # for SparcStation / Solaris 
  #XINCLUDE = -I/usr/openwin/include
  #XLIB = -R/usr/openwin/lib -L/usr/openwin/lib/ -lX11 
--- 55,67 ----
  # directories could be just about anywhere. Sigh.
  
  # for Debian Linux
  XINCLUDE = -I/usr/X11R6/include/X11
  XLIB = -L/usr/X11R6/lib -lX11
  
+ # for Red Hat Linux
+ #XINCLUDE = -I/usr/X11R6/include/X11
+ #XLIB = -L/usr/X11R6/lib -lX11
+ 
  # for SparcStation / Solaris 
  #XINCLUDE = -I/usr/openwin/include
  #XLIB = -R/usr/openwin/lib -L/usr/openwin/lib/ -lX11 
***************
*** 78,99 ****
  # If there is no JPEG lib available, uncomment this line.
  # JPEGFLAG = -DNO_JPEG_AVAILABLE
  
  # --------------------
  
  # Pick a C compiler.
  #CC = cc
  CC = gcc
  
! CFLAGS = -O -ansi $(PNGFLAG) $(JPEGFLAG) $(PNGINCLUDE) $(JPEGINCLUDE) -Wall -Wmissing-prototypes -Wstrict-prototypes -Wno-unused -Wbad-function-cast $(SYSTEMFLAGS) $(XINCLUDE)
! LDFLAGS =
! LIBS = $(XLIB) $(PNGLIB) $(JPEGLIB) $(SYSTEMLIBS)
  
  OBJS = main.o xglk.o xglk_vars.o xglk_prefs.o xglk_loop.o xglk_init.o \
    xglk_scrap.o xglk_msg.o xglk_key.o xglk_weggie.o xglk_pict.o \
    xglk_res.o \
    xg_event.o xg_window.o xg_stream.o xg_fileref.o xg_style.o xg_misc.o \
    xg_gestalt.o xg_win_textbuf.o xg_win_textgrid.o xg_win_graphics.o \
!   xg_schan.o \
    gi_dispa.o gi_blorb.o
  
  all: libxglk.a Make.xglk
--- 79,106 ----
  # If there is no JPEG lib available, uncomment this line.
  # JPEGFLAG = -DNO_JPEG_AVAILABLE
  
+ # definitions for the MikMod library. Will MIKMODCFLAGS have to be included
+ # in the Make.xglk file? How?
+ MIKMODCFLAGS = \$$\(shell libmikmod-config --cflags\)
+ MIKMODLDFLAGS = \$$\(shell libmikmod-config --ldadd\)
+ MIKMODLIB = \$$\(shell libmikmod-config --libs\)
+ 
  # --------------------
  
  # Pick a C compiler.
  #CC = cc
  CC = gcc
  
! CFLAGS = -g -O -ansi $(PNGFLAG) $(JPEGFLAG) $(PNGINCLUDE) $(JPEGINCLUDE) $(MIKMODINCLUDE) -Wall -Wmissing-prototypes -Wstrict-prototypes -Wno-unused -Wbad-function-cast $(SYSTEMFLAGS) $(XINCLUDE)
! LDFLAGS = $(MIKMODLDFLAGS)
! LIBS = $(XLIB) $(PNGLIB) $(JPEGLIB) $(MIKMODLIB) $(SYSTEMLIBS)
  
  OBJS = main.o xglk.o xglk_vars.o xglk_prefs.o xglk_loop.o xglk_init.o \
    xglk_scrap.o xglk_msg.o xglk_key.o xglk_weggie.o xglk_pict.o \
    xglk_res.o \
    xg_event.o xg_window.o xg_stream.o xg_fileref.o xg_style.o xg_misc.o \
    xg_gestalt.o xg_win_textbuf.o xg_win_textgrid.o xg_win_graphics.o \
!   xg_schan.o xg_list.o\
    gi_dispa.o gi_blorb.o
  
  all: libxglk.a Make.xglk
diff -bcrN xglk-virgin/xg_event.c xglk/xg_event.c
*** xglk-virgin/xg_event.c	Sun Jun  6 11:07:45 1999
--- xglk/xg_event.c	Fri Aug 17 13:20:43 2001
***************
*** 38,42 ****
  
  void glk_tick()
  {
!   /* Nothing for us to do. */
  }
--- 38,42 ----
  
  void glk_tick()
  {
!   gli_tick_schannels();
  }
diff -bcrN xglk-virgin/xg_fileref.c xglk/xg_fileref.c
*** xglk-virgin/xg_fileref.c	Sun Aug 29 15:37:42 1999
--- xglk/xg_fileref.c	Fri Aug 17 13:20:43 2001
***************
*** 1,7 ****
--- 1,9 ----
  #include <stdlib.h>
+ #include <stdio.h>
  #include <string.h>
  #include <unistd.h>
  #include <sys/stat.h>
+ #include <string.h>
  #include "xglk.h"
  #include "xg_internal.h"
  
***************
*** 129,135 ****
    /* This is a pretty good way to do this on Unix systems. 
       I have no idea about the DOS/Windows world. */
          
!   filename = tmpnam(NULL);
      
    fref = gli_new_fileref(filename, usage, rock);
    if (!fref) {
--- 131,138 ----
    /* This is a pretty good way to do this on Unix systems. 
       I have no idea about the DOS/Windows world. */
          
!   filename = strdup("/tmp/xglk-temp-XXXXXX");
!   mkstemp(filename);
      
    fref = gli_new_fileref(filename, usage, rock);
    if (!fref) {
diff -bcrN xglk-virgin/xg_gestalt.c xglk/xg_gestalt.c
*** xglk-virgin/xg_gestalt.c	Mon Jul 10 21:04:16 2000
--- xglk/xg_gestalt.c	Fri Aug 17 13:20:43 2001
***************
*** 91,97 ****
    case gestalt_SoundVolume:
    case gestalt_SoundNotify: 
    case gestalt_SoundMusic:
!     return FALSE;
  
    case gestalt_Hyperlinks: {
      return TRUE;
--- 91,97 ----
    case gestalt_SoundVolume:
    case gestalt_SoundNotify: 
    case gestalt_SoundMusic:
!     return TRUE;
  
    case gestalt_Hyperlinks: {
      return TRUE;
diff -bcrN xglk-virgin/xg_internal.h xglk/xg_internal.h
*** xglk-virgin/xg_internal.h	Wed Apr 12 19:51:23 2000
--- xglk/xg_internal.h	Sun Aug 19 16:14:35 2001
***************
*** 2,7 ****
--- 2,13 ----
  #define _XG_INTERNAL_H
  
  #include <X11/X.h>
+ 
+ #ifdef GLK_MODULE_SOUND
+ #include <mikmod.h>
+ #include "xg_list.h"
+ #endif
+ 
  #include "gi_dispa.h"
  
  /* --- General declarations --- */
***************
*** 12,17 ****
--- 18,24 ----
  typedef struct glk_window_struct window_t;
  typedef struct glk_stream_struct stream_t;
  typedef struct glk_fileref_struct fileref_t;
+ typedef struct glk_schannel_struct channel_t;
  typedef struct stylehints_struct stylehints_t;
  
  extern int gli_special_typable_table[keycode_MAXVAL+1];
***************
*** 131,136 ****
--- 138,164 ----
  
  extern int init_gli_filerefs(void);
  
+ /* --- Sound channels --- */
+ 
+ struct glk_schannel_struct {
+   glui32 rock;
+ 
+   MODULE *module;
+   glui32 snd;
+   glui32 vol;
+   glui32 notify;
+ 
+   gidispatch_rock_t disprock;
+   channel_t *chain_next, *chain_prev;
+ };
+ 
+ extern int init_gli_schannels(void);
+ extern void exit_gli_schannels(void);
+ extern void gli_tick_schannels(void);
+ extern Bool gli_eventloop_schannels(void);
+ extern Bool gli_eventloop_snddone(void);
+ extern void gli_snd_reaper(void);
+ 
  /* --- Styles --- */
  
  typedef struct styleval_struct {
***************
*** 155,160 ****
--- 183,200 ----
  extern void gli_styles_compute(fontset_t *font, stylehints_t *hints);
  
  /* --- Events --- */
+ 
+ /* This is the granularity at which we check for timer events; measured
+    in microseconds. 1/20 second sounds good, but if we have sound support
+    this may be too long. The documentation is a bit vague, but does say
+    that we never need to do more than a few hundred updates per second.
+    Based on the MikMod example programs, I've decided to set it at 1/100
+    seconds. */
+ #ifdef GLK_MODULE_SOUND
+ #define TICKLENGTH (10000)
+ #else
+ #define TICKLENGTH (50000)
+ #endif
  
  extern void eventloop_setevent(glui32 type, window_t *win, 
    glui32 val1, glui32 val2); 
diff -bcrN xglk-virgin/xg_list.c xglk/xg_list.c
*** xglk-virgin/xg_list.c	Wed Dec 31 18:00:00 1969
--- xglk/xg_list.c	Wed Aug 22 13:58:49 2001
***************
*** 0 ****
--- 1,47 ----
+ #include "xg_list.h"
+ #include <stdlib.h>
+ 
+ snd_list *snd_list_push(snd_list *list, snd_data *x) {
+   snd_list *p;
+   p = malloc(sizeof(snd_list));
+   if (p == NULL) return NULL;
+   p->data = x;
+   p->next = list;
+   p->prev = NULL;
+   return p;
+ }
+ 
+ snd_list *snd_list_new(snd_data *x) {
+   return snd_list_push(NULL, x);
+ }
+ 
+ snd_list *snd_list_pop(snd_list *list, snd_data **x) {
+   if (list) {
+     snd_list *head = list->next;
+     if (x)
+       *x = list->data;
+     if (list->prev) {
+       list->prev->next = head;
+     }
+     if (head) {
+       head->prev = list->prev;
+     }
+     free(list);
+     return head;
+   } else 
+     return NULL;
+ }
+ 
+ void snd_list_free(snd_list **list) {
+   snd_list *rest;
+ 
+   if (list == NULL)
+     return;
+   for ( ; *list; *list = rest) {
+     rest = (*list)->next;
+     if ((*list)->prev) {
+       (*list)->prev = rest;
+     }
+     free (*list);
+   }
+ }
diff -bcrN xglk-virgin/xg_list.h xglk/xg_list.h
*** xglk-virgin/xg_list.h	Wed Dec 31 18:00:00 1969
--- xglk/xg_list.h	Sun Aug 19 16:19:06 2001
***************
*** 0 ****
--- 1,29 ----
+ #ifndef _XG_LIST_T
+ #define _XG_LIST_T
+ #include "glk.h"
+ #include "sys/types.h"
+ 
+ typedef struct snd_data snd_data;
+ struct snd_data {
+   glui32     snd;
+   schanid_t  channel;
+   pid_t      pid;
+   char       *filename;
+   char       notify;
+ };  
+ 
+ typedef struct snd_list snd_list;
+ struct snd_list {
+   snd_list  *next;
+   snd_list  *prev;
+   snd_data  *data;
+ };
+ 
+ extern snd_list          *snd_list_push(snd_list *list, snd_data *data);
+ extern snd_list          *snd_list_pop(snd_list *list, snd_data **data);
+ extern snd_list          *snd_list_new(snd_data *x);
+ extern void         snd_list_free(snd_list **list);
+ 
+ #endif /* _XG_LIST_T */
+ 
+ 
diff -bcrN xglk-virgin/xg_misc.c xglk/xg_misc.c
*** xglk-virgin/xg_misc.c	Sun Jun  6 11:07:45 1999
--- xglk/xg_misc.c	Wed Aug 22 13:54:09 2001
***************
*** 107,112 ****
--- 107,113 ----
  {
    event_t ev;
  
+   exit_gli_schannels();
    xmsg_getchar("Hit any key to exit.");
  
    exit(1);
***************
*** 123,128 ****
--- 124,131 ----
      (*func)();
    }
  
+   exit_gli_schannels();
+ 
    exit(1);
  }
  
***************
*** 176,181 ****
--- 179,186 ----
      return ((stream_t *)obj)->disprock;
    case gidisp_Class_Fileref:
      return ((fileref_t *)obj)->disprock;
+   case gidisp_Class_Schannel:
+     return ((channel_t *)obj)->disprock;
    default: {
        gidispatch_rock_t dummy;
        dummy.num = 0;
diff -bcrN xglk-virgin/xg_schan.c xglk/xg_schan.c
*** xglk-virgin/xg_schan.c	Wed Jul 21 17:49:01 1999
--- xglk/xg_schan.c	Wed Aug 22 14:05:16 2001
***************
*** 1,61 ****
  #include <stdlib.h>
  #include <string.h>
  #include "xglk.h"
  #include "xg_internal.h"
  
- /* The whole sound-channel situation is very simple for us;
-    we don't support it. */
- 
  #ifdef GLK_MODULE_SOUND
  
! schanid_t glk_schannel_create(glui32 rock)
  {
    return NULL;
  }
  
! void glk_schannel_destroy(schanid_t chan)
  {
  }
  
! schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr)
  {
!   if (rockptr)
!     *rockptr = 0;
    return NULL;
  }
  
! glui32 glk_schannel_get_rock(schanid_t chan)
  {
    gli_strict_warning("schannel_get_rock: invalid id.");
    return 0;
  }
  
  glui32 glk_schannel_play(schanid_t chan, glui32 snd)
  {
!   gli_strict_warning("schannel_play: invalid id.");
!   return 0;
  }
  
  glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
      glui32 notify)
  {
    gli_strict_warning("schannel_play_ext: invalid id.");
    return 0;
  }
  
  void glk_schannel_stop(schanid_t chan)
  {
    gli_strict_warning("schannel_stop: invalid id.");
  }
  
  void glk_schannel_set_volume(schanid_t chan, glui32 vol)
  {
    gli_strict_warning("schannel_set_volume: invalid id.");
  }
  
  void glk_sound_load_hint(glui32 snd, glui32 flag)
  {
!   gli_strict_warning("schannel_sound_load_hint: invalid id.");
  }
  
  #endif /* GLK_MODULE_SOUND */
--- 1,600 ----
+ #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
+ #include <unistd.h>
+ #include <sys/time.h>
+ #include <sys/types.h>
+ #include <wait.h>
+ #include <signal.h>
+ #include <limits.h>
  #include "xglk.h"
  #include "xg_internal.h"
  
  #ifdef GLK_MODULE_SOUND
  
! #define DEBUG_MODULE_SOUND 1
! 
! /* TODO: The code assumes in several places that only the MOD player will
!          be active. Obviously this has to change when we add the ability to
!          play samples. */
! 
! /* Partial sound support, courtesy of MikMod (http://www.mikmod.org) */
! 
! #include "gi_blorb.h"
! 
! #define giblorb_ID_MOD      (giblorb_make_id('M', 'O', 'D', ' '))
! #define giblorb_ID_FORM     (giblorb_make_id('F', 'O', 'R', 'M'))
! #define EXECSTR_SIZE        100
! #define TNAM_SIZE           30
! 
! extern int kill(pid_t,int);
! 
! static channel_t *gli_channellist = NULL;
! static struct timeval last_update = { 0, 0 };
! 
! static snd_list *snd_notify_list = NULL;
! static snd_list *snd_file_map = NULL;
! static snd_list *snd_playing = NULL;
! 
! int init_gli_schannels()
! {
! #if DEBUG_MODULE_SOUND
!   char *buf;
! #endif
! 
!   gli_channellist = NULL;
! 
!   MikMod_RegisterAllDrivers();
!   MikMod_RegisterAllLoaders();
!   if (MikMod_Init("")) {
!     fprintf(stderr, "Could not initialize sound, reason: %s\n",
!       MikMod_strerror(MikMod_errno));
!     return FALSE;
!   }
! 
! #if DEBUG_MODULE_SOUND
!   printf("MikMod version %ld.%ld.%ld\n",
!     LIBMIKMOD_VERSION_MAJOR,
!     LIBMIKMOD_VERSION_MINOR,
!     LIBMIKMOD_REVISION);
!   buf = MikMod_InfoDriver();
!   if (buf) {
!     printf("Available drivers:\n%s\n", buf);
!     free(buf);
!   }
!   printf("Using: %s\n", md_driver->Name);
! #endif
! 
!   return TRUE;
! }
! 
! void exit_gli_schannels()
! {
!   snd_data *map;
! 
!   MikMod_Exit();
! 
!   while (snd_playing) {
!     snd_playing=snd_list_pop(snd_playing,&map);
!     /* No notification for interrupted sound */
!     map->notify = 0;
!     kill(map->pid,SIGTERM);
!   }
! 
!   while (snd_file_map) {
!     snd_file_map = snd_list_pop(snd_file_map,&map);
!     unlink(map->filename);
!   }
! 
!   /* Memory will be reclaimed on process exit anyway */
! }
! 
! #if DEBUG_MODULE_SOUND
! static long last_message_sec = 0;
! static long tick_count = 0;
! static long update_count = 0;
! static long pot_update_count = 0;
! #endif
! 
! void gli_tick_schannels()
  {
+   struct timeval curtime;
+   struct timezone tz;
+   long difftime;
+ 
+   /* We need to call MikMod_Update() often enough, or sound will be choppy.
+ 
+      Damn, this is much harder than I thought. I want there to be a nice and
+      steady stream of potential updates, and it works fine most of the time.
+ 
+      The scrolling map in the Zeta Space demo is a problem. Here I will get
+      large number of ticks, meaning that the VM is under heavier-than-usual
+      load, but relatively few potential updates.
+ 
+      Despite several rewrites, I have not been able to do anything about
+      this. By the time it is discovered that too much time has passed since
+      the last update, it's already too late to compensate for it.
+ 
+      I guess we have a bottleneck somewhere in the graphics handling. Perhaps
+      it just isn't possible to get this right without a separate thread... */
+ 
+   gettimeofday(&curtime, &tz);
+ 
+ #if DEBUG_MODULE_SOUND
+   tick_count++;
+ #endif
+ 
+   if (curtime.tv_sec - last_update.tv_sec <= 1) {
+     if (curtime.tv_sec == last_update.tv_sec)
+       difftime = curtime.tv_usec - last_update.tv_usec;
+     else
+       difftime = curtime.tv_usec + (1000000 - last_update.tv_usec);
+   } else
+     difftime = TICKLENGTH;
+ 
+   if (difftime >= (9 * TICKLENGTH) / 10) {
+     if (Player_Active()) {
+       MikMod_Update();
+ #if DEBUG_MODULE_SOUND
+       update_count++;
+ #endif
+     }
+ 
+     last_update.tv_sec = curtime.tv_sec;
+     last_update.tv_usec = curtime.tv_usec;
+ #if DEBUG_MODULE_SOUND
+     pot_update_count++;
+ #endif
+   }
+ 
+ #if DEBUG_MODULE_SOUND
+   if (curtime.tv_sec - last_message_sec >= 20) {
+     fprintf(stderr,
+       "%ld.%06ld: %ld ticks, %ld (%ld) updates\n",
+       curtime.tv_sec, curtime.tv_usec, tick_count, pot_update_count,
+       update_count);
+     tick_count = 0;
+     update_count = 0;
+     pot_update_count = 0;
+     last_message_sec = curtime.tv_sec;
+   }
+ #endif
+ }
+ 
+ /* It is assumed that this function is only called from the event loop.
+    Break the assumption if you like, but don't blame me if you lose sound
+    events... */
+ 
+ Bool gli_eventloop_schannels()
+ {
+   channel_t *chan = gli_channellist;
+ 
+   gli_tick_schannels();
+ 
+   if (Player_Active())
+     return FALSE;
+ 
+   while (chan) {
+     if (chan->module) {
+       Player_Free(chan->module);
+       chan->module = NULL;
+       if (chan->notify != 0) {
+         eventloop_setevent(evtype_SoundNotify, NULL, chan->snd, chan->notify);
+         chan->notify = 0;
+         return TRUE;
+       }
+     }
+     chan = chan->chain_next;
+   }
+ 
+   return FALSE;
+ }
+ 
+ Bool gli_eventloop_snddone()
+ {
+   snd_data *map;
+ 
+   /* Called from xglk_event_loop when sound process exits */
+   if (snd_notify_list == NULL)
+     return FALSE;
+ 
+   /*
+   if (map == NULL) {
+     gli_strict_warning("gli_eventloop_snddone: could not allocate map");
+     return FALSE;
+   }
+   */
+ 
+   while(snd_notify_list) {
+     snd_notify_list = snd_list_pop(snd_notify_list,&map);
+     if (map->notify)
+       eventloop_setevent(evtype_SoundNotify, NULL, map->snd, 1);
+   }
+   return TRUE;
+ }
+ 
+ void gli_snd_reaper()
+ {
+   int status;
+   snd_data *map;
+   snd_list *list;
+   pid_t pid = 0;
+ 
+   /*
+   map = malloc(sizeof(struct snd_data));
+   if (!map) {
+     gli_strict_warning("sli_snd_reaper: map allocation failed!");
+     return;
+   }
+   */
+   /* Called from xglk_event_loop to check for finished sound procs */
+   list = snd_playing;
+   while (list) {
+     pid = list->data->pid;
+     if (waitpid(pid,&status,WNOHANG) == pid) {
+ 
+       if (list->prev) {
+ 	list->prev->next = list->next;
+       }
+       if (list->next) {
+ 	list->next->prev = list->prev;
+       }
+       if (list->data->notify)
+ 	snd_notify_list = snd_list_push(snd_notify_list,list->data);
+       
+     } 
+     list = list->next;
+   }
+ }
+ 
+ channel_t *glk_schannel_create(glui32 rock)
+ {
+   channel_t *chan = (channel_t *)malloc(sizeof(channel_t));
+ 
+   if (!chan)
      return NULL;
+ 
+   chan->rock = rock;
+   chan->module = NULL;
+   chan->vol = 0x10000;
+ 
+   chan->chain_prev = NULL;
+   chan->chain_next = gli_channellist;
+   gli_channellist = chan;
+   if (chan->chain_next) {
+     chan->chain_next->chain_prev = chan;
+   }
+ 
+   if (gli_register_obj)
+     chan->disprock = (*gli_register_obj)(chan, gidisp_Class_Schannel);
+   else
+     chan->disprock.ptr = NULL;
+ 
+   return chan;
  }
  
! void glk_schannel_destroy(channel_t *chan)
  {
+   channel_t *prev, *next;
+ 
+   if (!chan) {
+     gli_strict_warning("schannel_destroy: invalid id.");
+     return;
+   }
+ 
+   if (gli_unregister_obj)
+     (*gli_unregister_obj)(chan, gidisp_Class_Schannel, chan->disprock);
+ 
+   if (chan->module) {
+     Player_Free(chan->module);
+     chan->module = NULL;
+   }
+ 
+   prev = chan->chain_prev;
+   next = chan->chain_next;
+   chan->chain_prev = NULL;
+   chan->chain_next = NULL;
+ 
+   if (prev)
+     prev->chain_next = next;
+   else
+     gli_channellist = next;
+   if (next)
+     next->chain_prev = prev;
+ 
+   free(chan);
  }
  
! channel_t *glk_schannel_iterate(channel_t *chan, glui32 *rock)
  {
!   if (!chan) {
!     chan = gli_channellist;
!   } else {
!     chan = chan->chain_next;
!   }
! 
!   if (chan) {
!     if (rock)
!       *rock = chan->rock;
!     return chan;
!   }
! 
!   if (rock)
!     *rock = 0;
    return NULL;
  }
  
! glui32 glk_schannel_get_rock(channel_t *chan)
  {
+   if (!chan) {
      gli_strict_warning("schannel_get_rock: invalid id.");
      return 0;
+   }
+   return chan->rock;
  }
  
  glui32 glk_schannel_play(schanid_t chan, glui32 snd)
  {
!   /* Error messages will be slightly wrong, but I'm lazy... */
!   return glk_schannel_play_ext(chan, snd, 1, 0);
  }
  
  glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
      glui32 notify)
  {
+   FILE *fl;
+   FILE *t;
+   char *buffer;
+   int i,j;
+   char *execstr; 
+   char *tnam = NULL;
+   pid_t pid;
+   size_t recs_read;
+   long pos,len;
+   event_t ev;
+   glui32 chunktype;
+   snd_list *list;
+   snd_data *filemap;
+   snd_data *playmap;
+ 
+   if (!chan) {
      gli_strict_warning("schannel_play_ext: invalid id.");
      return 0;
+   }
+ 
+   /* Another channel is already playing? */
+   if (!chan->module && Player_Active()) {
+     gli_strict_warning("schannel_play_ext: player can only handle one MOD at a time");
+     return 0;
+   }
+ 
+   /* TODO: Add picture_find()-style reading and caching, but that's beyond
+            the scope of this implementation. */
+   if (!xres_is_resource_map()) {
+     gli_strict_warning("schannel_play_ext: no resource map.");
+     return 0;
+   }
+ 
+   xres_get_resource(giblorb_ID_Snd, snd, &fl, &pos, &len, &chunktype);
+ 
+   if (!fl) {
+     gli_strict_warning("schannel_play_ext: internal error - no file pointer.");
+     return 0;
+   }
+ 
+   /* MikMod can play samples. This may or may not include AIFF, but at
+      present I only have test data for MOD. I'm not even going to try and
+      implement SONG... */
+ 
+   if (chunktype == giblorb_ID_FORM) { /* It's an AIFF */
+     i = 0;
+     list = snd_file_map;
+     while (list) {
+       if (list->data->snd == snd) {
+ 	i = 1;
+ 	tnam = list->data->filename;
+ 	break;
+       }
+       list = list->next;
+     }
+     if (!i) {
+       tnam = calloc(1,TNAM_SIZE);
+       if (tnam == NULL) {
+ 	gli_strict_warning("schannel_play_ext: allocation of file name failed");
+ 	return 0;
+       }
+       i = snprintf(tnam,TNAM_SIZE,"/tmp/snd_%ld_XXXXXX",snd);
+       if ((i < 0 ) || (i > TNAM_SIZE)) {
+ 	gli_strict_warning("schannel_play_ext: temp file name not created.");
+ 	return 0;
+       }
+       if (mkstemp(tnam) == -1)  {
+ 	gli_strict_warning("schannel_play_ext: temp file creation failed.");
+ 	free(tnam);
+ 	return 0;
+       }
+       fseek(fl,pos,0); /* Position at start of resource */
+       buffer=calloc(1,(size_t) len);
+       if (buffer == NULL) {
+ 	gli_strict_warning("schannel_play_ext: buffer allocation failed.");
+ 	unlink(tnam);
+ 	free(tnam);
+ 	return 0;
+       }
+       recs_read = fread(buffer,(size_t) len,1,fl);
+       if (recs_read < 1) {
+ 	gli_strict_warning("schannel_play_ext: read failed.");
+ 	unlink(tnam);
+ 	free(tnam);
+ 	free(buffer);
+ 	return 0;
+       }
+       t = fopen(tnam,"w");
+       if (t == NULL) {
+ 	gli_strict_warning("schannel_play_ext: open of temp file failed");
+ 	unlink(tnam);
+ 	free(tnam);
+ 	free(buffer);
+ 	return 0;
+       }
+       recs_read = fwrite(buffer,(size_t) len,1,t); /* copy to temp file */
+       if (recs_read < 1) {
+ 	gli_strict_warning("schannel_play_ext: write failed");
+ 	free(buffer);
+ 	unlink(tnam);
+ 	free(tnam);
+ 	return 0;
+       }      
+       filemap = malloc(sizeof(snd_data));
+       if (!filemap) {
+ 	gli_strict_warning("schannel_play_ext: filemap struct malloc() failed.");
+ 	return 0;
+       }
+       filemap->snd = snd;
+       filemap->notify = notify;
+       filemap->filename = tnam;
+       filemap->channel = chan;
+       filemap->pid = 0;
+       snd_file_map = snd_list_push(snd_file_map,filemap); 
+       /* Put new mapping on file map list */
+       fflush(t);
+       free(buffer);
+     }
+     execstr = calloc(1,EXECSTR_SIZE);
+     if (execstr == NULL) {
+       gli_strict_warning("schannel_play_ext: allocation of exec string failed.");
+       unlink(tnam);
+       free(tnam);
+       return 0;
+     }
+     if (getenv("GLK_SND_PLAYER")) {
+       execstr=strncpy(execstr,getenv("GLK_SND_PLAYER"),strlen(getenv("GLK_SND_PLAYER")));
+     } else {
+       execstr = strncpy(execstr,"/usr/bin/play -t aiff",strlen("/usr/bin/play -t aiff"));
+     }
+     execstr = strncat(execstr," ",1);
+     execstr = strncat(execstr,tnam,strlen(tnam));
+     glk_schannel_stop(chan); /* Wrong, but compatible with WinGlk */
+     pid = fork();
+     /* Here's the theory: this fork() lets us run asynchronously.  In turn,
+        the child process spawns potentially many sequential processes to play
+        the sound; this is done with system() and a loop counter.  But we only
+        use the pid of the first child process for the purposes of tracking
+        pids in the linked list, and that process only returns when the repeat
+        is done */
+     if (pid == -1) {
+       gli_strict_warning("schannel_play_ext: fork failed");
+       free(tnam);
+       free(execstr);
+       return 0;
+     }
+     if (pid == 0) { 
+       if (repeats == -1)
+ 	repeats = INT_MAX; 
+       for (i = 0; i < repeats; i++) {
+ 	if (repeats == INT_MAX)
+ 	  i = 0;  /* Yes, we cannot repeat exactly INT_MAX times */
+ 	j = system(execstr);
+ 	if (j) {
+ 	  gli_strict_warning("schannel_play_ext: external player call failed");
+ 	  exit(j);
+ 	}
+       }
+       exit(0); /* Done with sound, let reaper clean up after us */
+     }
+ 
+     /* Parent process */
+   
+     playmap = malloc(sizeof(struct snd_data));
+     if (!playmap) {
+       gli_strict_warning("schannel_play_ext: playmap malloc failed");
+       return 0;
+     }
+ 
+     playmap->channel = chan;
+     playmap->snd = snd;
+     playmap->notify = notify;
+     playmap->pid = pid;
+     playmap->filename = tnam;
+ 
+     snd_playing = snd_list_push(snd_playing,playmap);
+ 
+     free(execstr);
+     return 1;
+   }
+ 
+   if (chunktype != giblorb_ID_MOD) {
+     gli_strict_warning("schannel_play_ext: unsupported sound format");
+     return 0;
+   }
+ 
+   if (chan->module) {
+     Player_Free(chan->module);
+     chan->module = NULL;
+   }
+ 
+   if (repeats != 0) {
+     fseek(fl, pos, 0);
+     chan->module = Player_LoadFP(fl, 64, 0);
+     chan->snd = snd;
+     chan->notify = notify;
+     if (!chan->module) {
+       gli_strict_warning("schannel_play_ext: module loader failure");
+       return 0;
+     }
+ 
+     /* For now, only handle infinite looping. Laziness strikes again... */
+     if (repeats == -1)
+       chan->module->wrap = 1;
+ 
+     Player_Start(chan->module);
+     Player_SetVolume(chan->vol / 512);
+   }
+ 
+   return 1;
  }  
  
  void glk_schannel_stop(schanid_t chan)
  {
+   snd_list *list;
+ 
+   if (!chan) {
      gli_strict_warning("schannel_stop: invalid id.");
+     return;
+   }
+ 
+   if (chan->module) {
+     Player_Free(chan->module);
+     chan->module = NULL;
+   }
+ 
+   list=snd_playing;
+   while (list) {
+     if (list->data->channel == chan) {
+       list->data->notify = 0; /* No notify for killed
+ 				  sounds */
+        kill(list->data->pid,SIGTERM);
+     }
+     list = list->next;
+   }
+ 
  }
  
  void glk_schannel_set_volume(schanid_t chan, glui32 vol)
  {
+   if (!chan) {
      gli_strict_warning("schannel_set_volume: invalid id.");
+     return;
+   }
+ 
+   chan->vol = vol;
+   if (chan->module)
+     Player_SetVolume(chan->vol / 512);
  }
  
  void glk_sound_load_hint(glui32 snd, glui32 flag)
  {
!   /* I doubt this will make any difference, so make it a no-op for now. */
  }
  
  #endif /* GLK_MODULE_SOUND */
diff -bcrN xglk-virgin/xglk.c xglk/xglk.c
*** xglk-virgin/xglk.c	Wed Apr 12 22:00:50 2000
--- xglk/xglk.c	Fri Aug 17 13:20:43 2001
***************
*** 58,63 ****
--- 58,65 ----
      return FALSE;
    if (!init_gli_filerefs())
      return FALSE;
+   if (!init_gli_schannels())
+     return FALSE;
    if (!init_gli_windows())
      return FALSE;
  
diff -bcrN xglk-virgin/xglk_loop.c xglk/xglk_loop.c
*** xglk-virgin/xglk_loop.c	Sun Jun  6 11:07:45 1999
--- xglk/xglk_loop.c	Fri Aug 17 18:27:59 2001
***************
*** 1,4 ****
--- 1,7 ----
  #include <sys/time.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
+ #include <unistd.h>
  #include <stdlib.h>
  #include <X11/keysym.h>
  #include <X11/Xatom.h>
***************
*** 7,30 ****
  #include "xglk.h"
  #include "xg_internal.h"
  
- /* This is the granularity at which we check for timer events; measured
-    in microseconds. 1/20 second sounds good. */
- #define TICKLENGTH (50000)
- 
  static event_t *eventloop_event = NULL;
  static struct timeval lasttime = {0, 0};
  
  void xglk_event_poll(event_t *ev, glui32 millisec)
  {
    struct timeval tv, curtime, outtime;
-   struct timezone tz;
    /* just check for a timer event, nothing else. */
    
    eventloop_event = ev;
  
    if (millisec) {
      if (lasttime.tv_sec == 0)
!       gettimeofday(&lasttime, &tz);
      outtime.tv_sec = lasttime.tv_sec + (millisec/1000);
      outtime.tv_usec = lasttime.tv_usec + ((millisec%1000)*1000);
      if (outtime.tv_usec >= 1000000) {
--- 10,28 ----
  #include "xglk.h"
  #include "xg_internal.h"
  
  static event_t *eventloop_event = NULL;
  static struct timeval lasttime = {0, 0};
  
  void xglk_event_poll(event_t *ev, glui32 millisec)
  {
    struct timeval tv, curtime, outtime;
    /* just check for a timer event, nothing else. */
    
    eventloop_event = ev;
  
    if (millisec) {
      if (lasttime.tv_sec == 0)
!       gettimeofday(&lasttime, NULL);
      outtime.tv_sec = lasttime.tv_sec + (millisec/1000);
      outtime.tv_usec = lasttime.tv_usec + ((millisec%1000)*1000);
      if (outtime.tv_usec >= 1000000) {
***************
*** 34,40 ****
    }
  
    if (millisec) {
!     gettimeofday(&curtime, &tz);
      if (curtime.tv_sec > outtime.tv_sec 
        || (curtime.tv_sec == outtime.tv_sec 
  	&& curtime.tv_usec > outtime.tv_usec)) {
--- 32,38 ----
    }
  
    if (millisec) {
!     gettimeofday(&curtime, NULL);
      if (curtime.tv_sec > outtime.tv_sec 
        || (curtime.tv_sec == outtime.tv_sec 
  	&& curtime.tv_usec > outtime.tv_usec)) {
***************
*** 61,74 ****
    int eventp;
    int firsttime = TRUE;
    struct timeval tv, curtime, outtime;
-   struct timezone tz;
  
    eventloop_event = ev;
    xglk_perform_click(mouse_Reset, NULL, 0, 0);
  
    if (millisec) {
      if (lasttime.tv_sec == 0)
!       gettimeofday(&lasttime, &tz);
      outtime.tv_sec = lasttime.tv_sec + (millisec/1000);
      outtime.tv_usec = lasttime.tv_usec + ((millisec%1000)*1000);
      if (outtime.tv_usec >= 1000000) {
--- 59,71 ----
    int eventp;
    int firsttime = TRUE;
    struct timeval tv, curtime, outtime;
  
    eventloop_event = ev;
    xglk_perform_click(mouse_Reset, NULL, 0, 0);
  
    if (millisec) {
      if (lasttime.tv_sec == 0)
!       gettimeofday(&lasttime, NULL);
      outtime.tv_sec = lasttime.tv_sec + (millisec/1000);
      outtime.tv_usec = lasttime.tv_usec + ((millisec%1000)*1000);
      if (outtime.tv_usec >= 1000000) {
***************
*** 77,90 ****
      }
    }
  
    while (ev->type == evtype_None) {
  
      if (xio_any_invalid) {
        xglk_redraw();
      }
  
      if (millisec && !firsttime) {
!       gettimeofday(&curtime, &tz);
        if (curtime.tv_sec > outtime.tv_sec 
  	|| (curtime.tv_sec == outtime.tv_sec 
  	  && curtime.tv_usec > outtime.tv_usec)) {
--- 74,96 ----
      }
    }
  
+   gli_snd_reaper();
+ 
+ 
    while (ev->type == evtype_None) {
  
+     if (gli_eventloop_schannels())
+       continue;
+ 
+     if (gli_eventloop_snddone())
+       continue;
+ 
      if (xio_any_invalid) {
        xglk_redraw();
      }
  
      if (millisec && !firsttime) {
!       gettimeofday(&curtime, NULL);
        if (curtime.tv_sec > outtime.tv_sec 
  	|| (curtime.tv_sec == outtime.tv_sec 
  	  && curtime.tv_usec > outtime.tv_usec)) {
diff -bcrN xglk-virgin/xglk_msg.c xglk/xglk_msg.c
*** xglk-virgin/xglk_msg.c	Wed Apr 12 20:31:54 2000
--- xglk/xglk_msg.c	Fri Aug 17 13:20:43 2001
***************
*** 25,32 ****
  
  int init_xmsg()
  {
-   struct timezone tz;
- 
    message_size = 80;
    message = (char *)malloc(message_size * sizeof(char));
    if (!message)
--- 25,30 ----
***************
*** 36,42 ****
      LIBRARYNAME, LIBRARYVERSION);
    messagelen = strlen(message);
    messagesticky = FALSE;
!   gettimeofday(&messagetime, &tz);
    messagetime.tv_sec += TIMEOUT;
  
    return TRUE;
--- 34,40 ----
      LIBRARYNAME, LIBRARYVERSION);
    messagelen = strlen(message);
    messagesticky = FALSE;
!   gettimeofday(&messagetime, NULL);
    messagetime.tv_sec += TIMEOUT;
  
    return TRUE;
***************
*** 104,111 ****
  
  void xmsg_set_message(char *str, int sticky)
  {
-   struct timezone tz;
-    
    if (!message)
      return;
    if (!str)
--- 102,107 ----
***************
*** 121,139 ****
    xmsg_redraw();
  
    messagesticky = sticky;
!   gettimeofday(&messagetime, &tz);
    messagetime.tv_sec += TIMEOUT;
  }
  
  void xmsg_check_timeout()
  {
-   struct timezone tz;
    struct timeval tv;
  
    if (messagesticky)
      return;
  
!   gettimeofday(&tv, &tz);
    if (tv.tv_sec > messagetime.tv_sec
      || (tv.tv_sec == messagetime.tv_sec && tv.tv_usec > messagetime.tv_usec)) {
      xmsg_set_message(NULL, TRUE);
--- 117,134 ----
    xmsg_redraw();
  
    messagesticky = sticky;
!   gettimeofday(&messagetime, NULL);
    messagetime.tv_sec += TIMEOUT;
  }
  
  void xmsg_check_timeout()
  {
    struct timeval tv;
  
    if (messagesticky)
      return;
  
!   gettimeofday(&tv, NULL);
    if (tv.tv_sec > messagetime.tv_sec
      || (tv.tv_sec == messagetime.tv_sec && tv.tv_usec > messagetime.tv_usec)) {
      xmsg_set_message(NULL, TRUE);
diff -bcrN xglk-virgin/xglk_pict.c xglk/xglk_pict.c
*** xglk-virgin/xglk_pict.c	Thu Aug 26 13:42:12 1999
--- xglk/xglk_pict.c	Fri Aug 17 13:20:50 2001
***************
*** 305,311 ****
    channels = cinfo.output_components;
  
    destdepth = xiodepth;
!   destrowbytes = (width * destdepth + 7) / 8;
    if (destrowbytes & 31)
      destrowbytes = (destrowbytes | 31) + 1;
    destdata = malloc(destrowbytes * height);
--- 305,311 ----
    channels = cinfo.output_components;
  
    destdepth = xiodepth;
!   destrowbytes = (width * ((destdepth==24)?32:destdepth) + 7) / 8;
    if (destrowbytes & 31)
      destrowbytes = (destrowbytes | 31) + 1;
    destdata = malloc(destrowbytes * height);
***************
*** 406,412 ****
    channels = png_get_channels(png_ptr, info_ptr);
  
    destdepth = xiodepth;
!   destrowbytes = (width * destdepth + 7) / 8;
    if (destrowbytes & 31)
      destrowbytes = (destrowbytes | 31) + 1;
    destdata = malloc(destrowbytes * height);
--- 406,412 ----
    channels = png_get_channels(png_ptr, info_ptr);
  
    destdepth = xiodepth;
!   destrowbytes = (width * ((destdepth==24)?32:destdepth) + 7) / 8;
    if (destrowbytes & 31)
      destrowbytes = (destrowbytes | 31) + 1;
    destdata = malloc(destrowbytes * height);
***************
*** 712,718 ****
  	  destptr[gsb] = srcptr[0];
  	  destptr[bsb] = srcptr[0];
  	  srcptr += channels;
! 	  destptr += 3;
  	}
  	srcrowptr += srcrowbytes;
  	destrowptr += destrowbytes;
--- 712,718 ----
  	  destptr[gsb] = srcptr[0];
  	  destptr[bsb] = srcptr[0];
  	  srcptr += channels;
! 	  destptr += 4;
  	}
  	srcrowptr += srcrowbytes;
  	destrowptr += destrowbytes;
***************
*** 727,733 ****
  	  destptr[gsb] = srcptr[1];
  	  destptr[bsb] = srcptr[2];
  	  srcptr += channels;
! 	  destptr += 3;
  	}
  	srcrowptr += srcrowbytes;
  	destrowptr += destrowbytes;
--- 727,733 ----
  	  destptr[gsb] = srcptr[1];
  	  destptr[bsb] = srcptr[2];
  	  srcptr += channels;
! 	  destptr += 4;
  	}
  	srcrowptr += srcrowbytes;
  	destrowptr += destrowbytes;
***************
*** 896,902 ****
    int destiptr, srciptr;
    int *rowmap;
  
!   destrowbytes = (destwidth * depth + 7) / 8;
    if (destrowbytes & 31)
      destrowbytes = (destrowbytes | 31) + 1;
    destdata = (unsigned char *)malloc(destrowbytes * destheight);
--- 896,902 ----
    int destiptr, srciptr;
    int *rowmap;
  
!   destrowbytes = (destwidth * ((depth==24)?32:depth) + 7) / 8;
    if (destrowbytes & 31)
      destrowbytes = (destrowbytes | 31) + 1;
    destdata = (unsigned char *)malloc(destrowbytes * destheight);
***************
*** 938,944 ****
        destiptr += 2;
      }
    }
!   else if (depth == 32) {
      for (ix=0; ix<destwidth; ix++) { 
        rowmap[destiptr] = srciptr;
        rowmap[destiptr+1] = srciptr+1;
--- 938,944 ----
        destiptr += 2;
      }
    }
!   else if ((depth == 32) || (depth == 24)) {
      for (ix=0; ix<destwidth; ix++) { 
        rowmap[destiptr] = srciptr;
        rowmap[destiptr+1] = srciptr+1;
***************
*** 983,989 ****
  
      for (jx=0; jx<destheight; jx++) {
  
!       int val = destwidth * depth / 8;
        for (ix=0; ix<val; ix++) {
  	destrow[ix] = srcrow[rowmap[ix]];
        }
--- 983,989 ----
  
      for (jx=0; jx<destheight; jx++) {
  
!       int val = destwidth * ((depth==24)?32:depth) / 8;
        for (ix=0; ix<val; ix++) {
  	destrow[ix] = srcrow[rowmap[ix]];
        }
