How to call shell command synchronously?

633 views Asked by At

I am trying to teach dwm to open appropriate application in each tag (www, files, music...). In dwm.c there is a function called view that is responsible for tag switching.

void
view(const Arg *arg)
{
    if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
        return;
    /* toggle sel tagset */
    selmon->seltags ^= 1;
    if (arg->ui & TAGMASK)
        selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;

    // user specific edit
    prepareTag(arg->ui);

    focus(NULL);
    arrange(selmon);
}

I have added a line where prepareTag is called. This function has simple logic and do nothing else than a few verifications (is application already open?; what tag is it?) and the application spawn itself.

void
spawn(const Arg *arg)
{
    if (arg->v == dmenucmd)
        dmenumon[0] = '0' + selmon->num;
    if (fork() == 0) {
        if (dpy)
            close(ConnectionNumber(dpy));
        setsid();
        execvp(((char **)arg->v)[0], (char **)arg->v);
        fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
        perror(" failed");
        exit(EXIT_SUCCESS);
    }
}

It works but the code goes asynchronously. The tag is changed and I see my wallpaper, than after ~20-50ms the application is started. It causes notable flicker. The problem is I have never worked with C and do not know the reason why the code work asynchronously. I have tried system function instead of built-in spawn but dwm do not catch applications open this way. I could probably use key binder and some BASHing but the way is quite dirty. Not to mention, I would like to have an ability to use mouse buttons to change tag.

In case someone need code base.

git clone https://git.suckless.org/dwm
1

There are 1 answers

5
Ajay Brahmakshatriya On BEST ANSWER

If you read the manual for fork(), you will realize the it creates a copy of the running process.

After the fork both the processes are independent of each other and may get scheduled in any order. This is the asynchronous behaviour you are seeing.

To get a synchronous behavior your parent process needs to wait till the forked process has completed (exits). This has be achieved using the wait() system call.

You can modify your spawn function as -

void
spawn(const Arg *arg) 
{
    if (arg->v == dmenucmd)
        dmenumon[0] = '0' + selmon->num;
    if (fork() == 0) {
        if (dpy)
            close(ConnectionNumber(dpy));
        setsid();
        execvp(((char **)arg->v)[0], (char **)arg->v);
        fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
        perror(" failed");
        exit(EXIT_SUCCESS);
    } else { // fork returns a non zero pid in the parent process. So the else branch will be taken only in the parent. 
        wait(NULL); // Wait for the child process to change state. In this case exit
    }
}