Reputation: 21
I am working on building a simple X O (tic-tac-toe) game in C on a Linux system. I want to fork three child processes:
Each child process should open in a separate terminal window to interact with the user. For example, Player 1 will input the row and column to place their "X" on the board.
I would like to achieve this using only one program file. How can I run this program so that each child process opens its own terminal window for interaction?
fork()
function to create child processes.I have managed to open separate terminals for each child process using the system()
function and running simple commands like ls
, but I want to run the actual child process functions in these new terminals.
Upvotes: 2
Views: 185
Reputation: 21
The solution to running specific code in a child process involves creating a separate function for each task you want the child to perform. The key idea is to open a new terminal for each child process. Below is an example demonstrating how to achieve this using system() and passing command-line arguments to the child processes.
#include <stdio.h>
void display_code(/* appropriate parameters */);
void player1(/* appropriate parameters */);
void player2(/* appropriate parameters */);
int main(int argc, char *argv[]) {
// Initialize required variables and IPC mechanisms (message queue, shared memory, etc.)
if (argc > 1) {
if (strcmp(argv[1], "display") == 0) {
display_code(/* appropriate arguments */); // Function for display child
} else if (strcmp(argv[1], "player1") == 0) {
player1(/* appropriate arguments */); // Function for player 1 child
} else if (strcmp(argv[1], "player2") == 0) {
player2(/* appropriate arguments */); // Function for player 2 child
}
exit(0);
}
// Fork and execute each child process in a new terminal
if (fork() == 0) { // Display child
system("konsole --hold -e './bo display' --title 'Display'");
exit(0);
}
if (fork() == 0) { // Player 1 child
system("konsole --hold -e './bo player1' --title 'Player 1'");
exit(0);
}
if (fork() == 0) { // Player 2 child
system("konsole --hold -e './bo player2' --title 'Player 2'");
exit(0);
}
// Parent process
for (int i = 0; i < 3; i++) {
wait(NULL);
}
return 0;
}
In this code:
main() initializes the necessary resources and checks if any command-line arguments are passed to determine the function to execute.
Each child process is forked and executed in a new terminal using the system() function with the konsole command.
The parent process waits for all child processes to complete before cleaning up IPC resources and exiting.
Following this approach, you can run each child process in a separate terminal and execute specific functions based on the command-line arguments.
Upvotes: 0
Reputation: 1330
Interesting - I think you're making this a little bit too complicated though. I agree with the approach mentioned by @Hackerman. Here's the idea, assuming that your application is text or curses based, and when you run your app you want to see two GUI terminals appear, one for each player:
AF_UNIX
socket()
for the X child and for the O child.gnome-terminal -- myapp X
gnome-terminal -- myapp O
int main( int argc, const char* const argv[] )
Your question suggests you want the child process to create the terminal, but I don't see a way to open a "raw" gnome-terminal (I assume this is what you want to do based on your question tags). I had originally written to use pipe()
for IPC, but gnome-terminal isn't going to give you direct access to stdin
and stdout
of the child processes. Something else may do that but I didn't find a "raw xterm" or "GUI getty" type application.
You can get a new pty via forkpty()
and openpty()
but I'm not that familiar with these and don't think you want to write your own terminal emulator. So instead, make the child be the gnome-terminal
and have it run your logic based on the argument you pass. You could make these separate programs but you explicitly stated you want to use the same one, so your setup code will look something like:
struct sockaddr_un Xaddr, Oaddr;
int main( int argc, const char* const argv[] ) {
char *sockdir = getenv( "XDG_RUNTIME_HOME" );
if( !sockdir ) sockdir = "/tmp";
// Create Xaddr and Oaddr temporary socket names; tmpnam() or getpid()
// Alternately, use 1 socket and put "X" and "O" in the command stream.
switch( argv[1][0] ) {
case 'X': return do_X();
case 'O': return do_O();
} // default: assume the parent logic
int Xchild = fork();
if( 0 == Xchild ) execl( argv[0], "X", NULL );
int Ochild = fork();
if( 0 == Ochild ) execl( argv[0], "O", NULL );
int Xsocket, Osocket;
Xsocket = socket( AF_UNIX, SOCK_SEQPACKET, 0 );
Osocket = socket( AF_UNIX, SOCK_SEQPACKET, 0 );
/* ... See `man 7 unix` for details ... */
}
void do_X()
{
int socket = socket( AF_UNIX, SOCK_SEQPACKET, 0 );
connect( socket, Xaddr, sizeof( Xaddr ));
// "X" player logic
}
void do_O()
{
int socket = socket( AF_UNIX, SOCK_SEQPACKET, 0 );
connect( socket, Oaddr, sizeof( Oaddr ));
// "O" player logic
}
I hope that's clear enough but have to admit I didn't test it. The principles should be sound but I'd love to hear if there's an easier way.
Regarding the 3rd process to display the resultant output, I would ask why that can't be done from the main program. However: opening a 3rd window would be the same general logic.
Upvotes: 0