/* * This file is part of i3lock-extended * Copyright (C) 2020-2023 Simeon Simeonov * i3lock-extended is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * i3lock-extended is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "randr.h" #include "extras.h" extern int failed_attempts; static void i3lock_color_to_array(double *rgb16, const char *color) { char strgroups[3][3] = {{color[0], color[1], '\0'}, {color[2], color[3], '\0'}, {color[4], color[5], '\0'}}; *rgb16 = strtol(strgroups[0], NULL, 16) / 255.0; *(rgb16 + 1) = strtol(strgroups[1], NULL, 16) / 255.0; *(rgb16 + 2) = strtol(strgroups[2], NULL, 16) / 255.0; } static void i3lock_fetch_digital_clock_xy(int *x, int *y, const i3lock_digital_clock_t *dc, const uint32_t *resolution, int text_length) { /* x */ if (dc->horizontal_alignment == I3LOCK_ALIGN_CENTER) { *x = resolution[0] / 2 - text_length * dc->text_size / 2; } else if (dc->horizontal_alignment == I3LOCK_ALIGN_RIGHT) { *x = resolution[0] - resolution[0] / 10 - text_length * dc->text_size; } else { /* left */ *x = resolution[0] / 10; /* 10% */ } /* y */ if (dc->vertical_alignment == I3LOCK_ALIGN_MIDDLE) { *y = resolution[1] / 2 - dc->text_size / 2; } else if (dc->vertical_alignment == I3LOCK_ALIGN_BOTTOM) { *y = resolution[1] - resolution[1] / 10 - dc->text_size; } else { /* top */ *y = resolution[1] / 10; /* 10% */ } } static void i3lock_fetch_led_clock_xy(int *x, int *y, const i3lock_led_clock_t *lc, const uint32_t *resolution) { int clock_length, clock_height; /* x */ clock_length = 6 * resolution[1] / I3LOCK_SCALE_LED_SPACING + \ 5 * resolution[1] / I3LOCK_SCALE_LED_LENGTH; if (lc->horizontal_alignment == I3LOCK_ALIGN_CENTER) { *x = resolution[0] / 2 - clock_length / 2; } else if (lc->horizontal_alignment == I3LOCK_ALIGN_RIGHT) { *x = resolution[0] - resolution[0] / 10 - clock_length; } else { /* left */ *x = resolution[0] / 10; /* 10% */ } /* y */ clock_height = 3 * resolution[1] / I3LOCK_SCALE_LED_SPACING + \ 2 * resolution[1] / I3LOCK_SCALE_LED_LENGTH; if (lc->vertical_alignment == I3LOCK_ALIGN_MIDDLE) { *y = resolution[1] / 2 - clock_height / 2; } else if (lc->vertical_alignment == I3LOCK_ALIGN_BOTTOM) { *y = resolution[1] - resolution[1] / 10 - clock_height; } else { /* top */ *y = resolution[1] / 10; /* 10% */ } } static void i3lock_draw_led(cairo_t *cr, double x, double y, double length, int type, const i3lock_led_clock_t *lc) { double border_color[3], face_color[3]; i3lock_color_to_array(border_color, lc->led_border_color); if (type) { /* LED on */ i3lock_color_to_array(face_color, lc->led_on_color); } else { i3lock_color_to_array(face_color, lc->led_off_color); } cairo_set_line_width(cr, 2); cairo_rectangle(cr, x, y, length, length); cairo_set_source_rgb(cr, face_color[0], face_color[1], face_color[2]); cairo_fill(cr); cairo_rectangle(cr, x, y, length, length); cairo_set_source_rgb(cr, border_color[0], border_color[1], border_color[2]); cairo_stroke(cr); } static char * i3lock_replace_str(char *to, const char *from, const char *old_substr, const char *new_substr, size_t max_length) { int offset; /* the offset in the tmp_str after each match */ /* the offset in from needed to pick up the right side after a match */ int from_offset; int from_len = strlen(from); int new_substr_len = strlen(new_substr); int old_substr_len = strlen(old_substr); int len_diff = old_substr_len - new_substr_len; size_t free_space = max_length - 1; char tmp_str[max_length]; char *tmp_str_ptr; /* reserve the last byte for '\0' in case of clipping */ strncpy(tmp_str, from, free_space); for (int i = 0; ; ++i) { tmp_str_ptr = strstr(tmp_str, old_substr); if (tmp_str_ptr == NULL) { /* no more instances of old_substr */ break; } offset = tmp_str_ptr - tmp_str; free_space -= offset; if (free_space <= new_substr_len) { strncpy(tmp_str_ptr, new_substr, free_space); break; } strncpy(tmp_str_ptr, new_substr, new_substr_len); free_space -= new_substr_len; /* tmp_str offset -> from_offset mapping */ from_offset = offset + old_substr_len + i * len_diff; if (free_space <= from_len - from_offset) { strncpy(tmp_str_ptr + new_substr_len, from + from_offset, free_space); break; } strncpy(tmp_str_ptr + new_substr_len, from + from_offset, from_len - from_offset + 1); } strncpy(to, tmp_str, max_length - 1); *(to + (max_length - 1)) = '\0'; return to; } void i3lock_draw_digital_clock(cairo_t *cr, const i3lock_digital_clock_t *dc, const uint32_t *resolution, const struct tm *brokentime) { double rgb16[3]; char current_time_str[128]; int x, y; strftime(current_time_str, 128, dc->template, brokentime); i3lock_color_to_array(rgb16, dc->color); cairo_set_source_rgb(cr, rgb16[0], rgb16[1], rgb16[2]); cairo_set_font_size(cr, dc->text_size); if (xr_screens > 0) { /* xinerama */ int screen; uint32_t screen_resolution[2]; for (screen = 0; screen < xr_screens; ++screen) { screen_resolution[0] = xr_resolutions[screen].width; screen_resolution[1] = xr_resolutions[screen].height; i3lock_fetch_digital_clock_xy(&x, &y, dc, screen_resolution, strlen(current_time_str)); cairo_move_to(cr, x + xr_resolutions[screen].x, y + xr_resolutions[screen].y); cairo_show_text(cr, current_time_str); } return; } i3lock_fetch_digital_clock_xy(&x, &y, dc, resolution, strlen(current_time_str)); /* printf("%dx%d\n", *resolution, *(resolution + 1)); */ cairo_move_to(cr, x, y); cairo_show_text(cr, current_time_str); } void i3lock_draw_string(cairo_t *cr, const char *formatted_string, const i3lock_elapsed_time_t *et, const uint32_t *resolution) { double rgb16[3]; int x, y; /* create a dummy i3lock_digital_clock_t structure in order to reuse i3lock_fetch_digital_clock_xy */ i3lock_digital_clock_t dc = {et->text_size, "", "", et->horizontal_alignment, et->vertical_alignment}; i3lock_color_to_array(rgb16, et->color); cairo_set_source_rgb(cr, rgb16[0], rgb16[1], rgb16[2]); cairo_set_font_size(cr, et->text_size); if (xr_screens > 0) { /* xinerama */ int screen; uint32_t screen_resolution[2]; for (screen = 0; screen < xr_screens; ++screen) { screen_resolution[0] = xr_resolutions[screen].width; screen_resolution[1] = xr_resolutions[screen].height; i3lock_fetch_digital_clock_xy(&x, &y, &dc, screen_resolution, strlen(formatted_string)); cairo_move_to(cr, x + xr_resolutions[screen].x, y + xr_resolutions[screen].y); cairo_show_text(cr, formatted_string); } return; } i3lock_fetch_digital_clock_xy(&x, &y, &dc, resolution, strlen(formatted_string)); /* printf("%dx%d\n", *resolution, *(resolution + 1)); */ cairo_move_to(cr, x, y); cairo_show_text(cr, formatted_string); } void i3lock_draw_elapsed_time(cairo_t *cr, const i3lock_elapsed_time_t *et, const uint32_t *resolution, const time_t *now) { double rgb16[3]; char elapsed_time_str[128]; int x, y, seconds, minutes, hours, diff_seconds; /* create a dummy i3lock_digital_clock_t structure in ordet to reuse i3lock_fetch_digital_clock_xy */ i3lock_digital_clock_t dc = {et->text_size, "", "", et->horizontal_alignment, et->vertical_alignment}; diff_seconds = (int) difftime(*now, et->start_time); hours = diff_seconds / 3600; minutes = (diff_seconds - hours * 3600) / 60; seconds = diff_seconds - hours * 3600 - minutes * 60; if (hours) { snprintf(elapsed_time_str, 128, "%d:%02d:%02d", hours, minutes, seconds); } else { snprintf(elapsed_time_str, 128, "%02d:%02d", minutes, seconds); } if (failed_attempts) { char failed_attempts_str[16]; /* ... that many attempts */ snprintf(failed_attempts_str, 16, " (%d)", failed_attempts); strncat(elapsed_time_str, failed_attempts_str, 16); } i3lock_color_to_array(rgb16, et->color); cairo_set_source_rgb(cr, rgb16[0], rgb16[1], rgb16[2]); cairo_set_font_size(cr, et->text_size); if (xr_screens > 0) { /* xinerama */ int screen; uint32_t screen_resolution[2]; for (screen = 0; screen < xr_screens; ++screen) { screen_resolution[0] = xr_resolutions[screen].width; screen_resolution[1] = xr_resolutions[screen].height; i3lock_fetch_digital_clock_xy(&x, &y, &dc, screen_resolution, strlen(elapsed_time_str)); cairo_move_to(cr, x + xr_resolutions[screen].x, y + xr_resolutions[screen].y); cairo_show_text(cr, elapsed_time_str); } return; } i3lock_fetch_digital_clock_xy(&x, &y, &dc, resolution, strlen(elapsed_time_str)); /* printf("%dx%d\n", *resolution, *(resolution + 1)); */ cairo_move_to(cr, x, y); cairo_show_text(cr, elapsed_time_str); } void i3lock_draw_led_clock(cairo_t *cr, const i3lock_led_clock_t *lc, const uint32_t *resolution, const struct tm *brokentime) { int side_length, space_length, base_x, base_y; int base_x_h; /* used to make the code more readable */ if (xr_screens > 0) { /* xinerama */ int screen; uint32_t screen_resolution[2]; for (screen = 0; screen < xr_screens; ++screen) { screen_resolution[0] = xr_resolutions[screen].width; screen_resolution[1] = xr_resolutions[screen].height; side_length = xr_resolutions[screen].height / I3LOCK_SCALE_LED_SPACING; space_length = xr_resolutions[screen].height / I3LOCK_SCALE_LED_LENGTH; i3lock_fetch_led_clock_xy(&base_x, &base_y, lc, screen_resolution); base_x += xr_resolutions[screen].x; base_y += xr_resolutions[screen].y; base_x_h = base_x + space_length / 2 + side_length / 2; /* hours */ i3lock_draw_led(cr, base_x_h, base_y, side_length, brokentime->tm_hour & 16, lc); i3lock_draw_led(cr, base_x_h + space_length + side_length, base_y, side_length, brokentime->tm_hour & 8, lc); i3lock_draw_led(cr, base_x_h + 2 * (space_length + side_length), base_y, side_length, brokentime->tm_hour & 4, lc); i3lock_draw_led(cr, base_x_h + 3 * (space_length + side_length), base_y, side_length, brokentime->tm_hour & 2, lc); i3lock_draw_led(cr, base_x_h + 4 * (space_length + side_length), base_y, side_length, brokentime->tm_hour & 1, lc); /* minutes */ i3lock_draw_led(cr, base_x, base_y + side_length + space_length, side_length, brokentime->tm_min & 32, lc); i3lock_draw_led(cr, base_x + side_length + space_length, base_y + side_length + space_length, side_length, brokentime->tm_min & 16, lc); i3lock_draw_led(cr, base_x + 2 * (side_length + space_length), base_y + side_length + space_length, side_length, brokentime->tm_min & 8, lc); i3lock_draw_led(cr, base_x + 3 * (side_length + space_length), base_y + side_length + space_length, side_length, brokentime->tm_min & 4, lc); i3lock_draw_led(cr, base_x + 4 * (side_length + space_length), base_y + side_length + space_length, side_length, brokentime->tm_min & 2, lc); i3lock_draw_led(cr, base_x + 5 * (side_length + space_length), base_y + side_length + space_length, side_length, brokentime->tm_min & 1, lc); /* seconds */ i3lock_draw_led(cr, base_x, base_y + 2 * (side_length + space_length), side_length, brokentime->tm_sec & 32, lc); i3lock_draw_led(cr, base_x + side_length + space_length, base_y + 2 * (side_length + space_length), side_length, brokentime->tm_sec & 16, lc); i3lock_draw_led(cr, base_x + 2 * (side_length + space_length), base_y + 2 * (side_length + space_length), side_length, brokentime->tm_sec & 8, lc); i3lock_draw_led(cr, base_x + 3 * (side_length + space_length), base_y + 2 * (side_length + space_length), side_length, brokentime->tm_sec & 4, lc); i3lock_draw_led(cr, base_x + 4 * (side_length + space_length), base_y + 2 * (side_length + space_length), side_length, brokentime->tm_sec & 2, lc); i3lock_draw_led(cr, base_x + 5 * (side_length + space_length), base_y + 2 * (side_length + space_length), side_length, brokentime->tm_sec & 1, lc); } return; } } char * i3lock_format_elapsed_time(char *to, const char *template, int max_size, int seconds) { char tmp_str[max_size]; char int_str[32]; /* more than enough for int */ if (seconds < 0) { if (!(seconds % 3)) return NULL; seconds = 0; } int days = seconds / 86400; int hours = (seconds - days * 86400) / 3600; int minutes = (seconds - days * 86400 - hours * 3600) / 60; int rest_seconds = seconds - days * 86400 - hours * 3600 - minutes * 60; strncpy(tmp_str, template, max_size); if (strlen(template) >= max_size) { /* do a proper clipping */ *(tmp_str + (max_size - 1)) = '\0'; } /* days */ snprintf(int_str, 32, "%d", days); i3lock_replace_str(to, tmp_str, "%d", int_str, max_size); strcpy(tmp_str, to); /* hours */ snprintf(int_str, 32, "%d", hours); i3lock_replace_str(to, tmp_str, "%h", int_str, max_size); strcpy(tmp_str, to); /* minutes */ snprintf(int_str, 32, "%d", minutes); i3lock_replace_str(to, tmp_str, "%m", int_str, max_size); strcpy(tmp_str, to); /* rest. seconds */ snprintf(int_str, 32, "%d", rest_seconds); i3lock_replace_str(to, tmp_str, "%s", int_str, max_size); strcpy(tmp_str, to); /* seconds */ snprintf(int_str, 32, "%d", seconds); i3lock_replace_str(to, tmp_str, "%S", int_str, max_size); return to; }