Outputbox: Cannot see the output immediately

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Outputbox: Cannot see the output immediately

kyanh-2
Hi all,

I currently maintain winefish [ http://winefish.berlios.de/ ]

From winefish, some external tools (latex, pdflatex,...) will be excuted.
The output of theses tools are expected to be appear line-by-line in the
outputbox (GUI).

But when a tool is called, Winefish is suspend until the tool finishes, then
the lines of output appear (all at once, NOT line by line as desire).

I tried but didn't understand what the reason is.

Please read the code below and give me some advice.

Thank you very much.

Regards,

--
kyanh [ http://kyanh.dotgeek.org/ ]

PS:
[code]
/* $Id: outputbox.c,v 1.2 2005/07/02 08:28:49 kyanh Exp $ */

/* Winefish LaTeX Editor (based on Bluefish HTML Editor)
 * outputbox.c the output box
 *
 * Copyright (C) 2002 Olivier Sessink
 * Modified for Winefish (C) 2005 Ky Anh <[hidden email]>
 * License: GPL
 */

/* the header */

static void ob_lview_row_activated_lcb( GtkTreeView *tree, GtkTreePath *path,
GtkTreeViewColumn *column, Toutputbox *ob )
{...}

static void outputbox_close_clicked_lcb( GtkWidget *widget, Toutputbox *ob )
{...}

static sig_atomic_t child_exit_status;

static void clean_up_child_process (gint signal_number)
{
 /* Clean up the child process. */
 gint status;
 wait (&status);
 /* Store its exit status in a global variable. */
 child_exit_status = status;
}

static Toutputbox *init_outputbox( Tbfwin *bfwin )
{
 /* we call this once */
 /* Handle SIGCHLD by calling clean_up_child_process. */
 struct sigaction sigchld_action;
 memset (&sigchld_action, 0, sizeof (sigchld_action));
 sigchld_action.sa_handler = &clean_up_child_process;
 sigaction (SIGCHLD, &sigchld_action, NULL);

 /* create the backend ... */
 return ob;
}

static void outputbox_message( Toutputbox *ob, const char *string, const char
*markup )
{
 GtkTreeIter iter;
 gchar *tmpstr = g_markup_escape_text(string,-1);
 if (markup) {
  tmpstr = g_strdup_printf("&gt; <%s>%s</%s>", markup, string, markup);
 }else{
  tmpstr = g_strdup_printf("&gt; %s", string);
 }
 gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter );
 gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmpstr, -1 );
 g_free(tmpstr);

 /* TODO: Scroll as an Optional */
 /* The Outputbox may *NOT* be shown before scrolling :) */
 /* kyanh, added, 20050301 */
 GtkTreePath *treepath =
gtk_tree_model_get_path( GTK_TREE_MODEL( ob->lstore ), &iter );
 gtk_tree_view_scroll_to_cell( GTK_TREE_VIEW( ob->lview ), treepath, NULL,
FALSE /* skip align */, 0, 0 );
 gtk_tree_path_free( treepath );
}

static void fill_outputbox( Toutputbox *ob, const gchar *source )
{
 GtkTreeIter iter;
 gchar *tmp_src = NULL;
 if ( ob->def->show_all_output ) {
  tmp_src = g_markup_escape_text(source,-1);
  gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter );
  gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmp_src, -1 );
  g_free(tmp_src);
 }
}

/* kyanh, added, 20050301 */
static void free_ob( Toutputbox *ob, gboolean have_retfile )
{
 if ( have_retfile ) {
  /* free temporarily file */
  remove_secure_dir_and_filename( ob->retfile );
  DEBUG_MSG( "continue_execute: retfile=%s\n", ob->retfile );
  g_free( ob->retfile );
 }
 g_free( ob->def->pattern );
 regfree( &ob->def->preg );
 g_free( ob->def->command );
 g_free( ob->def );
 ob->def = NULL; /* to be check for next using */
 ob->pid = 0;
}

static void finish_execute( Toutputbox *ob )
{
 kill( ob->pid, SIGTERM );
 waitpid( ob->pid, &child_exit_status, WNOHANG );
 {
  gint exitcode = WEXITSTATUS( child_exit_status );
  gchar *str_status = g_strdup_printf(_("Exit code: %d"), exitcode);
  outputbox_message( ob, str_status, "b" );
  g_free( str_status );
 }

 gtk_tree_view_columns_autosize( GTK_TREE_VIEW( ob->lview ) );
 g_io_channel_unref( ob->io_channel );
 gtk_timeout_remove( ob->pollID );
 free_ob( ob, 1 );
}

/* Idea taken from SciTTEGTK.cxx */
static void continue_execute( Toutputbox *ob )
{
 gsize count = 0;
 GIOStatus io_status;
 GError *error = NULL;
 gchar *buf = NULL;
 gsize terminator_pos = 0;
 gboolean continued = TRUE;
 while ( continued ) {
  continued = FALSE;
  buf = NULL;

  io_status = g_io_channel_read_line( ob->io_channel, &buf, &count,
&terminator_pos, &error );
  switch ( io_status ) {
  case G_IO_STATUS_ERROR:
   {
    gchar * tmpstr;
    tmpstr = g_strdup_printf( _("IOChannel Error: %s"), error->message );
    outputbox_message( ob, tmpstr, "b" );
    g_free( tmpstr );
    finish_execute( ob );
   }
   break;
  case G_IO_STATUS_EOF: /* without this, we dump into an infinite loop */
   finish_execute( ob );
   break;
  case G_IO_STATUS_NORMAL:
   continued = TRUE;
   if ( terminator_pos < count ) {
    buf[ terminator_pos ] = '\0';
   }
   fill_outputbox( ob, buf );
   break;
  default:
   break;
  }
 }
 g_free( buf );
 g_clear_error( &error );
}

static void io_signal( GIOChannel *source, GIOCondition condition, Toutputbox
*ob )
{
 continue_execute( ob );
}

static int poll_tool( Toutputbox *ob )
{
 continue_execute( ob );
 return TRUE;
}

static gint xsystem( const gchar *command, const gchar *outfile )
{
 gint pid = 0;
 /* fork():
 create a child proccess the differs from the parent only in its PID and PPID;
 the resouce ultilisation are set to 0 */
 if ( ( pid = fork() ) == 0 ) {
  close( 0 );
  gint fh = open( outfile, O_WRONLY );
  close( 1 );
  dup( fh );
  close( 2 );
  dup( fh );
  DEBUG_MSG( "xsystem: running now [%s]\n", command );
  execlp( "/bin/sh", "sh", "-c", command, NULL );
  exit( 127 );
 }
 /* This is the parent process. */
 return pid;
}

static void run_command( Toutputbox *ob )
{
 file_save_cb( NULL, ob->bfwin );
 outputbox_message( ob, ob->def->command, "i" );
 {
  gchar *project_mode;
  if ( main_v->props.project_mode ) {
   project_mode = g_strdup( _("Project Mode: ON") );
  } else {
   project_mode = g_strdup( _("Project Mode: OFF") );
  }
  outputbox_message( ob, project_mode, "i" );
  g_free( project_mode );
 }
 if ( ob->bfwin->current_document->filename ) {
  /* if the user clicked cancel at file_save -> return */
  {
   gchar * tmpstring;
   if ( main_v->props.project_mode && ob->bfwin->project &&
ob->bfwin->project->basedir )
   {
    tmpstring = g_strdup( ob->bfwin->project->basedir );
   } else
   {
    tmpstring = g_path_get_dirname( ob->bfwin->current_document->filename );
   }
   /* outputbox_message(ob, g_strconcat("> Working dir: ", tmpstring, NULL));
*/
   chdir( tmpstring );
   g_free( tmpstring );
  }

  gchar *command = convert_command( ob->bfwin, ob->def->command );
  outputbox_message( ob, command, "b");

  ob->retfile = create_secure_dir_return_filename();

  gint fd = 1;
  if ( ob->retfile ) {
   fd = mkfifo( ob->retfile, S_IRUSR | S_IWUSR );
   if ( fd == 0 ) {
    ob->pid = xsystem( command, ob->retfile );
    GError *error = NULL;
    ob->io_channel = g_io_channel_new_file( ob->retfile, "r", &error );
    if ( ob->io_channel != NULL ) {
     /* Fix the BUGS[200503]#20 */
     g_io_channel_set_encoding( ob->io_channel, NULL, NULL );
     g_io_add_watch( ob->io_channel, G_IO_IN, ( GIOFunc ) io_signal, ob );
     /* add a background task in case there is no output from the tool */
     ob->pollID = g_timeout_add( 200, ( GSourceFunc ) poll_tool, ob );
    } else {
     gchar *tmpstr;
     tmpstr = g_strdup_printf( _("Error: %s"), error->message );
     outputbox_message( ob, tmpstr, "b" );
     if ( error->code == G_FILE_ERROR_INTR ) {
      outputbox_message( ob, _("Hint: You may call the tool again"), "i" );
     }
     g_free( tmpstr );
     outputbox_message( ob, _("Tool finished."), "b" );
     free_ob( ob, 1 );
    }
    g_clear_error( &error );
   }
  }
  if ( fd != 0 ) {
   outputbox_message( ob, _("Error: Cannot create PIPE."), "b" );
   free_ob( ob, 1 );
  }
  g_free( command );
 } else {
  outputbox_message( ob, _("Tool canceled."), "b" );
  free_ob( ob, 0 );
 }
}

void outputbox( Tbfwin *bfwin, gchar *pattern, gint file_subpat, gint
line_subpat, gint output_subpat, gchar *command, gboolean show_all_output )
{
 Toutputbox * ob;
 if ( bfwin->outputbox ) {
  ob = OUTPUTBOX( bfwin->outputbox );
  gtk_widget_show_all( ob->hbox ); /* fix BUGS[200503]#24 */
  setup_toggle_item_from_widget(bfwin->menubar, N_("/View/View Outputbox"),
TRUE); /* fix BUGS[200503]#25 */
 } else {
  ob = init_outputbox( bfwin );
 }
 if ( ob->pid ) { /* stop older output box */
  outputbox_message( ob, _("Tool is running. Try 'Stop' intead."), "i" );
  return;
  /*
  gchar * tmpstr;
  tmpstr = g_strdup_printf(_("Multiple calls... stopping: %s"),
ob->def->command );
  outputbox_message( ob, tmpstr, "i" );
  g_free( tmpstr );
  finish_execute( ob );
  */
 }
 gtk_list_store_clear( GTK_LIST_STORE( ob->lstore ) );

 ob->def = g_new0( Toutput_def, 1 );
 ob->def->pattern = g_strdup( pattern );
 ob->def->file_subpat = file_subpat;
 ob->def->line_subpat = line_subpat;
 ob->def->output_subpat = output_subpat;
 ob->def->show_all_output = show_all_output;
 regcomp( &ob->def->preg, ob->def->pattern, REG_EXTENDED );
 ob->def->command = g_strdup( command );
 /* kyanh */
 ob->retfile = NULL;
 ob->io_channel = NULL;
 ob->pollID = 0;
 ob->pid = 0;
 run_command( ob );
 /* gtk_widget_show_all(ob->hbox); */
}
[/code]
_______________________________________________
gtk-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-list
Reply | Threaded
Open this post in threaded view
|

Re: Outputbox: Cannot see the output immediately

kyanh-2
Let me show the way a tool is called:

* the function xsystem() will excute the tool
*  a watch is added by
[code]
    g_io_add_watch( ob->io_channel, G_IO_IN, ( GIOFunc ) io_signal, ob );
[/code]
the function 'io_signal' will read lines from channel and send to outputbox

* add a background task in case there is no output from the tool:
[code]
     ob->pollID = g_timeout_add( 200, ( GSourceFunc ) poll_tool, ob );
[/code]

Regards,

--
kyanh [ http://kyanh.dotgeek.org/ ]
_______________________________________________
gtk-list mailing list
[hidden email]
http://mail.gnome.org/mailman/listinfo/gtk-list