taler-mdb-qr-show.c (11892B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2019 GNUnet e.V. 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 FITNESS FOR 12 A PARTICULAR PURPOSE. See the GNU General Public License for more 13 details. 14 15 You should have received a copy of the GNU General Public License 16 along with 17 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file taler-mdb-qr-show.c 21 * @brief shows a QR code on the display for a defined amount of time 22 * @author Boss Marco 23 * @author Christian Grothoff 24 */ 25 #include "config.h" 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 #include <sys/socket.h> 31 #if HAVE_SYS_UN_H 32 #include <sys/un.h> 33 #endif 34 #if HAVE_NETINET_IN_H 35 #include <netinet/in.h> 36 #endif 37 #if HAVE_NETINET_IP_H 38 #include <netinet/ip.h> /* superset of previous */ 39 #endif 40 #include <sys/stat.h> 41 #include <sys/types.h> 42 #include <errno.h> 43 #include <termios.h> 44 #include <nfc/nfc.h> 45 #include <microhttpd.h> 46 #include <gnunet/gnunet_util_lib.h> 47 #include <qrencode.h> 48 #include <sys/mman.h> 49 #include <sys/ioctl.h> 50 #include <fcntl.h> 51 /* for adafruit pitft display */ 52 #include <linux/fb.h> 53 #include "taler_mdb_util.h" 54 55 56 /** 57 * Disable i18n support. 58 */ 59 #define _(s) (s) 60 61 /** 62 * Handle for the Framebuffer device 63 */ 64 struct Display 65 { 66 /** 67 * File descriptor for the screen 68 */ 69 int devicefd; 70 71 /** 72 * File descriptor to set backlight information 73 */ 74 int backlightfd; 75 76 /** 77 * The display memory to set the pixel information 78 */ 79 uint16_t *memory; 80 81 /** 82 * Original screen information 83 */ 84 struct fb_var_screeninfo orig_vinfo; 85 86 /** 87 * Variable screen information (color depth ...) 88 */ 89 struct fb_var_screeninfo var_info; 90 91 /** 92 * Fixed screen informtaion 93 */ 94 struct fb_fix_screeninfo fix_info; 95 }; 96 97 98 /** 99 * Next program to launch. 100 */ 101 static char *const *arg_next; 102 103 static struct GNUNET_TIME_Relative delay; 104 105 /** 106 * Reference to the delay task 107 */ 108 static struct GNUNET_SCHEDULER_Task *delay_task; 109 110 /** 111 * Name of the framebuffer device (i.e. /dev/fb1). 112 */ 113 static char *framebuffer_device_filename; 114 115 /** 116 * Name of the backlight file of @e framebuffer_device_filename (i.e. /sys/class/backlight/soc:backlight/brightness). 117 */ 118 static char *framebuffer_backlight_filename; 119 120 /** 121 * Global option '-i' to invert backlight on/off values 122 */ 123 static int backlight_invert; 124 125 /** 126 * Standard backlight on value 127 */ 128 static char backlight_on = '1'; 129 130 /** 131 * Standard backlight off value 132 */ 133 static char backlight_off = '0'; 134 135 /** 136 * Handle for the framebuffer device 137 */ 138 static struct Display qrDisplay; 139 140 141 /** 142 * @brief Create the QR code to pay and display it on screen 143 * 144 * @param uri what text to show in the QR code 145 */ 146 static void 147 show_qrcode (const char *uri) 148 { 149 QRinput *qri; 150 QRcode *qrc; 151 unsigned int size; 152 size_t xOff; 153 size_t yOff; 154 unsigned int nwidth; 155 156 if (0 > qrDisplay.devicefd) 157 return; /* no display, no dice */ 158 qri = QRinput_new2 (0, QR_ECLEVEL_L); 159 if (NULL == qri) 160 { 161 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 162 "QRinput_new2"); 163 return; 164 } 165 /* first try encoding as uppercase-only alpha-numerical 166 QR code (much smaller encoding); if that fails, also 167 try using binary encoding (in case nick contains 168 special characters). */ 169 if ( (0 != 170 QRinput_append (qri, 171 QR_MODE_AN, 172 strlen (uri), 173 (unsigned char *) uri)) && 174 (0 != 175 QRinput_append (qri, 176 QR_MODE_8, 177 strlen (uri), 178 (unsigned char *) uri))) 179 { 180 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 181 "QRinput_append"); 182 return; 183 } 184 qrc = QRcode_encodeInput (qri); 185 if (NULL == qrc) 186 { 187 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 188 "QRcode_encodeInput"); 189 QRinput_free (qri); 190 return; 191 } 192 193 /* +8 for 4-pixels border */ 194 size = GNUNET_MIN (qrDisplay.var_info.xres, 195 qrDisplay.var_info.yres); 196 nwidth = qrc->width + 8; /* 4 pixel border */ 197 xOff = 4 * size / nwidth; 198 yOff = 4 * size / nwidth; 199 if (qrDisplay.var_info.xres < qrDisplay.var_info.yres) 200 yOff += (qrDisplay.var_info.yres - qrDisplay.var_info.xres) / 2; 201 else 202 xOff += (qrDisplay.var_info.xres - qrDisplay.var_info.yres) / 2; 203 for (unsigned int x = 0; x < qrDisplay.var_info.xres - 2 * xOff; x++) 204 for (unsigned int y = 0; y < qrDisplay.var_info.yres - 2 * yOff; y++) 205 { 206 unsigned int xoff = x * nwidth / size; 207 unsigned int yoff = y * nwidth / size; 208 unsigned int off = xoff + yoff * qrc->width; 209 if ( (xoff >= (unsigned) qrc->width) || 210 (yoff >= (unsigned) qrc->width) ) 211 continue; 212 qrDisplay.memory[(y + yOff) * qrDisplay.var_info.xres + (x + xOff)] = 213 (0 == (qrc->data[off] & 1)) ? 0xFFFF : 0x0000; 214 } 215 216 QRcode_free (qrc); 217 QRinput_free (qri); 218 219 if (0 < qrDisplay.backlightfd) 220 (void) ! write (qrDisplay.backlightfd, 221 &backlight_on, 222 1); 223 } 224 225 226 static void 227 stop_task (void *cls) 228 { 229 (void) cls; 230 delay_task = NULL; 231 GNUNET_SCHEDULER_shutdown (); 232 } 233 234 235 static void 236 shutdown_task (void *cls) 237 { 238 (void) cls; 239 if (NULL != delay_task) 240 { 241 GNUNET_SCHEDULER_cancel (delay_task); 242 delay_task = NULL; 243 } 244 if (NULL != qrDisplay.memory) 245 memset (qrDisplay.memory, 246 0xFF, 247 qrDisplay.var_info.xres 248 * qrDisplay.var_info.yres 249 * sizeof (uint16_t)); 250 if (0 < qrDisplay.backlightfd) 251 (void) ! write (qrDisplay.backlightfd, 252 &backlight_off, 253 1); 254 if (NULL != qrDisplay.memory) 255 { 256 /* free the display data */ 257 munmap (qrDisplay.memory, 258 qrDisplay.fix_info.smem_len); 259 qrDisplay.memory = NULL; 260 /* reset original state */ 261 if (0 > ioctl (qrDisplay.devicefd, 262 FBIOPUT_VSCREENINFO, 263 &qrDisplay.orig_vinfo)) 264 { 265 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 266 "failed to reset original display state\n"); 267 } 268 /* close device */ 269 GNUNET_break (0 == close (qrDisplay.devicefd)); 270 qrDisplay.devicefd = -1; 271 if (0 < qrDisplay.backlightfd) 272 GNUNET_break (0 == close (qrDisplay.backlightfd)); 273 qrDisplay.backlightfd = -1; 274 } 275 } 276 277 278 /** 279 * @brief Start the application 280 * 281 * @param cls closure 282 * @param args arguments left 283 * @param cfgfile config file name 284 * @param cfg handle for the configuration file 285 */ 286 static void 287 run (void *cls, 288 char *const *args, 289 const char *cfgfile, 290 const struct GNUNET_CONFIGURATION_Handle *cfg) 291 { 292 (void) cls; 293 (void) cfgfile; 294 295 if (NULL == args[0]) 296 return; 297 arg_next = args + 1; 298 if (GNUNET_OK != 299 GNUNET_CONFIGURATION_get_value_filename ( 300 cfg, 301 "taler-mdb", 302 "FRAMEBUFFER_DEVICE", 303 &framebuffer_device_filename)) 304 { 305 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 306 "taler-mdb", 307 "FRAMEBUFFER_DEVICE"); 308 framebuffer_device_filename = GNUNET_strdup ("/dev/fb1"); 309 } 310 if (GNUNET_OK != 311 GNUNET_CONFIGURATION_get_value_filename ( 312 cfg, 313 "taler-mdb", 314 "FRAMEBUFFER_BACKLIGHT", 315 &framebuffer_backlight_filename)) 316 { 317 GNUNET_log_config_missing ( 318 GNUNET_ERROR_TYPE_ERROR, 319 "taler-mdb", 320 "FRAMEBUFFER_BACKLIGHT"); 321 framebuffer_backlight_filename = GNUNET_strdup ( 322 "/sys/class/backlight/soc:backlight/brightness"); 323 } 324 /* open the framebuffer device */ 325 qrDisplay.devicefd = open (framebuffer_device_filename, 326 O_RDWR); 327 if (0 < qrDisplay.devicefd) 328 { 329 /* read information about the screen */ 330 ioctl (qrDisplay.devicefd, 331 FBIOGET_VSCREENINFO, 332 &qrDisplay.var_info); 333 334 /* store current screeninfo for reset */ 335 qrDisplay.orig_vinfo = qrDisplay.var_info; 336 337 if (16 != qrDisplay.var_info.bits_per_pixel) 338 { 339 /* Change variable info to 16 bit per pixel */ 340 qrDisplay.var_info.bits_per_pixel = 16; 341 if (0 > ioctl (qrDisplay.devicefd, 342 FBIOPUT_VSCREENINFO, 343 &qrDisplay.var_info)) 344 { 345 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 346 "ioctl(FBIOPUT_VSCREENINFO)"); 347 return; 348 } 349 } 350 351 /* Get fixed screen information */ 352 if (0 > ioctl (qrDisplay.devicefd, 353 FBIOGET_FSCREENINFO, 354 &qrDisplay.fix_info)) 355 { 356 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 357 "ioctl(FBIOGET_FSCREENINFO)"); 358 return; 359 } 360 361 /* get pointer onto frame buffer */ 362 qrDisplay.memory 363 = mmap (NULL, 364 qrDisplay.fix_info.smem_len, 365 PROT_READ | PROT_WRITE, MAP_SHARED, 366 qrDisplay.devicefd, 367 0); 368 369 /* open backlight file to turn display backlight on and off */ 370 if (0 > qrDisplay.devicefd) 371 { 372 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, 373 "mmap"); 374 return; 375 } 376 377 memset (qrDisplay.memory, 378 0xFF, 379 qrDisplay.var_info.xres * qrDisplay.var_info.yres 380 * sizeof (uint16_t)); 381 382 qrDisplay.backlightfd = open ( 383 framebuffer_backlight_filename, O_WRONLY); 384 if (0 > qrDisplay.backlightfd) 385 { 386 GNUNET_log_strerror_file ( 387 GNUNET_ERROR_TYPE_WARNING, 388 "open", 389 framebuffer_backlight_filename); 390 } 391 else 392 { 393 if (backlight_invert) 394 { 395 backlight_on = '0'; 396 backlight_off = '1'; 397 } 398 (void) ! write (qrDisplay.backlightfd, 399 &backlight_off, 400 1); 401 } 402 } 403 else 404 { 405 GNUNET_log_strerror_file ( 406 GNUNET_ERROR_TYPE_WARNING, 407 "open", 408 framebuffer_device_filename); 409 } 410 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, 411 NULL); 412 show_qrcode (args[0]); 413 delay_task = GNUNET_SCHEDULER_add_delayed (delay, 414 &stop_task, 415 NULL); 416 } 417 418 419 int 420 main (int argc, 421 char*const*argv) 422 { 423 int ret; 424 /* the available command line options */ 425 struct GNUNET_GETOPT_CommandLineOption options[] = { 426 GNUNET_GETOPT_option_relative_time ('d', 427 "delay", 428 "DELAY", 429 "how long should we display the QR code before exiting", 430 &delay), 431 GNUNET_GETOPT_option_flag ('i', 432 "backlight-invert", 433 "invert the backlight on/off values (standard on = 1)", 434 &backlight_invert), 435 GNUNET_GETOPT_OPTION_END 436 }; 437 438 ret = GNUNET_PROGRAM_run (TALER_MDB_project_data (), 439 argc, 440 argv, 441 "taler-mdb-qr-show", 442 "This is an application to show a QR code for a defined period of time before starting another program.\n", 443 options, 444 &run, 445 NULL); 446 if (GNUNET_OK != ret) 447 return 1; 448 if ( (NULL == arg_next) || 449 (NULL == arg_next[0]) ) 450 return 0; 451 execvp (arg_next[0], 452 arg_next); 453 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 454 "execvp", 455 arg_next[0]); 456 return 1; 457 }