0

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
skink
  • 5,133
  • 6
  • 37
  • 58
Evgeniy
  • 756
  • 8
  • 17
  • Take a look at the [man page for `fork()`](http://man7.org/linux/man-pages/man2/fork.2.html). Your program is designed to run copies of itself asynchronously. – jwdonahue Jul 31 '18 at 18:47
  • You are halfway there with `fork()`. Now read about [`wait()`](http://man7.org/linux/man-pages/man2/waitpid.2.html) – Ajay Brahmakshatriya Jul 31 '18 at 20:05

1 Answers1

1

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
    }
}
Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49
  • Thank you for clarification but when I try to add `wait`, the application becomes unresponsive. It seems like no state has been changed. When I tried to remove `fork` at all, dwm stopped working. dwm is not my application, it is a window manager. That is why I have no idea what to do, let alone, I am total noob in C. – Evgeniy Aug 01 '18 at 10:25
  • I did some investigation. It does not work because the child process do not reach the `exit` function when I am trying to call say "chromium" or "thunar". When the command is "ls", all works as expected. How can I catch "open" event, not "termination"? – Evgeniy Aug 01 '18 at 12:06
  • @Evgeniy the wait waits for the child process to exit. In your case since it is chrome browser, it doesn't exit till it is closed. But a process lile ls or cat exits immediately after printing and hence the parent continues. There is no "open". The child process it created at the fork itself. If you just want to wait for the gui of the new process to be spawned, that depends on process to process. Really your dwm should not be synchronous. Once a new process is started jt should just continue with rest of it's work. – Ajay Brahmakshatriya Aug 01 '18 at 18:17
  • Your solution could be a simple sleep for a few seconds but you can never actually know how much time it takes to spawn the window for a particular application. – Ajay Brahmakshatriya Aug 01 '18 at 18:19
  • I got it. Yes, it is possible to use timer but I do not want to. Although you answer does not solve my problem, I think it solves the asked question. – Evgeniy Aug 02 '18 at 13:11