On Wed, Nov 27, 2013 at 10:48 AM, David Herrmann <dh.herrm...@gmail.com> wrote: > If we want to interact with a user in the initrd, during > emergency-situations, in single-user mode, or in any other rather limited > situation, we currently rely on the kernel to do input and graphics access > for us. More precisely, we rely on the VT layer to do keyboard parsing and > font rendering. Or in other words: The *kernel* provides our UI! > > This is bad for the same reasons we don't put any other UI into the > kernel. However, there are a few reasons to keep a minimal UI in the > kernel: > 1) show panic-screen with kernel oops message > 2) show log-screen during early boot > 3) allow kernel-debugging via kdb > While people using kdb are encouraged to keep VTs, for 1) and 2) there is > a replacement via fblog/drmlog. > > So we run out of reasons to keep a UI in the kernel. However, to allow > moving the UI handling to userspace, we need a bunch of helpers: > - keyboard handling: convert keycodes into keysyms and modifiers/.. > - modesetting: program the gfx pipeline and provide a rendering > infrastructure > - fonts: to render text, we need some basic fonts > - hotplugging: device detection and assignment during runtime > (Note that all these are implemented (often quite rudimentary) in the > kernel to allow a basic UI.) > > This patch introduces sd-gfx, a systemd-internal library dealing with all > these things. Note that it is designed to be exported some day, but for > now we keep it internal. > While the library itself will be kept small and almost self-contained, it > is designed to be extensible and can be used in rather complex graphics > applications. For systemd, we plan to add a basic emergency-console, an > initrd password-query, kernel-log-screen and a fallback login-screen. > These allow booting without CONFIG_VT and provide a basic system for > seats without VTs (either CONFIT_VT=n or seats != seat0). > > As a first step, we add the required header+build-chain and add the > font-handling. To avoid heavy font-pipelines in systemd, we only provide > a statically-sized fallback-font based on GNU-Unifont. It contains glyphs > for the *whole* Base-Multilingual-Plane of Unicode and thus allows > internationalized text. > > The "make-unifont.py" script is used by the "make update-unifont" custom > target to regenerate the unifont files. As this can take quite some time, > we check the result into git so only maintainers need to run this. > > The binary file contains all glyphs in a compressed 1-bit-per-pixel format > for all 8x16 and 16x16 glyphs. It is linked directly into libsystemd-gfx > via the *.bin makefile target. Binary size is 2.1MB, but thanks to paging, > the kernel only loads required pages into memory. A ASCII-only screen thus > only needs 40k VIRT mem. Do we have quad-glyphs (16x32) for the ASCII part of this for hi-res screens?
(also, if we use NFD we could reuse these for some of unicode[1], but that is less important) [1]http://blog.golang.org/normalization > --- > Hi > > I removed the unifont-data from this patch so this will not apply cleanly. > However, the ML would reject the huge patch otherwise. If anyone is interested > in the raw patch, the series is available on: > http://cgit.freedesktop.org/~dvdhrm/systemd/log/?h=console > > Thanks > David > > Makefile.am | 31 + > configure.ac | 10 + > make-unifont.py | 138 + > src/libsystemd-gfx/.gitignore | 1 + > src/libsystemd-gfx/Makefile | 1 + > src/libsystemd-gfx/gfx-unifont.c | 273 + > src/libsystemd-gfx/unifont.bin | Bin 0 -> 2162688 bytes > src/libsystemd-gfx/unifont.hex | 63488 > +++++++++++++++++++++++++++++++++++++ > src/systemd/sd-gfx.h | 65 + > 9 files changed, 64007 insertions(+) > create mode 100755 make-unifont.py > create mode 100644 src/libsystemd-gfx/.gitignore > create mode 120000 src/libsystemd-gfx/Makefile > create mode 100644 src/libsystemd-gfx/gfx-unifont.c > create mode 100644 src/libsystemd-gfx/unifont.bin > create mode 100644 src/libsystemd-gfx/unifont.hex > create mode 100644 src/systemd/sd-gfx.h > > diff --git a/Makefile.am b/Makefile.am > index ce27e82..2edb091 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -3853,6 +3853,37 @@ EXTRA_DIST += \ > endif > > # > ------------------------------------------------------------------------------ > +if HAVE_GFX > +noinst_LTLIBRARIES += \ > + libsystemd-gfx.la > + > +libsystemd_gfx_la_SOURCES = \ > + src/libsystemd-gfx/sd-gfx.h \ > + src/libsystemd-gfx/gfx-unifont.c > + > +libsystemd_gfx_la_CFLAGS = \ > + $(AM_CFLAGS) > + > +libsystemd_gfx_la_LIBADD = \ > + libsystemd-shared.la \ > + src/libsystemd-gfx/unifont.bin.lo > + > +src/libsystemd-gfx/unifont.bin: make-unifont.py > src/libsystemd-gfx/unifont.hex > + $(AM_V_GEN)cat $(top_srcdir)/src/libsystemd-gfx/unifont.hex | > $(PYTHON) $< >$@ > + > +src/libsystemd-gfx/unifont.cmp: make-unifont.py > src/libsystemd-gfx/unifont.bin > + $(AM_V_GEN)cat $(top_srcdir)/src/libsystemd-gfx/unifont.bin | > $(PYTHON) $< verify >$@ > + > +update-unifont: src/libsystemd-gfx/unifont.bin src/libsystemd-gfx/unifont.cmp > + @RET=`diff -u src/libsystemd-gfx/unifont.hex > src/libsystemd-gfx/unifont.cmp | wc -l` ; \ > + if test "x$$?" != "x0" -o "x$$RET" != "x0" ; then \ > + echo "Generated Unifont-file differs from original; > generator probably broken" ; \ > + exit 1 ; \ > + fi > + @echo "unifont.bin has been regenerated" > +endif > + > +# > ------------------------------------------------------------------------------ > if ENABLE_NETWORKD > rootlibexec_PROGRAMS += \ > systemd-networkd > diff --git a/configure.ac b/configure.ac > index 3fd05da..354673a 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -291,6 +291,15 @@ fi > AM_CONDITIONAL(HAVE_KMOD, [test "$have_kmod" = "yes"]) > > # > ------------------------------------------------------------------------------ > +have_gfx=no > +AC_ARG_ENABLE(gfx, AS_HELP_STRING([--disable-gfx], [disable sd-gfx graphics > and input support])) > +if test "x$enable_gfx" != "xno"; then > + AC_DEFINE(HAVE_GFX, 1, [Define if sd-gfx is built]) > + have_gfx=yes > +fi > +AM_CONDITIONAL(HAVE_GFX, [test "$have_gfx" = "yes"]) > + > +# > ------------------------------------------------------------------------------ > have_blkid=no > AC_ARG_ENABLE(blkid, AS_HELP_STRING([--disable-blkid], [disable blkid > support])) > if test "x$enable_blkid" != "xno"; then > @@ -1079,6 +1088,7 @@ AC_MSG_RESULT([ > polkit: ${have_polkit} > efi: ${have_efi} > kmod: ${have_kmod} > + sd-gfx: ${have_gfx} > blkid: ${have_blkid} > nss-myhostname: ${have_myhostname} > gudev: ${enable_gudev} > diff --git a/make-unifont.py b/make-unifont.py > new file mode 100755 > index 0000000..375c8a4 > --- /dev/null > +++ b/make-unifont.py > @@ -0,0 +1,138 @@ > +# -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */ > +# > +# This file is part of systemd. > +# > +# Copyright 2013 David Herrmann <dh.herrm...@gmail.com> > +# > +# systemd is free software; you can redistribute it and/or modify it > +# under the terms of the GNU Lesser General Public License as published by > +# the Free Software Foundation; either version 2.1 of the License, or > +# (at your option) any later version. > +# > +# systemd 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 > +# Lesser General Public License for more details. > +# > +# You should have received a copy of the GNU Lesser General Public License > +# along with systemd; If not, see <http://www.gnu.org/licenses/>. > + > +# > +# Parse a unifont.hex file and produce a compressed binary-format we use in > +# our gfx applications. Can also do the reverse to verify correctness. > +# > + > +from __future__ import print_function > +import re > +import sys > +import fileinput > +import struct > + > +# > +# Write "bits" array as binary output. > +# > + > +def write_bin_entry(entry): > + l = len(entry) > + if l != 32 and l != 64: > + entry = "0" * 64 > + l = 0 > + elif l < 64: > + entry += "0" * (64 - l) > + > + sys.stdout.buffer.write(struct.pack('B', int(l / 32))) > + > + for i in range(0, 64, 2): > + c = int(entry[i:i+2], 16) > + sys.stdout.buffer.write(struct.pack('B', c)) > + > +def write_bin(bits): > + for idx in range(len(bits)): > + write_bin_entry(bits[idx]) > + > +# > +# Parse hex file into "bits" array > +# > + > +def parse_hex_line(bits, line): > + m = re.match(r"^([0-9A-Fa-f]+):([0-9A-Fa-f]+)$", line) > + if m == None: > + return > + > + idx = int(m.group(1), 16) > + val = m.group(2) > + > + # insert skipped lines > + for i in range(len(bits), idx): > + bits.append("") > + > + bits.insert(idx, val) > + > +def parse_hex(): > + bits = [] > + > + for line in sys.stdin: > + if not line: > + continue > + if line.startswith("#"): > + continue > + > + parse_hex_line(bits, line) > + > + return bits > + > +# > +# Write "bits" array as text-file line-by-line to stdout. > +# > + > +def write_hex_line(idx, entry): > + if entry: > + print("%04X:%s" % (idx, entry)) > + > +def write_hex(bits): > + for idx, entry in enumerate(bits): > + if not entry: > + continue > + > + write_hex_line(idx, entry) > + > +# > +# Parse a binary file into "bits" so we can verify the correctness of a given > +# binary file. > +# > + > +def parse_bin_entry(bits, chunk): > + entry = "" > + l = struct.unpack('B', chunk[0:1])[0] > + for c in chunk[1:l*16+1]: > + entry += format(c, "02X") > + > + bits.append(entry) > + > +def parse_bin(): > + bits = [] > + > + while True: > + chunk = sys.stdin.buffer.read(33) > + if chunk: > + parse_bin_entry(bits, chunk) > + else: > + break > + > + return bits > + > +# > +# In normal mode we simply read line by line from standard-input (or first > +# argument) and write the binary-file to standard-output. > +# > +# In verify-mode, we read the binary-file from standard-input and write the > +# text-file line-by-line to standard-output. > +# > + > +if __name__ == "__main__": > + if len(sys.argv) > 1 and sys.argv[1] == "verify": > + bits = parse_bin() > + write_hex(bits) > + else: > + bits = parse_hex() > + write_bin(bits) > diff --git a/src/libsystemd-gfx/.gitignore b/src/libsystemd-gfx/.gitignore > new file mode 100644 > index 0000000..a792d8f > --- /dev/null > +++ b/src/libsystemd-gfx/.gitignore > @@ -0,0 +1 @@ > +/unifont.cmp > diff --git a/src/libsystemd-gfx/Makefile b/src/libsystemd-gfx/Makefile > new file mode 120000 > index 0000000..d0b0e8e > --- /dev/null > +++ b/src/libsystemd-gfx/Makefile > @@ -0,0 +1 @@ > +../Makefile > \ No newline at end of file > diff --git a/src/libsystemd-gfx/gfx-unifont.c > b/src/libsystemd-gfx/gfx-unifont.c > new file mode 100644 > index 0000000..c2fc879 > --- /dev/null > +++ b/src/libsystemd-gfx/gfx-unifont.c > @@ -0,0 +1,273 @@ > +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ > + > +/*** > + This file is part of systemd. > + > + Copyright 2013 David Herrmann <dh.herrm...@gmail.com> > + > + systemd is free software; you can redistribute it and/or modify it > + under the terms of the GNU Lesser General Public License as published by > + the Free Software Foundation; either version 2.1 of the License, or > + (at your option) any later version. > + > + systemd 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 > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public License > + along with systemd; If not, see <http://www.gnu.org/licenses/>. > +***/ > + > +#include <errno.h> > +#include <fcntl.h> > +#include <inttypes.h> > +#include <stdbool.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > + > +#include "def.h" > +#include "hashmap.h" > +#include "log.h" > +#include "macro.h" > +#include "sd-gfx.h" > +#include "util.h" > + > +/* Glyphs are linked as binary data. The data layout is a size-byte followed > by > + * 32 data bytes. The data bytes are padded with 0 if the size is smaller > than > + * 32. The size-byte specifies the size of a single line. Each glyph always > + * consists of 16 lines. > + * Currently, size==1 is used for single-width glyphs and size==2 for > + * double-width glyphs. */ > + > +struct unifont_data { > + uint8_t cells; > + uint8_t data[32]; > +} _packed_; > + > +extern const struct unifont_data > _binary_src_libsystemd_gfx_unifont_bin_start[]; > +extern const struct unifont_data > _binary_src_libsystemd_gfx_unifont_bin_end[]; > +static const unsigned int unifont_width = 8; > +static const unsigned int unifont_stride = 1; > +static const unsigned int unifont_max_cells = 2; > +static const unsigned int unifont_height = 16; > + > +static const struct unifont_data unifont_fallback = { > + .cells = 1, > + .data = > "\x00\x00\x00\x7E\x66\x5A\x5A\x7A\x76\x76\x7E\x76\x76\x7E\x00\x00", > +}; > + > +static const struct unifont_data *unifont_get(uint32_t ucs4) { > + const struct unifont_data *begin, *end, *g; > + > + begin = _binary_src_libsystemd_gfx_unifont_bin_start; > + end = _binary_src_libsystemd_gfx_unifont_bin_end; > + > + g = &begin[ucs4]; > + if (g >= end) > + return &unifont_fallback; > + if (g->cells == 0 || g->cells > unifont_max_cells) > + return &unifont_fallback; > + > + return g; > +} > + > +/* > + * Fonts > + * We provide a built-in static font. This can be used to render text in any > + * situation without depending on huge text-pipelines like pango. The font is > + * based on "GNU Unifont" which provides fixed-size glyphs for the whole > + * Unicode Basic Multilingual Plane. > + * This can be used in initrds, emergency-consoles and other situations > where a > + * full text-pipeline would be overkill. > + * > + * Note that we provide some enhanced features to properly support all > systems. > + * This currently includes: > + * - scaling: You can specify a "ppi" value to scale the static fonts. This > + * can only be done with integer-scaling and should only be used > + * as reading-aid or for high-DPI screens. > + * - combining: Glyph-combining (eg., for unicode combining characters) is > + * implemented in the renderer to correctly draw combined > + * characters for non-latin scripts. Character-combining is > left > + * to the caller, we only allow drawing a set of characters as > a > + * single glyph. > + * - caching: To support fast lookups we cache modified glyphs. > + */ > + > +typedef struct gfx_glyph gfx_glyph; > + > +struct gfx_glyph { > + sd_gfx_buffer buf; > +}; > + > +struct sd_gfx_font { > + unsigned int ppi; > + unsigned int width; > + unsigned int height; > + Hashmap *glyphs; > + > + gfx_glyph fallback; > +}; > + > +static void gfx_glyph_blend(gfx_glyph *g, unsigned int ppi, const struct > unifont_data *u) { > + unsigned int i, j; > + const uint8_t *src; > + uint8_t *dst; > + > + /* > + * TODO: scale glyph according to @ppi > + */ > + > + src = u->data; > + dst = g->buf.data; > + > + for (i = 0; i < unifont_height; ++i) { > + for (j = 0; j < u->cells; ++j) > + dst[j] |= src[j]; > + > + src += u->cells * unifont_stride; > + dst += g->buf.stride; > + } > +} > + > +static int gfx_glyph_render(gfx_glyph *g, unsigned int ppi, const struct > unifont_data *u) { > + g->buf.width = u->cells * unifont_width; > + g->buf.height = unifont_height; > + g->buf.stride = u->cells * unifont_stride; > + g->buf.format = SD_GFX_BUFFER_FORMAT_A1; > + > + g->buf.data = calloc(unifont_height, unifont_max_cells * > unifont_stride); > + if (!g->buf.data) > + return -ENOMEM; > + > + gfx_glyph_blend(g, ppi, u); > + return 0; > +} > + > +int sd_gfx_font_new(sd_gfx_font **out, unsigned int ppi) { > + sd_gfx_font *font; > + int r; > + > + ppi = CLAMP(ppi, 10U, 1000U); > + > + font = calloc(1, sizeof(*font)); > + if (!font) > + return log_oom(); > + > + font->ppi = ppi; > + font->width = unifont_width; > + font->height = unifont_height; > + > + font->glyphs = hashmap_new(trivial_hash_func, trivial_compare_func); > + if (!font->glyphs) { > + free(font); > + return log_oom(); > + } > + > + r = gfx_glyph_render(&font->fallback, font->ppi, &unifont_fallback); > + if (r < 0) { > + hashmap_free(font->glyphs); > + free(font); > + return log_oom(); > + } > + > + *out = font; > + return 0; > +} > + > +static void gfx_glyph_free(gfx_glyph *g) { > + free(g->buf.data); > + free(g); > +} > + > +void sd_gfx_font_free(sd_gfx_font *font) { > + gfx_glyph *g; > + > + if (!font) > + return; > + > + while ((g = hashmap_steal_first(font->glyphs))) > + gfx_glyph_free(g); > + > + hashmap_free(font->glyphs); > + free(font); > +} > + > +unsigned int sd_gfx_font_get_ppi(sd_gfx_font *font) { > + return font->ppi; > +} > + > +unsigned int sd_gfx_font_get_width(sd_gfx_font *font) { > + return font->width; > +} > + > +unsigned int sd_gfx_font_get_height(sd_gfx_font *font) { > + return font->height; > +} > + > +/* > + * Render a glyph > + * This first tries to look up the glyph in the cache. If found, it is > returned, > + * otherwise the glyph is rendered and put into the cache. > + * > + * This function never fails. If the glyph cannot be rendered, a fallback > glyph > + * is returned. Note that this function can render multiple glyphs into a > single > + * buffer. You simply have to pass multiple codepoints via @ucs4 / @len. > + * However, this should only be used for combining-characters. > + * > + * Whenever you render a glyph, this functions needs a unique ID to identify > it. > + * You have to pass it via @id. If you render only a single codepoint, simply > + * pass the codepoint as @id (or pass 0 in which case the first codepoint is > + * automatically taken as id). But if you render combined codepoints, you > need > + * to allocate a unique ID for them. See libtsm for an example how to do > that. > + * > + * The lifetime of the returned buffer is bound to @font. You don't have to > free > + * it yourself. > + */ > +void sd_gfx_font_render(sd_gfx_font *font, uint32_t id, const uint32_t > *ucs4, size_t len, sd_gfx_buffer **out) { > + const struct unifont_data *u; > + gfx_glyph *g; > + > + if (!len) > + goto error; > + > + if (!id) > + id = *ucs4; > + > + g = hashmap_get(font->glyphs, INT_TO_PTR(id)); > + if (g) > + goto out; > + > + g = calloc(1, sizeof(*g)); > + if (!g) { > + log_oom(); > + goto error; > + } > + > + u = unifont_get(*ucs4); > + if (gfx_glyph_render(g, font->ppi, u) < 0) { > + log_oom(); > + free(g); > + goto error; > + } > + > + while (--len) { > + u = unifont_get(*++ucs4); > + gfx_glyph_blend(g, font->ppi, u); > + } > + > + if (hashmap_put(font->glyphs, INT_TO_PTR(id), g) < 0) { > + log_oom(); > + free(g->buf.data); > + free(g); > + goto error; > + } > + > + goto out; > + > +error: > + g = &font->fallback; > +out: > + *out = &g->buf; > +} > diff --git a/src/libsystemd-gfx/unifont.bin b/src/libsystemd-gfx/unifont.bin > new file mode 100644 > index > 0000000000000000000000000000000000000000..0b32d80222dd465d0612188915ef67355dc73e4d > GIT binary patch > literal 2162688 > z9F*YJIJ$6q>;c^VB)T=i&<gm^zxN+SppMS|-OpAY!~fZzt8D^U-P?cj(n~*i<Be_n > > [... skipped for ML ...] > > h<$hpdYOEeTz{h7Yzz4==k?}x2v&LpIyNUqW`UBL12zCGf > > literal 0 > HcmV?d00001 > > diff --git a/src/libsystemd-gfx/unifont.hex b/src/libsystemd-gfx/unifont.hex > new file mode 100644 > index 0000000..90672a8 > --- /dev/null > +++ b/src/libsystemd-gfx/unifont.hex > @@ -0,0 +1,63488 @@ > +0000:AAAA00018000000180004A51EA505A51C99E0001800000018000000180005555 > +0001:AAAA00018000000180003993C252325F8A527193800000018000000180005555 > > [... skipped for ML ...] > > +FFFE:FFFFFFFFE187EFBFE38FEFBFEFBFFFFFFFFFE187EFBFE38FEFBFEF87FFFFFFFF > +FFFF:FFFFFFFFE187EFBFE38FEFBFEFBFFFFFFFFFE187EFBFE38FEFBFEFBFFFFFFFFF > diff --git a/src/systemd/sd-gfx.h b/src/systemd/sd-gfx.h > new file mode 100644 > index 0000000..c46fbd4 > --- /dev/null > +++ b/src/systemd/sd-gfx.h > @@ -0,0 +1,65 @@ > +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ > + > +#ifndef foosdgfxhfoo > +#define foosdgfxhfoo > + > +/*** > + This file is part of systemd. > + > + Copyright 2013 David Herrmann <dh.herrm...@gmail.com> > + > + systemd is free software; you can redistribute it and/or modify it > + under the terms of the GNU Lesser General Public License as published by > + the Free Software Foundation; either version 2.1 of the License, or > + (at your option) any later version. > + > + systemd 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 > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public License > + along with systemd; If not, see <http://www.gnu.org/licenses/>. > +***/ > + > +#include <errno.h> > +#include <inttypes.h> > +#include <stdbool.h> > +#include <stdlib.h> > +#include <systemd/_sd-common.h> > + > +_SD_BEGIN_DECLARATIONS; > + > +typedef struct sd_gfx_buffer sd_gfx_buffer; > +typedef struct sd_gfx_font sd_gfx_font; > + > +/* memory buffer */ > + > +enum { > + SD_GFX_BUFFER_FORMAT_A1, > + SD_GFX_BUFFER_FORMAT_A8, > + SD_GFX_BUFFER_FORMAT_XRGB8888, > +}; > + > +struct sd_gfx_buffer { > + unsigned int width; > + unsigned int height; > + int stride; > + unsigned int format; > + void *data; > +}; > + > +/* unifont */ > + > +int sd_gfx_font_new(sd_gfx_font **out, unsigned int ppi); > +void sd_gfx_font_free(sd_gfx_font *font); > + > +unsigned int sd_gfx_font_get_ppi(sd_gfx_font *font); > +unsigned int sd_gfx_font_get_width(sd_gfx_font *font); > +unsigned int sd_gfx_font_get_height(sd_gfx_font *font); > + > +void sd_gfx_font_render(sd_gfx_font *font, uint32_t id, const uint32_t > *ucs4, size_t len, sd_gfx_buffer **out); > + > +_SD_END_DECLARATIONS; > + > +#endif > -- > 1.8.4.2 > > _______________________________________________ > systemd-devel mailing list > systemd-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/systemd-devel _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel