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