show-qr.c (8238B)
1 /* 2 This file is part of TALER cash2ecash 3 Copyright (C) 2026 GNUnet e.V. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU Affero General Public License as 7 published by the Free Software Foundation, either version 3 of the 8 License, or (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 20 /** 21 * @brief coppied/edited from taler-mdb 22 */ 23 24 #include <stdio.h> 25 #include <sys/ioctl.h> 26 #include <gnunet/gnunet_util_lib.h> 27 #include <linux/fb.h> 28 #include <sys/mman.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <qrencode.h> 32 33 #define BACKLIGHT_INVERTER false 34 35 /** 36 * Standard backlight on value 37 */ 38 static char backlight_on; 39 40 /** 41 * Standard backlight off value 42 */ 43 static char backlight_off; 44 45 /** 46 * Handle for the Framebuffer device 47 */ 48 struct Display 49 { 50 /** 51 * File descriptor for the screen 52 */ 53 int devicefd; 54 55 /** 56 * File descriptor to set backlight information 57 */ 58 int backlightfd; 59 60 /** 61 * The display memory to set the pixel information 62 */ 63 uint16_t *memory; 64 65 /** 66 * Original screen information 67 */ 68 struct fb_var_screeninfo orig_vinfo; 69 70 /** 71 * Variable screen information (color depth ...) 72 */ 73 struct fb_var_screeninfo var_info; 74 75 /** 76 * Fixed screen informtaion 77 */ 78 struct fb_fix_screeninfo fix_info; 79 }; 80 81 void show_qr_init(struct Display *dp ,char *fb_device_file, char *backlight_device_file) 82 { 83 84 /* open the framebuffer device */ 85 dp->devicefd = open (fb_device_file, 86 O_RDWR); 87 if (-1 != dp->devicefd) 88 { 89 /* read information about the screen */ 90 ioctl (dp->devicefd, 91 FBIOGET_VSCREENINFO, 92 &dp->var_info); 93 94 /* store current screeninfo for reset */ 95 dp->orig_vinfo = dp->var_info; 96 97 if (16 != dp->var_info.bits_per_pixel) 98 { 99 /* Change variable info to 16 bit per pixel */ 100 dp->var_info.bits_per_pixel = 16; 101 if (0 > ioctl (dp->devicefd, 102 FBIOPUT_VSCREENINFO, 103 &dp->var_info)) 104 { 105 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,"ioctl(FBIOPUT_VSCREENINFO)"); 106 return; 107 } 108 } 109 110 /* Get fixed screen information */ 111 if (0 > ioctl (dp->devicefd, 112 FBIOGET_FSCREENINFO, 113 &dp->fix_info)) 114 { 115 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 116 "ioctl(FBIOGET_FSCREENINFO)"); 117 return; 118 } 119 120 /* get pointer onto frame buffer */ 121 dp->memory = mmap (NULL, 122 dp->fix_info.smem_len, 123 PROT_READ | PROT_WRITE, MAP_SHARED, 124 dp->devicefd, 125 0); 126 if (MAP_FAILED == dp->memory) 127 { 128 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 129 "mmap"); 130 return; 131 } 132 133 /* set the screen to white */ 134 memset (dp->memory, 135 0xFF, 136 dp->var_info.xres * dp->var_info.yres 137 * sizeof (uint16_t)); 138 139 /* open backlight file to turn display backlight on and off */ 140 dp->backlightfd = open ( 141 backlight_device_file, O_WRONLY); 142 if (0 > dp->backlightfd) 143 { 144 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 145 "open", 146 backlight_device_file); 147 } 148 else 149 { 150 if (BACKLIGHT_INVERTER) 151 { 152 backlight_on = '0'; 153 backlight_off = '1'; 154 } 155 else 156 { 157 backlight_off = '0'; 158 backlight_on = '1'; 159 } 160 /* turn off the backlight */ 161 (void) ! write (dp->backlightfd, 162 &backlight_off, 163 1); 164 } 165 } 166 else 167 { 168 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 169 "open", 170 fb_device_file); 171 } 172 }; 173 174 /** 175 * @brief Create the QR code to pay and display it on screen 176 * 177 * @param uri what text to show in the QR code 178 */ 179 static void 180 show_qrcode (const char *uri) 181 { 182 QRinput *qri; 183 QRcode *qrc; 184 unsigned int size; 185 char *upper; 186 char *base; 187 char *ubase; 188 size_t xOff; 189 size_t yOff; 190 const char *dddash; 191 unsigned int nwidth; 192 193 stop_advertising (); 194 hide_error (); 195 if (0 > qrDisplay.devicefd) 196 return; /* no display, no dice */ 197 /* find the fourth '/' in the payto://pay/hostname/-uri */ 198 dddash = strchr (uri, '/'); 199 if (NULL != dddash) 200 dddash = strchr (dddash + 1, '/'); 201 if (NULL != dddash) 202 dddash = strchr (dddash + 1, '/'); 203 if (NULL != dddash) 204 dddash = strchr (dddash + 1, '/'); 205 if (NULL == dddash) 206 { 207 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 208 "taler://pay/-URI malformed: `%s'\n", 209 uri); 210 return; 211 } 212 213 qri = QRinput_new2 (0, QR_ECLEVEL_L); 214 if (NULL == qri) 215 { 216 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 217 "QRinput_new2"); 218 return; 219 } 220 /* convert all characters until the fourth '/' to upper 221 case. The rest _should_ be upper case in a NICE setup, 222 but we can't warrant it and must not touch those. */ 223 base = GNUNET_strndup (uri, 224 dddash - uri); 225 226 ubase = GNUNET_STRINGS_utf8_toupper (base); 227 GNUNET_free (base); 228 GNUNET_asprintf (&upper, 229 "%s%s", 230 ubase, 231 dddash); 232 GNUNET_free (ubase); 233 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 234 "Showing QR code for `%s'\n", 235 upper); 236 /* first try encoding as uppercase-only alpha-numerical 237 QR code (much smaller encoding); if that fails, also 238 try using binary encoding (in case nick contains 239 special characters). */ 240 if ( (0 != 241 QRinput_append (qri, 242 QR_MODE_AN, 243 strlen (upper), 244 (unsigned char *) upper)) && 245 (0 != 246 QRinput_append (qri, 247 QR_MODE_8, 248 strlen (upper), 249 (unsigned char *) upper))) 250 { 251 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 252 "QRinput_append"); 253 GNUNET_free (upper); 254 return; 255 } 256 GNUNET_free (upper); 257 qrc = QRcode_encodeInput (qri); 258 if (NULL == qrc) 259 { 260 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 261 "QRcode_encodeInput"); 262 QRinput_free (qri); 263 return; 264 } 265 266 /* set QR-code border */ 267 memset (qrDisplay.memory, 268 0xFF, 269 qrDisplay.var_info.xres * qrDisplay.var_info.yres 270 * sizeof (uint16_t)); 271 size = GNUNET_MIN (qrDisplay.var_info.xres, 272 qrDisplay.var_info.yres); 273 274 nwidth = qrc->width + 8; /* +8 for 4 pixel border */ 275 xOff = 4 * size / nwidth; 276 yOff = 4 * size / nwidth; 277 278 /* calculate offset to show the code centered */ 279 if (qrDisplay.var_info.xres < qrDisplay.var_info.yres) 280 yOff += (qrDisplay.var_info.yres - qrDisplay.var_info.xres) / 2; 281 else 282 xOff += (qrDisplay.var_info.xres - qrDisplay.var_info.yres) / 2; 283 for (unsigned int x = 0; x < qrDisplay.var_info.xres - 2 * xOff; x++) 284 for (unsigned int y = 0; y < qrDisplay.var_info.yres - 2 * yOff; y++) 285 { 286 unsigned int xoff = x * nwidth / size; 287 unsigned int yoff = y * nwidth / size; 288 unsigned int off = xoff + yoff * qrc->width; 289 if ( (xoff >= (unsigned) qrc->width) || 290 (yoff >= (unsigned) qrc->width) ) 291 continue; 292 /* set the pixels in the display memory */ 293 qrDisplay.memory[(y + yOff) * qrDisplay.var_info.xres + (x + xOff)] = 294 (0 == (qrc->data[off] & 1)) ? 0xFFFF : 0x0000; 295 } 296 297 QRcode_free (qrc); 298 QRinput_free (qri); 299 300 /* Turn on backlight if supported */ 301 if (0 < qrDisplay.backlightfd) 302 (void) ! write (qrDisplay.backlightfd, 303 &backlight_on, 304 1); 305 }