cash2ecash

cash2ecash: cash acceptor that issues digital cash (experimental)
Log | Files | Refs | README | LICENSE

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 }