/*
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;
}