summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/charts.c12
-rw-r--r--src/key.c28
-rw-r--r--src/launch.c124
-rw-r--r--src/launchers.def16
-rw-r--r--src/vmon.c334
-rw-r--r--src/window.c9
-rw-r--r--src/window.h2
-rw-r--r--src/xevent.c9
-rw-r--r--src/xwindow.c34
-rw-r--r--src/xwindow.h1
10 files changed, 461 insertions, 108 deletions
diff --git a/src/charts.c b/src/charts.c
index 6566f0a..c01e8fe 100644
--- a/src/charts.c
+++ b/src/charts.c
@@ -59,7 +59,7 @@ typedef struct _vwm_charts_t {
unsigned long long last_idle, last_iowait, idle_delta, iowait_delta;
vmon_t vmon;
float prev_sampling_interval, sampling_interval;
- int sampling_paused, contiguous_drops;
+ int sampling_paused, contiguous_drops, primed;
/* X */
XFontStruct *chart_font;
@@ -144,7 +144,6 @@ typedef struct _vwm_perproc_ctxt_t {
static float sampling_intervals[] = {
- INFINITY, /* STOPPED */
1, /* ~1Hz */
.1, /* ~10Hz */
.05, /* ~20Hz */
@@ -795,7 +794,7 @@ static void draw_columns(vwm_charts_t *charts, vwm_chart_t *chart, vwm_column_t
case VWM_COLUMN_PROC_WALL: /* User Sys Wall times */
if (!row)
str_len = snpf(str, sizeof(str), "Wall");
- else if (!proc_stat->start)
+ else if (!proc_stat->start || proc_stat->start > sys_stat->boottime)
str_len = snpf(str, sizeof(str), "??s");
else
str_len = snpf(str, sizeof(str), "%.2fs",
@@ -1596,7 +1595,8 @@ int vwm_charts_update(vwm_charts_t *charts, int *desired_delay)
int ret = 0;
gettimeofday(&charts->maybe_sample, NULL);
- if ((charts->sampling_interval == INFINITY && !charts->sampling_paused) || /* XXX this is kind of a kludge to get the 0 Hz indicator drawn before pausing */
+ if (!charts->primed ||
+ (charts->sampling_interval == INFINITY && !charts->sampling_paused) || /* XXX this is kind of a kludge to get the 0 Hz indicator drawn before pausing */
(charts->sampling_interval != INFINITY && ((this_delta = delta(&charts->maybe_sample, &charts->this_sample)) >= charts->sampling_interval))) {
vmon_sys_stat_t *sys_stat;
@@ -1637,6 +1637,10 @@ int vwm_charts_update(vwm_charts_t *charts, int *desired_delay)
charts->sampling_paused = (charts->sampling_interval == INFINITY);
charts->prev_sampling_interval = charts->sampling_interval;
+
+ /* "primed" is just a flag to ensure we always perform the first sample */
+ if (!charts->primed)
+ charts->primed = 1;
}
/* TODO: make some effort to compute how long to sleep, but this is perfectly fine for now. */
diff --git a/src/key.c b/src/key.c
index da1a22b..07b3abf 100644
--- a/src/key.c
+++ b/src/key.c
@@ -107,12 +107,12 @@ void vwm_key_released(vwm_t *vwm, Window win, XKeyReleasedEvent *keyrelease)
/* Called in response to KeyPress events, I currenly only grab Mod1 keypress events */
void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
{
- vwm_window_t *vwin;
- KeySym sym;
- static KeySym last_sym;
- static typeof(keypress->state) last_state;
- static int repeat_cnt = 0;
- int do_grab = 0;
+ vwm_window_t *vwin;
+ KeySym sym;
+ static KeySym last_sym;
+ static typeof(keypress->state) last_state;
+ static int repeat_cnt = 0;
+ int do_grab = 0;
sym = XLookupKeysym(keypress, 0);
@@ -128,12 +128,12 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
switch (sym) {
#define launcher(_sym, _label, _argv)\
- case _sym: \
- { \
+ case _sym: { \
char *args[] = {"/bin/sh", "-c", "screen -dr " CONSOLE_SESSION_STRING " -X screen /bin/sh -i -x -c \"" _argv " || sleep 86400\"", NULL};\
vwm_launch(vwm, args, VWM_LAUNCH_MODE_BG);\
break; \
}
+
#include "launchers.def"
#undef launcher
case XK_Alt_L: /* transaction abort */
@@ -228,9 +228,13 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
} else if (send_it) { /* "send" the focused window to a new desktop in the current context, kind of an alias of send_it+XK_v */
if (vwin)
vwm_win_send(vwm, vwin, vwm_desktop_mru(vwm, vwm_desktop_create(vwm, vwin->desktop->context)));
- } else if (keypress->state & ShiftMask) { /* migrate the focused window with the desktop focus to the most recently used desktop */
- if (vwin)
+ } else if (keypress->state & ShiftMask) { /* migrate the focused window with the desktop focus to the most recently used desktop, creating one if none exists for convenience */
+ if (vwin) {
+ if (next_desktop == vwm->focused_desktop)
+ next_desktop = vwm_desktop_create(vwm, vwin->desktop->context);
+
vwm_win_migrate(vwm, vwin, next_desktop);
+ }
} else {
vwm_desktop_focus(vwm, next_desktop);
}
@@ -467,6 +471,10 @@ void vwm_key_pressed(vwm_t *vwm, Window win, XKeyPressedEvent *keypress)
vwm_charts_rate_decrease(vwm->charts);
break;
+ case XK_z: /* zero sampling frequency (disables monitoring) */
+ vwm_charts_rate_set(vwm->charts, 0);
+ break;
+
default:
VWM_TRACE("Unhandled keycode: %x", (unsigned int)sym);
break;
diff --git a/src/launch.c b/src/launch.c
index 3a30eca..9c481a1 100644
--- a/src/launch.c
+++ b/src/launch.c
@@ -17,6 +17,8 @@
*/
/* launching of external processes / X clients */
+#include <assert.h>
+#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/time.h>
@@ -26,21 +28,141 @@
#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(argv[0], argv);
+ 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);
}
diff --git a/src/launchers.def b/src/launchers.def
index ea06683..e47b711 100644
--- a/src/launchers.def
+++ b/src/launchers.def
@@ -1,8 +1,10 @@
/* it's up to you to ensure you don't conflict with the global VWM keys */
-/* keysym, label, cmd */
-launcher( XK_x, "xterm", "/usr/bin/xterm")
-launcher( XK_b, "firefox", "/usr/bin/firefox -ProfileManager -no-remote")
-launcher( XK_g, "gimp", "/usr/bin/gimp")
-launcher( XK_period, "lock", "/usr/bin/xlock")
-launcher( XK_minus, "dpms off", "/usr/bin/xset -dpms s off")
-launcher( XK_equal, "dpms on", "/usr/bin/xset +dpms s on")
+/* keysym, label, cmd */
+launcher( XK_x, "xterm", "xterm")
+launcher( XK_b, "firefox", "firefox -ProfileManager -no-remote")
+launcher( XK_g, "gimp", "gimp")
+launcher( XK_period, "lock", "xlock")
+launcher( XK_minus, "dpms off", "xset -dpms s off")
+launcher( XK_equal, "dpms on", "xset +dpms s on")
+launcher( XK_BackSpace, "record window", "recordmydesktop --need-shortcuts --no-frame --windowid %W --fps 60 --workdir ~/.rmd/work --output ~/.rmd/caps/vwm.ogv")
+launcher( XK_Delete, "record desktop", "recordmydesktop --need-shortcuts --no-frame --fps 60 --workdir ~/.rmd/work --output ~/.rmd/caps/vwm.ogv")
diff --git a/src/vmon.c b/src/vmon.c
index 5f865ab..cc60c5c 100644
--- a/src/vmon.c
+++ b/src/vmon.c
@@ -1,7 +1,7 @@
/*
* \/\/\
*
- * Copyright (C) 2012-2021 Vito Caputo - <vcaputo@pengaru.com>
+ * Copyright (C) 2012-2022 Vito Caputo - <vcaputo@pengaru.com>
*
* 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
@@ -54,6 +54,8 @@ typedef struct vmon_t {
char *output_dir;
char *name;
unsigned n_snapshots;
+ const char * const *execv;
+ unsigned n_execv;
} vmon_t;
@@ -63,7 +65,7 @@ typedef struct vmon_t {
#define WIDTH_MIN 200
#define HEIGHT_MIN 28
-static volatile int got_sigchld, got_sigusr1;
+static volatile int got_sigchld, got_sigusr1, got_sigint, got_sigquit;
/* return if arg == flag or altflag if provided */
static int is_flag(const char *arg, const char *flag, const char *altflag)
@@ -79,7 +81,7 @@ static int is_flag(const char *arg, const char *flag, const char *altflag)
/* parse integer out of opt, stores parsed opt in *res on success */
-static int parse_flag_int(char * const *flag, char * const *end, char * const *opt, int min, int max, int *res)
+static int parse_flag_int(const char * const *flag, const char * const *end, const char * const *opt, int min, int max, int *res)
{
long int num;
char *endp;
@@ -123,7 +125,7 @@ static int parse_flag_int(char * const *flag, char * const *end, char * const *o
/* parse string out of opt, stores parsed opt in newly allocated *res on success */
-static int parse_flag_str(char * const *flag, char * const *end, char * const *opt, int min_len, char **res)
+static int parse_flag_str(const char * const *flag, const char * const *end, const char * const *opt, int min_len, char **res)
{
char *tmp;
@@ -209,7 +211,7 @@ static void print_copyright(void)
{
puts(
"\n"
- "Copyright (C) 2012-2021 Vito Caputo <vcaputo@pengaru.com>\n"
+ "Copyright (C) 2012-2022 Vito Caputo <vcaputo@pengaru.com>\n"
"\n"
"This program comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
"you are welcome to redistribute it under certain conditions. For details\n"
@@ -231,10 +233,151 @@ static void handle_sigusr1(int signum)
}
+static void handle_sigint(int signum)
+{
+ got_sigint++;
+}
+
+
+static void handle_sigquit(int signum)
+{
+ got_sigquit++;
+}
+
+
+/* sanitize name so it's usable as a filename */
+static char * filenamify(const char *name)
+{
+ char *filename;
+
+ assert(name);
+
+ filename = calloc(strlen(name) + 1, sizeof(*name));
+ if (!filename) {
+ VWM_PERROR("unable to allocate filename for name \"%s\"", name);
+ return NULL;
+ }
+
+ for (size_t i = 0; name[i]; i++) {
+ char c = name[i];
+
+ switch (c) {
+ /* replace characters relevant to path interpolation */
+ case '/':
+ c = '\\';
+ break;
+
+ case '.':
+ /* no leading dot, and no ".." */
+ if (i == 0 || (i == 1 && !name[2]))
+ c = '_';
+ break;
+ }
+
+ filename[i] = c;
+ }
+
+ return filename;
+}
+
+
+/* return an interpolated copy of arg */
+static char * arg_interpolate(const vmon_t *vmon, const char *arg)
+{
+ FILE *memfp;
+ char *xarg = NULL;
+ size_t xarglen;
+ int fmt = 0;
+
+ assert(vmon);
+ assert(arg);
+
+ 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': /* vmon's X window id in hex */
+ fprintf(memfp, "%#x", (unsigned)vmon->window);
+ break;
+
+ case 'n': /* --name verbatim */
+ if (!vmon->name) {
+ VWM_ERROR("%%n requires --name");
+ goto _err;
+ }
+
+ fprintf(memfp, "%s", vmon->name);
+ break;
+
+ case 'N': { /* --name sanitized for filename use */
+ char *filename;
+
+ if (!vmon->name) {
+ VWM_ERROR("%%N requires --name");
+ goto _err;
+ }
+
+ filename = filenamify(vmon->name);
+ if (!filename)
+ goto _err;
+
+ fprintf(memfp, "%s", filename);
+ free(filename);
+ break;
+ }
+
+ case 'O': /* --output-dir */
+ fprintf(memfp, "%s", vmon->output_dir);
+ break;
+
+ case 'P': /* getpid() of vmon, convenient for triggering png snapshots on SIGUSR1 */
+ /* XXX: note this assumes arg_interpolate() occurs pre-fork, true ATM */
+ fprintf(memfp, "%li", (long)getpid());
+ 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;
+}
+
+
/* parse and apply argv, implementing an strace-like cli, mutates vmon. */
-static int vmon_handle_argv(vmon_t *vmon, int argc, char * const argv[])
+static int vmon_handle_argv(vmon_t *vmon, int argc, const char * const *argv)
{
- char *const*end = &argv[argc - 1], *const*last = argv;
+ const char * const *end = &argv[argc - 1], * const *last = argv;
assert(vmon);
assert(!vmon->pid);
@@ -272,7 +415,7 @@ static int vmon_handle_argv(vmon_t *vmon, int argc, char * const argv[])
return 0;
last = ++argv;
- } else if (is_flag(*argv, "-s", "--snapshot")) {
+ } else if (is_flag(*argv, "-s", "--snapshot")) {
vmon->snapshot = 1;
last = argv;
} else if (is_flag(*argv, "-f", "--fullscreen")) {
@@ -315,53 +458,85 @@ static int vmon_handle_argv(vmon_t *vmon, int argc, char * const argv[])
/* if more argv remains, treat as a command to run */
if (last != end) {
- pid_t pid;
+ last++;
+ vmon->n_execv = end - last + 1;
+ vmon->execv = last;
+ }
- if (vmon->pid) {
- VWM_ERROR("combining --pid with a command to run is not yet supported (TODO)\n");
- return 0;
- }
+ return 1;
+}
- last++;
- if (signal(SIGCHLD, handle_sigchld) == SIG_ERR) {
- VWM_PERROR("unable to set SIGCHLD handler");
- return 0;
- }
+/* turn vmon->execv into a new process and execute what's described there after interpolation. */
+int vmon_execv(vmon_t *vmon)
+{
+ char **xargv;
+ pid_t pid;
+
+ assert(vmon);
+ assert(vmon->execv);
+ assert(vmon->n_execv);
+
+ xargv = calloc(vmon->n_execv + 1, sizeof(*xargv));
+ if (!xargv) {
+ VWM_PERROR("unable to allocate interpolated argv");
+ return 0;
+ }
- pid = fork();
- if (pid == -1) {
- VWM_PERROR("unable to fork");
+ /* TODO: clean up xargv on failures perhaps? we just exit anyways */
+
+ /* clone args into xargs, performing any interpolations while at it */
+ for (unsigned i = 0; i < vmon->n_execv; i++) {
+ xargv[i] = arg_interpolate(vmon, vmon->execv[i]);
+ if (!xargv[i]) {
+ VWM_ERROR("unable to allocate interpolated arg");
return 0;
}
+ }
- if (pid == 0) {
- if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) {
- VWM_PERROR("unable to prctl(PR_SET_PDEATHSIG, SIGKILL)");
- exit(EXIT_FAILURE);
- }
+ if (signal(SIGCHLD, handle_sigchld) == SIG_ERR) {
+ VWM_PERROR("unable to set SIGCHLD handler");
+ return 0;
+ }
- /* TODO: would be better to synchronize with the monitoring loop starting before the execvp() occurs,
- * very early program start can be an interesting thing to observe. */
- if (execvp(*last, last) == -1) {
- VWM_PERROR("unable to exec \"%s\"", *last);
- exit(EXIT_FAILURE);
- }
+ if (signal(SIGINT, handle_sigint) == SIG_ERR) {
+ VWM_PERROR("unable to set SIGTERM handler");
+ return 0;
+ }
+
+ if (signal(SIGQUIT, handle_sigquit) == SIG_ERR) {
+ VWM_PERROR("unable to set SIGQUIT handler");
+ return 0;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ VWM_PERROR("unable to fork");
+ return 0;
+ }
+
+ if (pid == 0) {
+ if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) {
+ VWM_PERROR("unable to prctl(PR_SET_PDEATHSIG, SIGKILL)");
+ exit(EXIT_FAILURE);
}
- vmon->pid = pid;
+ /* TODO: would be better to synchronize with the monitoring loop starting before the execvp() occurs,
+ * very early program start can be an interesting thing to observe. */
+ if (execvp(*xargv, xargv) == -1) {
+ VWM_PERROR("unable to exec \"%s\"", *xargv);
+ exit(EXIT_FAILURE);
+ }
}
- /* default to PID 1 when no PID or command was supplied */
- if (!vmon->pid)
- vmon->pid = 1;
+ vmon->pid = pid;
return 1;
}
/* parse argv, connect to X, create window, attach libvmon to monitored process */
-static vmon_t * vmon_startup(int argc, char * const argv[])
+static vmon_t * vmon_startup(int argc, const char * const *argv)
{
vmon_t *vmon;
XRenderPictureAttributes pattr = {};
@@ -403,18 +578,12 @@ static vmon_t * vmon_startup(int argc, char * const argv[])
if (!vmon_handle_argv(vmon, argc, argv)) {
VWM_ERROR("unable to handle arguments");
- goto _err_charts;
+ goto _err_xserver;
}
if (signal(SIGUSR1, handle_sigusr1) == SIG_ERR) {
VWM_PERROR("unable to set SIGUSR1 handler");
- goto _err_charts;
- }
-
- vmon->chart = vwm_chart_create(vmon->charts, vmon->pid, vmon->width, vmon->height, vmon->name);
- if (!vmon->chart) {
- VWM_ERROR("unable to create chart");
- goto _err_charts;
+ goto _err_xserver;
}
vmon->window = XCreateSimpleWindow(vmon->xserver->display, XSERVER_XROOT(vmon->xserver), 0, 0, vmon->width, vmon->height, 1, 0, 0);
@@ -422,15 +591,31 @@ static vmon_t * vmon_startup(int argc, char * const argv[])
XStoreName(vmon->xserver->display, vmon->window, vmon->name);
XGetWindowAttributes(vmon->xserver->display, vmon->window, &wattr);
vmon->picture = XRenderCreatePicture(vmon->xserver->display, vmon->window, XRenderFindVisualFormat(vmon->xserver->display, wattr.visual), 0, &pattr);
-
XMapWindow(vmon->xserver->display, vmon->window);
-
XSelectInput(vmon->xserver->display, vmon->window, StructureNotifyMask|ExposureMask);
+ XSync(vmon->xserver->display, False);
+
+ if (vmon->execv) {
+ if (vmon->pid) {
+ VWM_ERROR("combining --pid with a command to run is not yet supported (TODO)\n");
+ goto _err_win;
+ }
+
+ if (!vmon_execv(vmon))
+ goto _err_win;
+ }
+
+ vmon->chart = vwm_chart_create(vmon->charts, vmon->pid ? : 1, vmon->width, vmon->height, vmon->name);
+ if (!vmon->chart) {
+ VWM_ERROR("unable to create chart");
+ goto _err_win;
+ }
return vmon;
-_err_charts:
- vwm_charts_destroy(vmon->charts);
+_err_win:
+ XDestroyWindow(vmon->xserver->display, vmon->window);
+ XRenderFreePicture(vmon->xserver->display, vmon->picture);
_err_xserver:
vwm_xserver_close(vmon->xserver);
_err_free:
@@ -530,9 +715,9 @@ static int vmon_snapshot_as_png(vmon_t *vmon, FILE *output)
PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
- png_write_info(png_ctx, info_ctx);
- png_write_image(png_ctx, row_pointers);
- png_write_end(png_ctx, NULL);
+ png_write_info(png_ctx, info_ctx);
+ png_write_image(png_ctx, row_pointers);
+ png_write_end(png_ctx, NULL);
XDestroyImage(chart_as_ximage);
free(row_pointers);
@@ -545,44 +730,33 @@ static int vmon_snapshot(vmon_t *vmon)
{
struct tm *start_time;
char start_str[32];
+ char *name = NULL;
char path[4096];
- char name[200] = {};
FILE *output;
int r;
assert(vmon);
if (vmon->name) {
- for (int i = 0; i < sizeof(name) - 1 && vmon->name[i]; i++) {
- char c = vmon->name[i];
- switch (c) {
- /* replace characters relevant to path interpolation */
- case '/':
- c = '\\';
- break;
-
- case '.':
- /* no leading dot, and no ".." */
- if (i == 0 || (i == 1 && !vmon->name[2]))
- c = '_';
- break;
- default:
- }
- name[i] = c;
- }
+ name = filenamify(vmon->name);
+ if (!name)
+ return -errno;
}
- if (mkdir(vmon->output_dir, 0755) == -1 && errno != EEXIST)
+ if (mkdir(vmon->output_dir, 0755) == -1 && errno != EEXIST) {
+ free(name);
return -errno;
+ }
start_time = localtime(&vmon->start_time);
strftime(start_str, sizeof(start_str), "%m.%d.%y-%T", start_time);
snprintf(path, sizeof(path), "%s/%s%s%s-%u.png",
vmon->output_dir,
- name,
- vmon->name ? "-" : "",
+ name ? name : "",
+ name ? "-" : "",
start_str,
vmon->n_snapshots++);
+ free(name);
output = fopen(path, "w+");
if (!output)
@@ -637,7 +811,7 @@ static void vmon_process_event(vmon_t *vmon)
}
-int main(int argc, char * const argv[])
+int main(int argc, const char * const *argv)
{
vmon_t *vmon;
struct pollfd pfd = { .events = POLLIN };
@@ -662,6 +836,18 @@ int main(int argc, char * const argv[])
if (XPending(vmon->xserver->display) || poll(&pfd, 1, delay) > 0)
vmon_process_event(vmon);
+ if (got_sigint > 2 || got_sigquit > 2) {
+ vmon->done = 1;
+ } else if (got_sigint == 1) {
+ got_sigint++;
+
+ kill(vmon->pid, SIGINT);
+ } else if (got_sigquit == 1) {
+ got_sigquit++;
+
+ kill(vmon->pid, SIGQUIT);
+ }
+
if (got_sigchld) {
int status;
diff --git a/src/window.c b/src/window.c
index 5410ba6..61931c1 100644
--- a/src/window.c
+++ b/src/window.c
@@ -81,7 +81,7 @@ vwm_window_t * vwm_win_lookup(vwm_t *vwm, Window win)
/* return the currently focused window, may return NULL */
-vwm_window_t * vwm_win_get_focused(vwm_t *vwm)
+vwm_window_t * vwm_win_get_focused(const vwm_t *vwm)
{
return vwm->focused_desktop->focused_window;
}
@@ -464,7 +464,10 @@ static void vwm_win_assimilate(vwm_t *vwm, vwm_window_t *vwin)
/* FIXME TODO: there's some situation where we get here but focused_desktop->focused_window == NULL,
* which shouldn't be possible; for there to be a non-empty screen, the focused_desktop must have a focused_window.
*/
- scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwm->focused_desktop->focused_window->xwindow);
+ if (vwm->focused_desktop->focused_window)
+ scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, vwm->focused_desktop->focused_window->xwindow);
+ else
+ VWM_BUG("Screen is not empty, but vwm->focused_desktop->focused_window is NULL!");
}
changes.x = scr->x_org;
@@ -555,7 +558,7 @@ vwm_window_t * vwm_win_manage_xwin(vwm_t *vwm, vwm_xwindow_t *xwin)
/* if the desktop has no focused window yet, automatically focus the newly managed one */
if (!vwm->focused_desktop->focused_window) {
- VWM_TRACE("Mapped new window \"%s\" is alone on desktop \"%s\", focusing", xwin->name, vwm->focused_desktop->name);
+ VWM_TRACE("Mapped new window \"%s\" is alone on desktop \"%p\", focusing", xwin->name, vwm->focused_desktop);
vwm_win_focus(vwm, vwin);
}
diff --git a/src/window.h b/src/window.h
index daddf14..74f30ab 100644
--- a/src/window.h
+++ b/src/window.h
@@ -42,7 +42,7 @@ void vwm_win_unmap(vwm_t *vwm, vwm_window_t *vwin);
void vwm_win_map(vwm_t *vwm, vwm_window_t *vwin);
vwm_window_t * vwm_win_mru(vwm_t *vwm, vwm_window_t *vwin);
vwm_window_t * vwm_win_lookup(vwm_t *vwm, Window win);
-vwm_window_t * vwm_win_get_focused(vwm_t *vwm);
+vwm_window_t * vwm_win_get_focused(const vwm_t *vwm);
void vwm_win_set_focused(vwm_t *vwm, vwm_window_t *vwin);
typedef enum _vwm_side_t {
diff --git a/src/xevent.c b/src/xevent.c
index 943ba78..8ee062c 100644
--- a/src/xevent.c
+++ b/src/xevent.c
@@ -152,13 +152,8 @@ void vwm_xevent_handle_map_notify(vwm_t *vwm, XMapEvent *ev)
if ((xwin = vwm_xwin_lookup(vwm, ev->window))) {
- /* XXX: in some circumstances (randomly mplayer -fs) it we see an event sequence for a window like:
- * create_notify->map_request (manage)->configure_request->unmap_notify (unmanage)->configure_notify->map_notify (unmanaged!)
- * which unless the window's an override_redirect is incorrect.
- * So implicitly manage the window if it's not managed and !override_redirect, since it's now mapped.
- */
- if (!xwin->managed && !xwin->attrs.override_redirect)
- xwin->managed = vwm_win_manage_xwin(vwm, xwin);
+ if (!xwin->managed && vwm_xwin_should_manage(vwm, xwin))
+ vwm_win_manage_xwin(vwm, xwin);
if (xwin->managed && xwin->managed->mapping) {
VWM_TRACE("swallowed vwm-induced MapNotify");
diff --git a/src/xwindow.c b/src/xwindow.c
index 18ebf5f..0b0cfbe 100644
--- a/src/xwindow.c
+++ b/src/xwindow.c
@@ -109,6 +109,35 @@ void vwm_xwin_setup_chart(vwm_t *vwm, vwm_xwindow_t *xwin)
}
}
+/* override_redirect windows typically should not be managed, and it'd be nice if we could
+ * just blindly respect that, but X is a dumpster fire and for multiple reasons I'm going
+ * to use some heuristics to only not manage override_redirect windows when they're substantially
+ * smaller than the size of the display (popup/popover type shit).
+ *
+ * When any old X client can create a fullscreen override_redirect window it not only makes
+ * fullscreen games and shit not explicitly focusable/managable from a vwm perspective, it
+ * also creates a real potential security issue.
+ */
+int vwm_xwin_should_manage(vwm_t *vwm, vwm_xwindow_t *xwin)
+{
+ const vwm_screen_t *scr;
+
+ if (!xwin->attrs.override_redirect)
+ return 1;
+
+ scr = vwm_screen_find(vwm, VWM_SCREEN_REL_XWIN, xwin);
+ if (!scr)
+ return 1;
+
+ /* TODO: for now just using an >= fullscreen heuristic, but should really
+ * trigger for > XX% coverage. This suffices for managing annoying
+ * override_redirect fullscreen windows.
+ */
+ if (xwin->attrs.width >= scr->width && xwin->attrs.height >= scr->height)
+ return 1;
+
+ return 0;
+}
/* creates and potentially manages a new window (called in response to CreateNotify events, and during startup for all existing windows) */
/* if the window is already mapped and not an override_redirect window, it becomes managed here. */
@@ -159,7 +188,10 @@ vwm_xwindow_t * vwm_xwin_create(vwm_t *vwm, Window win, vwm_grab_mode_t grabbed)
list_add_tail(&xwin->xwindows, &vwm->xwindows); /* created windows are always placed on the top of the stacking order */
- if (!attrs.override_redirect && xwin->client_mapped)
+ VWM_TRACE_WIN(win, "name=\"%s\" override_redirect=%i client_mapped=%i\n",
+ xwin->name, (int)attrs.override_redirect, (int)xwin->client_mapped);
+
+ if (xwin->client_mapped && vwm_xwin_should_manage(vwm, xwin))
vwm_win_manage_xwin(vwm, xwin);
_out_grabbed:
diff --git a/src/xwindow.h b/src/xwindow.h
index 1318421..65d0725 100644
--- a/src/xwindow.h
+++ b/src/xwindow.h
@@ -41,6 +41,7 @@ void vwm_xwin_message(vwm_t *vwm, vwm_xwindow_t *xwin, Atom type, long foo);
vwm_xwindow_t * vwm_xwin_lookup(vwm_t *vwm, Window win);
int vwm_xwin_is_mapped(vwm_t *vwm, vwm_xwindow_t *xwin);
void vwm_xwin_monitor(vwm_t *vwm, vwm_xwindow_t *xwin);
+int vwm_xwin_should_manage(vwm_t *vwm, vwm_xwindow_t *xwin);
vwm_xwindow_t * vwm_xwin_create(vwm_t *vwm, Window win, vwm_grab_mode_t grabbed);
void vwm_xwin_destroy(vwm_t *vwm, vwm_xwindow_t *xwin);
void vwm_xwin_restack(vwm_t *vwm, vwm_xwindow_t *xwin, Window new_above);
© All Rights Reserved