diff options
-rw-r--r-- | src/charts.c | 12 | ||||
-rw-r--r-- | src/key.c | 28 | ||||
-rw-r--r-- | src/launch.c | 124 | ||||
-rw-r--r-- | src/launchers.def | 16 | ||||
-rw-r--r-- | src/vmon.c | 334 | ||||
-rw-r--r-- | src/window.c | 9 | ||||
-rw-r--r-- | src/window.h | 2 | ||||
-rw-r--r-- | src/xevent.c | 9 | ||||
-rw-r--r-- | src/xwindow.c | 34 | ||||
-rw-r--r-- | src/xwindow.h | 1 |
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. */ @@ -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") @@ -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); |