/* This file is part of TALER Copyright (C) 2019 GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. TALER is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ /** * @file qr-show.c * @brief shows a QR code on the display for a defined amount of time * @author Boss Marco * @author Christian Grothoff */ #include "config.h" #include #include #include #include #include #if HAVE_SYS_UN_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_NETINET_IP_H #include /* superset of previous */ #endif #include #include #include #include #include #include #include #include #include #include #include /* for adafruit pitft display */ #include /** * Disable i18n support. */ #define _(s) (s) /** * Handle for the Framebuffer device */ struct Display { /** * File descriptor for the screen */ int devicefd; /** * File descriptor to set backlight information */ int backlightfd; /** * The display memory to set the pixel information */ uint16_t *memory; /** * Original screen information */ struct fb_var_screeninfo orig_vinfo; /** * Variable screen information (color depth ...) */ struct fb_var_screeninfo var_info; /** * Fixed screen informtaion */ struct fb_fix_screeninfo fix_info; }; /** * Next program to launch. */ static char *const *arg_next; static struct GNUNET_TIME_Relative delay; /** * Reference to the delay task */ static struct GNUNET_SCHEDULER_Task *delay_task; /** * Name of the framebuffer device (i.e. /dev/fb1). */ static char *framebuffer_device_filename; /** * Name of the backlight file of @e framebuffer_device_filename (i.e. /sys/class/backlight/soc:backlight/brightness). */ static char *framebuffer_backlight_filename; /** * Global option '-i' to invert backlight on/off values */ static int backlight_invert; /** * Standard backlight on value */ static char backlight_on = '1'; /** * Standard backlight off value */ static char backlight_off = '0'; /** * Handle for the framebuffer device */ static struct Display qrDisplay; /** * @brief Create the QR code to pay and display it on screen * * @param uri what text to show in the QR code */ static void show_qrcode (const char *uri) { QRinput *qri; QRcode *qrc; unsigned int size; size_t xOff; size_t yOff; if (0 > qrDisplay.devicefd) return; /* no display, no dice */ qri = QRinput_new2 (0, QR_ECLEVEL_L); if (NULL == qri) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRinput_new2"); return; } /* first try encoding as uppercase-only alpha-numerical QR code (much smaller encoding); if that fails, also try using binary encoding (in case nick contains special characters). */ if ( (0 != QRinput_append (qri, QR_MODE_AN, strlen (uri), (unsigned char *) uri)) && (0 != QRinput_append (qri, QR_MODE_8, strlen (uri), (unsigned char *) uri))) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRinput_append"); return; } qrc = QRcode_encodeInput (qri); if (NULL == qrc) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "QRcode_encodeInput"); QRinput_free (qri); return; } /* +8 for 4-pixels border */ size = GNUNET_MIN (qrDisplay.var_info.xres, qrDisplay.var_info.yres); unsigned int nwidth = qrc->width + 8; /* 4 pixel border */ xOff = 4 * size / nwidth; yOff = 4 * size / nwidth; if (qrDisplay.var_info.xres < qrDisplay.var_info.yres) yOff += (qrDisplay.var_info.yres - qrDisplay.var_info.xres) / 2; else xOff += (qrDisplay.var_info.xres - qrDisplay.var_info.yres) / 2; for (unsigned int x = 0; x < qrDisplay.var_info.xres - 2 * xOff; x++) for (unsigned int y = 0; y < qrDisplay.var_info.yres - 2 * yOff; y++) { unsigned int xoff = x * nwidth / size; unsigned int yoff = y * nwidth / size; unsigned int off = xoff + yoff * qrc->width; if ( (xoff >= (unsigned) qrc->width) || (yoff >= (unsigned) qrc->width) ) continue; qrDisplay.memory[(y + yOff) * qrDisplay.var_info.xres + (x + xOff)] = (0 == (qrc->data[off] & 1)) ? 0xFFFF : 0x0000; } QRcode_free (qrc); QRinput_free (qri); if (0 < qrDisplay.backlightfd) (void) ! write (qrDisplay.backlightfd, &backlight_on, 1); } static void stop_task (void *cls) { (void) cls; delay_task = NULL; GNUNET_SCHEDULER_shutdown (); } static void shutdown_task (void *cls) { (void) cls; if (NULL != delay_task) { GNUNET_SCHEDULER_cancel (delay_task); delay_task = NULL; } if (NULL != qrDisplay.memory) memset (qrDisplay.memory, 0xFF, qrDisplay.var_info.xres * qrDisplay.var_info.yres * sizeof (uint16_t)); if (0 < qrDisplay.backlightfd) (void) ! write (qrDisplay.backlightfd, &backlight_off, 1); if (NULL != qrDisplay.memory) { /* free the display data */ munmap (qrDisplay.memory, qrDisplay.fix_info.smem_len); qrDisplay.memory = NULL; /* reset original state */ if (0 > ioctl (qrDisplay.devicefd, FBIOPUT_VSCREENINFO, &qrDisplay.orig_vinfo)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "failed to reset original display state\n"); } /* close device */ GNUNET_break (0 == close (qrDisplay.devicefd)); qrDisplay.devicefd = -1; if (0 < qrDisplay.backlightfd) GNUNET_break (0 == close (qrDisplay.backlightfd)); qrDisplay.backlightfd = -1; } } /** * @brief Start the application * * @param cls closure * @param args arguments left * @param cfgfile config file name * @param cfg handle for the configuration file */ static void run (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { (void) cls; (void) cfgfile; if (NULL == args[0]) return; arg_next = args + 1; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "taler-mdb", "FRAMEBUFFER_DEVICE", &framebuffer_device_filename)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "taler-mdb", "FRAMEBUFFER_DEVICE"); framebuffer_device_filename = GNUNET_strdup ("/dev/fb1"); } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "taler-mdb", "FRAMEBUFFER_BACKLIGHT", &framebuffer_backlight_filename)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "taler-mdb", "FRAMEBUFFER_BACKLIGHT"); framebuffer_backlight_filename = GNUNET_strdup ( "/sys/class/backlight/soc:backlight/brightness"); } /* open the framebuffer device */ qrDisplay.devicefd = open (framebuffer_device_filename, O_RDWR); if (0 < qrDisplay.devicefd) { /* read information about the screen */ ioctl (qrDisplay.devicefd, FBIOGET_VSCREENINFO, &qrDisplay.var_info); /* store current screeninfo for reset */ qrDisplay.orig_vinfo = qrDisplay.var_info; if (16 != qrDisplay.var_info.bits_per_pixel) { /* Change variable info to 16 bit per pixel */ qrDisplay.var_info.bits_per_pixel = 16; if (0 > ioctl (qrDisplay.devicefd, FBIOPUT_VSCREENINFO, &qrDisplay.var_info)) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctl(FBIOPUT_VSCREENINFO)"); return; } } /* Get fixed screen information */ if (0 > ioctl (qrDisplay.devicefd, FBIOGET_FSCREENINFO, &qrDisplay.fix_info)) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctl(FBIOGET_FSCREENINFO)"); return; } /* get pointer onto frame buffer */ qrDisplay.memory = mmap (NULL, qrDisplay.fix_info.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, qrDisplay.devicefd, 0); /* open backlight file to turn display backlight on and off */ if (0 > qrDisplay.devicefd) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "mmap"); return; } memset (qrDisplay.memory, 0xFF, qrDisplay.var_info.xres * qrDisplay.var_info.yres * sizeof (uint16_t)); qrDisplay.backlightfd = open ( framebuffer_backlight_filename, O_WRONLY); if (0 > qrDisplay.backlightfd) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", framebuffer_backlight_filename); } else { if (backlight_invert) { backlight_on = '0'; backlight_off = '1'; } (void) ! write (qrDisplay.backlightfd, &backlight_off, 1); } } else { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", framebuffer_device_filename); } GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); show_qrcode (args[0]); delay_task = GNUNET_SCHEDULER_add_delayed (delay, &stop_task, NULL); } int main (int argc, char*const*argv) { int ret; /* the available command line options */ struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_option_relative_time ('d', "delay", "DELAY", "how long should we display the text before continuing", &delay), GNUNET_GETOPT_option_flag ('i', "backlight-invert", "invert the backlight on/off values (standard on = 1)", &backlight_invert), GNUNET_GETOPT_OPTION_END }; ret = GNUNET_PROGRAM_run (argc, argv, "qr-show", "This is an application to show a QR code for a defined period of time before starting another program.\n", options, &run, NULL); if (GNUNET_OK != ret) return 1; if ( (NULL == arg_next) || (NULL == arg_next[0]) ) return 0; execvp (arg_next[0], arg_next); GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", arg_next[0]); return 1; }