i3lock-color

The world's most popular non-default computer lockscreen.
Index Commits Files Refs README LICENSE
commit 0da5cdb4479da47109eea9530ae941ebf11462fa
parent d9fcd23c1941367509a5d9f20ad0ca51a14fbebf
Author: BenoƮt Dardenne <benoit.dardenne@gmail.com>
Date:   Sat, 11 Apr 2020 21:44:02 +0200

Use 32bit-deep drawables to allow setting a color with alpha channel as background

Diffstat:
Mi3lock.1 | 6+++---
Mi3lock.c | 41++++++++++++++++++-----------------------
Munlock_indicator.c | 17++++++++---------
Munlock_indicator.h | 2+-
Mxcb.c | 68+++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mxcb.h | 5+++--
6 files changed, 80 insertions(+), 59 deletions(-)
diff --git a/i3lock.1 b/i3lock.1
@@ -108,9 +108,9 @@ You can also use it to resize images to the screen ratio:
 Note that $(xdpyinfo | grep dimensions | sed -r 's/^[^0-9]*([0-9]+x[0-9]+).*$/\1/') gets you the current screen dimensions in the wxh (e.g. 1920x1080) format.
 
 .TP
-.BI \-c\  rrggbb \fR,\ \fB\-\-color= rrggbb
-Turn the screen into the given color instead of white. Color must be given in 3-byte
-format: rrggbb (i.e. ff0000 is red).
+.BI \-c\  rrggbbaa \fR,\ \fB\-\-color= rrggbbaa
+Turn the screen into the given color instead of white. Color must be given in 4-byte
+format: rrggbbaa (i.e. ff0000ff is opaque red).
 
 .TP
 .B \-t, \-\-tiling
diff --git a/i3lock.c b/i3lock.c
@@ -70,7 +70,7 @@
 typedef void (*ev_callback_t)(EV_P_ ev_timer *w, int revents);
 static void input_done(void);
 
-char color[7] = "ffffff";
+char color[9] = "ffffffff";
 
 /* options for unlock indicator colors */
 char insidevercolor[9] = "006effbf";
@@ -1497,6 +1497,13 @@ int main(int argc, char *argv[]) {
     char *optstring = "hvnbdc:p:ui:teI:frsS:kB:m";
     char *arg = NULL;
     int opt = 0;
+
+#define parse_color(acolor)\
+    arg = optarg;\
+    if (arg[0] == '#') arg++;\
+    if (strlen(arg) != 8 || sscanf(arg, "%08[0-9a-fA-F]", acolor) != 1)\
+        errx(1, #acolor " is invalid, color must be given in 4-byte format: rrggbbaa\n");
+
     while ((o = getopt_long(argc, argv, optstring, longopts, &longoptind)) != -1) {
         switch (o) {
             case 'v':
@@ -1514,18 +1521,6 @@ int main(int argc, char *argv[]) {
                 fprintf(stderr, "Inactivity timeout only makes sense with DPMS, which was removed. Please see the manpage i3lock(1).\n");
                 break;
             }
-            case 'c': {
-                arg = optarg;
-
-                /* Skip # if present */
-                if (arg[0] == '#')
-                    arg++;
-
-                if (strlen(arg) != 6 || sscanf(arg, "%06[0-9a-fA-F]", color) != 1)
-                    errx(EXIT_FAILURE, "color is invalid, it must be given in 3-byte hexadecimal format: rrggbb");
-
-                break;
-            }
             case 'u':
                 unlock_indicator = false;
                 break;
@@ -1575,11 +1570,9 @@ int main(int argc, char *argv[]) {
                 break;
 
             // Begin colors
-            #define parse_color(color)\
-                arg = optarg;\
-                if (arg[0] == '#') arg++;\
-                if (strlen(arg) != 8 || sscanf(arg, "%08[0-9a-fA-F]", color) != 1)\
-                    errx(1, #color " is invalid, color must be given in 4-byte format: rrggbbaa\n");
+            case 'c':
+            parse_color(color);
+                break;
             case 300:
                 parse_color(insidevercolor);
                 break;
@@ -2206,14 +2199,16 @@ int main(int argc, char *argv[]) {
         cairo_surface_destroy(xcb_img);
     }
 
-    /* Pixmap on which the image is rendered to (if any) */
-    xcb_pixmap_t bg_pixmap = draw_image(last_resolution);
-
     xcb_window_t stolen_focus = find_focused_window(conn, screen->root);
 
     /* Open the fullscreen window, already with the correct pixmap in place */
-    win = open_fullscreen_window(conn, screen, color, bg_pixmap);
-    xcb_free_pixmap(conn, bg_pixmap);
+    win = open_fullscreen_window(conn, screen, color);
+
+    xcb_pixmap_t pixmap = create_bg_pixmap(conn, win, last_resolution, color);
+    draw_image(last_resolution, pixmap);
+    xcb_change_window_attributes(conn, win, XCB_CW_BACK_PIXMAP, (uint32_t[]){pixmap});
+
+    xcb_free_pixmap(conn, pixmap);
     if (blur_pixmap) {
         xcb_free_pixmap(conn, *blur_pixmap);
         free(blur_pixmap);
diff --git a/unlock_indicator.c b/unlock_indicator.c
@@ -71,7 +71,7 @@ unsigned long lastCheck;
 /* Whether the image should be tiled. */
 extern bool tile;
 /* The background color to use (in hex). */
-extern char color[7];
+extern char color[9];
 /* indicator color options */
 extern char insidevercolor[9];
 extern char insidewrongcolor[9];
@@ -675,7 +675,7 @@ static void draw_elements(cairo_t *const ctx, DrawData const *const draw_data) {
  * resolution and returns it.
  *
  */
-xcb_pixmap_t draw_image(uint32_t *resolution) {
+void draw_image(uint32_t *resolution, xcb_drawable_t drawable) {
     const double scaling_factor = get_dpi_value() / 96.0;
     xcb_pixmap_t bg_pixmap = XCB_NONE;
     int button_diameter_physical = ceil(scaling_factor * BUTTON_DIAMETER);
@@ -683,7 +683,7 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
         scaling_factor, button_diameter_physical);
 
     if (!vistype)
-        vistype = get_root_visual_type(screen);
+        vistype = get_visualtype_by_depth(32, screen);
     bg_pixmap = create_bg_pixmap(conn, screen, resolution, color);
     /* Initialize cairo: Create one in-memory surface to render the unlock
      * indicator on, create one XCB surface to actually draw (one or more,
@@ -696,7 +696,7 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
 
     //    cairo_set_font_face(ctx, get_font_face(0));
 
-    cairo_surface_t *xcb_output = cairo_xcb_surface_create(conn, bg_pixmap, vistype, resolution[0], resolution[1]);
+    cairo_surface_t *xcb_output = cairo_xcb_surface_create(conn, drawable, vistype, resolution[0], resolution[1]);
     cairo_t *xcb_ctx = cairo_create(xcb_output);
 
     /*update image according to the slideshow_interval*/
@@ -1073,12 +1073,11 @@ xcb_pixmap_t draw_image(uint32_t *resolution) {
  */
 void redraw_screen(void) {
     DEBUG("redraw_screen(unlock_state = %d, auth_state = %d) @ [%lu]\n", unlock_state, auth_state, (unsigned long)time(NULL));
-    xcb_pixmap_t bg_pixmap = draw_image(last_resolution);
-    xcb_change_window_attributes(conn, win, XCB_CW_BACK_PIXMAP, (uint32_t[1]){bg_pixmap});
-    /* XXX: Possible optimization: Only update the area in the middle of the
-     * screen instead of the whole screen. */
+    xcb_pixmap_t pixmap = create_bg_pixmap(conn, win, last_resolution, color);
+    draw_image(last_resolution, pixmap);
+    xcb_change_window_attributes(conn, win, XCB_CW_BACK_PIXMAP, (uint32_t[1]){pixmap});
     xcb_clear_area(conn, 0, win, 0, 0, last_resolution[0], last_resolution[1]);
-    xcb_free_pixmap(conn, bg_pixmap);
+    xcb_free_pixmap(conn, pixmap);
     xcb_flush(conn);
 }
 
diff --git a/unlock_indicator.h b/unlock_indicator.h
@@ -38,7 +38,7 @@ typedef struct {
     double bar_offset;
 } DrawData;
 
-xcb_pixmap_t draw_image(uint32_t* resolution);
+void draw_image(uint32_t* resolution, xcb_drawable_t drawable);
 void init_colors_once(void);
 void redraw_screen(void);
 void clear_indicator(void);
diff --git a/xcb.c b/xcb.c
@@ -82,16 +82,37 @@ static unsigned char mask_windows_bits[] = {
     0x80, 0x01};
 
 static uint32_t get_colorpixel(char *hex) {
-    char strgroups[3][3] = {{hex[0], hex[1], '\0'},
+    char strgroups[4][3] = {{hex[0], hex[1], '\0'},
                             {hex[2], hex[3], '\0'},
-                            {hex[4], hex[5], '\0'}};
-    uint32_t rgb16[3] = {(strtol(strgroups[0], NULL, 16)),
+                            {hex[4], hex[5], '\0'},
+                            {hex[6], hex[4], '\0'}};
+    uint32_t rgb16[4] = {(strtol(strgroups[0], NULL, 16)),
                          (strtol(strgroups[1], NULL, 16)),
-                         (strtol(strgroups[2], NULL, 16))};
+                         (strtol(strgroups[2], NULL, 16)),
+                         (strtol(strgroups[3], NULL, 16))};
 
-    return (rgb16[0] << 16) + (rgb16[1] << 8) + rgb16[2];
+    return (rgb16[3] << 24) + (rgb16[0] << 16) + (rgb16[1] << 8) + rgb16[2];
 }
 
+xcb_visualtype_t *get_visualtype_by_depth(uint16_t depth, xcb_screen_t *root_screen) {
+    xcb_depth_iterator_t depth_iter;
+
+    depth_iter = xcb_screen_allowed_depths_iterator(root_screen);
+    for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
+        if (depth_iter.data->depth != depth)
+            continue;
+
+        xcb_visualtype_iterator_t visual_iter;
+
+        visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
+        if (!visual_iter.rem)
+            continue;
+        return visual_iter.data;
+    }
+    return NULL;
+}
+
+
 xcb_visualtype_t *get_root_visual_type(xcb_screen_t *screen) {
     xcb_visualtype_t *visual_type = NULL;
     xcb_depth_iterator_t depth_iter;
@@ -114,10 +135,9 @@ xcb_visualtype_t *get_root_visual_type(xcb_screen_t *screen) {
     return NULL;
 }
 
-xcb_pixmap_t create_bg_pixmap(xcb_connection_t *conn, xcb_screen_t *scr, u_int32_t *resolution, char *color) {
+xcb_pixmap_t create_bg_pixmap(xcb_connection_t *conn, xcb_drawable_t win, u_int32_t *resolution, char *color) {
     xcb_pixmap_t bg_pixmap = xcb_generate_id(conn);
-    xcb_create_pixmap(conn, scr->root_depth, bg_pixmap, scr->root,
-                      resolution[0], resolution[1]);
+    xcb_create_pixmap(conn, 32, bg_pixmap, win, resolution[0], resolution[1]);
 
     /* Generate a Graphics Context and fill the pixmap with background color
      * (for images that are smaller than your screen) */
@@ -131,9 +151,9 @@ xcb_pixmap_t create_bg_pixmap(xcb_connection_t *conn, xcb_screen_t *scr, u_int32
     return bg_pixmap;
 }
 
-xcb_window_t open_fullscreen_window(xcb_connection_t *conn, xcb_screen_t *scr, char *color, xcb_pixmap_t pixmap) {
+xcb_window_t open_fullscreen_window(xcb_connection_t *conn, xcb_screen_t *scr, char *color) {
     uint32_t mask = 0;
-    uint32_t values[3];
+    uint32_t values[5];
     xcb_window_t win = xcb_generate_id(conn);
     xcb_window_t parent_win = scr->root;
 
@@ -161,26 +181,32 @@ xcb_window_t open_fullscreen_window(xcb_connection_t *conn, xcb_screen_t *scr, c
         }
     }
 
-    if (pixmap == XCB_NONE) {
-        mask |= XCB_CW_BACK_PIXEL;
-        values[0] = get_colorpixel(color);
-    } else {
-        mask |= XCB_CW_BACK_PIXMAP;
-        values[0] = pixmap;
-    }
+
+    xcb_visualid_t visual = get_visualtype_by_depth(32, scr)->visual_id;
+    xcb_colormap_t win_colormap = xcb_generate_id(conn);
+    xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE, win_colormap, scr->root, visual);
+
+    mask |= XCB_CW_BACK_PIXEL;
+    values[0] = get_colorpixel(color);
+
+    mask |= XCB_CW_BORDER_PIXEL;
+    values[1] = 0x00000000;
 
     mask |= XCB_CW_OVERRIDE_REDIRECT;
-    values[1] = 1;
+    values[2] = 1;
 
     mask |= XCB_CW_EVENT_MASK;
-    values[2] = XCB_EVENT_MASK_EXPOSURE |
+    values[3] = XCB_EVENT_MASK_EXPOSURE |
                 XCB_EVENT_MASK_KEY_PRESS |
                 XCB_EVENT_MASK_KEY_RELEASE |
                 XCB_EVENT_MASK_VISIBILITY_CHANGE |
                 XCB_EVENT_MASK_STRUCTURE_NOTIFY;
 
+    mask |= XCB_CW_COLORMAP;
+    values[4] = win_colormap;
+
     xcb_create_window(conn,
-                      XCB_COPY_FROM_PARENT,
+                      32,
                       win, /* the window id */
                       parent_win,
                       0, 0,
@@ -188,7 +214,7 @@ xcb_window_t open_fullscreen_window(xcb_connection_t *conn, xcb_screen_t *scr, c
                       scr->height_in_pixels, /* dimensions */
                       0,                     /* border = 0, we draw our own */
                       XCB_WINDOW_CLASS_INPUT_OUTPUT,
-                      XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
+                      visual, /* copy visual from parent */
                       mask,
                       values);
 
diff --git a/xcb.h b/xcb.h
@@ -24,8 +24,9 @@ extern xcb_connection_t *conn;
 extern xcb_screen_t *screen;
 
 xcb_visualtype_t *get_root_visual_type(xcb_screen_t *s);
-xcb_pixmap_t create_bg_pixmap(xcb_connection_t *conn, xcb_screen_t *scr, u_int32_t *resolution, char *color);
-xcb_window_t open_fullscreen_window(xcb_connection_t *conn, xcb_screen_t *scr, char *color, xcb_pixmap_t pixmap);
+xcb_visualtype_t* get_visualtype_by_depth(uint16_t depth, xcb_screen_t* root_screen);
+xcb_pixmap_t create_bg_pixmap(xcb_connection_t *conn, xcb_drawable_t drawable, u_int32_t *resolution, char *color);
+xcb_window_t open_fullscreen_window(xcb_connection_t *conn, xcb_screen_t *scr, char *color);
 bool grab_pointer_and_keyboard(xcb_connection_t *conn, xcb_screen_t *screen, xcb_cursor_t cursor, int tries);
 xcb_cursor_t create_cursor(xcb_connection_t *conn, xcb_screen_t *screen, xcb_window_t win, int choice);
 xcb_window_t find_focused_window(xcb_connection_t *conn, const xcb_window_t root);