/* * \/\/\ * * Copyright (C) 2012-2018 Vito Caputo - * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* launching of external processes / X clients */ #include #include #include #include #include #include #include #include #include "launch.h" #include "vwm.h" #include "window.h" #include "xwindow.h" #define LAUNCHED_RELATIVE_PRIORITY 10 /* the wm priority plus this is used as the priority of launched processes */ /* return an interpolated copy of arg */ static char * arg_interpolate(const vwm_t *vwm, const char *arg) { FILE *memfp; char *xarg = NULL; size_t xarglen; int fmt = 0; assert(vwm); assert(arg); /* this came from vmon.c, it'd be nice if they could share */ memfp = open_memstream(&xarg, &xarglen); if (!memfp) { VWM_PERROR("unable to create memstream"); return NULL; } for (size_t i = 0; arg[i]; i++) { char c = arg[i]; if (!fmt) { if (c == '%') fmt = 1; else fputc(c, memfp); continue; } switch (c) { case 'W': { /* focused X window id in hex, root window if nothing focused */ vwm_window_t *vwin; Window winid; vwin = vwm_win_get_focused(vwm); if (vwin) winid = vwin->xwindow->id; else winid = VWM_XROOT(vwm); fprintf(memfp, "%#x", (unsigned)winid); break; } case '%': /* literal % */ fputc(c, memfp); break; default: VWM_ERROR("Unrecognized specifier \'%c\'", c); goto _err; } fmt = 0; } fclose(memfp); return xarg; _err: fclose(memfp); free(xarg); return NULL; } static char ** args_interpolate(vwm_t *vwm, char **argv) { char **args; int n_args; assert(vwm); assert(argv); for (n_args = 0; argv[n_args]; n_args++); args = calloc(n_args + 1, sizeof(*args)); if (!args) return NULL; for (int i = 0; i < n_args; i++) { args[i] = arg_interpolate(vwm, argv[i]); if (!args[i]) { for (int j = 0; j < n_args; j++) free(args[j]); free(args); return NULL; } } return args; } static void args_free(char **args) { assert(args); for (int i = 0; args[i]; i++) free(args[i]); free(args); } /* launch a child command specified in argv, mode decides if we wait for the child to exit before returning. */ void vwm_launch(vwm_t *vwm, char **argv, vwm_launch_mode_t mode) { char **args; args = args_interpolate(vwm, argv); if (!args) return; /* XXX: in BG mode I double fork and let init inherit the orphan so I don't have to collect the return status */ if (mode == VWM_LAUNCH_MODE_FG || !fork()) { if (!fork()) { /* child */ setpriority(PRIO_PROCESS, getpid(), vwm->priority + LAUNCHED_RELATIVE_PRIORITY); execvp(args[0], args); } if (mode == VWM_LAUNCH_MODE_BG) exit(0); } wait(NULL); /* TODO: could wait for the specific pid, particularly in FG mode ... */ args_free(args); }