]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/qrcode-util.c
hwdb: Add mapping for Xiaomi Mipad 2 bottom bezel capacitive buttons
[thirdparty/systemd.git] / src / shared / qrcode-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
4368277c 2
da3920c3 3#include "qrcode-util.h"
f1b82359
ZJS
4
5#if HAVE_QRENCODE
6#include <qrencode.h>
7
8#include "dlfcn-util.h"
9#include "locale-util.h"
1622ef77 10#include "log.h"
3f5225d7 11#include "strv.h"
da3920c3
LP
12#include "terminal-util.h"
13
14#define ANSI_WHITE_ON_BLACK "\033[40;37;1m"
5535484e
YW
15#define UNICODE_FULL_BLOCK u8"█"
16#define UNICODE_LOWER_HALF_BLOCK u8"▄"
17#define UNICODE_UPPER_HALF_BLOCK u8"▀"
da3920c3 18
ad914843
LP
19static void *qrcode_dl = NULL;
20
9dbabd0a
LP
21static DLSYM_FUNCTION(QRcode_encodeString);
22static DLSYM_FUNCTION(QRcode_free);
ad914843
LP
23
24int dlopen_qrencode(void) {
3f5225d7
DDM
25 int r;
26
27 FOREACH_STRING(s, "libqrencode.so.4", "libqrencode.so.3") {
28 r = dlopen_many_sym_or_warn(
29 &qrcode_dl, s, LOG_DEBUG,
ad914843 30 DLSYM_ARG(QRcode_encodeString),
1622ef77 31 DLSYM_ARG(QRcode_free));
3f5225d7
DDM
32 if (r >= 0)
33 break;
34 }
35
36 return r;
ad914843
LP
37}
38
fc7eb132
OJ
39static void print_border(FILE *output, unsigned width, unsigned row, unsigned column) {
40 assert(output);
41 assert(width);
da3920c3 42
fc7eb132
OJ
43 if (row != UINT_MAX && column != UINT_MAX) {
44 int r, fd;
da3920c3 45
fc7eb132
OJ
46 fd = fileno(output);
47 if (fd < 0)
48 return (void)log_debug_errno(errno, "Failed to get file descriptor from the file stream: %m");
49
50 r = set_terminal_cursor_position(fd, row, column);
51 if (r < 0)
52 log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
53
54 /* Four rows of border */
55 for (unsigned y = 0; y < 4; y += 2) {
56 fputs(ANSI_WHITE_ON_BLACK, output);
57
58 for (unsigned x = 0; x < 4 + width + 4; x++)
59 fputs(UNICODE_FULL_BLOCK, output);
60
61 fputs(ANSI_NORMAL "\n", output);
62 r = set_terminal_cursor_position(fd, row + 1, column);
63 if (r < 0)
64 log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
65 }
66 } else {
67 /* Four rows of border */
68 for (unsigned y = 0; y < 4; y += 2) {
69 fputs(ANSI_WHITE_ON_BLACK, output);
70
71 for (unsigned x = 0; x < 4 + width + 4; x++)
72 fputs(UNICODE_FULL_BLOCK, output);
73
74 fputs(ANSI_NORMAL "\n", output);
75 }
da3920c3
LP
76 }
77}
78
fc7eb132 79static void write_qrcode(FILE *output, QRcode *qr, unsigned int row, unsigned int column) {
da3920c3
LP
80 assert(qr);
81
82 if (!output)
83 output = stdout;
84
fc7eb132
OJ
85 print_border(output, qr->width, row, column);
86
87 if (row != UINT_MAX && column != UINT_MAX) {
88 /* After printing two rows of top border, we need to move the cursor down two rows before starting to print the actual QR code */
89 int r, fd, move_down = 2;
90 fd = fileno(output);
91 if (fd < 0)
92 return (void)log_debug_errno(errno, "Failed to get file descriptor from the file stream: %m");
93
94 r = set_terminal_cursor_position(fd, row + move_down, column);
95 if (r < 0)
96 log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
97
98 for (unsigned y = 0; y < (unsigned) qr->width; y += 2) {
99 const uint8_t *row1 = qr->data + qr->width * y;
100 const uint8_t *row2 = row1 + qr->width;
101
102 fputs(ANSI_WHITE_ON_BLACK, output);
103
104 for (unsigned x = 0; x < 4; x++)
105 fputs(UNICODE_FULL_BLOCK, output);
106
107 for (unsigned x = 0; x < (unsigned) qr->width; x++) {
108 bool a, b;
da3920c3 109
fc7eb132
OJ
110 a = row1[x] & 1;
111 b = (y+1) < (unsigned) qr->width ? (row2[x] & 1) : false;
da3920c3 112
fc7eb132
OJ
113 if (a && b)
114 fputc(' ', output);
115 else if (a)
116 fputs(UNICODE_LOWER_HALF_BLOCK, output);
117 else if (b)
118 fputs(UNICODE_UPPER_HALF_BLOCK, output);
119 else
120 fputs(UNICODE_FULL_BLOCK, output);
121 }
122
123 for (unsigned x = 0; x < 4; x++)
124 fputs(UNICODE_FULL_BLOCK, output);
125 r = set_terminal_cursor_position(fd, row + move_down, column);
126 if (r < 0)
127 log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
128 move_down += 1;
129 fputs(ANSI_NORMAL "\n", output);
130 }
da3920c3 131
fc7eb132
OJ
132 print_border(output, qr->width, row + move_down, column);
133 } else {
da3920c3 134
fc7eb132
OJ
135 for (unsigned y = 0; y < (unsigned) qr->width; y += 2) {
136 const uint8_t *row1 = qr->data + qr->width * y;
137 const uint8_t *row2 = row1 + qr->width;
da3920c3 138
fc7eb132
OJ
139 fputs(ANSI_WHITE_ON_BLACK, output);
140 for (unsigned x = 0; x < 4; x++)
5535484e 141 fputs(UNICODE_FULL_BLOCK, output);
fc7eb132
OJ
142
143 for (unsigned x = 0; x < (unsigned) qr->width; x++) {
144 bool a, b;
145
146 a = row1[x] & 1;
147 b = (y+1) < (unsigned) qr->width ? (row2[x] & 1) : false;
148
149 if (a && b)
150 fputc(' ', output);
151 else if (a)
152 fputs(UNICODE_LOWER_HALF_BLOCK, output);
153 else if (b)
154 fputs(UNICODE_UPPER_HALF_BLOCK, output);
155 else
156 fputs(UNICODE_FULL_BLOCK, output);
157 }
158
159 for (unsigned x = 0; x < 4; x++)
160 fputs(UNICODE_FULL_BLOCK, output);
161 fputs(ANSI_NORMAL "\n", output);
da3920c3
LP
162 }
163
fc7eb132 164 print_border(output, qr->width, row, column);
da3920c3
LP
165 }
166
da3920c3
LP
167 fflush(output);
168}
f1b82359 169
fc7eb132 170int print_qrcode_full(FILE *out, const char *header, const char *string, unsigned row, unsigned column, unsigned tty_width, unsigned tty_height) {
f1b82359
ZJS
171 QRcode* qr;
172 int r;
173
5bc9ea07 174 /* If this is not a UTF-8 system or ANSI colors aren't supported/disabled don't print any QR
f1b82359
ZJS
175 * codes */
176 if (!is_locale_utf8() || !colors_enabled())
177 return -EOPNOTSUPP;
178
ad914843 179 r = dlopen_qrencode();
f1b82359
ZJS
180 if (r < 0)
181 return r;
182
fd11201b 183 qr = sym_QRcode_encodeString(string, 0, QR_ECLEVEL_L, QR_MODE_8, 1);
f1b82359
ZJS
184 if (!qr)
185 return -ENOMEM;
186
fc7eb132
OJ
187 if (row != UINT_MAX && column != UINT_MAX) {
188 int fd;
189 unsigned qr_code_width, qr_code_height;
190 fd = fileno(out);
191 if (fd < 0)
192 return log_debug_errno(errno, "Failed to get file descriptor from the file stream: %m");
193 qr_code_width = qr_code_height = qr->width + 8;
194
195 if (column + qr_code_width > tty_width)
196 column = tty_width - qr_code_width;
197
198 /* Terminal characters are twice as high as they are wide so it's qr_code_height / 2,
199 * our QR code prints an extra new line, so we have -1 as well */
200 if (row + qr_code_height > tty_height)
201 row = tty_height - (qr_code_height / 2 ) - 1;
202
203 if (header) {
204 r = set_terminal_cursor_position(fd, row - 2, tty_width - qr_code_width - 2);
205 if (r < 0)
206 log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
207
208 fprintf(out, "%s:\n\n", header);
209 }
210 } else
211 if (header)
212 fprintf(out, "\n%s:\n\n", header);
f1b82359 213
fc7eb132 214 write_qrcode(out, qr, row, column);
f1b82359
ZJS
215
216 fputc('\n', out);
217
218 sym_QRcode_free(qr);
219 return 0;
220}
221#endif