]> code.delx.au - spectrwm/commitdiff
Let scrotwm adjust the font size on xterms as it squishes them in tiling
authorRyan McBride <mcbride@countersiege.com>
Sat, 7 Feb 2009 19:49:58 +0000 (19:49 +0000)
committerRyan McBride <mcbride@countersiege.com>
Sat, 7 Feb 2009 19:49:58 +0000 (19:49 +0000)
mode to keep the terminal width above a certan size (set with 'term_width'
in the config file.

We do this by sending the default font size change keystrokes to the xterm
in question. Because xterm does not accept 'synthetic' keystroke events by
default, and we don't want to force users to enable acceptSendEvents for
security reasons, hook XtAppNextEvent in the LD_PRELOAD hack, and clear
the send_event flag on the events in question. CAVEAT: Only works if xterm
is not setuid/setgid.

lib/swm_hack.c
scrotwm.1
scrotwm.c

index 459d8800cffb28f304b43c01a5b9dfaaf76eab0c..5d60d617b584f0635db47bc492b68575351de84f 100644 (file)
 #include <X11/Xlib.h>
 #include <X11/X.h>
 #include <X11/Xatom.h>
+#include <X11/Intrinsic.h>
 
-/* dlopened xlib so we can find the symbols in the real xlib to call them */
-static void        *lib_xlib = NULL;
+/* dlopened libs so we can find the symbols in the real one to call them */
+static void            *lib_xlib = NULL;
+static void            *lib_xtlib = NULL;
 
-static Window       root = None;
+static Window          root = None;
+static int             xterm = 0;
+static Display         *dpy = NULL;
 
 /* Find our root window */
 static              Window
@@ -111,8 +115,10 @@ XCreateWindow(Display * display, Window parent, int x, int y,
        /* find the real Xlib and the real X function */
        if (!lib_xlib)
                lib_xlib = dlopen("libX11.so", RTLD_GLOBAL | RTLD_LAZY);
-       if (!func)
+       if (!func) {
                func = (CWF *) dlsym(lib_xlib, "XCreateWindow");
+               dpy = display;
+       }
 
        if (parent == DefaultRootWindow(display))
                parent = MyRoot(display);
@@ -125,6 +131,10 @@ XCreateWindow(Display * display, Window parent, int x, int y,
                        set_property(display, id, "_SWM_WS", env);
                if ((env = getenv("_SWM_PID")) != NULL)
                        set_property(display, id, "_SWM_PID", env);
+               if ((env = getenv("_SWM_XTERM_FONTADJ")) != NULL) {
+                       unsetenv("_SWM_XTERM_FONTADJ");
+                       xterm = 1;
+               }
        }
        return (id);
 }
@@ -164,6 +174,10 @@ XCreateSimpleWindow(Display * display, Window parent, int x, int y,
                        set_property(display, id, "_SWM_WS", env);
                if ((env = getenv("_SWM_PID")) != NULL)
                        set_property(display, id, "_SWM_PID", env);
+               if ((env = getenv("_SWM_XTERM_FONTADJ")) != NULL) {
+                       unsetenv("_SWM_XTERM_FONTADJ");
+                       xterm = 1;
+               }
        }
        return (id);
 }
@@ -188,3 +202,44 @@ XReparentWindow(Display * display, Window window, Window parent, int x, int y)
 
        return (*func) (display, window, parent, x, y);
 }
+
+typedef                void (ANEF) (XtAppContext app_context, XEvent *event_return);
+int            evcount = 0;
+
+/*
+ * XtAppNextEvent Intercept Hack
+ * Normally xterm rejects "synthetic" (XSendEvent) events to prevent spoofing.
+ * We don't want to disable this completely, it's insecure. But hook here
+ * and allow these mostly harmless ones that we use to adjust fonts.
+ */
+void
+XtAppNextEvent(XtAppContext app_context, XEvent *event_return)
+{
+       static ANEF     *func = NULL;
+       static int      kp_add = 0, kp_subtract = 0;
+
+       /* find the real Xlib and the real X function */
+       if (!lib_xtlib)
+               lib_xtlib = dlopen("libXt.so", RTLD_GLOBAL | RTLD_LAZY);
+       if (!func) {
+               func = (ANEF *) dlsym(lib_xtlib, "XtAppNextEvent");
+               if (dpy != NULL) {
+                       kp_add = XKeysymToKeycode(dpy, XK_KP_Add);
+                       kp_subtract = XKeysymToKeycode(dpy, XK_KP_Subtract);
+               }
+       }
+
+       (*func) (app_context, event_return);
+
+       /* Return here if it's not an Xterm. */
+       if (!xterm)
+               return;
+       
+       /* Allow spoofing of font change keystrokes. */
+       if ((event_return->type == KeyPress ||  
+          event_return->type == KeyRelease) &&
+          event_return->xkey.state == ShiftMask &&
+          (event_return->xkey.keycode == kp_add ||
+          event_return->xkey.keycode == kp_subtract))
+               event_return->xkey.send_event = 0;
+}
index b133aea6ff49ae1180c99e76c4ecf4322d7e95ee..1d215453e9e24695773e32beb657f790fc49db8a 100644 (file)
--- a/scrotwm.1
+++ b/scrotwm.1
@@ -201,6 +201,21 @@ Set to the script that will take screenshots.
 It will be called with "full" or "window" as parameter 1 to indicate what
 screenshot action is expected.
 The script shall handle those cases accordingly.
+.It Cm term_width
+Set a preferred minimum width for the terminal
+If this value is greater than 0,
+.Nm
+will attempt to adjust the font sizes in the terminal to keep the terminal
+width above this number as the window is resized.
+Only 
+.Xr xterm 1
+is currently supported.
+The
+.Xr xterm 1
+binary must not be setuid or setgid, which it is by default on most systems.
+Users may need to set spawn term to use an alternate copy of the
+.Xr xterm 1
+binary without the setgid bit set.
 .El
 .Pp
 Colors need to be specified per the
index 99888d8e9e21c90426b96046c28436f5491ed8f6..835c1d413664ae1378ed0fb981e3acbc256e4dfa 100644 (file)
--- a/scrotwm.c
+++ b/scrotwm.c
@@ -132,6 +132,7 @@ u_int32_t           swm_debug = 0
 #define Y(r)                   (r)->g.y
 #define WIDTH(r)               (r)->g.w
 #define HEIGHT(r)              (r)->g.h
+#define SWM_MAX_FONT_STEPS     (3)
 
 #ifndef SWM_LIB
 #define SWM_LIB                        "/usr/X11R6/lib/swmhack.so"
@@ -151,6 +152,8 @@ Display                     *display;
 
 int                    cycle_empty = 0;
 int                    cycle_visible = 0;
+int                    term_width = 0;
+int                    font_adjusted = 0;
 
 /* dialog windows */
 double                 dialog_ratio = .6;
@@ -222,6 +225,9 @@ struct ws_win {
        int                     floating;
        int                     transient;
        int                     manual;
+       int                     font_size_boundary[SWM_MAX_FONT_STEPS];
+       int                     font_steps;
+       int                     last_inc;
        unsigned long           quirks;
        struct workspace        *ws;    /* always valid */
        struct swm_screen       *s;     /* always valid, never changes */
@@ -335,12 +341,14 @@ struct quirk {
 #define SWM_Q_FLOAT            (1<<0)  /* float this window */
 #define SWM_Q_TRANSSZ          (1<<1)  /* transiend window size too small */
 #define SWM_Q_ANYWHERE         (1<<2)  /* don't position this window */
+#define SWM_Q_XTERM_FONTADJ    (1<<3)  /* adjust xterm fonts when resizing */
 } quirks[] = {
        { "MPlayer",            "xv",           SWM_Q_FLOAT },
        { "OpenOffice.org 2.4", "VCLSalFrame",  SWM_Q_FLOAT },
        { "OpenOffice.org 3.0", "VCLSalFrame",  SWM_Q_FLOAT },
        { "Firefox-bin",        "firefox-bin",  SWM_Q_TRANSSZ},
        { "Gimp",               "gimp",         SWM_Q_FLOAT | SWM_Q_ANYWHERE},
+       { "XTerm",              "xterm",        SWM_Q_XTERM_FONTADJ},
        { NULL,                 NULL,           0},
 };
 
@@ -562,13 +570,23 @@ conf_load(char *filename)
                case 's':
                        if (!strncmp(var, "spawn_term", strlen("spawn_term")))
                                asprintf(&spawn_term[0], "%s", val);
-                       if (!strncmp(var, "screenshot_enabled",
+                       else if (!strncmp(var, "screenshot_enabled",
                            strlen("screenshot_enabled")))
                                ss_enabled = atoi(val);
-                       if (!strncmp(var, "screenshot_app",
+                       else if (!strncmp(var, "screenshot_app",
                            strlen("screenshot_app")))
                                asprintf(&spawn_screenshot[0], "%s", val);
+                       else
+                               goto bad;
                        break;
+
+               case 't':
+                       if (!strncmp(var, "term_width", strlen("term_width")))
+                               term_width = atoi(val);
+                       else
+                               goto bad;
+                       break;
+
                default:
                        goto bad;
                }
@@ -849,6 +867,34 @@ unmap_all(void)
                                XUnmapWindow(display, win->id);
 }
 
+void
+fake_keypress(struct ws_win *win, int keysym, int modifiers)
+{
+       XKeyEvent event;
+
+       event.display = display;        /* Ignored, but what the hell */
+       event.window = win->id;
+       event.root = win->s->root;
+       event.subwindow = None;
+       event.time = CurrentTime;
+       event.x = win->g.x;
+       event.y = win->g.y;
+       event.x_root = 1;
+       event.y_root = 1;
+       event.same_screen = True;
+       event.keycode = XKeysymToKeycode(display, keysym);
+       event.state = modifiers;
+
+       event.type = KeyPress;
+       XSendEvent(event.display, event.window, True,
+           KeyPressMask, (XEvent *)&event);
+
+       event.type = KeyRelease;
+       XSendEvent(event.display, event.window, True,
+           KeyPressMask, (XEvent *)&event);
+
+}
+
 void
 restart(struct swm_region *r, union arg *args)
 {
@@ -950,6 +996,16 @@ spawn(struct swm_region *r, union arg *args)
        wait(0);
 }
 
+void
+spawnterm(struct swm_region *r, union arg *args)
+{
+       DNPRINTF(SWM_D_MISC, "spawnterm\n");
+
+       if (term_width)
+               setenv("_SWM_XTERM_FONTADJ", "", 1);
+       spawn(r, args);
+}
+
 void
 spawnmenu(struct swm_region *r, union arg *args)
 {
@@ -1280,6 +1336,8 @@ stack(void) {
                        r->ws->cur_layout->l_stack(r->ws, &g);
                }
        }
+       if (font_adjusted)
+               font_adjusted--;
        XSync(display, False);
 }
 
@@ -1312,6 +1370,35 @@ stack_floater(struct ws_win *win, struct swm_region *r)
        XConfigureWindow(display, win->id, mask, &wc);
 }
 
+/*
+ * Send keystrokes to terminal to decrease/increase the font size as the
+ * window size changes.
+ */
+void
+adjust_font(struct ws_win *win)
+{
+       if (!(win->quirks & SWM_Q_XTERM_FONTADJ) ||
+           win->floating || win->transient)
+               return;
+       
+       if (win->sh.width_inc && win->last_inc != win->sh.width_inc &&
+           win->g.w / win->sh.width_inc < term_width &&
+           win->font_steps < SWM_MAX_FONT_STEPS) {
+               win->font_size_boundary[win->font_steps] =
+                   (win->sh.width_inc * term_width) + win->sh.base_width;
+               win->font_steps++;
+               font_adjusted++;
+               win->last_inc = win->sh.width_inc;
+               fake_keypress(win, XK_KP_Subtract, ShiftMask);
+       } else if (win->font_steps && win->last_inc != win->sh.width_inc &&
+           win->g.w > win->font_size_boundary[win->font_steps - 1]) {
+               win->font_steps--;
+               font_adjusted++;
+               win->last_inc = win->sh.width_inc;
+               fake_keypress(win, XK_KP_Add, ShiftMask);
+       }
+}
+
 #define SWAPXY(g)      do {                            \
        int tmp;                                        \
        tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp;    \
@@ -1461,6 +1548,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
                        win->g.w = wc.width = win_g.w;
                        win->g.h = wc.height = win_g.h;
                }
+               adjust_font(win);
                mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
                XConfigureWindow(display, win->id, mask, &wc);
                XMapRaised(display, win->id);
@@ -1739,7 +1827,7 @@ struct key {
        { MODKEY,               XK_k,           focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
        { MODKEY | ShiftMask,   XK_j,           swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
        { MODKEY | ShiftMask,   XK_k,           swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
-       { MODKEY | ShiftMask,   XK_Return,      spawn,          {.argv = spawn_term} },
+       { MODKEY | ShiftMask,   XK_Return,      spawnterm,      {.argv = spawn_term} },
        { MODKEY,               XK_p,           spawnmenu,      {.argv = spawn_menu} },
        { MODKEY | ShiftMask,   XK_q,           quit,           {0} },
        { MODKEY,               XK_q,           restart,        {0} },
@@ -2298,8 +2386,19 @@ configurerequest(XEvent *e)
 void
 configurenotify(XEvent *e)
 {
+       struct ws_win           *win;
+       long                    mask;
+
        DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n",
            e->xconfigure.window);
+
+       win = find_window(e->xconfigure.window);
+       XMapWindow(display, win->id);
+       XGetWMNormalHints(display, win->id, &win->sh, &mask);
+       adjust_font(win);
+       XMapWindow(display, win->id);
+       if (font_adjusted)
+               stack();
 }
 
 void