Example #1
0
static void setitimer_intrinsic (void)
{
   SLang_Ref_Type *interval_ref = NULL, *value_ref = NULL;
   int w;
   struct itimerval new_value, old_value;
   double interval = 0.0, value;
   int argc = SLang_Num_Function_Args;

   if (SLang_peek_at_stack () == SLANG_REF_TYPE)
     {
	if (-1 == SLang_pop_ref (&value_ref))
	  return;
	argc--;
	if (SLang_peek_at_stack() == SLANG_REF_TYPE)
	  {
	     interval_ref = value_ref;
	     if (-1 == SLang_pop_ref (&value_ref))
	       goto free_and_return;
	     argc--;
	  }
     }

   switch (argc)
     {
      case 3:
	if (-1 == SLang_pop_double (&interval))
	  goto free_and_return;
	/* drop */
      case 2:
      default:
	if ((-1 == SLang_pop_double (&value))
	    || (-1 == SLang_pop_int (&w)))
	  goto free_and_return;
     }

   double_to_timeval (interval, &new_value.it_interval);
   double_to_timeval (value, &new_value.it_value);

   if (-1 == setitimer (w, &new_value, &old_value))
     {
	SLerrno_set_errno (errno);
	SLang_verror (SL_OS_Error, "setitimer failed: %s", SLerrno_strerror (errno));
	goto free_and_return;
     }

   if (value_ref != NULL)
     {
	value = timeval_to_double (&old_value.it_value);
	if (-1 == SLang_assign_to_ref (value_ref, SLANG_DOUBLE_TYPE, &value))
	  goto free_and_return;
     }
   if (interval_ref != NULL)
     {
	interval = timeval_to_double (&old_value.it_interval);
	if (-1 == SLang_assign_to_ref (interval_ref, SLANG_DOUBLE_TYPE, &interval))
	  goto free_and_return;
     }

free_and_return:
   if (value_ref != NULL)
     SLang_free_ref (value_ref);
   if (interval_ref != NULL)
     SLang_free_ref (interval_ref);
}
Example #2
0
/*
 * [ application_name local_ip local_port cname sdp_file [-t trace_file] ]
 */
int main(int argc, char **argv){

       /* create a rtp session, we will assign unique value to identify */
        err = rtp_create(&sid);
        if (err) {
                err_handle(err);
        }

        sdp_parse(sid, argv);	/* parse the sdp file */

	/* The application will sleep "rtp_start_time" time if "rtp_start_time" > 0 when the application is start. 
	 *
	 * NOTE! This is not the same with Start Time in NCTUns 
	 * EX:	if the Start Time in NCTUn is 5, and rtp_start_time is 3, 
	 * 	then the real time to startup the applicaion is 8.
	 */
        if (rtp_start_time > 0) 
                usleep( ((int) rtp_start_time * 1000000) );

        initial_session(argc, argv, sid);	/* set local receive addr, CNAME, and startup the connectoin */

        if (is_audio) 				/* media type is audio */
                delay   = (ms_pkt / 1000.);
        else 
                delay   = (1. / framerate);

	err = rtp_get_sour_rtpsocket(sid, &rtp_sockt);		/* get source rtp socket */
	if (err)
		err_handle(err);
	err = rtp_get_sour_rtcpsocket(sid, &rtcp_sockt);	/* get source rtcp socket */
	if (err)
		err_handle(err);
	
	if (rtp_sockt > nfds || rtcp_sockt > nfds) {
		if (rtp_sockt > rtcp_sockt)
			nfds = rtp_sockt;
		else
			nfds = rtcp_sockt;
	}
	
	FD_ZERO(&afds);
	FD_ZERO(&rfds);
	FD_SET(rtp_sockt, &afds);
	FD_SET(rtcp_sockt, &afds);
	
	gettimeofday(&start_tv, NULL);
	starttime = (start_tv.tv_sec + start_tv.tv_usec / 1000000.);
	now = starttime;
	
	rtp_interval = rtp_stop_time - rtp_start_time;
	
        /* bytes of each packet = ((bits/sample) / 8 ) * (clock rate) * ( each delay of packet in sec ) */
        // packet_size = (int) ( (bits_per_sample / 8.) * sampling_rate * delay);
	
	while ((now - starttime) <= rtp_interval) {
		
		memcpy(&rfds, &afds, sizeof(rfds));
		
		err = get_rtcp_timeout(sid, &timeout_tv);		/* get the send_rtcp_packet time */
		if (err) 
			err_handle(err);
		
		if (time_expire(&timeout_tv, &now_tv)) { 
				
			err = rtp_check_on_expire();			/* rtcp on_expire */
			if (err) 
				err_handle(err);
			
			err = get_rtcp_timeout(sid, &timeout_tv);	/* get the send_rtcp_packet time */
			if (err) 
				err_handle(err);
		}
			
		nexttime_tv = double_to_timeval(timeval_to_double(timeout_tv) - now);
			
		if (select(nfds + 1, &rfds, (fd_set *)0, (fd_set *)0, &nexttime_tv) < 0) {
			if (errno == EINTR)
				continue;
			else {	
				printf("nexttime_tv.tv_sec = %ld\n", nexttime_tv.tv_sec);
				printf("nexttime_tv.tv_usec = %ld\n", nexttime_tv.tv_usec);
				printf("select error: %d\n", errno);
			}
		}
		
		if (FD_ISSET(rtp_sockt, &rfds)) {
			err = on_receive(sid, rtp_sockt, recvbuf, &recbuflen);
			if (err) 
				err_handle(err);
		}
		else if (FD_ISSET(rtcp_sockt, &rfds)) {
			
			err = on_receive(sid, rtcp_sockt, recvbuf, &recbuflen);
			if (err) 
				err_handle(err);
		}
		
		gettimeofday(&now_tv, NULL);
		now = (now_tv.tv_sec + now_tv.tv_usec / 1000000.);
		
	} // while ((now - starttime) <= rtp_interval)
	
	err = rtp_close_connection(sid, reason);
	if (err) {
		err_handle(err);
	}
	
	err = rtp_delete(sid);
	if (err) {
		err_handle(err);
	}
				
	return 0;
}
Example #3
0
void serve_clients(struct server *s, struct frame_buffers *fbs, double timeout) {
	fd_set read_set, write_set;
	int select_result, sock, client_sock, highest_sock_num = max(s->sock4, s->sock6);
    struct sockaddr_storage client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    struct client *c;
    struct timeval timeout_tv;
    double now;

    while (timeout > 0) {
        FD_ZERO(&read_set);
        FD_ZERO(&write_set);

        if (s->sock4 >= 0) {
            FD_SET(s->sock4, &read_set);
        }

        if (s->sock6 >= 0) {
            FD_SET(s->sock6, &read_set);
        }

        for (sock = 0; sock < FD_SETSIZE; sock++) {
            c = s->clients[sock];
            if (c != NULL) {
                FD_SET(c->sock, &read_set);
                FD_SET(c->sock, &write_set);
                highest_sock_num = max(highest_sock_num, c->sock);
            }
        }

        double_to_timeval(timeout, &timeout_tv);
        now = gettime();
        if ((select_result = select(highest_sock_num + 1, &read_set, &write_set, NULL, &timeout_tv)) < 0) {
            serrchk("select() failed");
        }

        if (!select_result) {
            break;
        }

        for (sock = 0; sock <= highest_sock_num; sock++) {

            // Look over read_set
            if (FD_ISSET(sock, &read_set)) {
                if (sock == s->sock4 || sock == s->sock6) {
                    // Accept client
                    if ((client_sock = accept(sock, (struct sockaddr *) &client_addr, &client_addr_len)) < 0) {
                        serrchk("accept() failed");
                    }
                    
                    add_client(s, client_sock, &client_addr, fbs);
                    continue;
                }
                else {
                    c = s->clients[sock];
                    if (c != NULL) {
                        read_request(s, c, fbs);
                    }
                }
            }

            // Look over write set
            if (FD_ISSET(sock, &write_set)) {
                c = s->clients[sock];
                if (c != NULL) {
                    respond_to_client(s, c);
                }
            }

            // Prune clinets that are not communicating
            if ((c = s->clients[sock]) != NULL) {
                if (now - c->last_communication > KEEP_ALIVE_TIMEOUT) {
                    remove_client(s, c);
                }
            }

        }

        timeout -= gettime() - now;
    }
}
Example #4
0
int main(int argc, char **argv){

        /* setting commad line information */
        local_ip        = argv[1];
        local_port      = atoi(argv[2]);
        cname           = argv[3];
        proto           = udp;

	/* create a rtp session, we will assign unique value to identify */
	err = rtp_create(&sid);	
	if (err) {
		err_handle(err);
	}

	sdp_parse(sid, argv);

	if (rtp_start_time > 0) {
		usleep( ((int) rtp_start_time * 1000000) );
	}

        if ((i = initial_session(argc, argv, sid))) {
                printf("WARNING : initial_session warning = %d\n", i);
        }
        else {
                printf("initial_session is ok\n");
        }

	if (is_audio) {
		delay	= (ms_pkt / 1000.);
	}
	else {
		delay	= (1. / framerate);	// unit: ms/sec. we assume that 300 samples/frame, 
	}

	/* bytes of each packet = ((bits/sample) / 8 ) * (clock rate) * ( each delay of packet in sec ) */
	packet_size = (int) ( bits_per_sample * sampling_rate * delay / 8.);
	
	/* original_bw = min_bw = 50, so we use the same pkt size */
	minbw_pktsize = packet_size;
	
	printf("bits_per_sample = %lf, (bits_per_sample/8.) = %lf\n", bits_per_sample, (bits_per_sample/8.));
	printf("sampling_rate = %lf\n", sampling_rate);
	printf("delay = %lf\n", delay);
	printf("packet_size = %d\n", packet_size);
	fflush(stdout);
	
	/* the bandwidth wa are using, 30 = frames/sec */
	cur_bw = get_cur_bw(packet_size);
	
	err = rtp_get_sour_rtpsocket(sid, &rtp_sockt);
	if (err) {
		err_handle(err);
	}
	err = rtp_get_sour_rtcpsocket(sid, &rtcp_sockt);
	if (err) {
		err_handle(err);
	}	
	
	if (rtp_sockt > nfds || rtcp_sockt > nfds) {
		if (rtp_sockt > rtcp_sockt)
			nfds = rtp_sockt;
		else
			nfds = rtcp_sockt;
	}
	
	FD_ZERO(&afds);
	FD_ZERO(&rfds);
	FD_SET(rtp_sockt, &afds);
	FD_SET(rtcp_sockt, &afds);
	
	gettimeofday(&start_tv, NULL);
	starttime = (start_tv.tv_sec + start_tv.tv_usec / 1000000.);
	nexttime = starttime;
	
	rtp_interval = rtp_stop_time - rtp_start_time;
	
	printf("rtp_interval = %f\n", rtp_interval);
	fflush(stdout);
	
	while (rtp_interval >= 0) {
		
		memcpy(&rfds, &afds, sizeof(rfds));
		
		if (RTP_MAX_PKT_SIZE < packet_size){
			fprintf(stderr, "RTP_MAX_PKT_SIZE < reads\n");
			continue;
		}
		
		addtimestamp = ((int) packet_size * (bits_per_sample / 8.) );

		err = rtp_send(sid, marker, addtimestamp, codec_num, (int8 *)sendbuf, packet_size);
		if (err) {
			err_handle(err);
		}
		
		marker = 0;	/* not the first packet of talkspurt */
		rtp_interval -= delay;
		nexttime += delay;
		gettimeofday(&now_tv, NULL);
		now = (now_tv.tv_sec + now_tv.tv_usec / 1000000.);
		
		err = get_rtcp_timeout(sid, &timeout_tv);
		if (err) {
			err_handle(err);
		}
		
		while (now < nexttime) {	/* send next packet until now >= nexttime */
		
			//printf("now = %lf\n", now);
			//printf("nexttime = %lf\n", nexttime);
			
			if (time_expire(&timeout_tv, &now_tv)) { 
				
				err = rtp_check_on_expire();
				if (err) {
					err_handle(err);
				}
				
				err = get_rtcp_timeout(sid, &timeout_tv);
				if (err) {
					err_handle(err);
				}
				printf("timeval_to_double(timeout_tv) = %lf\n", timeval_to_double(timeout_tv));	
			}
			
			/* BECAREFUL, if we disable RTCP, the timeval we get will be 0 */
			if (timeval_to_double(timeout_tv) == 0 || nexttime < timeval_to_double(timeout_tv)) {
				nexttime_tv = double_to_timeval(nexttime - now);
			}
			else {
				nexttime_tv = double_to_timeval(timeval_to_double(timeout_tv) - now);
			}
			
			if (select(nfds + 1, &rfds, (fd_set *)0, (fd_set *)0, &nexttime_tv) < 0) {
				if (errno == EINTR)
					continue;
				else {	
					printf("nexttime_tv.tv_sec = %ld\n", nexttime_tv.tv_sec);
					printf("nexttime_tv.tv_usec = %ld\n", nexttime_tv.tv_usec);
					printf("select error: %d\n", errno);
					//exit(1);
				}
			}
			
			if (FD_ISSET(rtp_sockt, &rfds)) {

				err = on_receive(sid, rtp_sockt, recvbuf, &recbuflen);
				if (err) {
					err_handle(err);
				}	
			}
			else if (FD_ISSET(rtcp_sockt, &rfds)) {
			
				err = on_receive(sid, rtcp_sockt, recvbuf, &recbuflen);
				if (err) {
					err_handle(err);
				}
				
				adapt_bw(get_recent_recv_loosrate(sid, &ssrc));
			}
			
			gettimeofday(&now_tv, NULL);
			now = (now_tv.tv_sec + now_tv.tv_usec / 1000000.);
		} // while(now < nexttime)
	} // while (interval)
	
	err = rtp_close_connection(sid, reason);
	if (err) {
		err_handle(err);
	}
	
	err = rtp_delete(sid);
	if (err) {
		err_handle(err);
	}
			
	return 0;
}
Example #5
0
File: timer.c Project: tcava/bx2
/*
 * You call this to register a timer callback.
 *
 * The arguments:
 *  update:      This should be 1 if we're updating the specified refnum
 *  refnum_want: The refnum requested.  This should only be sepcified
 *		 by the user, functions wanting callbacks should specify
 *		 the empty string, which means "dont care".
 * The rest of the arguments are dependant upon the value of "callback"
 *	-- if "callback" is NULL then:
 *  callback:	 NULL
 *  what:	 some ircII commands to run when the timer goes off
 *  subargs:	 what to use to expand $0's, etc in the 'what' variable.
 *
 *	-- if "callback" is non-NULL then:
 *  callback:	 function to call when timer goes off
 *  what:	 argument to pass to "callback" function.  Should be some
 *		 non-auto storage, perhaps a struct or a malloced char *
 *		 array.  The caller is responsible for disposing of this
 *		 area when it is called, since the timer mechanism does not
 *		 know anything of the nature of the argument.
 * subargs:	 should be NULL, its ignored anyhow.
 */
char *add_timer (int update, const char *refnum_want, double interval, long events, int (callback) (void *), void *commands, const char *subargs, TimerDomain domain, int domref, int cancelable)
{
	Timer	*ntimer, *otimer = NULL;
	char *	refnum_got = NULL;
	Timeval right_now;
	char *	retval;

	/* XXX Eh, maybe it's a hack to check this here. */
	if (interval < 0.01 && events == -1)
	{
		say("You can't infinitely repeat a timer that runs more "
			"than 100 times a second.");
		return NULL;
	}

	right_now = get_time(NULL);

	if (update)
	{
	    if (!(otimer = get_timer(refnum_want)))
		update = 0;		/* Ok so we're not updating! */
	}

	if (update)
	{
		unlink_timer(otimer);
		ntimer = clone_timer(otimer);
		delete_timer(otimer);

		if (interval != -1)
		{
			ntimer->interval = double_to_timeval(interval);
			ntimer->time = time_add(right_now, ntimer->interval);
		}
		if (events != -2)
			ntimer->events = events;

		if (callback)
		{
			/* Delete the previous timer, if necessary */
			if (ntimer->command)
				new_free(&ntimer->command);
			if (ntimer->subargs)
				new_free(&ntimer->subargs);
			ntimer->callback = callback;
			/* Unfortunately, command is "sometimes const". */
			ntimer->callback_data = commands;
			ntimer->subargs = NULL;
		}
		else
		{
			if (ntimer->callback)
				ntimer->callback = NULL;
			malloc_strcpy(&ntimer->command, (const char *)commands);
			malloc_strcpy(&ntimer->subargs, subargs);
		}

		if (domref != -1)
			ntimer->domref = domref;
	}
	else
	{
		if (create_timer_ref(refnum_want, &refnum_got) == -1)
		{
			say("TIMER: Refnum '%s' already exists", refnum_want);
			return NULL;
		}

		ntimer = new_timer();
		ntimer->ref = refnum_got;
		ntimer->interval = double_to_timeval(interval);
		ntimer->time = time_add(right_now, ntimer->interval);
		ntimer->events = events;
		ntimer->callback = callback;
		/* Unfortunately, command is "sometimes const". */
		if (callback)
			ntimer->callback_data = commands;
		else
			malloc_strcpy(&ntimer->command, (const char *)commands);
		malloc_strcpy(&ntimer->subargs, subargs);
		ntimer->domain = domain;
		ntimer->domref = domref;
		ntimer->cancelable = cancelable;
		ntimer->fires = 0;
	}

	schedule_timer(ntimer);
	retval = ntimer->ref;
	return retval;		/* Eliminates a specious warning from gcc */
}
Example #6
0
int main(int argc, char **argv){


	/* create a rtp session, we will assign unique value to identify */
	err = rtp_create(&sid);	
	if (err) {
		err_handle(err);
	}

	sdp_parse(sid, argv);	/* parse the sdp file */

        /* The application will sleep "rtp_start_time" time if "rtp_start_time" > 0 when the application is start.
         *
         * NOTE! This is not the same with Start Time in NCTUns
         * EX:  if the Start Time in NCTUn is 5, and rtp_start_time is 3,
         *      then the real time to startup the applicaion is 8.
         */
	if (rtp_start_time > 0) {
		usleep( ((int) rtp_start_time * 1000000) );
	}

        initial_session(argc, argv, sid);	/* set local receive addr, CNAME, and startup the connectoin */

        if (!trans_mode) {      /* throughput is static */
                if (is_audio)                           /* media type is audio */
                        delay   = (ms_pkt / 1000.);
                else
                        delay   = (1. / framerate);

                /* bytes of each packet = ((bits/sample) / 8 ) * (clock rate) * ( each delay of packet in sec ) */
                packet_size = (int) ( (bits_per_sample / 8.) * sampling_rate * delay);
                if (RTP_MAX_PKT_SIZE < packet_size) {
                        fprintf(stderr, "The packet size is bigger than RTP_MAX_PKT_SIZE\n");
                        exit(1);
                }
        }

	/* original_bw = min_bw = 50, so we use the same pkt size */
	minbw_pktsize = packet_size;
	
	printf("bits_per_sample = %lf, (bits_per_sample/8.) = %lf\n", bits_per_sample, (bits_per_sample/8.));
	printf("sampling_rate = %lf\n", sampling_rate);
	printf("delay = %lf\n", delay);
	printf("packet_size = %d\n", packet_size);
	
	/* the bandwidth wa are using, 30 = frames/sec */
	cur_bw = get_cur_bw(packet_size);
	
	err = rtp_get_sour_rtpsocket(sid, &rtp_sockt);
	if (err) {
		err_handle(err);
	}
	err = rtp_get_sour_rtcpsocket(sid, &rtcp_sockt);
	if (err) {
		err_handle(err);
	}	
	
	if (rtp_sockt > nfds || rtcp_sockt > nfds) {
		if (rtp_sockt > rtcp_sockt)
			nfds = rtp_sockt;
		else
			nfds = rtcp_sockt;
	}
	
	FD_ZERO(&afds);
	FD_ZERO(&rfds);
	FD_SET(rtp_sockt, &afds);
	FD_SET(rtcp_sockt, &afds);
	
	gettimeofday(&start_tv, NULL);
	starttime = (start_tv.tv_sec + start_tv.tv_usec / 1000000.);
	nexttime = starttime;
	
	rtp_interval = rtp_stop_time - rtp_start_time;
	
	printf("rtp_interval = %f\n", rtp_interval);
	fflush(stdout);
	
	while (rtp_interval >= 0) {
		
		memcpy(&rfds, &afds, sizeof(rfds));
		
		if (RTP_MAX_PKT_SIZE < packet_size){
			fprintf(stderr, "RTP_MAX_PKT_SIZE < reads\n");
			continue;
		}
		
		addtimestamp = ((int) packet_size * (bits_per_sample / 8.) );

		err = rtp_send(sid, marker, addtimestamp, codec_num, (int8 *)sendbuf, packet_size);
		if (err) {
			err_handle(err);
		}
		
		marker = 0;	/* not the first packet of talkspurt */
		rtp_interval -= delay;
		nexttime += delay;
		gettimeofday(&now_tv, NULL);
		now = (now_tv.tv_sec + now_tv.tv_usec / 1000000.);
		
		err = get_rtcp_timeout(sid, &timeout_tv);
		if (err) {
			err_handle(err);
		}
		
		while (now < nexttime) {	/* send next packet until now >= nexttime */
		
			//printf("now = %lf\n", now);
			//printf("nexttime = %lf\n", nexttime);
			
			if (time_expire(&timeout_tv, &now_tv)) { 
				
				err = rtp_check_on_expire();
				if (err) {
					err_handle(err);
				}
				
				err = get_rtcp_timeout(sid, &timeout_tv);
				if (err) {
					err_handle(err);
				}
				printf("timeval_to_double(timeout_tv) = %lf\n", timeval_to_double(timeout_tv));	
			}
			
			/* BECAREFUL, if we disable RTCP, the timeval we get will be 0 */
			if (timeval_to_double(timeout_tv) == 0 || nexttime < timeval_to_double(timeout_tv)) {
				nexttime_tv = double_to_timeval(nexttime - now);
			}
			else {
				nexttime_tv = double_to_timeval(timeval_to_double(timeout_tv) - now);
			}
			
			if (select(nfds + 1, &rfds, (fd_set *)0, (fd_set *)0, &nexttime_tv) < 0) {
				if (errno == EINTR)
					continue;
				else {	
					printf("nexttime_tv.tv_sec = %ld\n", nexttime_tv.tv_sec);
					printf("nexttime_tv.tv_usec = %ld\n", nexttime_tv.tv_usec);
					printf("select error: %d\n", errno);
					//exit(1);
				}
			}
			
			if (FD_ISSET(rtp_sockt, &rfds)) {

				err = on_receive(sid, rtp_sockt, recvbuf, &recbuflen);
				if (err) {
					err_handle(err);
				}	
			}
			else if (FD_ISSET(rtcp_sockt, &rfds)) {
			
				err = on_receive(sid, rtcp_sockt, recvbuf, &recbuflen);
				if (err) {
					err_handle(err);
				}
				
				adapt_bw(get_recent_recv_loosrate(sid, &ssrc));
			}
			
			gettimeofday(&now_tv, NULL);
			now = (now_tv.tv_sec + now_tv.tv_usec / 1000000.);
		} // while(now < nexttime)
	} // while (interval)
	
	err = rtp_close_connection(sid, reason);
	if (err) {
		err_handle(err);
	}
	
	err = rtp_delete(sid);
	if (err) {
		err_handle(err);
	}
			
	return 0;
}
Example #7
0
/*
 * You call this to register a timer callback.
 *
 * The arguments:
 *  update:      This should be 1 if we're updating the specified refnum
 *  refnum_want: The refnum requested.  
 *		 (1) User-supplied for /TIMER timers
 *		 (2) the empty string for system timers ("dont care")
 *  interval:	 How long until the timer should fire; 
 *		 (1) for repeating timers (events != 1), the timer will 
 *		     fire with this interval.
 *		 (2) for "snap" timers, the first fire will be the next
 *		     time time() % interval == 0.
 *  events:	 The number of times this event should fire.  
 *		 (1) The value -1 means "repeat forever"
 *		 (2) Timers automatically delete after they fire the
 *		     requested number of times.
 *
 * Scenario 1: You want to run ircII commands (/TIMER timers)
 * | callback:	 NULL
 * | commands:	 some ircII commands to run when the timer goes off
 * | subargs:	 what to use to expand $0's, etc in the 'what' variable.
 *
 * Scenario 2: You want to call an internal function (system timers)
 * | callback:	 function to call when timer goes off
 * | commands:	 argument to pass to "callback" function.  Should be some
 * |		 non-auto storage, perhaps a struct or a malloced char *
 * |		 array.  The caller is responsible for disposing of this
 * |		 area when it is called, since the timer mechanism does not
 * |		 know anything of the nature of the argument.
 * | subargs:	 should be NULL, its ignored anyhow.
 *
 *  domain:	What the TIMER should bind to:
 *		(a) SERVER_TIMER  - 'domref' refers to a server refnum.
 *			Each time this timer runs, set from_server to 'domref'
 *			and set the current window to whatever 'domref's 
 *			current window is at that time.
 *		(b) WINDOW_TIME - 'domref' refers to a window refnum.
 *			Each time this timer runs, set current_window to
 *			'domref' and set the current server to whatever 
 *			'domref's server is at that time.
 *		(c) GENERAL_TIMER - 'domref' is ignored.  
 *			Do not save or restore from_server or current_window: 
 *			run in whatever the context is when it goes off.
 *  domref:	Either a server refnum, window refnum, or -1 (see 'domain')
 *  cancelable:	A "Cancelable" timer will not fire if its context cannot be
 *		restored (ie, the server it is bound to is disconnected or
 *		deleted; or the window it is bound to is deleted).  A normal
 *		(non-cancelable) timer will turn into a GENERAL_TIMER if its
 *		context cannot be restored.
 *  snap:	A "snap" timer runs every time (time() % interval == 0).
 *		This is useful for things that (eg) run at the top of every 
 *		minute (60), hour (3600), or day (86400)
 */
char *add_timer (int update, const char *refnum_want, double interval, long events, int (callback) (void *), void *commands, const char *subargs, TimerDomain domain, int domref, int cancelable, int snap)
{
	Timer	*ntimer, *otimer = NULL;
	char *	refnum_got = NULL;
	Timeval right_now;
	char *	retval;

	right_now = get_time(NULL);

	/*
	 * We do this first, because if 'interval' is invalid, we don't
	 * want to do the expensive clone/create/delete operation.
 	 * It is ineligant to check for this error here.
	 */
	if (update == 1 && interval == -1)	/* Not changing the interval */
		(void) 0;	/* XXX sigh */
	else if (interval < 0.01 && events == -1)
	{
		say("You can't infinitely repeat a timer that runs more "
			"than 100 times a second.");
		return NULL;
	}

	/* 
	 * If we say we're updating; but the timer does not exist, 
	 * then we're not updating. ;-)
	 */
	if (update)
	{
	    if (!(otimer = get_timer(refnum_want)))
		update = 0;		/* Ok so we're not updating! */
	}

	/*
	 * Arrange for an appropriate Timer to be in 'ntimer'.
	 */
	if (update)
	{
		unlink_timer(otimer);
		ntimer = clone_timer(otimer);
		delete_timer(otimer);
	}
	else
	{
		if (create_timer_ref(refnum_want, &refnum_got) == -1)
		{
			say("TIMER: Refnum '%s' already exists", refnum_want);
			return NULL;
		}

		ntimer = new_timer();
		ntimer->ref = refnum_got;
	}

	/* Update the interval */
	if (update == 1 && interval == -1)
		(void) 0;	/* XXX sigh - not updating interval */
	else
	{
		ntimer->interval = double_to_timeval(interval);
		if (snap)
		{
			double x = time_to_next_interval(interval);
			ntimer->time = time_add(right_now, double_to_timeval(x));
		}
		else
			ntimer->time = time_add(right_now, ntimer->interval);
	}

	/* Update the repeat events */
	if (update == 1 && events == -2)
		(void) 0;	/* XXX sigh - not updating events */
	else
		ntimer->events = events;


	/* Update the callback */
	if (callback)
	{
		/* Delete the previous timer, if necessary */
		if (ntimer->command)
			new_free(&ntimer->command);
		if (ntimer->subargs)
			new_free(&ntimer->subargs);
		ntimer->callback = callback;

		/* Unfortunately, command is "sometimes const". */
		ntimer->callback_data = commands;
		ntimer->subargs = NULL;
	}
	else
	{
		ntimer->callback = NULL;
		malloc_strcpy(&ntimer->command, (const char *)commands);
		malloc_strcpy(&ntimer->subargs, subargs);
	}

	/* Update the domain refnum */
	ntimer->domain = domain;
	if (update == 1 && domref == -1)
		(void) 0;	/* XXX sigh - not updating domref */
	else
		ntimer->domref = domref;

	/* Update the cancelable */
	if (update == 1 && cancelable == -1)
		(void) 0;	/* XXX sigh - not updating cancelable */
	else
		ntimer->cancelable = cancelable;


	/* Schedule up the new/updated timer! */
	schedule_timer(ntimer);
	retval = ntimer->ref;
	return retval;		/* Eliminates a specious warning from gcc */
}