i3lock-color

The world's most popular non-default computer lockscreen.
Index Commits Files Refs README LICENSE
commit 3d6b294d117d1f2ebf387cae3af708169612c61d
parent 73948efc1a0923d5aa36ff1c6d67ac4368cc74d3
Author: Chris Guillott <chris@techfo.xyz>
Date:   Sun, 19 Nov 2017 15:12:43 -0500

initial keylayout work

Diffstat:
Mi3lock.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mlock.sh | 2++
Munlock_indicator.c | 331++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Munlock_indicator.h | 27+++++++++++++++++++++++++++
4 files changed, 313 insertions(+), 182 deletions(-)
diff --git a/i3lock.c b/i3lock.c
@@ -70,6 +70,7 @@ char ringwrongcolor[9] = "7d3300ff";
 char ringcolor[9] = "337d00ff";
 char linecolor[9] = "000000ff";
 char textcolor[9] = "000000ff";
+char layoutcolor[9] = "000000ff";
 char timecolor[9] = "000000ff";
 char datecolor[9] = "000000ff";
 char keyhlcolor[9] = "33db00ff";
@@ -95,22 +96,27 @@ char time_format[32] = "%H:%M:%S\0";
 char date_format[32] = "%A, %m %Y\0";
 char time_font[32] = "sans-serif\0";
 char date_font[32] = "sans-serif\0";
+char layout_font[32] = "sans-serif\0";
 char ind_x_expr[32] = "x + (w / 2)\0";
 char ind_y_expr[32] = "y + (h / 2)\0";
 char time_x_expr[32] = "ix - (cw / 2)\0";
 char time_y_expr[32] = "iy - (ch / 2)\0";
 char date_x_expr[32] = "tx\0";
 char date_y_expr[32] = "ty+30\0";
+char layout_x_expr[32] = "dx\0";
+char layout_y_expr[32] = "dy+30\0";
 
 double time_size = 32.0;
 double date_size = 14.0;
 double text_size = 28.0;
 double modifier_size = 14.0;
+double layout_size = 14.0;
 double circle_radius = 90.0;
 double ring_width = 7.0;
 
 char* verif_text = "verifying…";
 char* wrong_text = "wrong!";
+char* layout_text = NULL;
 
 /* opts for blurring */
 bool blur = false;
@@ -173,47 +179,77 @@ void u8_dec(char *s, int *i) {
  * Loads the XKB keymap from the X11 server and feeds it to xkbcommon.
  * Necessary so that we can properly let xkbcommon track the keyboard state and
  * translate keypresses to utf-8.
- *
+ * arg: 0 (show full string returned)
+ *      1 (show the text, sans parenthesis)
+ *      2 (show just what's in the parenthesis)
  */
 
-char* get_keylayoutname(void) {
+char* get_keylayoutname(int mode) {
+
     Display *display;
     XkbDescPtr keyboard;
     XkbStateRec    state;
-    int result;
-
-    display = XkbOpenDisplay( getenv("DISPLAY") , NULL, NULL, NULL, NULL, &result );
-    if( !display ) {
-        errx(1, "X server unreachable");
+    char* answer;
+    char* newans = NULL;
+    int res, i;
+
+    display = XkbOpenDisplay(getenv("DISPLAY"), NULL, NULL, NULL, NULL, &res);
+    if(!display) {
+        DEBUG("X server unreachable\n");
+        return NULL;
     }
 
     keyboard = XkbAllocKeyboard();
 
-    if ( XkbGetNames(display, XkbGroupNamesMask, keyboard) != Success ) {
-        errx(1, "Error obtaining symbolic names");
+    if (XkbGetNames(display, XkbGroupNamesMask, keyboard) != Success ) {
+        DEBUG("Error obtaining symbolic names");
+        return NULL;
     }
 
-    if( XkbGetState(display, XkbUseCoreKbd, &state) != Success ) {
-        errx(1, "Error getting keyboard state");
+    if(XkbGetState(display, XkbUseCoreKbd, &state) != Success) {
+        DEBUG("Error getting keyboard state");
+        return NULL;
     }    
 
-    printf( "aaa: %s\n", XGetAtomName(display, keyboard->names->groups[state.group]) );
-
-    Atom current_group;
-    int i = 0;
-    for (; i < XkbNumKbdGroups; i++) {
-        if ( (current_group = keyboard->names->groups[i]) != 0 ) {
-            char* group_name = XGetAtomName(display, current_group);
-            if (group_name != NULL) {
-                printf( "bbb: %d. %s\n", i, group_name );
+    answer = XGetAtomName(display, keyboard->names->groups[state.group]);
+    DEBUG("keylayout answer is: [%s]\n", answer);
+    switch (mode) {
+        case 1:
+            // truncate the string at the first parens
+            for(i = 0; answer[i] != '\0'; ++i) {
+                if (answer[i] == '(') {
+                    if (i != 0 && answer[i - 1] == ' ') {
+                        answer[i - 1] = '\0';
+                        break;
+                    } else {
+                        answer[i] = '\0';
+                        break;
+                    }
+                }
             }
-            XFree(group_name);
-        }
+            break;
+        case 2:
+            for(i = 0; answer[i] != '\0'; ++i) {
+                if (answer[i] == '(') {
+                    newans = &answer[i + 1];
+                } else if (answer[i] == ')' && newans != NULL) {
+                    answer[i] = '\0';
+                    break;
+                }
+            }
+            if (newans != NULL)
+                answer = newans;
+            break;
+        case 0:
+            // fall through
+        default:
+            break;
     }
-
+    DEBUG("answer after mode parsing: [%s]\n", answer);
     // Free symbolic names structures 
     XkbFreeNames(keyboard, XkbGroupNamesMask, True);
-    return NULL;
+    // note: this is called in option parsing, so this debug() may not trigger unless --debug is the first option
+    return answer;
 }
 
 static bool load_keymap(void) {
@@ -965,6 +1001,7 @@ int main(int argc, char *argv[]) {
         {"ringcolor", required_argument, NULL, 0},        // --r-c
         {"linecolor", required_argument, NULL, 0},        // --l-c
         {"textcolor", required_argument, NULL, 0},        // --t-c
+        {"layoutcolor", required_argument, NULL, 0},        // --t-c
         {"timecolor", required_argument, NULL, 0},       
         {"datecolor", required_argument, NULL, 0},       
         {"keyhlcolor", required_argument, NULL, 0},       // --k-c
@@ -983,12 +1020,16 @@ int main(int argc, char *argv[]) {
         
         {"timestr", required_argument, NULL, 0},
         {"datestr", required_argument, NULL, 0},
+        {"keylayout", required_argument, NULL, 0},
         {"timefont", required_argument, NULL, 0},
         {"datefont", required_argument, NULL, 0},
+        {"layoutfont", required_argument, NULL, 0},
         {"timesize", required_argument, NULL, 0},
         {"datesize", required_argument, NULL, 0},
+        {"layoutsize", required_argument, NULL, 0},
         {"timepos", required_argument, NULL, 0},
         {"datepos", required_argument, NULL, 0},
+        {"layoutpos", required_argument, NULL, 0},
         {"indpos", required_argument, NULL, 0},
 
         {"veriftext", required_argument, NULL, 0},
@@ -1005,8 +1046,6 @@ int main(int argc, char *argv[]) {
     if ((username = pw->pw_name) == NULL)
         errx(EXIT_FAILURE, "pw->pw_name is NULL.\n");
 
-    (void) get_keylayoutname();
-
     char *optstring = "hvnbdc:p:ui:teI:frsS:kB:";
     while ((o = getopt_long(argc, argv, optstring, longopts, &longoptind)) != -1) {
         switch (o) {
@@ -1167,6 +1206,16 @@ int main(int argc, char *argv[]) {
                     if (strlen(arg) != 8 || sscanf(arg, "%08[0-9a-fA-F]", textcolor) != 1)
                         errx(1, "textcolor is invalid, color must be given in 4-byte format: rrggbbaa\n");
                 }
+                else if (strcmp(longopts[longoptind].name, "layoutcolor") == 0) {
+                    char *arg = optarg;
+
+                    /* Skip # if present */
+                    if (arg[0] == '#')
+                        arg++;
+
+                    if (strlen(arg) != 8 || sscanf(arg, "%08[0-9a-fA-F]", layoutcolor) != 1)
+                        errx(1, "layoutcolor is invalid, color must be given in 4-byte format: rrggbbaa\n");
+                }
                 else if (strcmp(longopts[longoptind].name, "timecolor") == 0) {
                     char *arg = optarg;
 
@@ -1217,6 +1266,12 @@ int main(int argc, char *argv[]) {
                     if (strlen(arg) != 8 || sscanf(arg, "%08[0-9a-fA-F]", separatorcolor) != 1)
                         errx(1, "separator is invalid, color must be given in 4-byte format: rrggbbaa\n");
                 }
+                else if (strcmp(longopts[longoptind].name, "keylayout") == 0) {
+                    // if layout is NULL, do nothing
+                    // if not NULL, attempt to display stuff
+                    // need to code some sane defaults for it
+                    layout_text = get_keylayoutname(atoi(optarg));
+                }
                 else if (strcmp(longopts[longoptind].name, "timestr") == 0) {
                     //read in to timestr
                     if (strlen(optarg) > 31) {
@@ -1231,6 +1286,13 @@ int main(int argc, char *argv[]) {
                     }
                     strcpy(date_format,optarg);
                 }
+                else if (strcmp(longopts[longoptind].name, "layoutfont") == 0) {
+                    //read in to time_font
+                    if (strlen(optarg) > 31) {
+                        errx(1, "layout font string can be at most 31 characters\n");
+                    }
+                    strcpy(layout_font,optarg);
+                }
                 else if (strcmp(longopts[longoptind].name, "timefont") == 0) {
                     //read in to time_font
                     if (strlen(optarg) > 31) {
@@ -1261,6 +1323,14 @@ int main(int argc, char *argv[]) {
                     if (date_size < 1)
                         errx(1, "datesize must be larger than 0\n");
                 }
+                else if (strcmp(longopts[longoptind].name, "layoutsize") == 0) {
+                    char *arg = optarg;
+
+                    if (sscanf(arg, "%lf", &layout_size) != 1)
+                        errx(1, "layoutsize must be a number\n");
+                    if (date_size < 1)
+                        errx(1, "layoutsize must be larger than 0\n");
+                }
                 else if (strcmp(longopts[longoptind].name, "indpos") == 0) {
                     //read in to ind_x_expr and ind_y_expr
                     if (strlen(optarg) > 31) {
@@ -1294,6 +1364,17 @@ int main(int argc, char *argv[]) {
                         errx(1, "datepos must be of the form x:y\n");
                     }
                 }
+                else if (strcmp(longopts[longoptind].name, "layoutpos") == 0) {
+                    //read in to time_x_expr and time_y_expr
+                    if (strlen(optarg) > 31) {
+                        // this is overly restrictive since both the x and y string buffers have size 32, but it's easier to check.
+                        errx(1, "layout position string can be at most 31 characters\n");
+                    }
+                    char* arg = optarg;
+                    if (sscanf(arg, "%30[^:]:%30[^:]", layout_x_expr, layout_y_expr) != 2) {
+                        errx(1, "layoutpos must be of the form x:y\n");
+                    }
+                }
                 else if (strcmp(longopts[longoptind].name, "refresh-rate") == 0) {
                     char* arg = optarg;
                     refresh_rate = strtof(arg, NULL);
@@ -1463,6 +1544,8 @@ int main(int argc, char *argv[]) {
     xcb_change_window_attributes(conn, screen->root, XCB_CW_EVENT_MASK,
                                  (uint32_t[]){XCB_EVENT_MASK_STRUCTURE_NOTIFY});
 
+    init_colors_once();
+
     if (image_path) {
         /* Create a pixmap to render on, fill it with the background color */
         img = cairo_image_surface_create_from_png(image_path);
diff --git a/lock.sh b/lock.sh
@@ -22,6 +22,7 @@ V='#bb00bbbb'  # verifying
 --textcolor=$T        \
 --timecolor=$T        \
 --datecolor=$T        \
+--layoutcolor=$T      \
 --keyhlcolor=$W       \
 --bshlcolor=$W        \
 \
@@ -31,6 +32,7 @@ V='#bb00bbbb'  # verifying
 --indicator           \
 --timestr="%H:%M:%S"  \
 --datestr="%A, %m %Y" \
+--keylayout 2         \
 
 # --veriftext="Drinking verification can..."
 # --wrongtext="Nope!"
diff --git a/unlock_indicator.c b/unlock_indicator.c
@@ -75,6 +75,7 @@ extern char ringwrongcolor[9];
 extern char ringcolor[9];
 extern char linecolor[9];
 extern char textcolor[9];
+extern char layoutcolor[9];
 extern char timecolor[9];
 extern char datecolor[9];
 extern char keyhlcolor[9];
@@ -91,20 +92,25 @@ extern char time_format[32];
 extern char date_format[32];
 extern char time_font[32];
 extern char date_font[32];
+extern char layout_font[32];
 extern char ind_x_expr[32];
 extern char ind_y_expr[32];
 extern char time_x_expr[32];
 extern char time_y_expr[32];
 extern char date_x_expr[32];
 extern char date_y_expr[32];
+extern char layout_x_expr[32];
+extern char layout_y_expr[32];
 
 extern double time_size;
 extern double date_size;
 extern double text_size;
 extern double modifier_size;
+extern double layout_size;
 
 extern char* verif_text;
 extern char* wrong_text;
+extern char* layout_text;
 
 /* Whether the failed attempts should be displayed. */
 extern bool show_failed_attempts;
@@ -133,6 +139,94 @@ static xcb_visualtype_t *vistype;
 unlock_state_t unlock_state;
 auth_state_t auth_state;
 
+// color arrays
+rgba_t insidever16[4];
+rgba_t insidewrong16[4];
+rgba_t inside16[4];
+rgba_t ringver16[4];
+rgba_t ringwrong16[4];
+rgba_t ring16[4];
+rgba_t line16[4];
+rgba_t text16[4];
+rgba_t layout16[4];
+rgba_t time16[4];
+rgba_t date16[4];
+rgba_t keyhl16[4];
+rgba_t bshl16[4];
+rgba_t sep16[4];
+// just rgb
+rgb_t  rgb16[3];
+
+/*
+ * Initialize all the color arrays once.
+ * Called once after options are parsed.
+ */
+
+/*
+    colorstring: 8-character RGBA string ("ff0000ff", "00000000", "ffffffff", etc)
+    colorstring16: array of 4 integers (r, g, b, a).
+    MAKE_COLORGROUPS(colorstring, colorstring16) =>
+
+    char colorstring_tmparr[4][3] = {{colorstring[0], colorstring[1], '\0'},
+                                     {colorstring[2], colorstring[3], '\0'},
+                                     {colorstring[4], colorstring[5], '\0'},
+                                       {colorstring[6], colorstring[7], '\0'}};
+    uint32_t colorstring16[4] = {(strtol(colorstring_tmparr[0], NULL, 16)),
+                                 (strtol(colorstring_tmparr[1], NULL, 16)),
+                                 (strtol(colorstring_tmparr[2], NULL, 16)),
+                     (strtol(colorstring_tmparr[3], NULL, 16))};
+ */
+
+inline void set_color(char* dest, const char* src, int offset) {
+    dest[0] = src[offset];
+    dest[1] = src[offset + 1];
+    dest[2] = '\0';
+}
+
+inline void colorgen(rgba_str_t* tmp, const char* src, rgba_t* dest) {
+    set_color(tmp->red, src, 0);
+    set_color(tmp->green, src, 2);
+    set_color(tmp->blue, src, 4);
+    set_color(tmp->alpha, src, 6);
+
+    dest->red = strtol(tmp->red, NULL, 16);
+    dest->green = strtol(tmp->green, NULL, 16);
+    dest->blue = strtol(tmp->blue, NULL, 16);
+    dest->alpha = strtol(tmp->alpha, NULL, 16);
+}
+
+inline void colorgen_rgb(rgb_str_t* tmp, const char* src, rgb_t* dest) {
+    set_color(tmp->red, src, 0);
+    set_color(tmp->green, src, 2);
+    set_color(tmp->blue, src, 4);
+
+    dest->red = strtol(tmp->red, NULL, 16);
+    dest->green = strtol(tmp->green, NULL, 16);
+    dest->blue = strtol(tmp->blue, NULL, 16);
+}
+
+void init_colors_once(void) {
+    rgba_str_t tmp;
+    rgb_str_t  tmp_rgb;
+
+    /* build indicator color arrays */
+    colorgen(&tmp, insidevercolor, insidever16);
+    colorgen(&tmp, insidewrongcolor, insidewrong16);
+    colorgen(&tmp, insidecolor, inside16);
+    colorgen(&tmp, ringvercolor, ringver16);
+    colorgen(&tmp, ringwrongcolor, ringwrong16);
+    colorgen(&tmp, ringcolor, ring16);
+    colorgen(&tmp, linecolor, line16);
+    colorgen(&tmp, textcolor, text16);
+    colorgen(&tmp, layoutcolor, layout16);
+    colorgen(&tmp, timecolor, time16);
+    colorgen(&tmp, datecolor, date16);
+    colorgen(&tmp, keyhlcolor, keyhl16);
+    colorgen(&tmp, bshlcolor, bshl16);
+    colorgen(&tmp, separatorcolor, sep16);
+    colorgen_rgb(&tmp_rgb, color, rgb16);
+}
+
 /*
  * Returns the scaling factor of the current screen. E.g., on a 227 DPI MacBook
  * Pro 13" Retina screen, the scaling factor is 227/96 = 2.36.
@@ -144,6 +238,7 @@ static double scaling_factor(void) {
     return (dpi / 96.0);
 }
 
+
 /*
  * Draws global image with fill color onto a pixmap with the given
  * resolution and returns it.
@@ -174,6 +269,9 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
     cairo_surface_t *date_output = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clock_width_physical, clock_height_physical);
     cairo_t *date_ctx = cairo_create(date_output);
 
+    cairo_surface_t *layout_output = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, clock_width_physical, clock_height_physical);
+    cairo_t *layout_ctx = cairo_create(layout_output);
+
     cairo_surface_t *xcb_output = cairo_xcb_surface_create(conn, bg_pixmap, vistype, resolution[0], resolution[1]);
     cairo_t *xcb_ctx = cairo_create(xcb_output);
 
@@ -198,122 +296,11 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
             }
         }
     } else {
-        char strgroups[3][3] = {{color[0], color[1], '\0'},
-                                {color[2], color[3], '\0'},
-                                {color[4], color[5], '\0'}};
-        uint32_t rgb16[3] = {(strtol(strgroups[0], NULL, 16)),
-                             (strtol(strgroups[1], NULL, 16)),
-                             (strtol(strgroups[2], NULL, 16))};
-        cairo_set_source_rgb(xcb_ctx, rgb16[0] / 255.0, rgb16[1] / 255.0, rgb16[2] / 255.0);
+        cairo_set_source_rgb(xcb_ctx, rgb16->red / 255.0, rgb16->green / 255.0, rgb16->blue / 255.0);
         cairo_rectangle(xcb_ctx, 0, 0, resolution[0], resolution[1]);
         cairo_fill(xcb_ctx);
     }
-
-    /* build indicator color arrays */
-    char strgroupsiv[4][3] = {{insidevercolor[0], insidevercolor[1], '\0'},
-                              {insidevercolor[2], insidevercolor[3], '\0'},
-                              {insidevercolor[4], insidevercolor[5], '\0'},
-                              {insidevercolor[6], insidevercolor[7], '\0'}};
-    uint32_t insidever16[4] = {(strtol(strgroupsiv[0], NULL, 16)),
-                               (strtol(strgroupsiv[1], NULL, 16)),
-                               (strtol(strgroupsiv[2], NULL, 16)),
-                               (strtol(strgroupsiv[3], NULL, 16))};
-    char strgroupsiw[4][3] = {{insidewrongcolor[0], insidewrongcolor[1], '\0'},
-                              {insidewrongcolor[2], insidewrongcolor[3], '\0'},
-                              {insidewrongcolor[4], insidewrongcolor[5], '\0'},
-                              {insidewrongcolor[6], insidewrongcolor[7], '\0'}};
-    uint32_t insidewrong16[4] = {(strtol(strgroupsiw[0], NULL, 16)),
-                                 (strtol(strgroupsiw[1], NULL, 16)),
-                                 (strtol(strgroupsiw[2], NULL, 16)),
-                                 (strtol(strgroupsiw[3], NULL, 16))};
-    char strgroupsi[4][3] = {{insidecolor[0], insidecolor[1], '\0'},
-                             {insidecolor[2], insidecolor[3], '\0'},
-                             {insidecolor[4], insidecolor[5], '\0'},
-                             {insidecolor[6], insidecolor[7], '\0'}};
-    uint32_t inside16[4] = {(strtol(strgroupsi[0], NULL, 16)),
-                            (strtol(strgroupsi[1], NULL, 16)),
-                            (strtol(strgroupsi[2], NULL, 16)),
-                            (strtol(strgroupsi[3], NULL, 16))};
-    char strgroupsrv[4][3] = {{ringvercolor[0], ringvercolor[1], '\0'},
-                              {ringvercolor[2], ringvercolor[3], '\0'},
-                              {ringvercolor[4], ringvercolor[5], '\0'},
-                              {ringvercolor[6], ringvercolor[7], '\0'}};
-    uint32_t ringver16[4] = {(strtol(strgroupsrv[0], NULL, 16)),
-                             (strtol(strgroupsrv[1], NULL, 16)),
-                             (strtol(strgroupsrv[2], NULL, 16)),
-                             (strtol(strgroupsrv[3], NULL, 16))};
-    char strgroupsrw[4][3] = {{ringwrongcolor[0], ringwrongcolor[1], '\0'},
-                              {ringwrongcolor[2], ringwrongcolor[3], '\0'},
-                              {ringwrongcolor[4], ringwrongcolor[5], '\0'},
-                              {ringwrongcolor[6], ringwrongcolor[7], '\0'}};
-    uint32_t ringwrong16[4] = {(strtol(strgroupsrw[0], NULL, 16)),
-                               (strtol(strgroupsrw[1], NULL, 16)),
-                               (strtol(strgroupsrw[2], NULL, 16)),
-                               (strtol(strgroupsrw[3], NULL, 16))};
-    char strgroupsr[4][3] = {{ringcolor[0], ringcolor[1], '\0'},
-                             {ringcolor[2], ringcolor[3], '\0'},
-                             {ringcolor[4], ringcolor[5], '\0'},
-                             {ringcolor[6], ringcolor[7], '\0'}};
-    uint32_t ring16[4] = {(strtol(strgroupsr[0], NULL, 16)),
-                          (strtol(strgroupsr[1], NULL, 16)),
-                          (strtol(strgroupsr[2], NULL, 16)),
-                          (strtol(strgroupsr[3], NULL, 16))};
-    char strgroupsl[4][3] = {{linecolor[0], linecolor[1], '\0'},
-                             {linecolor[2], linecolor[3], '\0'},
-                             {linecolor[4], linecolor[5], '\0'},
-                             {linecolor[6], linecolor[7], '\0'}};
-    uint32_t line16[4] = {(strtol(strgroupsl[0], NULL, 16)),
-                          (strtol(strgroupsl[1], NULL, 16)),
-                          (strtol(strgroupsl[2], NULL, 16)),
-                          (strtol(strgroupsl[3], NULL, 16))};
-    char strgroupst[4][3] = {{textcolor[0], textcolor[1], '\0'},
-                             {textcolor[2], textcolor[3], '\0'},
-                             {textcolor[4], textcolor[5], '\0'},
-                             {textcolor[6], textcolor[7], '\0'}};
-    uint32_t text16[4] = {(strtol(strgroupst[0], NULL, 16)),
-                          (strtol(strgroupst[1], NULL, 16)),
-                          (strtol(strgroupst[2], NULL, 16)),
-                          (strtol(strgroupst[3], NULL, 16))};
-    char strgroupsc[4][3] = {{timecolor[0], timecolor[1], '\0'},
-                             {timecolor[2], timecolor[3], '\0'},
-                             {timecolor[4], timecolor[5], '\0'},
-                             {timecolor[6], timecolor[7], '\0'}};
-    uint32_t time16[4] = {(strtol(strgroupsc[0], NULL, 16)),
-                          (strtol(strgroupsc[1], NULL, 16)),
-                          (strtol(strgroupsc[2], NULL, 16)),
-                          (strtol(strgroupsc[3], NULL, 16))};
-    char strgroupsd[4][3] = {{datecolor[0], datecolor[1], '\0'},
-                             {datecolor[2], datecolor[3], '\0'},
-                             {datecolor[4], datecolor[5], '\0'},
-                             {datecolor[6], datecolor[7], '\0'}};
-    uint32_t date16[4] = {(strtol(strgroupsd[0], NULL, 16)),
-                          (strtol(strgroupsd[1], NULL, 16)),
-                          (strtol(strgroupsd[2], NULL, 16)),
-                          (strtol(strgroupsd[3], NULL, 16))};
-    char strgroupsk[4][3] = {{keyhlcolor[0], keyhlcolor[1], '\0'},
-                             {keyhlcolor[2], keyhlcolor[3], '\0'},
-                             {keyhlcolor[4], keyhlcolor[5], '\0'},
-                             {keyhlcolor[6], keyhlcolor[7], '\0'}};
-    uint32_t keyhl16[4] = {(strtol(strgroupsk[0], NULL, 16)),
-                           (strtol(strgroupsk[1], NULL, 16)),
-                           (strtol(strgroupsk[2], NULL, 16)),
-                           (strtol(strgroupsk[3], NULL, 16))};
-    char strgroupsb[4][3] = {{bshlcolor[0], bshlcolor[1], '\0'},
-                             {bshlcolor[2], bshlcolor[3], '\0'},
-                             {bshlcolor[4], bshlcolor[5], '\0'},
-                             {bshlcolor[6], bshlcolor[7], '\0'}};
-    uint32_t bshl16[4] = {(strtol(strgroupsb[0], NULL, 16)),
-                          (strtol(strgroupsb[1], NULL, 16)),
-                          (strtol(strgroupsb[2], NULL, 16)),
-                          (strtol(strgroupsb[3], NULL, 16))};
-    char strgroupss[4][3] = {{separatorcolor[0], separatorcolor[1], '\0'},
-                             {separatorcolor[2], separatorcolor[3], '\0'},
-                             {separatorcolor[4], separatorcolor[5], '\0'},
-                             {separatorcolor[6], separatorcolor[7], '\0'}};
-    uint32_t sep16[4] = {(strtol(strgroupss[0], NULL, 16)),
-                          (strtol(strgroupss[1], NULL, 16)),
-                          (strtol(strgroupss[2], NULL, 16)),
-                          (strtol(strgroupss[3], NULL, 16))};
+    
 
     /* https://github.com/ravinrabbid/i3lock-clock/commit/0de3a411fa5249c3a4822612c2d6c476389a1297 */
     time_t rawtime;
@@ -339,14 +326,14 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
         switch (auth_state) {
             case STATE_AUTH_VERIFY:
             case STATE_AUTH_LOCK:
-                cairo_set_source_rgba(ctx, (double)insidever16[0]/255, (double)insidever16[1]/255, (double)insidever16[2]/255, (double)insidever16[3]/255);
+                cairo_set_source_rgba(ctx, (double)insidever16->red/255, (double)insidever16->green/255, (double)insidever16->blue/255, (double)insidever16->alpha/255);
                 break;
             case STATE_AUTH_WRONG:
             case STATE_I3LOCK_LOCK_FAILED:
-                cairo_set_source_rgba(ctx, (double)insidewrong16[0]/255, (double)insidewrong16[1]/255, (double)insidewrong16[2]/255, (double)insidewrong16[3]/255);
+                cairo_set_source_rgba(ctx, (double)insidewrong16->red/255, (double)insidewrong16->green/255, (double)insidewrong16->blue/255, (double)insidewrong16->alpha/255);
                 break;
             default:
-                cairo_set_source_rgba(ctx, (double)inside16[0]/255, (double)inside16[1]/255, (double)inside16[2]/255, (double)inside16[3]/255);
+                cairo_set_source_rgba(ctx, (double)inside16->red/255, (double)inside16->green/255, (double)inside16->blue/255, (double)inside16->alpha/255);
                 break;
         }
         cairo_fill_preserve(ctx);
@@ -354,31 +341,31 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
         switch (auth_state) {
             case STATE_AUTH_VERIFY:
             case STATE_AUTH_LOCK:
-                cairo_set_source_rgba(ctx, (double)ringver16[0]/255, (double)ringver16[1]/255, (double)ringver16[2]/255, (double)ringver16[3]/255);
+                cairo_set_source_rgba(ctx, (double)ringver16->red/255, (double)ringver16->green/255, (double)ringver16->blue/255, (double)ringver16->alpha/255);
                 if (internal_line_source == 1) {
-                  line16[0] = ringver16[0];
-                  line16[1] = ringver16[1];
-                  line16[2] = ringver16[2];
-                  line16[3] = ringver16[3];
+                  line16->red = ringver16->red;
+                  line16->green = ringver16->green;
+                  line16->blue = ringver16->blue;
+                  line16->alpha = ringver16->alpha;
                 }
                 break;
             case STATE_AUTH_WRONG:
             case STATE_I3LOCK_LOCK_FAILED:
-                cairo_set_source_rgba(ctx, (double)ringwrong16[0]/255, (double)ringwrong16[1]/255, (double)ringwrong16[2]/255, (double)ringwrong16[3]/255);
+                cairo_set_source_rgba(ctx, (double)ringwrong16->red/255, (double)ringwrong16->green/255, (double)ringwrong16->blue/255, (double)ringwrong16->alpha/255);
                 if (internal_line_source == 1) {
-                  line16[0] = ringwrong16[0];
-                  line16[1] = ringwrong16[1];
-                  line16[2] = ringwrong16[2];
-                  line16[3] = ringwrong16[3];
+                  line16->red = ringwrong16->red;
+                  line16->green = ringwrong16->green;
+                  line16->blue = ringwrong16->blue;
+                  line16->alpha = ringwrong16->alpha;
                 }
                 break;
             case STATE_AUTH_IDLE:
-                cairo_set_source_rgba(ctx, (double)ring16[0]/255, (double)ring16[1]/255, (double)ring16[2]/255, (double)ring16[3]/255);
+                cairo_set_source_rgba(ctx, (double)ring16->red/255, (double)ring16->green/255, (double)ring16->blue/255, (double)ring16->alpha/255);
                 if (internal_line_source == 1) {
-                  line16[0] = ring16[0];
-                  line16[1] = ring16[1];
-                  line16[2] = ring16[2];
-                  line16[3] = ring16[3];
+                  line16->red = ring16->red;
+                  line16->green = ring16->green;
+                  line16->blue = ring16->blue;
+                  line16->alpha = ring16->alpha;
                 }
                 break;
         }
@@ -386,7 +373,7 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
 
         /* Draw an inner separator line. */
         if (internal_line_source != 2) { //pretty sure this only needs drawn if it's being drawn over the inside?
-          cairo_set_source_rgba(ctx, (double)line16[0]/255, (double)line16[1]/255, (double)line16[2]/255, (double)line16[3]/255);
+          cairo_set_source_rgba(ctx, (double)line16->red/255, (double)line16->green/255, (double)line16->blue/255, (double)line16->alpha/255);
           cairo_set_line_width(ctx, 2.0);
           cairo_arc(ctx,
                     BUTTON_CENTER /* x */,
@@ -405,7 +392,7 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
         /* We don't want to show more than a 3-digit number. */
         char buf[4];
 
-        cairo_set_source_rgba(ctx, (double)text16[0]/255, (double)text16[1]/255, (double)text16[2]/255, (double)text16[3]/255);
+        cairo_set_source_rgba(ctx, (double)text16->red/255, (double)text16->green/255, (double)text16->blue/255, (double)text16->alpha/255);
         cairo_select_font_face(ctx, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
         cairo_set_font_size(ctx, text_size);
         switch (auth_state) {
@@ -479,17 +466,17 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
                       highlight_start + (M_PI / 3.0));
             if (unlock_state == STATE_KEY_ACTIVE) {
                 /* For normal keys, we use a lighter green. */ //lol no
-                cairo_set_source_rgba(ctx, (double)keyhl16[0]/255, (double)keyhl16[1]/255, (double)keyhl16[2]/255, (double)keyhl16[3]/255);
+                cairo_set_source_rgba(ctx, (double)keyhl16->red/255, (double)keyhl16->green/255, (double)keyhl16->blue/255, (double)keyhl16->alpha/255);
             } else {
                 /* For backspace, we use red. */ //lol no
-                cairo_set_source_rgba(ctx, (double)bshl16[0]/255, (double)bshl16[1]/255, (double)bshl16[2]/255, (double)bshl16[3]/255);
+                cairo_set_source_rgba(ctx, (double)bshl16->red/255, (double)bshl16->green/255, (double)bshl16->blue/255, (double)bshl16->alpha/255);
             }
 
             cairo_stroke(ctx);
 
             /* Draw two little separators for the highlighted part of the
              * unlock indicator. */
-            cairo_set_source_rgba(ctx, (double)sep16[0]/255, (double)sep16[1]/255, (double)sep16[2]/255, (double)sep16[3]/255);
+            cairo_set_source_rgba(ctx, (double)sep16->red/255, (double)sep16->green/255, (double)sep16->blue/255, (double)sep16->alpha/255);
             cairo_arc(ctx,
                       BUTTON_CENTER /* x */,
                       BUTTON_CENTER /* y */,
@@ -512,6 +499,9 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
         char *date = NULL;
         char time_text[40] = {0};
         char date_text[40] = {0};
+        // common vars for each if block
+        double x, y;
+        cairo_text_extents_t extents;
 
         strftime(time_text, 40, time_format, timeinfo);
         strftime(date_text, 40, date_format, timeinfo);
@@ -519,12 +509,9 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
         date = date_text;
 
         if (text) {
-            double x, y;
-            cairo_text_extents_t extents;
-
             cairo_set_font_size(time_ctx, time_size);
             cairo_select_font_face(time_ctx, time_font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
-            cairo_set_source_rgba(time_ctx, (double)time16[0]/255, (double)time16[1]/255, (double)time16[2]/255, (double)time16[3]/255);
+            cairo_set_source_rgba(time_ctx, (double)time16->red/255, (double)time16->green/255, (double)time16->blue/255, (double)time16->alpha/255);
 
             cairo_text_extents(time_ctx, text, &extents);
             x = CLOCK_WIDTH/2 - ((extents.width / 2) + extents.x_bearing);
@@ -536,11 +523,8 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
         }
 
         if (date) {
-            double x, y;
-            cairo_text_extents_t extents;
-
             cairo_select_font_face(date_ctx, date_font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
-            cairo_set_source_rgba(date_ctx, (double)date16[0]/255, (double)date16[1]/255, (double)date16[2]/255, (double)date16[3]/255);
+            cairo_set_source_rgba(date_ctx, (double)date16->red/255, (double)date16->green/255, (double)date16->blue/255, (double)date16->alpha/255);
             cairo_set_font_size(date_ctx, date_size);
 
             cairo_text_extents(date_ctx, date, &extents);
@@ -551,6 +535,20 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
             cairo_show_text(date_ctx, date);
             cairo_close_path(date_ctx);
         }
+        if (layout_text) {
+            cairo_select_font_face(layout_ctx, layout_font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+            cairo_set_source_rgba(layout_ctx, (double)layout16->red/255, (double)layout16->green/255, (double)layout16->blue/255, (double)layout16->alpha/255);
+            cairo_set_font_size(layout_ctx, layout_size);
+
+            cairo_text_extents(layout_ctx, layout_text, &extents);
+            x = CLOCK_WIDTH/2 - ((extents.width / 2) + extents.x_bearing);
+            y = CLOCK_HEIGHT/2;
+
+            cairo_move_to(layout_ctx, x, y);
+            cairo_show_text(layout_ctx, layout_text);
+            cairo_close_path(layout_ctx);
+        
+        }
     }
 
     double ix, iy;
@@ -559,6 +557,8 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
     double w, h;
     double tx = 0;
     double ty = 0;
+    double dx = 0;
+    double dy = 0;
 
     double clock_width = CLOCK_WIDTH;
     double clock_height = CLOCK_HEIGHT;
@@ -573,16 +573,20 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
         {"x", &screen_x}, {"y", &screen_y},
         {"ix", &ix}, {"iy", &iy},
         {"tx", &tx}, {"ty", &ty},
+        {"dx", &dx}, {"dy", &dy},
         {"cw", &clock_width}, {"ch", &clock_height}, // pretty sure this is fine.
         {"r", &radius}
     };
+#define NUM_VARS 13
 
-    te_expr *te_ind_x_expr = te_compile(ind_x_expr, vars, 11, &te_x_err);
-    te_expr *te_ind_y_expr = te_compile(ind_y_expr, vars, 11, &te_y_err);
-    te_expr *te_time_x_expr = te_compile(time_x_expr, vars, 11, &te_x_err);
-    te_expr *te_time_y_expr = te_compile(time_y_expr, vars, 11, &te_y_err);
-    te_expr *te_date_x_expr = te_compile(date_x_expr, vars, 11, &te_x_err);
-    te_expr *te_date_y_expr = te_compile(date_y_expr, vars, 11, &te_y_err);
+    te_expr *te_ind_x_expr = te_compile(ind_x_expr, vars, NUM_VARS, &te_x_err);
+    te_expr *te_ind_y_expr = te_compile(ind_y_expr, vars, NUM_VARS, &te_y_err);
+    te_expr *te_time_x_expr = te_compile(time_x_expr, vars, NUM_VARS, &te_x_err);
+    te_expr *te_time_y_expr = te_compile(time_y_expr, vars, NUM_VARS, &te_y_err);
+    te_expr *te_date_x_expr = te_compile(date_x_expr, vars, NUM_VARS, &te_x_err);
+    te_expr *te_date_y_expr = te_compile(date_y_expr, vars, NUM_VARS, &te_y_err);
+    te_expr *te_layout_x_expr = te_compile(layout_x_expr, vars, NUM_VARS, &te_x_err);
+    te_expr *te_layout_y_expr = te_compile(layout_y_expr, vars, NUM_VARS, &te_y_err);
 
     if (xr_screens > 0) {
         /* Composite the unlock indicator in the middle of each screen. */
@@ -617,10 +621,15 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
                 ty = te_eval(te_time_y_expr);
                 double time_x = tx;
                 double time_y = ty;
-                double date_x = te_eval(te_date_x_expr);
-                double date_y = te_eval(te_date_y_expr);
+                dx = te_eval(te_date_x_expr);
+                dy = te_eval(te_date_y_expr);
+                double date_x = dx;
+                double date_y = dy;
+                double layout_x = te_eval(te_layout_x_expr);
+                double layout_y = te_eval(te_layout_y_expr);
+
                 DEBUG("tx: %f ty: %f ix: %f, iy: %f\n", tx, ty, ix, iy);
-                DEBUG("\ttime_x: %f time_y: %f date_x: %f date_y: %f screen_number: %d\n", time_x, time_y, date_x, date_y, screen_number);
+                DEBUG("\ttime_x: %f time_y: %f date_x: %f date_y: %f layout_x: %f layout_y: %f screen_number: %d\n", time_x, time_y, date_x, date_y, layout_x, layout_y, screen_number);
                 DEBUG("\tscreen x: %d screen y: %d screen w: %f screen h: %f\n", xr_resolutions[screen_number].x, xr_resolutions[screen_number].y, w, h);
                 cairo_set_source_surface(xcb_ctx, time_output, time_x, time_y);
                 cairo_rectangle(xcb_ctx, time_x, time_y, CLOCK_WIDTH, CLOCK_HEIGHT);
@@ -628,6 +637,9 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
                 cairo_set_source_surface(xcb_ctx, date_output, date_x, date_y);
                 cairo_rectangle(xcb_ctx, date_x, date_y, CLOCK_WIDTH, CLOCK_HEIGHT);
                 cairo_fill(xcb_ctx);
+                cairo_set_source_surface(xcb_ctx, layout_output, layout_x, layout_y);
+                cairo_rectangle(xcb_ctx, layout_x, layout_y, CLOCK_WIDTH, CLOCK_HEIGHT);
+                cairo_fill(xcb_ctx);
             }
         } else {
             for (int screen = 0; screen < xr_screens; screen++) {
@@ -658,10 +670,14 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
                     ty = te_eval(te_time_y_expr);
                     double time_x = tx;
                     double time_y = ty;
-                    double date_x = te_eval(te_date_x_expr);
-                    double date_y = te_eval(te_date_y_expr);
+                    dx = te_eval(te_date_x_expr);
+                    dy = te_eval(te_date_y_expr);
+                    double date_x = dx;
+                    double date_y = dy;
+                    double layout_x = te_eval(te_layout_x_expr);
+                    double layout_y = te_eval(te_layout_y_expr);
                     DEBUG("tx: %f ty: %f f ix: %f iy: %f\n", tx, ty, ix, iy);
-                    DEBUG("\ttime_x: %f time_y: %f date_x: %f date_y: %f screen_number: %d\n", time_x, time_y, date_x, date_y, screen);
+                    DEBUG("\ttime_x: %f time_y: %f date_x: %f date_y: %f layout_x: %f layout_y: %f screen_number: %d\n", time_x, time_y, date_x, date_y, layout_x, layout_y, screen_number);
                     DEBUG("\tscreen x: %d screen y: %d screen w: %f screen h: %f\n", xr_resolutions[screen].x, xr_resolutions[screen].y, w, h);
                     cairo_set_source_surface(xcb_ctx, time_output, time_x, time_y);
                     cairo_rectangle(xcb_ctx, time_x, time_y, CLOCK_WIDTH, CLOCK_HEIGHT);
@@ -669,6 +685,9 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
                     cairo_set_source_surface(xcb_ctx, date_output, date_x, date_y);
                     cairo_rectangle(xcb_ctx, date_x, date_y, CLOCK_WIDTH, CLOCK_HEIGHT);
                     cairo_fill(xcb_ctx);
+                    cairo_set_source_surface(xcb_ctx, layout_output, layout_x, layout_y);
+                    cairo_rectangle(xcb_ctx, layout_x, layout_y, CLOCK_WIDTH, CLOCK_HEIGHT);
+                    cairo_fill(xcb_ctx);
                 } else {
                     DEBUG("error codes for exprs are %d, %d\n", te_x_err, te_y_err);
                     DEBUG("exprs: %s, %s\n", time_x_expr, time_y_expr);
diff --git a/unlock_indicator.h b/unlock_indicator.h
@@ -3,6 +3,32 @@
 
 #include <ev.h>
 
+typedef struct rgb {
+    uint32_t red;
+    uint32_t green;
+    uint32_t blue;
+} rgb_t;
+
+typedef struct rgb_str {
+    char red[3];
+    char green[3];
+    char blue[3];
+} rgb_str_t;
+
+typedef struct rgba {
+    uint32_t red;
+    uint32_t green;
+    uint32_t blue;
+    uint32_t alpha;
+} rgba_t;
+
+typedef struct rgba_str {
+    char red[3];
+    char green[3];
+    char blue[3];
+    char alpha[3];
+} rgba_str_t;
+
 typedef enum {
     STATE_STARTED = 0,         /* default state */
     STATE_KEY_PRESSED = 1,     /* key was pressed, show unlock indicator */
@@ -21,6 +47,7 @@ typedef enum {
 } auth_state_t;
 
 xcb_pixmap_t draw_image(uint32_t* resolution);
+void init_colors_once(void);
 void redraw_screen(void);
 void clear_indicator(void);
 void start_time_redraw_timeout(void);