Beispiel #1
0
static void destroy_stream(MyStream *v)
{
  pthread_mutex_destroy(&v->data_mutex);
  if (has_input(v)) {
    pthread_mutex_destroy(&v->in_mutex);
    pthread_cond_destroy(&v->in_cond);
  }
  if (has_output(v)) {
    pthread_mutex_destroy(&v->out_mutex);
    pthread_cond_destroy(&v->out_cond);
  }
  if (has_input(v)) fini_buf(&v->in_buf, &v->in_data);
  if (has_output(v)) fini_buf(&v->out_buf, &v->out_data);
  if (v->prev) v->prev->next = v->next;
  if (v->next) v->next->prev = v->prev;
  if (v == current) current = v->next;
}
static int manage_calls(char *host)
{
	newtComponent form;
	newtComponent quit;
	newtComponent hangup;
	newtComponent redirect;
	newtComponent channels;
	struct newtExitStruct es;
	char tmp[80];

	/* If there's one thing you learn from this code, it is this...
	   Never, ever fly Air France.  Their customer service is absolutely
	   the worst.  I've never heard the words "That's not my problem" as 
	   many times as I have from their staff -- It should, without doubt
	   be their corporate motto if it isn't already.  Don't bother giving 
	   them business because you're just a pain in their side and they
	   will be sure to let you know the first time you speak to them.
	   
	   If you ever want to make me happy just tell me that you, too, will
	   never fly Air France again either (in spite of their excellent
	   cuisine). */
	snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host);
	newtCenteredWindow(74, 20, tmp);
	form = newtForm(NULL, NULL, 0);
	newtFormWatchFd(form, session.fd, NEWT_FD_READ);
	newtFormSetTimer(form, 100);
	quit = newtButton(62, 16, "Quit");
	redirect = newtButton(35, 16, "Redirect");
	hangup = newtButton(50, 16, "Hangup");
	channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL);
	newtFormAddComponents(form, channels, redirect, hangup, quit, NULL);
	newtListboxSetWidth(channels, 72);
	
	show_doing("Getting Status", "Retrieving system status...");
	try_status();
	hide_doing();

	for(;;) {
		newtFormRun(form, &es);
		if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) {
			if (input_check(&session, NULL)) {
				show_message("Disconnected", "Disconnected from remote host");
				break;
			}
		} else if (es.reason == NEWT_EXIT_COMPONENT) {
			if (es.u.co == quit)
				break;
			if (es.u.co == hangup) {
				try_hangup(channels);
			} else if (es.u.co == redirect) {
				try_redirect(channels);
			}
		}
		rebuild_channels(channels);
	}
	newtFormDestroy(form);
	return 0;
}
Beispiel #3
0
pure_expr *audio_stream_info(MyStream *v, PaStream *as)
{
  int in_info[] = {v->in, v->in_channels, v->in_format, v->in_bps},
    out_info[] = {v->out, v->out_channels, v->out_format, v->out_bps};
  return pure_tuplel
    (4, pure_double(v->sample_rate),
     pure_int(v->size),
     matrix_from_int_array(1, has_input(v)?4:0, in_info),
     matrix_from_int_array(1, has_output(v)?4:0, out_info));
}
Beispiel #4
0
std::vector<DelayEdge> RecurrentNN::inputsToNode(long n, std::vector<DelayEdge> edgeSet)
{
    std::vector<DelayEdge> found;
    
    has_input pred = has_input(n);
    
    std::vector<DelayEdge>::iterator it = std::find_if(edgeSet.begin(), edgeSet.end(), pred);
    for (; it != edgeSet.end(); it = std::find_if(++it, edgeSet.end(), pred)) {
        found.push_back(*it);
    }
    return found;
}
Beispiel #5
0
static void fini_stream(MyStream *v, bool abort)
{
  if (v->as) {
    if (abort)
      Pa_AbortStream(v->as);
    else
      Pa_StopStream(v->as);
    pthread_cleanup_push(unlock_stream, (void*)v);
    lock_stream(v);
    Pa_CloseStream(v->as);
    v->as = NULL;
    /* wake up threads waiting to read or write the stream */
    if (has_input(v)) pthread_cond_broadcast(&v->in_cond);
    if (has_output(v)) pthread_cond_broadcast(&v->out_cond);
    pthread_cleanup_pop(1);
  }
}
void piped_process::OnTerminate( int pid, int status )
{
    wxLogDebug( "Entering piped_process::OnTerminate. Showing rest of stdout/stderr" );
    // Go into a endless loop, showing the stderr/stdout, while has_input remains true
    while ( has_input() ) {}    
    
    // Mark this process as no longer running 
    // (needed in progress_dialog::execute_command_redirect_to_listbox() )
    m_process_data->is_running = FALSE;
    
    // Tell the parent progress dialog too that there is nothing running 
    // (used by kill_most_recent_process()
    // TODO-GURU: I think this should go before the OnTerminate, so that don't try 
    // to terminate it twice.
    m_parent->set_is_process_running( FALSE );
    
    m_process_data->exit_code = status;
    wxProcess::OnTerminate( pid, status );
}
Beispiel #7
0
int read_audio_stream_double(MyStream *v, PaStream *as, double *buf, long size)
{
  if (!has_input(v)) return -1;
  if (size < 0) return -1;
  if (size == 0) return 0;
  if (v->in_format != paFloat32)
    return -1;
  else {
    /* Read into a temporary buffer. */
    float *m = malloc(size*v->in_bpf);
    int ret = read_audio_stream(v, as, m, size);
    long i;
    if (ret <= 0) {
      free(m); return ret;
    }
    /* Convert to double. */
    size = ret*v->in_channels;
    for (i = 0; i < size; i++) buf[i] = m[i];
    free(m);
    return ret;
  }
}
Beispiel #8
0
int read_audio_stream_int(MyStream *v, PaStream *as, int *buf, long size)
{
  if (!has_input(v)) return -1;
  if (size < 0) return -1;
  if (size == 0) return 0;
  if (v->in_format == paInt32) /* immediate */
    return read_audio_stream(v, as, buf, size);
  else {
    /* Read into a temporary buffer. */
    void *p = malloc(size*v->in_bpf);
    int ret = read_audio_stream(v, as, p, size);
    long i;
    if (ret <= 0) {
      free(p); return ret;
    }
    /* Convert to int. */
    size = ret*v->in_channels;
    switch (v->in_format) {
    case paInt16: {
      short *m = (short*)p;
      for (i = 0; i < size; i++) buf[i] = m[i];
    }
    case paInt8: {
      char *m = (char*)p;
      for (i = 0; i < size; i++) buf[i] = m[i];
    }
    case paUInt8: {
      unsigned char *m = (unsigned char*)p;
      for (i = 0; i < size; i++) buf[i] = (unsigned)m[i];
    }
    case paInt24: /* TODO */
    default:
      /* Unsupported format. */
      ret = -1; break;
    }
    free(p);
    return ret;
  }
}
Beispiel #9
0
/** Check for available input on any console.
 *
 * @v None		-
 * @ret True		Input is available on a console
 * @ret False		Input is not available on any console
 * @err None		-
 *
 * All enabled console devices are checked once for available input
 * using each device's console_driver::iskey() method.  If any console
 * device has input available, this call will return True.  If this
 * call returns True, you can then safely call getchar() without
 * blocking.
 *
 */
int iskey ( void ) {
	return has_input() ? 1 : 0;
}
Beispiel #10
0
int main (const int argc, const char** argv)
{
	GF_Err e;
	Bool run;

	/* location of the configuration file: 0 wait for config on a socket, 1 use the given file */
	u32 config_flag;	
	char config_file_name[MAX_BUF];

	int dest_port;
	unsigned short tcp_port = 0;
	/* Should be fine on WIFI network */
	unsigned short mtu_size = 1492;
	int debug = 0;
	TCP_Input *tcp_conf = NULL;
	GF_Thread *tcp_thread;
	GF_Err th_err_tcp;

	GF_Err th_err_rap;
	RAP_Input *rap_conf;
	GF_Thread *rap_thread;

	CONF_Data *conf;	
	GF_Config *gf_config_file;
	GF_Err res;
	
	GF_Socket *UDP_feedback_socket;
	u32 socketType_for_updates;
	
	PNC_CallbackData * data;
	GF_RTPChannel * chan;
	GF_RTPHeader hdr;
	u32 timer = -1;
	
	GF_Mutex *carrousel_mutex;	
	char sdp_fmt[5000];
	tcp_thread = NULL;
	
	/* init gpac lib */
	gf_sys_init();
	gf_log_set_level(GF_LOG_ERROR);
	gf_log_set_tools(GF_LOG_NETWORK|GF_LOG_RTP|GF_LOG_SCENE|GF_LOG_PARSER|GF_LOG_AUTHOR|GF_LOG_CODING|GF_LOG_SCRIPT);
	
	GF_SAFEALLOC(conf, CONF_Data);
		
	tcp_port = config_flag = 0;
	socketType_for_updates = GF_SOCK_TYPE_UDP;
	if (command_line_parsing(argc, argv, &tcp_port, config_file_name, (int *) &config_flag, &mtu_size, &debug, &socketType_for_updates)){
		print_usage();
		return -1;
	}
	setDebugMode( debug );
	gf_config_file = NULL;
	if (config_flag == 1)
	{
		char *cfg_path;
		char *cfg_fname;
		char *tmp;
		
		cfg_fname = config_file_name;
		cfg_path = config_file_name;
		tmp = strrchr(cfg_fname, GF_PATH_SEPARATOR);
		if (tmp) {
			cfg_fname = tmp+1;
			tmp[0] = 0;
		} else {
			cfg_path = ".";
		}
		gf_config_file = gf_cfg_new(cfg_path, cfg_fname);	
		if (!gf_config_file) {
			fprintf(stderr, "Cannot open config file %s\n", config_file_name);
			return -1;
		} else {
			dprintf(DEBUG_broadcaster, "Using config file %s.\n", config_file_name);
		}
		if (parse_config(gf_config_file, conf, debug)) return -1;
		tcp_port = atoi(conf->config_input_port);
	}
	else
	{
		GF_SAFEALLOC(tcp_conf, TCP_Input);
		tcp_conf->config_flag = &config_flag;
		tcp_conf->RAPtimer = &timer;
		tcp_conf->port = tcp_port;
		tcp_conf->config = conf;
		tcp_thread = gf_th_new("TCPInterface");

		/* Starting the thread which will write the received config in a temporary file */
		th_err_tcp = gf_th_run(tcp_thread, tcp_server, tcp_conf);
		
		fprintf(stdout, "Waiting for configuration on port %d...\n", tcp_conf->port);

		while(config_flag == 0) { 
			gf_sleep(1000); 
		}
		fprintf(stdout, "Configuration File received. Starting Streaming ...\n");
	}
	
	timer = atoi(conf->rap_timer);
	dest_port = atoi(conf->dest_port);
	res = PNC_InitRTP(&chan, (char *)conf->dest_ip, dest_port, mtu_size);
 	if (res != 0) {
		fprintf(stderr, "Cannot initialize RTP output (error: %d)\n", res); 
		exit(1);
	} 

	carrousel_mutex = gf_mx_new("Carrousel");
	data = PNC_Init_SceneGenerator(chan, &hdr, (char *) conf->scene_init_file,
								   socketType_for_updates, (u16) atoi(conf->modif_input_port), debug); 
	if (!data) {
		fprintf(stderr, "Cannot initialize Scene Generator\n"); 
		exit(1);
	}
	data->carrousel_mutex = carrousel_mutex;
	data->RAPsent = 1;
	
	UDP_feedback_socket = gf_sk_new(GF_SOCK_TYPE_UDP);
	e = gf_sk_bind(UDP_feedback_socket, NULL, (u16)atoi(conf->feedback_port), (char*)conf->feedback_ip, (u16)atoi(conf->feedback_port), 0);
	if (e) {
		fprintf(stderr, "Cannot bind socket for bitrate feedback information (%s)\n", gf_error_to_string(e));
	} else {
		e = gf_sk_set_block_mode(UDP_feedback_socket, 1);
		if (e) {
			fprintf(stderr, "Cannot set feedback socket block mode (%s)\n", gf_error_to_string(e));
		}
	}
	data->feedback_socket = UDP_feedback_socket;

	PNC_InitPacketiser(data, sdp_fmt, mtu_size); 
	PNC_SendInitScene(data);

	GF_SAFEALLOC(rap_conf, RAP_Input);
	rap_conf->RAPtimer = &timer;
	rap_conf->carrousel_mutex = carrousel_mutex;
	rap_conf->data = data;
	rap_thread = gf_th_new("RAPGenerator");
	th_err_rap = gf_th_run(rap_thread, RAP_send, rap_conf);

	sdp_generator(data, (char *)conf->dest_ip, sdp_fmt);
	
	run = 1;
	while (run)
	{
		GF_Err e = PNC_processBIFSGenerator(data); 
		if (e) {
			fprintf(stderr, "Cannot Process BIFS data (%s)\n", gf_error_to_string(e));
			break;
		}

		if (has_input()) {
			char c = get_a_char();
			switch (c) {
			case 'q':
				run = 0;
				break;
			}
		}
		gf_sleep(10);
	}

	/* waiting for termination of the RAP thread */
	rap_conf->status = 0;
	while (rap_conf->status != 2)
		gf_sleep(0);
	gf_free(rap_conf);
	gf_th_del(rap_thread);

	/* waiting for termination of the TCP listening thread */
	if (tcp_conf) {
		tcp_conf->status = 0;
		while (tcp_conf->status != 2)
			gf_sleep(0);
		gf_free(tcp_conf);
		gf_th_del(tcp_thread);
	}

	PNC_Close_SceneGenerator(data);
	
	gf_free(conf);
	
	if (gf_config_file)
		gf_cfg_del(gf_config_file);

	gf_mx_del(carrousel_mutex);
	gf_sys_close();
	return 0;
}
Beispiel #11
0
void TMoveStatic::disp(SDL_Surface *sf, TFont *font, int x, int y, int w, int h,
  int myfg, int mybg, int xoptions) {
    int fg = myfg, bg = mybg;
    char buff[256];
    /* Copy the string to allow to modify it without problem */
    strcpy(buff,menu->label);
    char *s = buff;
    char *old = s;
    int old_min = min_font_size;
    min_font_size = 1;
    font->set_utf(is_utf);
    int white = mymakecol(255,255,255);
    // All the translations are taken from http://home.comcast.net/~plotor/command.html
    while (*s) {
	if (*s != '_' && *s != '^' && *s != '@') {
	    s++;
	    continue;
	}
	char pre = *s;
	if (s > old) {
	    // Eventually display what's before
	    *s = 0;
	    int w,h;
	    font->dimensions(old,&w,&h);
	    font->surf_string(sf,x,y,old,fg,bg,w);
	    x += w;
	    *s = pre;
	}
	s++;
	font->dimensions("mp",&w,&h);
	int col = 0;
	char str[4];
	TFont *f0 = NULL;
	str[0] = *s;
	str[1] = 0;
	TSketcher *d = new TSketcher(sf,x,y,w,h,10,9);
	if (pre == '_') {
	    switch(*s) {
	    case 'A':
	    case 'a':
	    case 'S':
	    case '5':	col = mymakecol(255,64,64); break;
	    case 'B':
	    case 'b':	col = mymakecol(255,238,0); break;
	    case 'C':
	    case 'c':	col = mymakecol(0,255,64); break;
	    case 'D':
	    case 'd':	col = mymakecol(0,170,255); break;
	    case 'P':
	    case 'e':	col = mymakecol(255,0,170); break;
	    case 'K':
	    case 'Z':
	    case 'f':	col = mymakecol(170,0,255); break;
	    case 'g': col = mymakecol(0,255,204); break;
	    case 'i': col = mymakecol(255,160,0); break;
	    case 'G': col = mymakecol(0,170,255); break;
	    case 'H':
	    case 'h': col = mymakecol(255,0,255); break;
	    case 'j': col = mymakecol(190,190,190); break;
	    }
	    if (*s >= 'a' && *s <= 'j')
		sprintf(str,"%d",*s-'a'+1);
	    else if (*s == 'L')
		sprintf(str,">>"); // too many drawings !!!
	    else if (*s == 'M')
		sprintf(str,"<<");
	    else if (*s == 'X')
		sprintf(str,"TAP");
	    else if (*s == '^')
		sprintf(str,"AIR");
	    else if (*s == '?')
		sprintf(str,"DIR");
	    else if (*s == 'S')
		sprintf(str,"St"); // tss...
	    else if (*s == '.')
		sprintf(str,"...");

	    d->set_filled_poly(1);

	} else if (pre == '^') {
	    d->set_filled_poly(0);
	    switch (*s) {
	    case 'S':
	    case 'E': col = mymakecol(255,238,0); break;
	    case 's':
	    case 'F': col = mymakecol(255,160,0); break;
	    case 'G':	col = mymakecol(255,64,64); break;
	    case 'H': col = mymakecol(190,190,190); break;
	    case 'I': col = mymakecol(0,255,204); break;
	    case 'J':	col = mymakecol(0,170,255); break;
	    case 'T':	col = mymakecol(170,0,255); break;
	    case 'W':
	    case 'U':	col = mymakecol(255,0,170); break;
	    case 'V':	col = mymakecol(170,0,255); break;
	    }
	    if (*s >= 'E' && *s <= 'J') {
		if (has_input(KB_DEF_P1_B6)) {
		    // Street fighter games
		    char *keys[] = { "lp","mp","sp","lk","mk","sk" };
		    sprintf(str,"%s",keys[*s-'E']);
		} else
		    sprintf(str,"b%d",*s-'E'+1); // button n for other games
	    } else if (*s == 'T')
		sprintf(str,"3K");
	    else if (*s == 'U')
		sprintf(str,"3P");
	    else if (*s == 'V')
		sprintf(str,"2K");
	    else if (*s == 'W')
		sprintf(str,"2P");
	    else if (*s == 'S')
		sprintf(str,"SE"); // ?!
	    else if (*s == 'M')
		sprintf(str,"MAX");
	} else if (pre == '@') {
	    if (!strncmp(s,"W-button",8)) {
		sprintf(str,"W");
		col = mymakecol(255,238,0);
		s += 7;
	    }
	}

	// I finally keep a constant base width to have all the circles of the
	// same size...
	// font->dimensions(str,&w,&h);
	// w += 2; // some small margin
	if (col)
	    filledEllipseColor(sf, x+w/2, y+h/2, w/2,h/2, col);

	if (strlen(str) > 2) {
	    // Try to find a font size which fits in this space !
	    f0 = font;
	    int h = f0->get_font_height()/2;
	    do {
		font = new TFont_ttf(h,"VeraMono.ttf");
		if (h <= 3) break;
		int w0,h0;
		font->dimensions(str,&w0,&h0);
		if (w0 > w) {
		    h--;
		    delete font;
		    font = NULL;
		}
	    } while (!font);
	}
	// The coordinates below are supposed to be on & 10x9 matrix, except
	// that the picture I am using has clearly been resized and so it's
	// only an approximation...

	// For the arrows they are rotated and mirrored, so I do the rotation/
	// mirror instead of risking more errors with more coordinates...
	Sint16 kx[13] = {1,3,6,4,4,7,7,9,9,7,4,2,2};
	Sint16 ky[13] = {3,1,3,3,5,5,3,3,5,7,7,5,3};
	Sint16 mkx[13],mky[13];
	mirror(13,kx,mkx);
	mirror(13,ky,mky);

	Sint16 ox[10] = {1,3,3,5,5,7,7,5,3,3};
	Sint16 oy[10] = {6,4,5,5,1,1,5,7,7,8};
	Sint16 mox[10],moy[10];
	mirror(10,ox,mox);
	mirror(10,oy,moy);

	Sint16 wx[16] = {3,1,1,3,6,8,8,10,7,5,7,5,3,3,5,4};
	Sint16 wy[16] = {8,6,3,1,1,3,5,5, 7,5,5,3,3,6,7,8},mwx[16],mwy[16];
	mirror(16,wx,mwx);
	mirror(16,wy,mwy);

	if (pre == '@') {
	    // Very special case, W Button, 1 letter.
	    font->surf_string(sf,x+w/4,y,str,(col ? 0 : fg),bg,w);
	    goto end_loop;
	}

	if (*s == '1')
	    d->poly(white,
		    6,1,
		    2,6,
		    1,5,
		    1,8,
		    4,8,
		    3,7,
		    8,2,
		    -1,-1);
	else if (*s == '2')
	    d->poly(white,
		    2,1,
		    2,6,
		    1,6,
		    3,8,
		    5,6,
		    4,6,
		    4,1,
		    -1,-1);
	else if (*s == '3')
	    d->poly(white,
		    0,2,
		    5,8,
		    4,9,
		    8,9,
		    8,5,
		    7,6,
		    3,1,
		    -1,-1);
	else if (*s == '4')
	    d->poly(white,
		    3,2,
		    0,5,
		    3,7,
		    3,6,
		    9,6,
		    9,4,
		    3,4,
		    -1,-1);
	else if (*s == '6')
	    d->poly(white,
		    1,4,
		    6,4,
		    6,2,
		    9,5,
		    6,7,
		    6,6,
		    1,6,
		    -1,-1);
	else if (*s == '7')
	    d->poly(white,
		    1,1,
		    1,5,
		    2,4,
		    8,9,
		    9,7,
		    4,2,
		    5,1,
		    -1,-1);
	else if (*s == '8')
	    d->poly(white,
		    2,8,
		    2,3,
		    1,3,
		    3,1,
		    5,3,
		    4,3,
		    4,8,
		    -1,-1);
	else if (*s == '9')
	    d->poly(white,
		    8,1,
		    8,5,
		    7,4,
		    1,9,
		    0,7,
		    5,2,
		    4,1,
		    -1,-1);
	else if (*s == 'k')
	    d->polytab(13,kx,ky,white);
	else if (*s == 'm') // horizontal mirror of k (10-x)
	    d->polytab(13,mkx,ky,white);
	else if (*s == 'l') // vertical mirror of m (8-y)
	    d->polytab(13,mkx,mky,white);
	else if (*s == 'n') // horizontal mirror of l (10-x)
	    d->polytab(13,kx,mky,white);
	else if (*s == 'o')
	    d->polytab(10,ox,oy,white);
	else if (*s == 'p')
	    d->polytab(10,moy,ox,white);
	else if (*s == 'q')
	    d->polytab(10,mox,moy,white);
	else if (*s == 'r')
	    d->polytab(10,oy,mox,white);
	else if (*s == 's' && pre == '_')
	    d->polytab(10,mox,oy,white);
	else if (*s == 't')
	    d->polytab(10,oy,ox,white);
	else if (*s == 'u')
	    d->polytab(10,ox,moy,white);
	else if (*s == 'v')
	    d->polytab(10,moy,mox,white);
	else if (*s == 'w')
	    d->polytab(16,wx,wy,white);
	else if (*s == 'x')
	    d->polytab(16,mwx,mwy,white);
	else if (*s == 'y')
	    d->polytab(16,wx,mwy,white);
	else if (*s == 'z')
	    d->polytab(16,mwx,wy,white);
	else if (*s == 'Q')
	    d->poly(white,
		    1,1,
		    1,3,
		    3,3,
		    3,4,
		    1,6,
		    7,6,
		    7,7,
		    9,5,
		    7,3,
		    7,4,
		    4,4,
		    4,3,
		    6,1,
		    -1,-1);
	else if (*s == 'R') // horiz mirror of Q (10-x)
	    d->poly(white,
		    9,1,
		    9,3,
		    7,3,
		    7,4,
		    9,6,
		    3,6,
		    3,7,
		    1,5,
		    3,3,
		    3,4,
		    6,4,
		    6,3,
		    4,1,
		    -1,-1);
	else if (*s == '-' && pre == '_') { // not used for any raine game afaik
	    d->lineC(3,1,7,1,white);
	    d->lineC(3,5,5,3,white);
	    d->lineC(5,3,7,5,white);
	    d->lineC(5,3,5,7,white);
	} else if (*s == '-' && pre == '^') {
	    d->lineC(1,4,6,4,white);
	    d->lineC(6,4,4,2,white);
	    d->lineC(6,4,4,6,white);
	    d->lineC(8,2,8,6,white);
	} else if (*s == '=' && pre == '^') {
	    d->lineC(1,4,6,4,white);
	    d->lineC(1,4,3,2,white);
	    d->lineC(1,4,3,6,white);
	    d->lineC(8,2,8,6,white);
	} else if (*s == '`' && pre == '_') {
	    filledCircleColor(sf, x+w/2, y+h/2, w/10, white);
	} else if (str[1] == 0) {
	    int ws,hs;
	    font->dimensions(str,&ws,&hs);
	    font->surf_string(sf,x+(w-ws)/2,y,str,(col ? 0 : fg),bg,w);
	} else {
	    int ws,hs;
	    font->dimensions(str,&ws,&hs);
	    font->surf_string(sf,x+(w-ws)/2,y,str,(col ? 0 : fg),bg,w);
	}
	if (f0) {
	    delete font;
	    font = f0;
	}
	delete d;

end_loop:
	s++;
	old = s;
	x += w;
    }

    if (*old)
	font->surf_string(sf,x,y,old,fg,bg,w);
    min_font_size = old_min;
}