Logo Search packages:      
Sourcecode: vertex version File versions

proccon.c

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include "../include/fio.h"
#include "../include/prochandle.h"
#include "guiutils.h"
#include "proccon.h"


/*
 *    Process console GtkObject data key.
 */
#define PROCCON_OBJECT_DATA_KEY           "process_console"

/*
 *    Process console structure:
 */
typedef struct {

      GtkWidget   *toplevel,
                  *text;
      GdkColormap *colormap;
      guint       toid;
      guint       display_flags;
      gint        pid;        /* 0 = nothing running. */
      gchar       *stdout_path,
                  *stderr_path;
      FILE        *stdout_fp,
                  *stderr_fp;
      GdkFont           *font;
      GdkColor    stdout_color,
                  stderr_color;
      GdkCursor   *text_cur;

      gpointer    client_data;
                  /* GtkWidget *w, gint pid, gpointer client_data */
      void        (*termination_cb)(GtkWidget *, gint, gpointer);

} proccon_struct;

#define PROCCON(p)      ((proccon_struct *)(p))
#define PROCCON_TOPLEVEL(p)   (((p) != NULL) ?  \
 (PROCCON(p)->toplevel) : NULL)
#define PROCCON_TEXT(p)       (((p) != NULL) ?  \
 (PROCCON(p)->text) : NULL)


static void ProcConDestroyCB(GtkObject *object, gpointer data);
static void ProcConRealizeCB(GtkWidget *widget, gpointer data);
static gint ProcConTimeoutCB(gpointer data);

static void ProcConDoAppendString(
        proccon_struct *pc, GdkFont *font,
        GdkColor *fg_color, GdkColor *bg_color,
        const gchar *buf, gboolean allow_auto_scroll
);
static void ProcConDoClose(proccon_struct *pc);

GtkWidget *ProcConNew(guint display_flags);

gchar *ProcConGetText(GtkWidget *w);
void ProcConClear(GtkWidget *w);

gint ProcConExecuteNotify(
        GtkWidget *w, const gchar *cmd,
        gpointer client_data,
        void (*termination_cb)(GtkWidget *, gint, gpointer)
);
gint ProcConExecute(GtkWidget *w, const gchar *cmd);
gint ProcConGetPid(GtkWidget *w);
void ProcConKill(GtkWidget *w);


#define PROCCON_FONT_NAME     \
 "-adobe-courier-medium-r-normal-*-12-*-*-*-m-*-iso8859-1"

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define ABSOLUTE(x)     (((x) < 0) ? ((x) * -1) : (x))


/*
 *    Process console toplevel GtkWidget "destroy" signal callback.
 */
static void ProcConDestroyCB(GtkObject *object, gpointer data)
{
      proccon_struct *pc = PROCCON(data);
      if(pc == NULL)
          return;

      /* Remove timeout callback (if any). */
      if(pc->toid != (guint)-1)
      {
          gtk_timeout_remove(pc->toid);
          pc->toid = (guint)-1;
      }

/* Do not call termination callback. */

        /* Send SIGINT if process is still running. */
        if(pc->pid > 0)
        {
            kill(pc->pid, SIGINT);
            pc->pid = 0;
        }

      /* Close stdout and stderr files. */
      ProcConDoClose(pc);


      /* Destroy pointer cursors. */
      if(pc->text_cur != NULL)
      {
          gdk_cursor_destroy(pc->text_cur);
          pc->text_cur = NULL;
      }

      /* Unref fonts. */
      if(pc->font != NULL)
      {
          gdk_font_unref(pc->font);
          pc->font = NULL;
      }

/* printf("ProcConDestroyCB(): 0x%.8x.\n", (guint)pc); */

      /* Unref colors and colormap. */
      if(pc->colormap != NULL)
      {
          gdk_colormap_free_colors(
            pc->colormap, &pc->stdout_color, 1
          );
            gdk_colormap_free_colors(
                pc->colormap, &pc->stderr_color, 1
            );
          gdk_colormap_unref(pc->colormap);
          pc->colormap = NULL;
      }

      /* Deallocate structure itself. */
      g_free(pc);
}

/*
 *    Process console text "realize" signal callback.
 */
static void ProcConRealizeCB(GtkWidget *widget, gpointer data)
{
      GdkColor *c;
      GdkColormap *colormap;
      GdkWindow *window;
      GtkWidget *w;
        proccon_struct *pc = PROCCON(data);
        if(pc == NULL)
            return;

      w = pc->text;
      window = (w != NULL) ? w->window : NULL;
      if(window == NULL)
          return;

      /* Already realized before? */
      if(pc->colormap != NULL)
          return;

      /* Get colormap from window and add a refcount so we can
       * hold onto it.
       */
      pc->colormap = colormap = gdk_window_get_colormap(window);
      if(colormap == NULL)
          return;

      gdk_colormap_ref(colormap);


      /* Allocate colors. */

      c = &pc->stdout_color;
      c->red            = (gushort)-1 * 0.0f;
      c->green    = (gushort)-1 * 0.0f;
      c->blue           = (gushort)-1 * 0.0f;
      gdk_colormap_alloc_color(colormap, c, TRUE, TRUE);

        c = &pc->stderr_color;
        c->red          = (gushort)-1 * 1.0f;
        c->green        = (gushort)-1 * 0.0f;
        c->blue         = (gushort)-1 * 0.0f;
        gdk_colormap_alloc_color(colormap, c, TRUE, TRUE);

      /* Load fonts. */
      pc->font = gdk_font_load(PROCCON_FONT_NAME);

      /* Load pointer cursors. */
      pc->text_cur = gdk_cursor_new(GDK_XTERM);
      gdk_window_set_cursor(window, pc->text_cur);
}

/*
 *    Process console timeout callback.
 */
static gint ProcConTimeoutCB(gpointer data)
{
      gint pid;
      FILE *fp;
        proccon_struct *pc = PROCCON(data);
        if(pc == NULL)
            return(FALSE);

      /* Get process id and check if it is no longer running, in
       * which case we mark pid as 0.  The pid will be checked again
       * further below and termination cleanup will be made after the
       * files are read (this is to allow the reading of any data
       * left over just after the process terminated).
       */
      pid = pc->pid;
      if((pid > 0) ? !ExecProcessExists(pid) : TRUE)
          pid = 0;


        /* Read from stdout file. */
        fp = pc->stdout_fp;
        if(fp != NULL)
        {
#define buf_len   1024
            gint bytes_read = 1;    /* Set 1 for initial loop. */
            gchar buf[buf_len];

            while(bytes_read > 0)
            {
                bytes_read = fread(
                buf, sizeof(gchar), buf_len - 1, fp
            );
                if(bytes_read > 0)
                {
                if(bytes_read < buf_len)
                  buf[bytes_read] = '\0';
                else
                  buf[buf_len - 1] = '\0';

                    ProcConDoAppendString(
                        pc,
                        pc->font,
                        NULL, NULL,
                        buf, TRUE
                    );
                }
            }
#undef buf_len
        }

        /* Read from stderr file. */
        fp = pc->stderr_fp;
        if(fp != NULL)
        {
#define buf_len 1024
            gint bytes_read = 1;        /* Set 1 for initial loop. */
            gchar buf[buf_len];

            while(bytes_read > 0)
            {
                bytes_read = fread(
                    buf, sizeof(gchar), buf_len - 1, fp
                );
                if(bytes_read > 0)
                {
                    if(bytes_read < buf_len)
                        buf[bytes_read] = '\0';
                    else
                        buf[buf_len - 1] = '\0';

                    ProcConDoAppendString(
                        pc,
                        pc->font,
                        &pc->stderr_color, NULL,
                        buf, TRUE
                    );
                }
            }
#undef buf_len
        }

      /* Process terminated? */
      if(pid <= 0)
        {
            /* Call termination callback as needed. */
            if(pc->termination_cb != NULL)
                pc->termination_cb(
                    PROCCON_TOPLEVEL(pc), pid, pc->client_data
                );

            /* Close stdout and stderr files, reset timeout callback and
             * pid.
             */
            ProcConDoClose(pc);
            pc->toid = (guint)-1;
            pc->pid = 0;
            return(FALSE);
        }
      else
      {
          return(TRUE);
      }
}

/*
 *    Appends the string buf to the process console's GtkText widget
 *    with the specified attributes.
 *
 *    If allow_auto_scroll is TRUE then the GtkText widget will scroll
 *    to the end of the text as needed.
 */
static void ProcConDoAppendString(
      proccon_struct *pc, GdkFont *font,
      GdkColor *fg_color, GdkColor *bg_color,
      const gchar *buf, gboolean allow_auto_scroll
)
{
        GtkWidget *w;
        GtkEditable *editable;
        GtkText *text;


        if((pc == NULL) || (buf == NULL))
            return;

        w = pc->text;
        if(w == NULL)
            return;

        editable = GTK_EDITABLE(w);
        text = GTK_TEXT(w);

        gtk_text_insert(
            text, font, fg_color, bg_color, buf, -1
        );

        if(allow_auto_scroll)
        {
            GtkAdjustment *adj = text->vadj;

            /* Need to scroll down to show text not visible? */
            if(((adj->upper - adj->lower) > adj->page_size) &&
               ((adj->value + adj->page_size) < adj->upper)
            )
            {
                adj->value = adj->upper - adj->page_size;
                if(adj->value < adj->lower)
                    adj->value = adj->lower;
                gtk_signal_emit_by_name(
                    GTK_OBJECT(adj), "value_changed"
                );
            }
        }
}

/*
 *    Closes all file descriptors to stdout and stderr files.
 */
static void ProcConDoClose(proccon_struct *pc)
{
      if(pc == NULL)
          return;

      /* Close files as needed. */
        if(pc->stdout_fp != NULL)
        {
            FClose(pc->stdout_fp);
            pc->stdout_fp = NULL;
        }
        if(pc->stderr_fp != NULL)
        {
            FClose(pc->stderr_fp);
            pc->stderr_fp = NULL;
        }

      /* Remove tempory files. */
      if(pc->stdout_path != NULL)
      {
          unlink(pc->stdout_path);
          g_free(pc->stdout_path);
          pc->stdout_path = NULL;
      }
      if(pc->stderr_path != NULL)
      {
          unlink(pc->stderr_path);
          g_free(pc->stderr_path);
          pc->stderr_path = NULL;
      }
}


/*
 *    Creates a new process console.
 */
GtkWidget *ProcConNew(guint display_flags)
{
      gint border_minor = 2 /*, border_major = 5 */;
      GtkWidget *w, *parent, *parent2;
      GtkEditable *editable;
      GtkText *text;
      proccon_struct *pc = PROCCON(g_malloc0(
          sizeof(proccon_struct)
      ));
      pc->colormap = NULL;
      pc->toid = (guint)-1;
      pc->display_flags = display_flags;
      pc->pid = 0;
      pc->stdout_path = pc->stderr_path = NULL;
      pc->stdout_fp = pc->stderr_fp = NULL;
      pc->font = NULL;
      pc->client_data = NULL;
      pc->termination_cb = NULL;

        /* Create toplevel GtkTable for holding the GtkText widget
       * and its scroll bar widgets.
       */
      pc->toplevel = parent = w = gtk_table_new(2, 2, FALSE);
        gtk_table_set_row_spacing(GTK_TABLE(w), 0, border_minor);
        gtk_table_set_col_spacing(GTK_TABLE(w), 0, border_minor);
/*    gtk_container_border_width(GTK_CONTAINER(w), border_major); */
      gtk_signal_connect(
          GTK_OBJECT(w), "destroy",
          GTK_SIGNAL_FUNC(ProcConDestroyCB), pc
      );
        gtk_widget_show(w);

      /* Record user data on toplevel widget. */
      gtk_object_set_data(GTK_OBJECT(w), PROCCON_OBJECT_DATA_KEY, pc);

      /* Create text GtkText widget. */
        pc->text = w = gtk_text_new(NULL, NULL);
        editable = GTK_EDITABLE(w);
        text = GTK_TEXT(w);
        gtk_text_set_editable(text, FALSE);
        gtk_text_set_word_wrap(text, TRUE);
        gtk_table_attach(
            GTK_TABLE(parent), w,
            0, 1, 0, 1,
            GTK_EXPAND | GTK_SHRINK | GTK_FILL,
            GTK_EXPAND | GTK_SHRINK | GTK_FILL,
            0, 0
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "realize",
            GTK_SIGNAL_FUNC(ProcConRealizeCB), pc
        );
        gtk_widget_show(w);

      /* Vertical scroll bar. */
        parent2 = gtk_vscrollbar_new(GTK_TEXT(w)->vadj);
        gtk_table_attach(
            GTK_TABLE(parent), parent2,
            1, 2, 0, 1,
            GTK_FILL,
            GTK_EXPAND | GTK_SHRINK | GTK_FILL,
            0, 0
        );
        gtk_widget_show(parent2);


      return(pc->toplevel);
}

/*
 *    Returns a pointer to the current text value on the process
 *    console.
 */
gchar *ProcConGetText(GtkWidget *w)
{
      proccon_struct *pc = PROCCON((w != NULL) ?
          gtk_object_get_data(GTK_OBJECT(w), PROCCON_OBJECT_DATA_KEY) : NULL
      );
      w = (pc != NULL) ? pc->text : NULL;
      if(w == NULL)
          return(NULL);
      return(gtk_editable_get_chars(GTK_EDITABLE(w), 0, -1));
}

/*
 *    Clears the process console.
 */
void ProcConClear(GtkWidget *w)
{
        proccon_struct *pc = PROCCON((w != NULL) ?
            gtk_object_get_data(GTK_OBJECT(w), PROCCON_OBJECT_DATA_KEY) : NULL
        );
        w = (pc != NULL) ? pc->text : NULL;
        if(w == NULL)
            return;
/*    gtk_text_freeze(GTK_TEXT(w)); */
      gtk_editable_delete_text(GTK_EDITABLE(w), 0, -1);
/*    gtk_text_thaw(GTK_TEXT(w)); */
}

/*
 *    Executes the given command.
 *
 *    Returns the pid or 0 on error.
 */
gint ProcConExecuteNotify(
      GtkWidget *w, const gchar *cmd,
        gpointer client_data,
      void (*termination_cb)(GtkWidget *, gint, gpointer)
)
{
      gint pid = 0;
      gchar *stdout_path, *stderr_path;
        proccon_struct *pc = PROCCON((w != NULL) ?
            gtk_object_get_data(GTK_OBJECT(w), PROCCON_OBJECT_DATA_KEY) : NULL
        );
        if((pc == NULL) || (cmd == NULL))
            return(pid);

      if(*cmd == '\0')
          return(pid);

      /* Text widget must be realized. */
      w = PROCCON_TEXT(pc);
      if((w != NULL) ? !GTK_WIDGET_REALIZED(w) : TRUE)
          return(pid);


      /* Begin cleaning up values from previous execution (if any). */

      /* Remove previous timeout callback (if any). */
        if(pc->toid != (guint)-1)
        {
            gtk_timeout_remove(pc->toid);
            pc->toid = (guint)-1;
        }

      /* Close files from previous execution (if any). */
      ProcConDoClose(pc);


      /* Begin setting up values for new execution. */

      /* Generate tempory filenames for stdout and stderr files. */
      stdout_path = tempnam(NULL, "stdo");
        stderr_path = tempnam(NULL, "stde");

      /* Execute. */
      pc->pid = pid = ExecOE(cmd, stdout_path, stderr_path);
      if(pid > 0)
      {
          /* Execution was successful, now open stdout and stderr
           * files and set timeout callback.
           */
          pc->stdout_path = stdout_path;
          stdout_path = NULL;
          pc->stderr_path = stderr_path;
          stderr_path = NULL;

          pc->stdout_fp = FOpen(pc->stdout_path, "rb");
          pc->stderr_fp = FOpen(pc->stderr_path, "rb");

          pc->client_data = client_data;
          pc->termination_cb = termination_cb;

          pc->toid = gtk_timeout_add(
            100,
            (GtkFunction)ProcConTimeoutCB,
            pc
          );
      }
      else
      {
          /* Execution failed, remove stdout and stderr files. */
          if(stdout_path != NULL)
            unlink(stdout_path);
          if(stderr_path != NULL)
            unlink(stderr_path);
      }

      /* Deallocate tempory filenames (if they were not transfered
       * to the process console structure).
       */
      g_free(stdout_path);
      g_free(stderr_path);

      return(pid);
}
gint ProcConExecute(GtkWidget *w, const gchar *cmd)
{
      return(ProcConExecuteNotify(
          w, cmd, NULL, NULL
      ));
}

/*
 *    Returns the pid of the last running process or 0 if none.
 *
 *    The pid will only be returned if it is actually running.
 */
gint ProcConGetPid(GtkWidget *w)
{
        proccon_struct *pc = PROCCON((w != NULL) ?
            gtk_object_get_data(GTK_OBJECT(w), PROCCON_OBJECT_DATA_KEY) : NULL
        );
      gint pid = ((pc != NULL) ? pc->pid : 0);
      if((pid > 0) ? ExecProcessExists(pid) : FALSE)
          return(pid);
      else
          return(0);
}

/*
 *    Sends a SIGINT signal to the last running process (if any).
 */
void ProcConKill(GtkWidget *w)
{
      gint pid = ProcConGetPid(w);
      if(pid > 0)
          kill(pid, SIGINT);
}

Generated by  Doxygen 1.6.0   Back to index