Ever wondered how to create a Linux Terminal like GNOME Terminal or xTerm etc. What are the steps for it?
Well, First thing read POSIX, you have to be a good C programmer too. Although you can also create it in Python or Java or C#, but I will here show you how to create it in C. The reason for C is not only performance but it also gives more insight of actually what is happening and what above languages do. This Terminal will be created using C and GTK+. Hence, you should install GTK+ in your system. I hope you will have a good knowledge of GTK+ so that you can understand the GTK+ code. I will only show how to deal with the POSIX code. Lets go with it.
Here’s the code:
Compile the file with `pkg-config gtk+-2.0` flag.
I will explain it one by one, in different post. Those following this post will be dedicated to this tutorial.
Here’s a general view how a Linux Terminal works.
1. It calls a Shell like /usr/bin/sh or bash (Bourne Again Shell) or csh (C Shell). Anyone, available you can call. Here, I will use bash. Mostly sh and bash are available always.
2. Then sends commands you type to the above Shell.
3. Recieve output from the above Shell and show it.
It seems so easy isn’t it? You may be thinking, lets create a new Process, connect to its standard output and input and do all the above thing, well, that may work but not always.
Hence, I will introduce the concept of psuedo-terminals here. I would recommend you to read it from the book “The Linux Programming Interface: A Linux and Unix System Programming Handbook”.
Lets start with function “main()”. In main(), “tcgetattr()” is used to get the attribute (Standard Input) of the current program. Then “ioctl()” is used. Next we encounter “ptyFork” function. So lets move to it.
“ptyFork()” takes argument as pointer to master file descriptor, slave name, slave name length, slave termois, slaves winsize. It then opens the psuedo-terminal using “posix_openpt()”. This functions finds and opens an unused psuedo terminal master device and returns a file descriptor to it. O_RDWR means opened for both read and write. O_NCTTY to ensure that this psuedo-terminal is not the master terminal of the process. “grantpt()” will change the slave permissions and ownerships. Many jobs are done including change the group to “tty”, allow owner to read and write etc. “unlockpt()” removes the lock set by the master device which is passed as parameter. “ptsname()” returns the psuedoterminal slave name of the psuedoterminal master name. It will be of the form /dev/pts/<something>.
Now, use “fork()” to create a child process. Then after doing something with “termios” so as to make the output look nicer, we copy the Standard Output, Input and Error streams to the slaveFd. Also, in the child process, it will copy its masterFd to the one supplied by argument.
Now, whatever you want to write will be written to masterFd and whatever you read will be read from masterFd.
Lets return to main function. In main, we replace the child process created by fork()’s image with the /usr/bin/bash process using exec*() family of functions. Now commands will be sent to bash and can be read from bash.
So, main process is to
1. Create a psuedo-terminal.
2. Do something with its slave, as above.
3. Use fork to create its child process.
4. Copy standard file descriptors of child process.
5. Replace the child process image with that of sh or bash.
Now, how to monitor, when standard output is available to read? Well, for this many API is available like select or poll etc. but if you are using GTK+, then that should be integerated with GTK+ main loop. You could try using select or poll running in another thread and then trying to write input in the shell, but it may corrupt the GTK+ data and hence, your program wil be unstable. To overcome this difficulty, GTK+ provides a good solution in the form of GIOChannel.
Just create a new GIOChannel to monitor, masterFd and then register a callback, that whenever data is available for reading it will be applied to GtkTextView.
Here’s how to create a Linux Terminal. It will be very hard for you to understand. But it does give you the insight of how Linux Terminal work. I will show, how to do the same in Python also. 😉