summaryrefslogtreecommitdiff
path: root/src/qr-show.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2019-12-10 15:24:39 +0100
committerChristian Grothoff <christian@grothoff.org>2019-12-10 15:24:39 +0100
commit742ae057b9ad85644a1c6621f92e9b710d416beb (patch)
treef8c41f715047d0c87ddca80fc80b52cbd8284848 /src/qr-show.c
parent8841dfec1ad670fc584fd5d47c1f157108888976 (diff)
downloadtaler-mdb-742ae057b9ad85644a1c6621f92e9b710d416beb.tar.gz
taler-mdb-742ae057b9ad85644a1c6621f92e9b710d416beb.tar.bz2
taler-mdb-742ae057b9ad85644a1c6621f92e9b710d416beb.zip
implement process to just show QR code for some time
Diffstat (limited to 'src/qr-show.c')
-rw-r--r--src/qr-show.c425
1 files changed, 425 insertions, 0 deletions
diff --git a/src/qr-show.c b/src/qr-show.c
new file mode 100644
index 0000000..c96dab1
--- /dev/null
+++ b/src/qr-show.c
@@ -0,0 +1,425 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+*/
+/**
+* @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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#if HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP_H
+#include <netinet/ip.h> /* superset of previous */
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <termios.h>
+#include <nfc/nfc.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <qrencode.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+/* for adafruit pitft display */
+#include <linux/fb.h>
+
+/**
+ * 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;
+
+/**
+ * Refenence 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;
+
+static int backlight_invert;
+static char backlight_on = '1';
+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 originial display state\n");
+ }
+ /* close device */
+ close (qrDisplay.devicefd);
+ qrDisplay.devicefd = -1;
+ if (0 < qrDisplay.backlightfd)
+ 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 configuation 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_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;
+}