GTK+ Forums

Discussion forum for GTK+ and Programming. Ask questions, troubleshoot problems, view and post example code, or express your opinions.
It is currently Thu Oct 30, 2014 12:13 pm

All times are UTC




Post new topic Reply to topic  [ 3 posts ] 
Author Message
 Post subject: GTK+ with pseudo terminal
PostPosted: Tue Apr 24, 2012 1:06 pm 
Offline
GTK+ Geek

Joined: Sun Apr 03, 2011 11:12 am
Posts: 63
Hello, everyone, I am creating a terminal emulator like gnome terminal and for that I am using pseudo terminal with GTK+ in C.
There two threads, one is main thread and the other thread created using pthread API in POSIX is responsible for reading output from STDOUT of the /bin/bash. Also, to check whether the output is available or not I used epoll API of Linux. I have also created a function gtk_text_buffer_append_output, which will just append the output from /bin/bash to the current position of buffer and will store the offset of the iterator. The problem I am getting is that whenever I execute my program in terminal I got gtk_text_view_invalidate (I didn't remember the name exactly) and also I may get GtkTextIter Invalidate error, which occurs when we try to access the iterator which has been invalidated by buffer due to text changes. Here's the code
Code:
#define _XOPEN_SOURCE 600
#include<gtk/gtk.h>
#include<gtksourceview/gtksourceview.h>
#include<gtksourceview/gtksourcelanguage.h>
#include<gtksourceview/gtksourcelanguagemanager.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include<errno.h>
#include<termios.h>
#include<sys/ioctl.h>
#include<sys/types.h>
#include<sys/select.h>
#include<unistd.h>
#include<stdio.h>
#include<sys/epoll.h>

#define BUF_SIZE 256
#define MAX_SNAME 1000
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#define MAX_EVENTS 1000

void destroy(GtkWidget *,void *);
gboolean delete_event(GtkWidget*, GdkEvent*);
void empty_stream();
GtkWidget *window, *txtInput;
static gboolean txtinput_key_press_event(GtkWidget *widget,GdkEventKey *event);
void gtk_text_buffer_append_output(GtkTextBuffer *buffer, char *text, int len);
void *read_masterFd(void *data);
int min_cursor_offset = 0;

GThread *thread;

char slaveName[MAX_SNAME];
char *shell;
int masterFd, scriptFd;
struct winsize ws;
fd_set inFds;

ssize_t numRead;
pid_t childPid;   

struct termios ttyOrig;

pid_t ptyFork(int *masterFd, char *slaveName, size_t snLen, struct termios *slaveTermios, const struct winsize *slaveWS)
{
    int mfd, slaveFd, savedErrno;
    pid_t childPid;
    char slname[100];   
    char *p;
    mfd = posix_openpt(O_RDWR | O_NOCTTY);     
    grantpt(mfd);     
    unlockpt(mfd);
    p = ptsname(mfd);
    strncpy(slname, p, 100);
    if (slaveName != NULL)
    {
        /* Return slave name to caller */
        if (strlen(slname) < snLen)
        {
            strncpy(slaveName, slname, snLen);
            }   
        }
    childPid = fork();   
    if (childPid != 0)
    {
        *masterFd = mfd;
        return childPid;
        }
   
    setsid();
    close(mfd);
    slaveFd = open(slname, O_RDWR); 
    #ifdef TIOCSCTTY
    ioctl(slaveFd, TIOCSCTTY, 0);   
    #endif
    if (slaveTermios != NULL)   
    {
        slaveTermios->c_lflag &= ~ECHO;
        slaveTermios->c_lflag |= ICANON;       
        slaveTermios->c_lflag |= ISIG;       
        slaveTermios->c_lflag |= IEXTEN;       
        slaveTermios->c_cflag |= HUPCL;
        slaveTermios->c_iflag |= BRKINT;
        slaveTermios->c_iflag |= ICRNL;
        slaveTermios->c_iflag |= IXON;
        slaveTermios->c_iflag |= IXANY;
        slaveTermios->c_iflag |= IUTF8;
        slaveTermios->c_oflag |= OPOST;
        slaveTermios->c_oflag |= ONLCR;
        cfsetspeed(slaveTermios,B38400);
        if (tcsetattr(slaveFd, TCSANOW, slaveTermios) == -1);
       }

    /*struct termios attr;   
    attr.c_iflag |= ICRNL;
    attr.c_iflag |= IXON;
    attr.c_oflag |= OPOST;
    attr.c_oflag |= ONOCR;
    attr.c_lflag &= (~ECHO);   
    attr.c_lflag &= (~ECHOE);
    attr.c_lflag &= (~ECHOK);   
    attr.c_lflag |= ISIG;
    attr.c_lflag |= ICANON;
    cfsetspeed(&attr,B38400);
    tcsetattr(slaveFd, TCSANOW, &attr);*/
   
    if (slaveWS != NULL)   
        if (ioctl(slaveFd, TIOCSWINSZ, slaveWS) == -1);           
   
    dup2(slaveFd, 0);       
    dup2(slaveFd, 1);       
    dup2(slaveFd, 2);
       
    if (slaveFd > 2)
        close(slaveFd);
    return 0;
    }

static void ttyReset(void)
{
    if (tcsetattr(STDIN_FILENO, TCSANOW, &ttyOrig) == -1);
    }

int main(int argc, char *argv[])
{   
    tcgetattr(STDIN_FILENO, &ttyOrig);   
    ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
    childPid = ptyFork(&masterFd, slaveName, MAX_SNAME, &ttyOrig, &ws);   
    if (childPid == 0)
    {       
        shell = getenv("SHELL");
        if (shell == NULL |5| *shell == '\0')
        shell = "/bin/bash";
        execlp(shell, shell, (char *) NULL);       
        }   
   
    atexit(ttyReset);
   
    GtkSourceBuffer *buffer;
    GtkWidget *scrollwin;   
   
    gtk_init(&argc, &argv);   
   
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    buffer = gtk_source_buffer_new(NULL);
    txtInput = gtk_source_view_new_with_buffer(buffer);
    //gtk_text_buffer_append_output(GTK_TEXT_BUFFER(buffer),buf,-1);
    scrollwin = gtk_scrolled_window_new(NULL,NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);   
    gtk_container_add(GTK_CONTAINER(scrollwin),txtInput);
   
    int s;
    pthread_t tid;
    printf("STARTING THREAD\n");
    s = pthread_create(&tid,NULL,read_masterFd,NULL);
       
    gtk_container_add(GTK_CONTAINER(window),scrollwin);
    gtk_widget_set_size_request(window,600,400);
    gtk_window_set_title(GTK_WINDOW(window),"Terminal");
    g_signal_connect(G_OBJECT(txtInput),"key-press-event",G_CALLBACK(txtinput_key_press_event),NULL);   
    g_signal_connect(G_OBJECT(window),"destroy",G_CALLBACK(destroy),(void *)&tid);
    g_signal_connect(G_OBJECT(window),"delete_event",G_CALLBACK(delete_event),NULL);
    gtk_widget_show_all(window);     

    gtk_main(); 
   
    return 0;
    }

void gtk_text_buffer_append_output(GtkTextBuffer *buffer, char *text, int len)
{
    gtk_text_buffer_insert_at_cursor(buffer,text,len);
    GtkTextIter iter;
    gtk_text_buffer_get_iter_at_mark(buffer,&iter,gtk_text_buffer_get_insert(buffer));
    min_cursor_offset = gtk_text_iter_get_offset(&iter);
    }

void *read_masterFd(void *data)
{
   
    printf("Thread started\n");     
    char buf[BUF_SIZE];
    while(1)
    {
        struct epoll_event ev;
        int epfd = epoll_create(1);
        ev.events = EPOLLIN;
        ev.data.fd  = masterFd;
        epoll_ctl(epfd, EPOLL_CTL_ADD, masterFd, &ev);
        struct epoll_event evlist[MAX_EVENTS];
        int ready = epoll_wait(epfd, evlist,MAX_EVENTS,-1);
        printf("ready %d\n",ready);       
        if(ready>0 && evlist[0].events & EPOLLIN)
        {
            memset(&buf,0,sizeof(buf));
            numRead = read(masterFd, buf, BUF_SIZE);
            buf[numRead] = '\0';
            printf("RECIEVING FROM PTY %s\n",buf);                       
            GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(txtInput));
            gtk_text_buffer_append_output(buffer,buf,-1);           
            }                 
        }   
    return;
    }

static gboolean txtinput_key_press_event(GtkWidget *widget,GdkEventKey *event)
{
    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
    GtkTextIter current_iter,start_iter;
    gtk_text_buffer_get_iter_at_mark(buffer,&current_iter,gtk_text_buffer_get_insert(buffer));
    gtk_text_buffer_get_iter_at_offset(buffer,&start_iter,min_cursor_offset);
    char *text,*text2;
    asprintf(&text2,"");
    switch(event->keyval)
    {
        case 65293: // 65293 for Return Key                       
            printf("getting text\n");
            text = gtk_text_buffer_get_text(buffer,&start_iter,&current_iter,FALSE);
            asprintf(&text2,"%s\n",text);
            gtk_text_buffer_insert_at_cursor(buffer,"\n",-1);
        }
    if(strcmp(text2,"")==0)
        return FALSE;   
    printf("SENDING TO PTY %s\n",text2);
    write(masterFd, text2, strlen(text2));
    free(text2);           
    return TRUE;
    }

void destroy(GtkWidget *window,void *id)
{   
    gtk_main_quit();   
    }

gboolean delete_event(GtkWidget *window, GdkEvent *event)
{
    return FALSE;
    }   

please help me!!!


Top
 Profile  
 
 Post subject: Re: GTK+ with pseudo terminal
PostPosted: Tue Apr 24, 2012 6:22 pm 
Offline
Never Seen the Sunlight

Joined: Mon Apr 28, 2008 5:52 am
Posts: 766
Location: UK
Hi,

Your code has many problems and needs serious reviewing.

- You are using Linux specific calls. These are not portable and your code can not be used on other UNIX based systems such as FreeBSD, BSD, SYS V, MacOS X etc. Since you are already using GTK consider using the Glib library calls first then POSIX calls.

- You are trusting the environment variable "SHELL" and executing the value of it with out validating it. This is a serious security flaw.

- You are using threads incorrectly. GTK can not be called from more than one thread. By calling GTK from more than one thread you are corrupting the internal data structures used by GTK and X11. There are Glib functions to add signals to monitor file descriptors to see if there is any input, output or connection problems. The signal then forms part of the main GTK loop and you do not need threads.

- Use the key constants and not the numerical value for the keyval

- There is a GTK library called libvte which does exactly what you want and is used by the gnome terminal emulator and many other applications including IDEs

_________________
E.


Top
 Profile  
 
 Post subject: Re: GTK+ with pseudo terminal
PostPosted: Tue Apr 24, 2012 9:07 pm 
Offline
GTK+ Geek

Joined: Sun Apr 03, 2011 11:12 am
Posts: 63
Thanku once again errol!!
I am to correct the problem using glib IO Channels. :)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group