From 57effd65b29c5e4215b0408f416df0f5683a8ea4 Mon Sep 17 00:00:00 2001
From: Axel Wagner <mail@merovius.de>
Date: Sun, 5 Aug 2012 21:41:36 +0200
Subject: [PATCH] Make horizontal edge-borders hidable too

---
 docs/userguide   |  8 ++++----
 include/config.h |  7 ++++---
 include/data.h   |  6 ++++--
 src/cfgparse.l   |  1 +
 src/cfgparse.y   | 13 ++++++++++++-
 src/con.c        | 34 ++++++++++++++++++++++++----------
 src/x.c          | 22 ++++++++++++++--------
 7 files changed, 63 insertions(+), 28 deletions(-)

diff --git a/docs/userguide b/docs/userguide
index 46a19e93..37e55d46 100644
--- a/docs/userguide
+++ b/docs/userguide
@@ -471,17 +471,17 @@ new_window 1pixel
 === Hiding vertical borders
 
 You can hide vertical borders adjacent to the screen edges using
-+hide_edge_borders+. This is useful if you are using scrollbars. This option is
-disabled by default.
++hide_edge_borders+. This is useful if you are using scrollbars, or do not want
+to waste even two pixels in displayspace. Default is none.
 
 *Syntax*:
 ----------------------------
-hide_edge_borders <yes|no>
+hide_edge_borders <none|vertical|horizontal|both>
 ----------------------------
 
 *Example*:
 ----------------------
-hide_edge_borders yes
+hide_edge_borders vertical
 ----------------------
 
 === Arbitrary commands for specific windows (for_window)
diff --git a/include/config.h b/include/config.h
index b6f356ad..ebb24864 100644
--- a/include/config.h
+++ b/include/config.h
@@ -108,10 +108,11 @@ struct Config {
      * It is not planned to add any different focus models. */
     bool disable_focus_follows_mouse;
 
-    /** Remove vertical borders if they are adjacent to the screen edge.
+    /** Remove borders if they are adjacent to the screen edge.
      * This is useful if you are reaching scrollbar on the edge of the
-     * screen. By default, this is disabled. */
-    bool hide_edge_borders;
+     * screen or do not want to waste a single pixel of displayspace.
+     * By default, this is disabled. */
+    adjacent_t hide_edge_borders;
 
     /** By default, a workspace bar is drawn at the bottom of the screen.
      * If you want to have a more fancy bar, it is recommended to replace
diff --git a/include/data.h b/include/data.h
index acce59be..a5ac943c 100644
--- a/include/data.h
+++ b/include/data.h
@@ -62,8 +62,10 @@ typedef enum { DONT_KILL_WINDOW = 0, KILL_WINDOW = 1, KILL_CLIENT = 2 } kill_win
 
 /** describes if the window is adjacent to the output (physical screen) edges. */
 typedef enum { ADJ_NONE = 0,
-               ADJ_LEFT_SCREEN_EDGE = 1,
-               ADJ_RIGHT_SCREEN_EDGE = 2} adjacent_t;
+               ADJ_LEFT_SCREEN_EDGE = (1 << 0),
+               ADJ_RIGHT_SCREEN_EDGE = (1 << 1),
+               ADJ_UPPER_SCREEN_EDGE = (1 << 2),
+               ADJ_LOWER_SCREEN_EDGE = (1 << 4)} adjacent_t;
 
 enum {
     BIND_NONE = 0,
diff --git a/src/cfgparse.l b/src/cfgparse.l
index e2705b17..04117624 100644
--- a/src/cfgparse.l
+++ b/src/cfgparse.l
@@ -201,6 +201,7 @@ normal                          { return TOK_NORMAL; }
 none                            { return TOK_NONE; }
 1pixel                          { return TOK_1PIXEL; }
 hide_edge_borders               { return TOK_HIDE_EDGE_BORDERS; }
+both                            { return TOK_BOTH; }
 focus_follows_mouse             { return TOKFOCUSFOLLOWSMOUSE; }
 force_focus_wrapping            { return TOK_FORCE_FOCUS_WRAPPING; }
 force_xinerama                  { return TOK_FORCE_XINERAMA; }
diff --git a/src/cfgparse.y b/src/cfgparse.y
index a2f1b9f4..af7d77cf 100644
--- a/src/cfgparse.y
+++ b/src/cfgparse.y
@@ -736,6 +736,7 @@ void parse_file(const char *f) {
 %token                  TOK_NONE                    "none"
 %token                  TOK_1PIXEL                  "1pixel"
 %token                  TOK_HIDE_EDGE_BORDERS       "hide_edge_borders"
+%token                  TOK_BOTH                    "both"
 %token                  TOKFOCUSFOLLOWSMOUSE        "focus_follows_mouse"
 %token                  TOK_FORCE_FOCUS_WRAPPING    "force_focus_wrapping"
 %token                  TOK_FORCE_XINERAMA          "force_xinerama"
@@ -800,6 +801,8 @@ void parse_file(const char *f) {
 %type   <number>        layout_mode
 %type   <number>        border_style
 %type   <number>        new_window
+%type   <number>        hide_edge_borders
+%type   <number>        edge_hiding_mode
 %type   <number>        new_float
 %type   <number>        colorpixel
 %type   <number>        bool
@@ -1477,13 +1480,21 @@ bool:
     ;
 
 hide_edge_borders:
-    TOK_HIDE_EDGE_BORDERS bool
+    TOK_HIDE_EDGE_BORDERS edge_hiding_mode
     {
         DLOG("hide edge borders = %d\n", $2);
         config.hide_edge_borders = $2;
     }
     ;
 
+edge_hiding_mode:
+    TOK_NONE        { $$ = ADJ_NONE; }
+    | TOK_VERT      { $$ = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; }
+    | TOK_HORIZ     { $$ = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; }
+    | TOK_BOTH      { $$ = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE | ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; }
+    | bool          { $$ = ($1 ? ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE : ADJ_NONE); }
+    ;
+
 focus_follows_mouse:
     TOKFOCUSFOLLOWSMOUSE bool
     {
diff --git a/src/con.c b/src/con.c
index 35ad540c..6f2a85b2 100644
--- a/src/con.c
+++ b/src/con.c
@@ -934,31 +934,41 @@ Con *con_descend_direction(Con *con, direction_t direction) {
  *
  */
 Rect con_border_style_rect(Con *con) {
-    adjacent_t adjacent_to = ADJ_NONE;
+    adjacent_t borders_to_hide = ADJ_NONE;
     Rect result;
-    if (config.hide_edge_borders)
-        adjacent_to = con_adjacent_borders(con);
+    borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
     switch (con_border_style(con)) {
     case BS_NORMAL:
         result = (Rect){2, 0, -(2 * 2), -2};
-        if (adjacent_to & ADJ_LEFT_SCREEN_EDGE) {
+        if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
             result.x -= 2;
             result.width += 2;
         }
-        if (adjacent_to & ADJ_RIGHT_SCREEN_EDGE) {
+        if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) {
             result.width += 2;
         }
+        /* With normal borders we never hide the upper border */
+        if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) {
+            result.height += 2;
+        }
         return result;
 
     case BS_1PIXEL:
         result = (Rect){1, 1, -2, -2};
-        if (adjacent_to & ADJ_LEFT_SCREEN_EDGE) {
+        if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
             result.x -= 1;
             result.width += 1;
         }
-        if (adjacent_to & ADJ_RIGHT_SCREEN_EDGE) {
+        if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) {
             result.width += 1;
         }
+        if (borders_to_hide & ADJ_UPPER_SCREEN_EDGE) {
+            result.y -= 1;
+            result.height += 1;
+        }
+        if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) {
+            result.height += 1;
+        }
         return result;
 
     case BS_NONE:
@@ -975,11 +985,15 @@ Rect con_border_style_rect(Con *con) {
  */
 adjacent_t con_adjacent_borders(Con *con) {
     adjacent_t result = ADJ_NONE;
-    Con *output = con_get_output(con);
-    if (con->rect.x == output->rect.x)
+    Con *workspace = con_get_workspace(con);
+    if (con->rect.x == workspace->rect.x)
         result |= ADJ_LEFT_SCREEN_EDGE;
-    if (con->rect.x + con->rect.width == output->rect.x + output->rect.width)
+    if (con->rect.x + con->rect.width == workspace->rect.x + workspace->rect.width)
         result |= ADJ_RIGHT_SCREEN_EDGE;
+    if (con->rect.y == workspace->rect.y)
+        result |= ADJ_UPPER_SCREEN_EDGE;
+    if (con->rect.y + con->rect.height == workspace->rect.y + workspace->rect.height)
+        result |= ADJ_LOWER_SCREEN_EDGE;
     return result;
 }
 
diff --git a/src/x.c b/src/x.c
index f4d1416f..cd85bcc6 100644
--- a/src/x.c
+++ b/src/x.c
@@ -299,19 +299,19 @@ void x_window_kill(xcb_window_t window, kill_window_t kill_window) {
 void x_draw_decoration(Con *con) {
     Con *parent = con->parent;
     bool leaf = con_is_leaf(con);
-    adjacent_t adjacent_to = ADJ_NONE;
-    if (config.hide_edge_borders)
-        adjacent_to = con_adjacent_borders(con);
+
     /* This code needs to run for:
      *  • leaf containers
      *  • non-leaf containers which are in a stacked/tabbed container
      *
      * It does not need to run for:
+     *  • direct children of outputs
      *  • floating containers (they don’t have a decoration)
      */
     if ((!leaf &&
          parent->layout != L_STACKED &&
          parent->layout != L_TABBED) ||
+        parent->type == CT_OUTPUT ||
         con->type == CT_FLOATING_CON)
         return;
 
@@ -399,6 +399,10 @@ void x_draw_decoration(Con *con) {
 
     /* 3: draw a rectangle in border color around the client */
     if (p->border_style != BS_NONE && p->con_is_leaf) {
+        /* We might hide some borders adjacent to the screen-edge */
+        adjacent_t borders_to_hide = ADJ_NONE;
+        borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
+
         Rect br = con_border_style_rect(con);
 #if 0
         DLOG("con->rect spans %d x %d\n", con->rect.width, con->rect.height);
@@ -411,18 +415,20 @@ void x_draw_decoration(Con *con) {
          * rectangle because some childs are not freely resizable and we want
          * their background color to "shine through". */
         xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){ p->color->background });
-        if (!(adjacent_to & ADJ_LEFT_SCREEN_EDGE)) {
+        if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) {
             xcb_rectangle_t leftline = { 0, 0, br.x, r->height };
             xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline);
         }
-        if (!(adjacent_to & ADJ_RIGHT_SCREEN_EDGE)) {
+        if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) {
             xcb_rectangle_t rightline = { r->width + br.width + br.x, 0, r->width, r->height };
             xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline);
         }
-        xcb_rectangle_t bottomline = { 0, r->height + br.height + br.y, r->width, r->height };
-        xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline);
+        if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) {
+            xcb_rectangle_t bottomline = { 0, r->height + br.height + br.y, r->width, r->height };
+            xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline);
+        }
         /* 1pixel border needs an additional line at the top */
-        if (p->border_style == BS_1PIXEL) {
+        if (p->border_style == BS_1PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) {
             xcb_rectangle_t topline = { br.x, 0, con->rect.width + br.width + br.x, br.y };
             xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline);
         }