mirror of
https://github.com/edu4rdshl/gnome-shell.git
synced 2026-07-17 23:24:51 +00:00
src: Add function for generating QR codes
In order to implement Web Login in GDM we're going to need to be able to generate QR codes. It's possible we'll need QR codes for other future features as well. As a first step, this commit adds API for outputting a GIcon of a QR code for a specified URL.
This commit is contained in:
parent
a9fe6d1f75
commit
c42d33aa46
5 changed files with 316 additions and 0 deletions
|
|
@ -355,3 +355,11 @@ $_gdm_dialog_width: 25em;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// QR Code
|
||||
.qr-code {
|
||||
background: black;
|
||||
border-radius: $base_border_radius * .5;
|
||||
border-color: white;
|
||||
border-width: 1em;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ systemd_req = '>= 246'
|
|||
ibus_req = '>= 1.5.19'
|
||||
gnome_desktop_req = '>= 40'
|
||||
pipewire_req = '>= 0.3.49'
|
||||
qrencode_req = '>= 4.1.1'
|
||||
|
||||
nm_req = '>= 1.10.4'
|
||||
secret_req = '>= 0.18'
|
||||
|
|
@ -93,6 +94,7 @@ ibus_dep = dependency('ibus-1.0', version: ibus_req)
|
|||
x11_dep = dependency('x11')
|
||||
schemas_dep = dependency('gsettings-desktop-schemas', version: schemas_req)
|
||||
gnome_desktop_dep = dependency('gnome-desktop-4', version: gnome_desktop_req)
|
||||
qrencode_dep = dependency('libqrencode', version: qrencode_req)
|
||||
|
||||
nm_deps = []
|
||||
if get_option('networkmanager')
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ gnome_shell_deps = [
|
|||
gcr_dep,
|
||||
libsystemd_dep,
|
||||
libpipewire_dep,
|
||||
qrencode_dep
|
||||
]
|
||||
|
||||
gnome_shell_deps += nm_deps
|
||||
|
|
@ -156,6 +157,8 @@ libshell_sources = [
|
|||
'shell-perf-log.c',
|
||||
'shell-polkit-authentication-agent.c',
|
||||
'shell-polkit-authentication-agent.h',
|
||||
'shell-qr-code-generator.c',
|
||||
'shell-qr-code-generator.h',
|
||||
'shell-screenshot.c',
|
||||
'shell-secure-text-buffer.c',
|
||||
'shell-secure-text-buffer.h',
|
||||
|
|
|
|||
275
src/shell-qr-code-generator.c
Normal file
275
src/shell-qr-code-generator.c
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <cogl/cogl.h>
|
||||
#include <meta/display.h>
|
||||
#include <meta/util.h>
|
||||
#include <meta/meta-plugin.h>
|
||||
#include <st/st.h>
|
||||
|
||||
#include <qrencode.h>
|
||||
|
||||
#include "shell-qr-code-generator.h"
|
||||
|
||||
#define BYTES_PER_RGB_888 3
|
||||
|
||||
typedef struct _ShellQrCodeGeneratorPrivate ShellQrCodeGeneratorPrivate;
|
||||
|
||||
struct _ShellQrCodeGenerator
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
ShellQrCodeGeneratorPrivate *priv;
|
||||
};
|
||||
|
||||
struct _ShellQrCodeGeneratorPrivate
|
||||
{
|
||||
char *url;
|
||||
size_t width;
|
||||
size_t height;
|
||||
GTask *image_task;
|
||||
GTask *icon_task;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (ShellQrCodeGenerator, shell_qr_code_generator, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
shell_qr_code_generator_dispose (GObject *object)
|
||||
{
|
||||
ShellQrCodeGenerator *self = SHELL_QR_CODE_GENERATOR (object);
|
||||
g_clear_pointer (&self->priv->url, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_qr_code_generator_class_init (ShellQrCodeGeneratorClass *qr_code_generator_class)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) qr_code_generator_class;
|
||||
gobject_class->dispose = shell_qr_code_generator_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_qr_code_generator_init (ShellQrCodeGenerator *qr_code_generator)
|
||||
{
|
||||
qr_code_generator->priv = shell_qr_code_generator_get_instance_private (qr_code_generator);
|
||||
}
|
||||
|
||||
static guint8 *
|
||||
generate_icon (const char *url,
|
||||
size_t width,
|
||||
size_t height,
|
||||
GError **error)
|
||||
{
|
||||
QRcode *qrcode;
|
||||
g_autofree guint8 *pixel_data = NULL;
|
||||
guint8 white_pixel[BYTES_PER_RGB_888] = {255, 255, 255};
|
||||
guint8 black_pixel[BYTES_PER_RGB_888] = {0, 0, 0};
|
||||
size_t pixel_size = sizeof (white_pixel);
|
||||
size_t symbol_size;
|
||||
size_t symbols_per_row, number_of_rows;
|
||||
size_t code_width;
|
||||
size_t code_height;
|
||||
size_t offset_x;
|
||||
size_t offset_y;
|
||||
size_t row, symbol, symbol_x, symbol_y;
|
||||
|
||||
qrcode = QRcode_encodeString (url, 1, QR_ECLEVEL_L, QR_MODE_8, 1);
|
||||
|
||||
if (!qrcode)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_FAILED,
|
||||
"QRCode generation failed for url %s",
|
||||
url);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
symbols_per_row = qrcode->width;
|
||||
number_of_rows = qrcode->width;
|
||||
|
||||
symbol_size = MIN (width, height) / symbols_per_row;
|
||||
code_width = symbol_size * symbols_per_row;
|
||||
code_height = symbol_size * number_of_rows;
|
||||
offset_x = (width - code_width) / 2;
|
||||
offset_y = (height - code_height) / 2;
|
||||
|
||||
pixel_data = calloc (height, width * BYTES_PER_RGB_888);
|
||||
|
||||
for (row = 0; row < number_of_rows; row++)
|
||||
{
|
||||
for (symbol = 0; symbol < symbols_per_row; symbol++)
|
||||
{
|
||||
guint8 *pixel;
|
||||
|
||||
if (qrcode->data[row * symbols_per_row + symbol] & 1)
|
||||
pixel = black_pixel;
|
||||
else
|
||||
pixel = white_pixel;
|
||||
|
||||
for (symbol_y = 0; symbol_y < symbol_size; symbol_y++)
|
||||
{
|
||||
for (symbol_x = 0; symbol_x < symbol_size; symbol_x++)
|
||||
{
|
||||
size_t x, y;
|
||||
y = offset_y + (row * symbol_size) + symbol_y;
|
||||
x = offset_x + (symbol * symbol_size) + symbol_x;
|
||||
|
||||
memcpy (&pixel_data[(y * width + x) * pixel_size],
|
||||
pixel,
|
||||
pixel_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return g_steal_pointer (&pixel_data);
|
||||
}
|
||||
|
||||
static void
|
||||
qr_code_generator_thread (GTask *task,
|
||||
gpointer source,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GError *error = NULL;
|
||||
ShellQrCodeGenerator *self = task_data;
|
||||
g_autofree guint8 *pixel_data = NULL;
|
||||
|
||||
pixel_data = generate_icon (self->priv->url, self->priv->width, self->priv->height, &error);
|
||||
|
||||
if (error != NULL)
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_pointer (task, g_steal_pointer (&pixel_data), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
on_image_task_complete (ShellQrCodeGenerator *self,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
guint8 *pixel_data;
|
||||
g_autoptr (ClutterContent) content = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
gboolean data_set;
|
||||
|
||||
pixel_data = g_task_propagate_pointer (G_TASK (result), &error);
|
||||
|
||||
if (error != NULL)
|
||||
{
|
||||
g_task_return_error (self->priv->icon_task, error);
|
||||
return;
|
||||
}
|
||||
|
||||
content = st_image_content_new_with_preferred_size (self->priv->width, self->priv->height);
|
||||
data_set = clutter_image_set_data (CLUTTER_IMAGE (content),
|
||||
pixel_data,
|
||||
COGL_PIXEL_FORMAT_RGB_888,
|
||||
self->priv->width,
|
||||
self->priv->height,
|
||||
self->priv->width * BYTES_PER_RGB_888,
|
||||
&error);
|
||||
|
||||
if (!data_set)
|
||||
{
|
||||
g_task_return_error (self->priv->icon_task, error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_task_return_pointer (self->priv->icon_task, g_steal_pointer (&content), g_object_unref);
|
||||
g_clear_object (&self->priv->image_task);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_qr_code_generator_generator_qr_code:
|
||||
* @qr_code_generator: the #ShellQrCodeGenerator
|
||||
* @stream: The stream for the QR code generator
|
||||
* @callback: (scope async): function to call returning success or failure
|
||||
* of the async grabbing
|
||||
* @user_data: the data to pass to callback function
|
||||
*
|
||||
*/
|
||||
void
|
||||
shell_qr_code_generator_generate_qr_code (ShellQrCodeGenerator *self,
|
||||
const char *url,
|
||||
size_t width,
|
||||
size_t height,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellQrCodeGeneratorPrivate *priv;
|
||||
|
||||
g_return_if_fail (SHELL_IS_QR_CODE_GENERATOR (self));
|
||||
|
||||
priv = self->priv;
|
||||
|
||||
if (!url || *url == '\0')
|
||||
{
|
||||
if (callback)
|
||||
g_task_report_new_error (self,
|
||||
callback,
|
||||
user_data,
|
||||
shell_qr_code_generator_generate_qr_code,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"No valid QR code URI provided");
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->url != NULL)
|
||||
{
|
||||
if (callback)
|
||||
g_task_report_new_error (self,
|
||||
callback,
|
||||
user_data,
|
||||
shell_qr_code_generator_generate_qr_code,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_PENDING,
|
||||
"Only one QR code generator operation at a time "
|
||||
"is permitted");
|
||||
return;
|
||||
}
|
||||
|
||||
priv->url = g_strdup (url);
|
||||
priv->width = width;
|
||||
priv->height = height;
|
||||
|
||||
priv->icon_task = g_task_new (self, NULL, callback, user_data);
|
||||
g_task_set_source_tag (priv->icon_task, shell_qr_code_generator_generate_qr_code);
|
||||
g_task_set_task_data (priv->icon_task, self, NULL);
|
||||
|
||||
priv->image_task = g_task_new (self, NULL, (GAsyncReadyCallback) on_image_task_complete, NULL);
|
||||
g_task_set_source_tag (priv->image_task, on_image_task_complete);
|
||||
g_task_set_task_data (priv->image_task, self, NULL);
|
||||
g_task_run_in_thread (priv->image_task, qr_code_generator_thread);
|
||||
}
|
||||
|
||||
/**
|
||||
* shell_qr_code_generator_generate_qr_code_finish:
|
||||
* @result: the #GAsyncResult that was provided to the callback
|
||||
* @error: #GError for error reporting
|
||||
*
|
||||
* Finish the asynchronous operation started by shell_qr_code_generator_generate_qr_code()
|
||||
* and obtain its result.
|
||||
*
|
||||
* Returns: (transfer full): a #GIcon of the QR code
|
||||
*
|
||||
*/
|
||||
GIcon *
|
||||
shell_qr_code_generator_generate_qr_code_finish (ShellQrCodeGenerator *self,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (SHELL_IS_QR_CODE_GENERATOR (self), FALSE);
|
||||
g_return_val_if_fail (G_IS_TASK (result), FALSE);
|
||||
g_return_val_if_fail (g_async_result_is_tagged (result,
|
||||
shell_qr_code_generator_generate_qr_code),
|
||||
NULL);
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
ShellQrCodeGenerator *
|
||||
shell_qr_code_generator_new (void)
|
||||
{
|
||||
return g_object_new (SHELL_TYPE_QR_CODE_GENERATOR, NULL);
|
||||
}
|
||||
28
src/shell-qr-code-generator.h
Normal file
28
src/shell-qr-code-generator.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __SHELL_QR_CODE_GENERATOR_H__
|
||||
#define __SHELL_QR_CODE_GENERATOR_H__
|
||||
|
||||
/**
|
||||
* SECTION:shell-qr-code-generator
|
||||
* @short_description: Generates a QR code for a given url.
|
||||
*
|
||||
* The #ShellQrCodeGenerator object is used to generate QR codes for URLs
|
||||
*
|
||||
*/
|
||||
#define SHELL_TYPE_QR_CODE_GENERATOR (shell_qr_code_generator_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (ShellQrCodeGenerator, shell_qr_code_generator,
|
||||
SHELL, QR_CODE_GENERATOR, GObject)
|
||||
|
||||
ShellQrCodeGenerator *shell_qr_code_generator_new (void);
|
||||
|
||||
void shell_qr_code_generator_generate_qr_code (ShellQrCodeGenerator *qr_code_generator,
|
||||
const char *url,
|
||||
size_t width,
|
||||
size_t height,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
GIcon *shell_qr_code_generator_generate_qr_code_finish (ShellQrCodeGenerator *qr_code_generator,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
#endif /* ___SHELL_QR_CODE_GENERATOR_H__ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue