Compare commits

..

10 Commits

Author SHA1 Message Date
Louis Pearson adadca6827 feat: reduce repetition when calling wayland api 2024-02-07 23:34:00 -07:00
Louis Pearson 18a8befacd feat: get touch working 2024-02-07 01:47:13 -07:00
Louis Pearson 76365322f2 remove unneeded stuff 2024-02-07 01:46:49 -07:00
LeRoyce Pearson 025ab53055 copy example 1 -> example 2; simplify example 1 2024-01-30 14:12:50 -07:00
Louis Pearson ab10b7292c feat: append to one buffer instead of allocating many 2024-01-22 00:42:20 -07:00
Louis Pearson e87f718f5d feat: add text editing 2024-01-18 00:51:43 -07:00
Louis Pearson dad4b23cf5 feat: add renderText and renderGradient functions 2024-01-17 20:18:05 -07:00
Louis Pearson fcd58ad7e6 feat: Add font8x8 2024-01-17 20:10:47 -07:00
Louis Pearson ccc90c80ca Remove submodule 2024-01-16 21:19:40 -07:00
Louis Pearson b77c6302ac Add xkb-common 2024-01-16 21:13:00 -07:00
21 changed files with 2108 additions and 1561 deletions

View File

@ -4,18 +4,6 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const module = b.addModule("wayland", .{
.root_source_file = .{ .path = "src/main.zig" },
});
const lib = b.addStaticLibrary(.{
.name = "zig-wayland-wire",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
b.installArtifact(lib);
const main_tests = b.addTest(.{ const main_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" }, .root_source_file = .{ .path = "src/main.zig" },
.target = target, .target = target,
@ -27,20 +15,14 @@ pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Run library tests"); const test_step = b.step("test", "Run library tests");
test_step.dependOn(&run_main_tests.step); test_step.dependOn(&run_main_tests.step);
const client_connect_raw_exe = b.addExecutable(.{ const exe = b.addExecutable(.{
.name = "00_client_connect", .name = "pinephone-test",
.root_source_file = .{ .path = "examples/00_client_connect.zig" }, .root_source_file = .{ .path = "src/main.zig" },
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
b.installArtifact(client_connect_raw_exe);
const client_connect_exe = b.addExecutable(.{ exe.addIncludePath(.{ .path = "deps/font8x8/" });
.name = "01_client_connect",
.root_source_file = .{ .path = "examples/01_client_connect.zig" }, b.installArtifact(exe);
.target = target,
.optimize = optimize,
});
client_connect_exe.root_module.addImport("wayland", module);
b.installArtifact(client_connect_exe);
} }

79
deps/font8x8/README vendored Normal file
View File

@ -0,0 +1,79 @@
8x8 monochrome bitmap font for rendering
=========================================
A collection of header files containing a 8x8 bitmap font.
font8x8.h contains all available characters
font8x8_basic.h contains unicode points U+0000 - U+007F
font8x8_latin.h contains unicode points U+0000 - U+00FF
Author: Daniel Hepper <daniel@hepper.net>
License: Public Domain
Encoding
========
Every character in the font is encoded row-wise in 8 bytes.
The least significant bit of each byte corresponds to the first pixel in a
row.
The character 'A' (0x41 / 65) is encoded as
{ 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}
0x0C => 0000 1100 => ..XX....
0X1E => 0001 1110 => .XXXX...
0x33 => 0011 0011 => XX..XX..
0x33 => 0011 0011 => XX..XX..
0x3F => 0011 1111 => xxxxxx..
0x33 => 0011 0011 => XX..XX..
0x33 => 0011 0011 => XX..XX..
0x00 => 0000 0000 => ........
To access the nth pixel in a row, right-shift by n.
. . X X . . . .
| | | | | | | |
(0x0C >> 0) & 1 == 0-+ | | | | | | |
(0x0C >> 1) & 1 == 0---+ | | | | | |
(0x0C >> 2) & 1 == 1-----+ | | | | |
(0x0C >> 3) & 1 == 1-------+ | | | |
(0x0C >> 4) & 1 == 0---------+ | | |
(0x0C >> 5) & 1 == 0-----------+ | |
(0x0C >> 6) & 1 == 0-------------+ |
(0x0C >> 7) & 1 == 0---------------+
Renderer
========
To visualize the font, a simple renderer is included in render.c
$ gcc render.c -o render
$ ./render 65
XX
XXXX
XX XX
XX XX
XXXXXX
XX XX
XX XX
Credits
=======
These header files are directly derived from an assembler file fetched from:
http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
Original header:
; Summary: font8_8.asm
; 8x8 monochrome bitmap fonts for rendering
;
; Author:
; Marcel Sondaar
; International Business Machines (public domain VGA fonts)
;
; License:
; Public Domain
;

19
deps/font8x8/font8x8.h vendored Normal file
View File

@ -0,0 +1,19 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
#include "font8x8_basic.h"
#include "font8x8_control.h"
#include "font8x8_ext_latin.h"
#include "font8x8_greek.h"
#include "font8x8_misc.h"
#include "font8x8_box.h"
#include "font8x8_block.h"
#include "font8x8_hiragana.h"
#include "font8x8_sga.h"

152
deps/font8x8/font8x8_basic.h vendored Normal file
View File

@ -0,0 +1,152 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
// Constant: font8x8_basic
// Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin)
char font8x8_basic[128][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space)
{ 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!)
{ 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (")
{ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#)
{ 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($)
{ 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%)
{ 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&)
{ 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (')
{ 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (()
{ 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ())
{ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*)
{ 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,)
{ 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.)
{ 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/)
{ 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0)
{ 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1)
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2)
{ 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3)
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4)
{ 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5)
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6)
{ 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7)
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8)
{ 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9)
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:)
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;)
{ 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<)
{ 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=)
{ 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>)
{ 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?)
{ 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@)
{ 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A)
{ 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B)
{ 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C)
{ 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D)
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E)
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F)
{ 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G)
{ 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H)
{ 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I)
{ 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J)
{ 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K)
{ 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L)
{ 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M)
{ 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N)
{ 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O)
{ 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P)
{ 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q)
{ 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R)
{ 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S)
{ 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T)
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U)
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V)
{ 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W)
{ 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X)
{ 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y)
{ 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z)
{ 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([)
{ 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\)
{ 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (])
{ 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_)
{ 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`)
{ 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a)
{ 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b)
{ 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c)
{ 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d)
{ 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e)
{ 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f)
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g)
{ 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h)
{ 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i)
{ 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j)
{ 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k)
{ 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l)
{ 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m)
{ 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n)
{ 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o)
{ 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p)
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q)
{ 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r)
{ 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s)
{ 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v)
{ 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w)
{ 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y)
{ 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z)
{ 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({)
{ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|)
{ 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (})
{ 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F
};

56
deps/font8x8/font8x8_block.h vendored Normal file
View File

@ -0,0 +1,56 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
// Constant: font8x8_2580
// Contains an 8x8 font map for unicode points U+2580 - U+259F (block elements)
char font8x8_block[32][8] = {
{ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, // U+2580 (top half)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+2581 (box 1/8)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF}, // U+2582 (box 2/8)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF}, // U+2583 (box 3/8)
{ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2584 (bottom half)
{ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2585 (box 5/8)
{ 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2586 (box 6/8)
{ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2587 (box 7/8)
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2588 (solid)
{ 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F}, // U+2589 (box 7/8)
{ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F}, // U+258A (box 6/8)
{ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F}, // U+258B (box 5/8)
{ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, // U+258C (left half)
{ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07}, // U+258D (box 3/8)
{ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}, // U+258E (box 2/8)
{ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, // U+258F (box 1/8)
{ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, // U+2590 (right half)
{ 0x55, 0x00, 0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00}, // U+2591 (25% solid)
{ 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA}, // U+2592 (50% solid)
{ 0xFF, 0xAA, 0xFF, 0x55, 0xFF, 0xAA, 0xFF, 0x55}, // U+2593 (75% solid)
{ 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+2594 (box 1/8)
{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, // U+2595 (box 1/8)
{ 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F}, // U+2596 (box bottom left)
{ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0}, // U+2597 (box bottom right)
{ 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00}, // U+2598 (box top left)
{ 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2599 (boxes left and bottom)
{ 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0}, // U+259A (boxes top-left and bottom right)
{ 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F}, // U+259B (boxes top and left)
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0}, // U+259C (boxes top and right)
{ 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00}, // U+259D (box top right)
{ 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F}, // U+259E (boxes top right and bottom left)
{ 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF}, // U+259F (boxes right and bottom)
};

152
deps/font8x8/font8x8_box.h vendored Normal file
View File

@ -0,0 +1,152 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
// Constant: font8x8_2500
// Contains an 8x8 font map for unicode points U+2500 - U+257F (box drawing)
char font8x8_box[128][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00}, // U+2500 (thin horizontal)
{ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00}, // U+2501 (thick horizontal)
{ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, // U+2502 (thin vertical)
{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, // U+2503 (thich vertical)
{ 0x00, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00}, // U+2504 (thin horizontal dashed)
{ 0x00, 0x00, 0x00, 0xBB, 0xBB, 0x00, 0x00, 0x00}, // U+2505 (thick horizontal dashed)
{ 0x08, 0x00, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08}, // U+2506 (thin vertical dashed)
{ 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18}, // U+2507 (thich vertical dashed)
{ 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00}, // U+2508 (thin horizontal dotted)
{ 0x00, 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x00}, // U+2509 (thick horizontal dotted)
{ 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08}, // U+250A (thin vertical dotted)
{ 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18}, // U+250B (thich vertical dotted)
{ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08}, // U+250C (down L, right L)
{ 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x08, 0x08, 0x08}, // U+250D (down L, right H)
{ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18}, // U+250E (down H, right L)
{ 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18}, // U+250F (down H, right H)
{ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08}, // U+2510 (down L, left L)
{ 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x08, 0x08, 0x08}, // U+2511 (down L, left H)
{ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18}, // U+2512 (down H, left L)
{ 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18}, // U+2513 (down H, left H)
{ 0x08, 0x08, 0x08, 0x08, 0xf8, 0x00, 0x00, 0x00}, // U+2514 (up L, right L)
{ 0x08, 0x08, 0x08, 0xf8, 0xf8, 0x00, 0x00, 0x00}, // U+2515 (up L, right H)
{ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00}, // U+2516 (up H, right L)
{ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00}, // U+2517 (up H, right H)
{ 0x08, 0x08, 0x08, 0x08, 0x0f, 0x00, 0x00, 0x00}, // U+2518 (up L, left L)
{ 0x08, 0x08, 0x08, 0x0f, 0x0f, 0x00, 0x00, 0x00}, // U+2519 (up L, left H)
{ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00}, // U+251A (up H, left L)
{ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00}, // U+251B (up H, left H)
{ 0x08, 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, 0x08}, // U+251C (down L, right L, up L)
{ 0x08, 0x08, 0x08, 0xf8, 0xf8, 0x08, 0x08, 0x08}, // U+251D (down L, right H, up L)
{ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x08, 0x08, 0x08}, // U+251E (down L, right L, up H)
{ 0x08, 0x08, 0x08, 0x08, 0xf8, 0x18, 0x18, 0x18}, // U+251F (down H, right L, up L)
{ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18}, // U+2520 (down H, right L, up H)
{ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x08, 0x08, 0x08}, // U+2521 (down L, right H, up H)
{ 0x08, 0x08, 0x08, 0xf8, 0xf8, 0x18, 0x18, 0x18}, // U+2522 (down H, right H, up L)
{ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18}, // U+2523 (down H, right H, up H)
{ 0x08, 0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x08}, // U+2524 (down L, left L, up L)
{ 0x08, 0x08, 0x08, 0x0f, 0x0f, 0x08, 0x08, 0x08}, // U+2525 (down L, left H, up L)
{ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x08, 0x08, 0x08}, // U+2526 (down L, left L, up H)
{ 0x08, 0x08, 0x08, 0x08, 0x1f, 0x18, 0x18, 0x18}, // U+2527 (down H, left L, up L)
{ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18}, // U+2528 (down H, left L, up H)
{ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x08, 0x08, 0x08}, // U+2529 (down L, left H, up H)
{ 0x08, 0x08, 0x08, 0x1f, 0x1f, 0x18, 0x18, 0x18}, // U+252A (down H, left H, up L)
{ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18}, // U+252B (down H, left H, up H)
{ 0x00, 0x00, 0x00, 0x00, 0xff, 0x08, 0x08, 0x08}, // U+252C (down L, right L, left L)
{ 0x00, 0x00, 0x00, 0x0f, 0xff, 0x08, 0x08, 0x08}, // U+252D (down L, right L, left H)
{ 0x00, 0x00, 0x00, 0xf8, 0xff, 0x08, 0x08, 0x08}, // U+252E (down L, right H, left L)
{ 0x00, 0x00, 0x00, 0xff, 0xff, 0x08, 0x08, 0x08}, // U+252F (down L, right H, left H)
{ 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18}, // U+2530 (down H, right L, left L)
{ 0x00, 0x00, 0x00, 0x1f, 0xff, 0x18, 0x18, 0x18}, // U+2531 (down H, right L, left H)
{ 0x00, 0x00, 0x00, 0xf8, 0xff, 0x18, 0x18, 0x18}, // U+2532 (down H, right H, left L)
{ 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18}, // U+2533 (down H, right H, left H)
{ 0x08, 0x08, 0x08, 0x08, 0xff, 0x00, 0x00, 0x00}, // U+2534 (up L, right L, left L)
{ 0x08, 0x08, 0x08, 0x0f, 0xff, 0x00, 0x00, 0x00}, // U+2535 (up L, right L, left H)
{ 0x08, 0x08, 0x08, 0xf8, 0xff, 0x00, 0x00, 0x00}, // U+2536 (up L, right H, left L)
{ 0x08, 0x08, 0x08, 0xff, 0xff, 0x00, 0x00, 0x00}, // U+2537 (up L, right H, left H)
{ 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00}, // U+2538 (up H, right L, left L)
{ 0x18, 0x18, 0x18, 0x1f, 0xff, 0x00, 0x00, 0x00}, // U+2539 (up H, right L, left H)
{ 0x18, 0x18, 0x18, 0xf8, 0xff, 0x00, 0x00, 0x00}, // U+253A (up H, right H, left L)
{ 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00}, // U+253B (up H, right H, left H)
{ 0x08, 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08}, // U+253C (up L, right L, left L, down L)
{ 0x08, 0x08, 0x08, 0x0f, 0xff, 0x08, 0x08, 0x08}, // U+253D (up L, right L, left H, down L)
{ 0x08, 0x08, 0x08, 0xf8, 0xff, 0x08, 0x08, 0x08}, // U+253E (up L, right H, left L, down L)
{ 0x08, 0x08, 0x08, 0xff, 0xff, 0x08, 0x08, 0x08}, // U+253F (up L, right H, left H, down L)
{ 0x18, 0x18, 0x18, 0x18, 0xff, 0x08, 0x08, 0x08}, // U+2540 (up H, right L, left L, down L)
{ 0x08, 0x08, 0x08, 0x08, 0xff, 0x18, 0x18, 0x18}, // U+2541 (up L, right L, left L, down H)
{ 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18}, // U+2542 (up H, right L, left L, down H)
{ 0x18, 0x18, 0x18, 0x1f, 0xff, 0x08, 0x08, 0x08}, // U+2543 (up H, right L, left H, down L)
{ 0x18, 0x18, 0x18, 0xf8, 0xff, 0x08, 0x08, 0x08}, // U+2544 (up H, right H, left L, down L)
{ 0x08, 0x08, 0x08, 0x1f, 0xff, 0x18, 0x18, 0x18}, // U+2545 (up L, right L, left H, down H)
{ 0x08, 0x08, 0x08, 0xf8, 0xff, 0x18, 0x18, 0x18}, // U+2546 (up L, right H, left L, down H)
{ 0x08, 0x08, 0x08, 0xff, 0xff, 0x18, 0x18, 0x18}, // U+2547 (up L, right H, left H, down H)
{ 0x18, 0x18, 0x18, 0xff, 0xff, 0x08, 0x08, 0x08}, // U+254B (up H, right H, left H, down L)
{ 0x18, 0x18, 0x18, 0xf8, 0xff, 0x18, 0x18, 0x18}, // U+254A (up H, right H, left L, down H)
{ 0x18, 0x18, 0x18, 0x1f, 0xff, 0x18, 0x18, 0x18}, // U+2549 (up H, right L, left H, down H)
{ 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18}, // U+254B (up H, right H, left H, down H)
{ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00}, // U+254C (thin horizontal broken)
{ 0x00, 0x00, 0x00, 0xE7, 0xE7, 0x00, 0x00, 0x00}, // U+254D (thick horizontal broken)
{ 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08}, // U+254E (thin vertical broken)
{ 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18}, // U+254F (thich vertical broken)
{ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2550 (double horizontal)
{ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, // U+2551 (double vertical)
{ 0x00, 0x00, 0x00, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+2552 (down L, right D)
{ 0x00, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x14, 0x14}, // U+2553 (down D, right L)
{ 0x00, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14}, // U+2554 (down D, right D)
{ 0x00, 0x00, 0x00, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2555 (down L, left D)
{ 0x00, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x14, 0x14}, // U+2556 (down D, left L)
{ 0x00, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x14, 0x14}, // U+2557 (down D, left D)
{ 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x00, 0x00}, // U+2558 (up L, right D)
{ 0x14, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x00, 0x00}, // U+2559 (up D, right L)
{ 0x14, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x00, 0x00}, // U+255A (up D, right D)
{ 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x00, 0x00}, // U+255B (up L, left D)
{ 0x14, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x00, 0x00}, // U+255C (up D, left L)
{ 0x14, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x00, 0x00}, // U+255D (up D, left D)
{ 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+255E (up L, down L, right D)
{ 0x14, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x14, 0x14}, // U+255F (up D, down D, right L)
{ 0x14, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x14, 0x14}, // U+2560 (up D, down D, right D)
{ 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2561 (up L, down L, left D)
{ 0x14, 0x14, 0x14, 0x14, 0x17, 0x14, 0x14, 0x14}, // U+2562 (up D, down D, left L)
{ 0x14, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14}, // U+2563 (up D, down D, left D)
{ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x08, 0x08}, // U+2564 (left D, right D, down L)
{ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x14, 0x14}, // U+2565 (left L, right L, down D)
{ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14}, // U+2566 (left D, right D, down D)
{ 0x08, 0x08, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2567 (left D, right D, up L)
{ 0x14, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x00, 0x00}, // U+2568 (left L, right L, up D)
{ 0x14, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00}, // U+2569 (left D, right D, up D)
{ 0x08, 0x08, 0x08, 0xFF, 0x08, 0xFF, 0x08, 0x08}, // U+256A (left D, right D, down L, up L)
{ 0x14, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x14, 0x14}, // U+256B (left L, right L, down D, up D)
{ 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14}, // U+256C (left D, right D, down D, up D)
{ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x10, 0x08, 0x08}, // U+256D (curve down-right)
{ 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x08, 0x08}, // U+256E (curve down-left)
{ 0x08, 0x08, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00}, // U+256F (curve up-left)
{ 0x08, 0x08, 0x08, 0x10, 0xE0, 0x00, 0x00, 0x00}, // U+2570 (curve up-right)
{ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, // U+2571 (diagonal bottom-left to top-right)
{ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}, // U+2572 (diagonal bottom-right to top-left)
{ 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81}, // U+2573 (diagonal cross)
{ 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00}, // U+2574 (left L)
{ 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00}, // U+2575 (up L)
{ 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00}, // U+2576 (right L)
{ 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08}, // U+2577 (down L)
{ 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00}, // U+2578 (left H)
{ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00}, // U+2579 (up H)
{ 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x00}, // U+257A (right H)
{ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18}, // U+257B (down H)
{ 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x00, 0x00, 0x00}, // U+257C (right H, left L)
{ 0x08, 0x08, 0x08, 0x08, 0x18, 0x18, 0x18, 0x18}, // U+257D (up L, down H)
{ 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00}, // U+257E (right L, left H)
{ 0x18, 0x18, 0x18, 0x18, 0x08, 0x08, 0x08, 0x08} // U+257F (up H, down L)
};

56
deps/font8x8/font8x8_control.h vendored Normal file
View File

@ -0,0 +1,56 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
// Constant: font8x8_0080
// Contains an 8x8 font map for unicode points U+0080 - U+009F (C1/C2 control)
char font8x8_control[32][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0080
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0081
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0082
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0083
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0084
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0085
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0086
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0087
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0088
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0089
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+008A
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+008B
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+008C
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+008D
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+008E
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+008F
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0090
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0091
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0092
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0093
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0094
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0095
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0096
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0097
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0098
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0099
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+009A
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+009B
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+009C
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+009D
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+009E
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+009F
};

120
deps/font8x8/font8x8_ext_latin.h vendored Normal file
View File

@ -0,0 +1,120 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
// Constant: font8x8_00A0
// Contains an 8x8 font map for unicode points U+00A0 - U+00FF (extended latin)
char font8x8_ext_latin[96][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+00A0 (no break space)
{ 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00}, // U+00A1 (inverted !)
{ 0x18, 0x18, 0x7E, 0x03, 0x03, 0x7E, 0x18, 0x18}, // U+00A2 (dollarcents)
{ 0x1C, 0x36, 0x26, 0x0F, 0x06, 0x67, 0x3F, 0x00}, // U+00A3 (pound sterling)
{ 0x00, 0x00, 0x63, 0x3E, 0x36, 0x3E, 0x63, 0x00}, // U+00A4 (currency mark)
{ 0x33, 0x33, 0x1E, 0x3F, 0x0C, 0x3F, 0x0C, 0x0C}, // U+00A5 (yen)
{ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+00A6 (broken pipe)
{ 0x7C, 0xC6, 0x1C, 0x36, 0x36, 0x1C, 0x33, 0x1E}, // U+00A7 (paragraph)
{ 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+00A8 (diaeresis)
{ 0x3C, 0x42, 0x99, 0x85, 0x85, 0x99, 0x42, 0x3C}, // U+00A9 (copyright symbol)
{ 0x3C, 0x36, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00}, // U+00AA (superscript a)
{ 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00}, // U+00AB (<<)
{ 0x00, 0x00, 0x00, 0x3F, 0x30, 0x30, 0x00, 0x00}, // U+00AC (gun pointing left)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+00AD (soft hyphen)
{ 0x3C, 0x42, 0x9D, 0xA5, 0x9D, 0xA5, 0x42, 0x3C}, // U+00AE (registered symbol)
{ 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+00AF (macron)
{ 0x1C, 0x36, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00}, // U+00B0 (degree)
{ 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x7E, 0x00}, // U+00B1 (plusminus)
{ 0x1C, 0x30, 0x18, 0x0C, 0x3C, 0x00, 0x00, 0x00}, // U+00B2 (superscript 2)
{ 0x1C, 0x30, 0x18, 0x30, 0x1C, 0x00, 0x00, 0x00}, // U+00B2 (superscript 3)
{ 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+00B2 (aigu)
{ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x03}, // U+00B5 (mu)
{ 0xFE, 0xDB, 0xDB, 0xDE, 0xD8, 0xD8, 0xD8, 0x00}, // U+00B6 (pilcrow)
{ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00}, // U+00B7 (central dot)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x1E}, // U+00B8 (cedille)
{ 0x08, 0x0C, 0x08, 0x1C, 0x00, 0x00, 0x00, 0x00}, // U+00B9 (superscript 1)
{ 0x1C, 0x36, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00}, // U+00BA (superscript 0)
{ 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00}, // U+00BB (>>)
{ 0xC3, 0x63, 0x33, 0xBD, 0xEC, 0xF6, 0xF3, 0x03}, // U+00BC (1/4)
{ 0xC3, 0x63, 0x33, 0x7B, 0xCC, 0x66, 0x33, 0xF0}, // U+00BD (1/2)
{ 0x03, 0xC4, 0x63, 0xB4, 0xDB, 0xAC, 0xE6, 0x80}, // U+00BE (3/4)
{ 0x0C, 0x00, 0x0C, 0x06, 0x03, 0x33, 0x1E, 0x00}, // U+00BF (inverted ?)
{ 0x07, 0x00, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x00}, // U+00C0 (A grave)
{ 0x70, 0x00, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x00}, // U+00C1 (A aigu)
{ 0x1C, 0x36, 0x00, 0x3E, 0x63, 0x7F, 0x63, 0x00}, // U+00C2 (A circumflex)
{ 0x6E, 0x3B, 0x00, 0x3E, 0x63, 0x7F, 0x63, 0x00}, // U+00C3 (A ~)
{ 0x63, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x63, 0x00}, // U+00C4 (A umlaut)
{ 0x0C, 0x0C, 0x00, 0x1E, 0x33, 0x3F, 0x33, 0x00}, // U+00C5 (A ring)
{ 0x7C, 0x36, 0x33, 0x7F, 0x33, 0x33, 0x73, 0x00}, // U+00C6 (AE)
{ 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x18, 0x30, 0x1E}, // U+00C7 (C cedille)
{ 0x07, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00}, // U+00C8 (E grave)
{ 0x38, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00}, // U+00C9 (E aigu)
{ 0x0C, 0x12, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00}, // U+00CA (E circumflex)
{ 0x36, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00}, // U+00CB (E umlaut)
{ 0x07, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00CC (I grave)
{ 0x38, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00CD (I aigu)
{ 0x0C, 0x12, 0x00, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+00CE (I circumflex)
{ 0x33, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00CF (I umlaut)
{ 0x3F, 0x66, 0x6F, 0x6F, 0x66, 0x66, 0x3F, 0x00}, // U+00D0 (Eth)
{ 0x3F, 0x00, 0x33, 0x37, 0x3F, 0x3B, 0x33, 0x00}, // U+00D1 (N ~)
{ 0x0E, 0x00, 0x18, 0x3C, 0x66, 0x3C, 0x18, 0x00}, // U+00D2 (O grave)
{ 0x70, 0x00, 0x18, 0x3C, 0x66, 0x3C, 0x18, 0x00}, // U+00D3 (O aigu)
{ 0x3C, 0x66, 0x18, 0x3C, 0x66, 0x3C, 0x18, 0x00}, // U+00D4 (O circumflex)
{ 0x6E, 0x3B, 0x00, 0x3E, 0x63, 0x63, 0x3E, 0x00}, // U+00D5 (O ~)
{ 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00}, // U+00D6 (O umlaut)
{ 0x00, 0x36, 0x1C, 0x08, 0x1C, 0x36, 0x00, 0x00}, // U+00D7 (multiplicative x)
{ 0x5C, 0x36, 0x73, 0x7B, 0x6F, 0x36, 0x1D, 0x00}, // U+00D8 (O stroke)
{ 0x0E, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00}, // U+00D9 (U grave)
{ 0x70, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00}, // U+00DA (U aigu)
{ 0x3C, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x00}, // U+00DB (U circumflex)
{ 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+00DC (U umlaut)
{ 0x70, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x00}, // U+00DD (Y aigu)
{ 0x0F, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+00DE (Thorn)
{ 0x00, 0x1E, 0x33, 0x1F, 0x33, 0x1F, 0x03, 0x03}, // U+00DF (beta)
{ 0x07, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E0 (a grave)
{ 0x38, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E1 (a aigu)
{ 0x7E, 0xC3, 0x3C, 0x60, 0x7C, 0x66, 0xFC, 0x00}, // U+00E2 (a circumflex)
{ 0x6E, 0x3B, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E3 (a ~)
{ 0x33, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E4 (a umlaut)
{ 0x0C, 0x0C, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E5 (a ring)
{ 0x00, 0x00, 0xFE, 0x30, 0xFE, 0x33, 0xFE, 0x00}, // U+00E6 (ae)
{ 0x00, 0x00, 0x1E, 0x03, 0x03, 0x1E, 0x30, 0x1C}, // U+00E7 (c cedille)
{ 0x07, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E8 (e grave)
{ 0x38, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E9 (e aigu)
{ 0x7E, 0xC3, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00}, // U+00EA (e circumflex)
{ 0x33, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00EB (e umlaut)
{ 0x07, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EC (i grave)
{ 0x1C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00ED (i augu)
{ 0x3E, 0x63, 0x1C, 0x18, 0x18, 0x18, 0x3C, 0x00}, // U+00EE (i circumflex)
{ 0x33, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EF (i umlaut)
{ 0x1B, 0x0E, 0x1B, 0x30, 0x3E, 0x33, 0x1E, 0x00}, // U+00F0 (eth)
{ 0x00, 0x1F, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x00}, // U+00F1 (n ~)
{ 0x00, 0x07, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F2 (o grave)
{ 0x00, 0x38, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F3 (o aigu)
{ 0x1E, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F4 (o circumflex)
{ 0x6E, 0x3B, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F5 (o ~)
{ 0x00, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F6 (o umlaut)
{ 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00}, // U+00F7 (division)
{ 0x00, 0x60, 0x3C, 0x76, 0x7E, 0x6E, 0x3C, 0x06}, // U+00F8 (o stroke)
{ 0x00, 0x07, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00F9 (u grave)
{ 0x00, 0x38, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FA (u aigu)
{ 0x1E, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FB (u circumflex)
{ 0x00, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FC (u umlaut)
{ 0x00, 0x38, 0x00, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+00FD (y aigu)
{ 0x00, 0x00, 0x06, 0x3E, 0x66, 0x3E, 0x06, 0x00}, // U+00FE (thorn)
{ 0x00, 0x33, 0x00, 0x33, 0x33, 0x3E, 0x30, 0x1F} // U+00FF (y umlaut)
};

82
deps/font8x8/font8x8_greek.h vendored Normal file
View File

@ -0,0 +1,82 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
// Constant: font8x8_0390
// Contains an 8x8 font map for unicode points U+0390 - U+03C9 (greek characters)
char font8x8_greek[58][8] = {
{ 0x2D, 0x00, 0x0C, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0390 (iota with tonos and diaeresis)
{ 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0391 (Alpha)
{ 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0392 (Beta)
{ 0x3F, 0x33, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00}, // U+0393 (Gamma)
{ 0x08, 0x1C, 0x1C, 0x36, 0x36, 0x63, 0x7F, 0x00}, // U+0394 (Delta)
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0395 (Epsilon)
{ 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+0396 (Zeta)
{ 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0397 (Eta)
{ 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x36, 0x1C, 0x00}, // U+0398 (Theta)
{ 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0399 (Iota)
{ 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+039A (Kappa)
{ 0x08, 0x1C, 0x1C, 0x36, 0x36, 0x63, 0x63, 0x00}, // U+039B (Lambda)
{ 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+039C (Mu)
{ 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+039D (Nu)
{ 0x7F, 0x63, 0x00, 0x3E, 0x00, 0x63, 0x7F, 0x00}, // U+039E (Xi)
{ 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+039F (Omikron)
{ 0x7F, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00}, // U+03A0 (Pi)
{ 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+03A1 (Rho)
{ 0x00, 0x01, 0x02, 0x04, 0x4F, 0x90, 0xA0, 0x40}, // U+03A2
{ 0x7F, 0x63, 0x06, 0x0C, 0x06, 0x63, 0x7F, 0x00}, // U+03A3 (Sigma 2)
{ 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+03A4 (Tau)
{ 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+03A5 (Upsilon)
{ 0x18, 0x7E, 0xDB, 0xDB, 0xDB, 0x7E, 0x18, 0x00}, // U+03A6 (Phi)
{ 0x63, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x63, 0x00}, // U+03A7 (Chi)
{ 0xDB, 0xDB, 0xDB, 0x7E, 0x18, 0x18, 0x3C, 0x00}, // U+03A8 (Psi)
{ 0x3E, 0x63, 0x63, 0x63, 0x36, 0x36, 0x77, 0x00}, // U+03A9 (Omega)
{ 0x33, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0399 (Iota with diaeresis)
{ 0x33, 0x00, 0x33, 0x33, 0x1E, 0x0C, 0x1E, 0x00}, // U+03A5 (Upsilon with diaeresis)
{ 0x70, 0x00, 0x6E, 0x3B, 0x13, 0x3B, 0x6E, 0x00}, // U+03AC (alpha aigu)
{ 0x38, 0x00, 0x1E, 0x03, 0x0E, 0x03, 0x1E, 0x00}, // U+03AD (epsilon aigu)
{ 0x38, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x30}, // U+03AE (eta aigu)
{ 0x38, 0x00, 0x0C, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+03AF (iota aigu)
{ 0x2D, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+03B0 (upsilon with tonos and diaeresis)
{ 0x00, 0x00, 0x6E, 0x3B, 0x13, 0x3B, 0x6E, 0x00}, // U+03B1 (alpha)
{ 0x00, 0x1E, 0x33, 0x1F, 0x33, 0x1F, 0x03, 0x03}, // U+03B2 (beta)
{ 0x00, 0x00, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x00}, // U+03B3 (gamma)
{ 0x38, 0x0C, 0x18, 0x3E, 0x33, 0x33, 0x1E, 0x00}, // U+03B4 (delta)
{ 0x00, 0x00, 0x1E, 0x03, 0x0E, 0x03, 0x1E, 0x00}, // U+03B5 (epsilon)
{ 0x00, 0x3F, 0x06, 0x03, 0x03, 0x1E, 0x30, 0x1C}, // U+03B6 (zeta)
{ 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x30}, // U+03B7 (eta)
{ 0x00, 0x00, 0x1E, 0x33, 0x3F, 0x33, 0x1E, 0x00}, // U+03B8 (theta)
{ 0x00, 0x00, 0x0C, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+03B9 (iota)
{ 0x00, 0x00, 0x33, 0x1B, 0x0F, 0x1B, 0x33, 0x00}, // U+03BA (kappa)
{ 0x00, 0x03, 0x06, 0x0C, 0x1C, 0x36, 0x63, 0x00}, // U+03BB (lambda)
{ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x03}, // U+03BC (mu)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+03BD (nu)
{ 0x1E, 0x03, 0x0E, 0x03, 0x03, 0x1E, 0x30, 0x1C}, // U+03BE (xi)
{ 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+03BF (omikron)
{ 0x00, 0x00, 0x7F, 0x36, 0x36, 0x36, 0x36, 0x00}, // U+03C0 (pi)
{ 0x00, 0x00, 0x3C, 0x66, 0x66, 0x36, 0x06, 0x06}, // U+03C1 (rho)
{ 0x00, 0x00, 0x3E, 0x03, 0x03, 0x1E, 0x30, 0x1C}, // U+03C2 (sigma 1)
{ 0x00, 0x00, 0x7E, 0x1B, 0x1B, 0x1B, 0x0E, 0x00}, // U+03C3 (sigma 2)
{ 0x00, 0x00, 0x7E, 0x18, 0x18, 0x58, 0x30, 0x00}, // U+03C4 (tau)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+03C5 (upsilon)
{ 0x00, 0x00, 0x76, 0xDB, 0xDB, 0x7E, 0x18, 0x00}, // U+03C6 (phi)
{ 0x00, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+03C7 (chi)
{ 0x00, 0x00, 0xDB, 0xDB, 0xDB, 0x7E, 0x18, 0x00}, // U+03C8 (psi)
{ 0x00, 0x00, 0x36, 0x63, 0x6B, 0x7F, 0x36, 0x00} // U+03C9 (omega)
};

120
deps/font8x8/font8x8_hiragana.h vendored Normal file
View File

@ -0,0 +1,120 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
// Contains an 8x8 font map for unicode points U+3040 - U+309F (Hiragana)
// Constant: font8x8_3040
char font8x8_hiragana[96][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+3040
{ 0x04, 0x3F, 0x04, 0x3C, 0x56, 0x4D, 0x26, 0x00}, // U+3041 (Hiragana a)
{ 0x04, 0x3F, 0x04, 0x3C, 0x56, 0x4D, 0x26, 0x00}, // U+3042 (Hiragana A)
{ 0x00, 0x00, 0x00, 0x11, 0x21, 0x25, 0x02, 0x00}, // U+3043 (Hiragana i)
{ 0x00, 0x01, 0x11, 0x21, 0x21, 0x25, 0x02, 0x00}, // U+3044 (Hiragana I)
{ 0x00, 0x1C, 0x00, 0x1C, 0x22, 0x20, 0x18, 0x00}, // U+3045 (Hiragana u)
{ 0x3C, 0x00, 0x3C, 0x42, 0x40, 0x20, 0x18, 0x00}, // U+3046 (Hiragana U)
{ 0x1C, 0x00, 0x3E, 0x10, 0x38, 0x24, 0x62, 0x00}, // U+3047 (Hiragana e)
{ 0x1C, 0x00, 0x3E, 0x10, 0x38, 0x24, 0x62, 0x00}, // U+3048 (Hiragana E)
{ 0x24, 0x4F, 0x04, 0x3C, 0x46, 0x45, 0x22, 0x00}, // U+3049 (Hiragana o)
{ 0x24, 0x4F, 0x04, 0x3C, 0x46, 0x45, 0x22, 0x00}, // U+304A (Hiragana O)
{ 0x04, 0x24, 0x4F, 0x54, 0x52, 0x12, 0x09, 0x00}, // U+304B (Hiragana KA)
{ 0x44, 0x24, 0x0F, 0x54, 0x52, 0x52, 0x09, 0x00}, // U+304C (Hiragana GA)
{ 0x08, 0x1F, 0x08, 0x3F, 0x1C, 0x02, 0x3C, 0x00}, // U+304D (Hiragana KI)
{ 0x44, 0x2F, 0x04, 0x1F, 0x0E, 0x01, 0x1E, 0x00}, // U+304E (Hiragana GI)
{ 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00}, // U+304F (Hiragana KU)
{ 0x28, 0x44, 0x12, 0x21, 0x02, 0x04, 0x08, 0x00}, // U+3050 (Hiragana GU)
{ 0x00, 0x22, 0x79, 0x21, 0x21, 0x22, 0x10, 0x00}, // U+3051 (Hiragana KE)
{ 0x40, 0x22, 0x11, 0x3D, 0x11, 0x12, 0x08, 0x00}, // U+3052 (Hiragana GE)
{ 0x00, 0x00, 0x3C, 0x00, 0x02, 0x02, 0x3C, 0x00}, // U+3053 (Hiragana KO)
{ 0x20, 0x40, 0x16, 0x20, 0x01, 0x01, 0x0E, 0x00}, // U+3054 (Hiragana GO)
{ 0x10, 0x7E, 0x10, 0x3C, 0x02, 0x02, 0x1C, 0x00}, // U+3055 (Hiragana SA)
{ 0x24, 0x4F, 0x14, 0x2E, 0x01, 0x01, 0x0E, 0x00}, // U+3056 (Hiragana ZA)
{ 0x00, 0x02, 0x02, 0x02, 0x42, 0x22, 0x1C, 0x00}, // U+3057 (Hiragana SI)
{ 0x20, 0x42, 0x12, 0x22, 0x02, 0x22, 0x1C, 0x00}, // U+3058 (Hiragana ZI)
{ 0x10, 0x7E, 0x18, 0x14, 0x18, 0x10, 0x0C, 0x00}, // U+3059 (Hiragana SU)
{ 0x44, 0x2F, 0x06, 0x05, 0x06, 0x04, 0x03, 0x00}, // U+305A (Hiragana ZU)
{ 0x20, 0x72, 0x2F, 0x22, 0x1A, 0x02, 0x1C, 0x00}, // U+305B (Hiragana SE)
{ 0x80, 0x50, 0x3A, 0x17, 0x1A, 0x02, 0x1C, 0x00}, // U+305C (Hiragana ZE)
{ 0x1E, 0x08, 0x04, 0x7F, 0x08, 0x04, 0x38, 0x00}, // U+305D (Hiragana SO)
{ 0x4F, 0x24, 0x02, 0x7F, 0x08, 0x04, 0x38, 0x00}, // U+305E (Hiragana ZO)
{ 0x02, 0x0F, 0x02, 0x72, 0x02, 0x09, 0x71, 0x00}, // U+305F (Hiragana TA)
{ 0x42, 0x2F, 0x02, 0x72, 0x02, 0x09, 0x71, 0x00}, // U+3060 (Hiragana DA)
{ 0x08, 0x7E, 0x08, 0x3C, 0x40, 0x40, 0x38, 0x00}, // U+3061 (Hiragana TI)
{ 0x44, 0x2F, 0x04, 0x1E, 0x20, 0x20, 0x1C, 0x00}, // U+3062 (Hiragana DI)
{ 0x00, 0x00, 0x00, 0x1C, 0x22, 0x20, 0x1C, 0x00}, // U+3063 (Hiragana tu)
{ 0x00, 0x1C, 0x22, 0x41, 0x40, 0x20, 0x1C, 0x00}, // U+3064 (Hiragana TU)
{ 0x40, 0x20, 0x1E, 0x21, 0x20, 0x20, 0x1C, 0x00}, // U+3065 (Hiragana DU)
{ 0x00, 0x3E, 0x08, 0x04, 0x04, 0x04, 0x38, 0x00}, // U+3066 (Hiragana TE)
{ 0x00, 0x3E, 0x48, 0x24, 0x04, 0x04, 0x38, 0x00}, // U+3067 (Hiragana DE)
{ 0x04, 0x04, 0x08, 0x3C, 0x02, 0x02, 0x3C, 0x00}, // U+3068 (Hiragana TO)
{ 0x44, 0x24, 0x08, 0x3C, 0x02, 0x02, 0x3C, 0x00}, // U+3069 (Hiragana DO)
{ 0x32, 0x02, 0x27, 0x22, 0x72, 0x29, 0x11, 0x00}, // U+306A (Hiragana NA)
{ 0x00, 0x02, 0x7A, 0x02, 0x0A, 0x72, 0x02, 0x00}, // U+306B (Hiragana NI)
{ 0x08, 0x09, 0x3E, 0x4B, 0x65, 0x55, 0x22, 0x00}, // U+306C (Hiragana NU)
{ 0x04, 0x07, 0x34, 0x4C, 0x66, 0x54, 0x24, 0x00}, // U+306D (Hiragana NE)
{ 0x00, 0x00, 0x3C, 0x4A, 0x49, 0x45, 0x22, 0x00}, // U+306E (Hiragana NO)
{ 0x00, 0x22, 0x7A, 0x22, 0x72, 0x2A, 0x12, 0x00}, // U+306F (Hiragana HA)
{ 0x80, 0x51, 0x1D, 0x11, 0x39, 0x15, 0x09, 0x00}, // U+3070 (Hiragana BA)
{ 0x40, 0xB1, 0x5D, 0x11, 0x39, 0x15, 0x09, 0x00}, // U+3071 (Hiragana PA)
{ 0x00, 0x00, 0x13, 0x32, 0x51, 0x11, 0x0E, 0x00}, // U+3072 (Hiragana HI)
{ 0x40, 0x20, 0x03, 0x32, 0x51, 0x11, 0x0E, 0x00}, // U+3073 (Hiragana BI)
{ 0x40, 0xA0, 0x43, 0x32, 0x51, 0x11, 0x0E, 0x00}, // U+3074 (Hiragana PI)
{ 0x1C, 0x00, 0x08, 0x2A, 0x49, 0x10, 0x0C, 0x00}, // U+3075 (Hiragana HU)
{ 0x4C, 0x20, 0x08, 0x2A, 0x49, 0x10, 0x0C, 0x00}, // U+3076 (Hiragana BU)
{ 0x4C, 0xA0, 0x48, 0x0A, 0x29, 0x48, 0x0C, 0x00}, // U+3077 (Hiragana PU)
{ 0x00, 0x00, 0x04, 0x0A, 0x11, 0x20, 0x40, 0x00}, // U+3078 (Hiragana HE)
{ 0x20, 0x40, 0x14, 0x2A, 0x11, 0x20, 0x40, 0x00}, // U+3079 (Hiragana BE)
{ 0x20, 0x50, 0x24, 0x0A, 0x11, 0x20, 0x40, 0x00}, // U+307A (Hiragana PE)
{ 0x7D, 0x11, 0x7D, 0x11, 0x39, 0x55, 0x09, 0x00}, // U+307B (Hiragana HO)
{ 0x9D, 0x51, 0x1D, 0x11, 0x39, 0x55, 0x09, 0x00}, // U+307C (Hiragana BO)
{ 0x5D, 0xB1, 0x5D, 0x11, 0x39, 0x55, 0x09, 0x00}, // U+307D (Hiragana PO)
{ 0x7E, 0x08, 0x3E, 0x08, 0x1C, 0x2A, 0x04, 0x00}, // U+307E (Hiragana MA)
{ 0x00, 0x07, 0x24, 0x24, 0x7E, 0x25, 0x12, 0x00}, // U+307F (Hiragana MI)
{ 0x04, 0x0F, 0x64, 0x06, 0x05, 0x26, 0x3C, 0x00}, // U+3080 (Hiragana MU)
{ 0x00, 0x09, 0x3D, 0x4A, 0x4B, 0x45, 0x2A, 0x00}, // U+3081 (Hiragana ME)
{ 0x02, 0x0F, 0x02, 0x0F, 0x62, 0x42, 0x3C, 0x00}, // U+3082 (Hiragana MO)
{ 0x00, 0x00, 0x12, 0x1F, 0x22, 0x12, 0x04, 0x00}, // U+3083 (Hiragana ya)
{ 0x00, 0x12, 0x3F, 0x42, 0x42, 0x34, 0x04, 0x00}, // U+3084 (Hiragana YA)
{ 0x00, 0x00, 0x11, 0x3D, 0x53, 0x39, 0x11, 0x00}, // U+3085 (Hiragana yu)
{ 0x00, 0x11, 0x3D, 0x53, 0x51, 0x39, 0x11, 0x00}, // U+3086 (Hiragana YU)
{ 0x00, 0x08, 0x38, 0x08, 0x1C, 0x2A, 0x04, 0x00}, // U+3087 (Hiragana yo)
{ 0x08, 0x08, 0x38, 0x08, 0x1C, 0x2A, 0x04, 0x00}, // U+3088 (Hiragana YO)
{ 0x1E, 0x00, 0x02, 0x3A, 0x46, 0x42, 0x30, 0x00}, // U+3089 (Hiragana RA)
{ 0x00, 0x20, 0x22, 0x22, 0x2A, 0x24, 0x10, 0x00}, // U+308A (Hiragana RI)
{ 0x1F, 0x08, 0x3C, 0x42, 0x49, 0x54, 0x38, 0x00}, // U+308B (Hiragana RU)
{ 0x04, 0x07, 0x04, 0x0C, 0x16, 0x55, 0x24, 0x00}, // U+308C (Hiragana RE)
{ 0x3F, 0x10, 0x08, 0x3C, 0x42, 0x41, 0x30, 0x00}, // U+308D (Hiragana RO)
{ 0x00, 0x00, 0x08, 0x0E, 0x38, 0x4C, 0x2A, 0x00}, // U+308E (Hiragana wa)
{ 0x04, 0x07, 0x04, 0x3C, 0x46, 0x45, 0x24, 0x00}, // U+308F (Hiragana WA)
{ 0x0E, 0x08, 0x3C, 0x4A, 0x69, 0x55, 0x32, 0x00}, // U+3090 (Hiragana WI)
{ 0x06, 0x3C, 0x42, 0x39, 0x04, 0x36, 0x49, 0x00}, // U+3091 (Hiragana WE)
{ 0x04, 0x0F, 0x04, 0x6E, 0x11, 0x08, 0x70, 0x00}, // U+3092 (Hiragana WO)
{ 0x08, 0x08, 0x04, 0x0C, 0x56, 0x52, 0x21, 0x00}, // U+3093 (Hiragana N)
{ 0x40, 0x2E, 0x00, 0x3C, 0x42, 0x40, 0x38, 0x00}, // U+3094 (Hiragana VU)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+3095
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+3096
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+3097
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+3098
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+3099 (voiced combinator mark)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+309A (semivoiced combinator mark)
{ 0x40, 0x80, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00}, // U+309B (Hiragana voiced mark)
{ 0x40, 0xA0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+309C (Hiragana semivoiced mark)
{ 0x00, 0x00, 0x08, 0x08, 0x10, 0x30, 0x0C, 0x00}, // U+309D (Hiragana iteration mark)
{ 0x20, 0x40, 0x14, 0x24, 0x08, 0x18, 0x06, 0x00}, // U+309E (Hiragana voiced iteration mark)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+309F
};

23
deps/font8x8/font8x8_latin.h vendored Normal file
View File

@ -0,0 +1,23 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
#include "font8x8_basic.h"
#include "font8x8_control.h"
#include "font8x8_ext_latin.h"

33
deps/font8x8/font8x8_misc.h vendored Normal file
View File

@ -0,0 +1,33 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
// for later use
char font8x8_misc[10][8] = {
{ 0x1F, 0x33, 0x33, 0x5F, 0x63, 0xF3, 0x63, 0xE3}, // U+20A7 (Spanish Pesetas/Pt)
{ 0x70, 0xD8, 0x18, 0x3C, 0x18, 0x18, 0x1B, 0x0E}, // U+0192 (dutch florijn)
{ 0x3C, 0x36, 0x36, 0x7C, 0x00, 0x7E, 0x00, 0x00}, // U+ (underlined superscript a)
{ 0x1C, 0x36, 0x36, 0x1C, 0x00, 0x3E, 0x00, 0x00}, // U+ (underlined superscript 0)
{ 0x00, 0x00, 0x00, 0x3F, 0x03, 0x03, 0x00, 0x00}, // U+2310 (gun pointing right)
{ 0x30, 0x18, 0x0C, 0x18, 0x30, 0x00, 0x7E, 0x00}, // U+ (less than or equal)
{ 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x00, 0x7E, 0x00}, // U+ (greater than or equal)
{ 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+ (grave)
{ 0x0E, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x00}, // U+ (Y grave)
{ 0x00, 0x07, 0x00, 0x33, 0x33, 0x3E, 0x30, 0x1F} // U+ (y grave)
};

48
deps/font8x8/font8x8_sga.h vendored Normal file
View File

@ -0,0 +1,48 @@
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
char font8x8_sga[26][8] = {
{ 0x00, 0x00, 0x38, 0x66, 0x06, 0x06, 0x07, 0x00}, // U+E541 (SGA A)
{ 0x00, 0x00, 0x0C, 0x0C, 0x18, 0x30, 0x7F, 0x00}, // U+E542 (SGA B)
{ 0x00, 0x00, 0x0C, 0x00, 0x0C, 0x30, 0x30, 0x00}, // U+E543 (SGA C)
{ 0x00, 0x00, 0x7F, 0x00, 0x03, 0x1C, 0x60, 0x00}, // U+E544 (SGA D)
{ 0x00, 0x00, 0x63, 0x03, 0x03, 0x03, 0x7F, 0x00}, // U+E545 (SGA E)
{ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xDB, 0x00, 0x00}, // U+E546 (SGA F)
{ 0x00, 0x00, 0x30, 0x30, 0x3E, 0x30, 0x30, 0x00}, // U+E547 (SGA G)
{ 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x18, 0x18, 0x00}, // U+E548 (SGA H)
{ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00}, // U+E549 (SGA I)
{ 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00}, // U+E54A (SGA J)
{ 0x00, 0x00, 0x18, 0x18, 0x5A, 0x18, 0x18, 0x00}, // U+E54B (SGA K)
{ 0x00, 0x00, 0x03, 0x33, 0x03, 0x33, 0x03, 0x00}, // U+E54C (SGA L)
{ 0x00, 0x00, 0x63, 0x60, 0x60, 0x60, 0x7F, 0x00}, // U+E54D (SGA M)
{ 0x00, 0x00, 0x66, 0x60, 0x30, 0x18, 0x0C, 0x00}, // U+E54E (SGA N)
{ 0x00, 0x00, 0x3C, 0x60, 0x30, 0x18, 0x0C, 0x00}, // U+E54F (SGA O)
{ 0x00, 0x00, 0x66, 0x60, 0x66, 0x06, 0x66, 0x00}, // U+E550 (SGA P)
{ 0x00, 0x00, 0x18, 0x00, 0x7E, 0x60, 0x7E, 0x00}, // U+E551 (SGA Q)
{ 0x00, 0x00, 0x00, 0x66, 0x00, 0x66, 0x00, 0x00}, // U+E552 (SGA R)
{ 0x00, 0x00, 0x0C, 0x0C, 0x3C, 0x30, 0x30, 0x00}, // U+E553 (SGA S)
{ 0x00, 0x00, 0x3C, 0x30, 0x30, 0x00, 0x30, 0x00}, // U+E554 (SGA T)
{ 0x00, 0x00, 0x00, 0x36, 0x00, 0x7F, 0x00, 0x00}, // U+E555 (SGA U)
{ 0x00, 0x00, 0x18, 0x18, 0x7E, 0x00, 0x7E, 0x00}, // U+E556 (SGA V)
{ 0x00, 0x00, 0x00, 0x18, 0x00, 0x66, 0x00, 0x00}, // U+E557 (SGA W)
{ 0x00, 0x00, 0x66, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+E558 (SGA X)
{ 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00}, // U+E559 (SGA Y)
{ 0x00, 0x00, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x00} // U+E55A (SGA Z)
};

39
deps/font8x8/render.c vendored Normal file
View File

@ -0,0 +1,39 @@
#include <stdio.h>
#include <stdlib.h>
#include "font8x8_basic.h"
void usage(char *exec) {
printf("Usage: %s <char_code>\n", exec);
printf(" <char_code> Decimal character code between 0 and 127\n");
}
void render(char *bitmap) {
int x,y;
int set;
int mask;
for (x=0; x < 8; x++) {
for (y=0; y < 8; y++) {
set = bitmap[x] & 1 << y;
printf("%c", set ? 'X' : ' ');
}
printf("\n");
}
}
int main(int argc, char **argv) {
int ord;
if (argc != 2) {
usage(argv[0]);
return 1;
}
ord = atoi(argv[1]);
if (ord > 127 || ord < 0) {
usage(argv[0]);
return 2;
}
char *bitmap = font8x8_basic[ord];
render(bitmap);
return 0;
}

View File

@ -1,556 +0,0 @@
const std = @import("std");
/// The version of the wl_shm protocol we will be targeting.
const WL_SHM_VERSION = 1;
/// The version of the wl_compositor protocol we will be targeting.
const WL_COMPOSITOR_VERSION = 5;
/// The version of the xdg_wm_base protocol we will be targeting.
const XDG_WM_BASE_VERSION = 2;
/// https://wayland.app/protocols/xdg-shell#xdg_surface:request:ack_configure
const XDG_SURFACE_REQUEST_ACK_CONFIGURE = 4;
// https://wayland.app/protocols/wayland#wl_registry:event:global
const WL_REGISTRY_EVENT_GLOBAL = 0;
pub fn main() !void {
var general_allocator = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = general_allocator.deinit();
const gpa = general_allocator.allocator();
const display_path = try getDisplayPath(gpa);
defer gpa.free(display_path);
const socket = try std.net.connectUnixSocket(display_path);
defer socket.close();
const display_id = 1;
var next_id: u32 = 2;
// reserve an object id for the registry
const registry_id = next_id;
next_id += 1;
try socket.writeAll(std.mem.sliceAsBytes(&[_]u32{
// ID of the object; in this case the default wl_display object at 1
1,
// The size (in bytes) of the message and the opcode, which is object specific.
// In this case we are using opcode 1, which corresponds to `wl_display::get_registry`.
//
// The size includes the size of the header.
(0x000C << 16) | (0x0001),
// Finally, we pass in the only argument that this opcode takes: an id for the `wl_registry`
// we are creating.
registry_id,
}));
// create a sync callback so we know when we are caught up with the server
const registry_done_callback_id = next_id;
next_id += 1;
try socket.writeAll(std.mem.sliceAsBytes(&[_]u32{
display_id,
// The size (in bytes) of the message and the opcode.
// In this case we are using opcode 0, which corresponds to `wl_display::sync`.
//
// The size includes the size of the header.
(0x000C << 16) | (0x0000),
// Finally, we pass in the only argument that this opcode takes: an id for the `wl_registry`
// we are creating.
registry_done_callback_id,
}));
var shm_id_opt: ?u32 = null;
var compositor_id_opt: ?u32 = null;
var xdg_wm_base_id_opt: ?u32 = null;
// How do we know that the opcode for WL_REGISTRY_REQUEST is 0? Because it is the first `request` in the protocol for `wl_registry`.
const WL_REGISTRY_REQUEST_BIND = 0;
var message_buffer = std.ArrayList(u8).init(gpa);
defer message_buffer.deinit();
while (true) {
const event = try Event.read(socket, &message_buffer);
// Parse event messages based on which object it is for
if (event.header.object_id == registry_done_callback_id) {
// No need to parse the message body, there is only one possible opcode
break;
}
if (event.header.object_id == registry_id and event.header.opcode == WL_REGISTRY_EVENT_GLOBAL) {
// Parse out the fields of the global event
const name: u32 = @bitCast(event.body[0..4].*);
const interface_str_len: u32 = @bitCast(event.body[4..8].*);
// The interface_str is `interface_str_len - 1` because `interface_str_len` includes the null pointer
const interface_str: [:0]const u8 = event.body[8..][0 .. interface_str_len - 1 :0];
const interface_str_len_u32_align = std.mem.alignForward(u32, interface_str_len, @alignOf(u32));
const version: u32 = @bitCast(event.body[8 + interface_str_len_u32_align ..][0..4].*);
// Check to see if the interface is one of the globals we are looking for
if (std.mem.eql(u8, interface_str, "wl_shm")) {
if (version < WL_SHM_VERSION) {
std.log.err("compositor supports only {s} version {}, client expected version >= {}", .{ interface_str, version, WL_SHM_VERSION });
return error.WaylandInterfaceOutOfDate;
}
shm_id_opt = next_id;
next_id += 1;
try writeRequest(socket, registry_id, WL_REGISTRY_REQUEST_BIND, &[_]u32{
// The numeric name of the global we want to bind.
name,
// `new_id` arguments have three parts when the sub-type is not specified by the protocol:
// 1. A string specifying the textual name of the interface
"wl_shm".len + 1, // length of "wl_shm" plus one for the required null byte
@bitCast(@as([4]u8, "wl_s".*)),
@bitCast(@as([4]u8, "hm\x00\x00".*)), // we have two 0x00 bytes to align the string with u32
// 2. The version you are using, affects which functions you can access
WL_SHM_VERSION,
// 3. And the `new_id` part, where we tell it which client id we are giving it
shm_id_opt.?,
});
} else if (std.mem.eql(u8, interface_str, "wl_compositor")) {
if (version < WL_COMPOSITOR_VERSION) {
std.log.err("compositor supports only {s} version {}, client expected version >= {}", .{ interface_str, version, WL_COMPOSITOR_VERSION });
return error.WaylandInterfaceOutOfDate;
}
compositor_id_opt = next_id;
next_id += 1;
try writeRequest(socket, registry_id, WL_REGISTRY_REQUEST_BIND, &[_]u32{
name,
"wl_compositor".len + 1, // add one for the required null byte
@bitCast(@as([4]u8, "wl_c".*)),
@bitCast(@as([4]u8, "ompo".*)),
@bitCast(@as([4]u8, "sito".*)),
@bitCast(@as([4]u8, "r\x00\x00\x00".*)),
WL_COMPOSITOR_VERSION,
compositor_id_opt.?,
});
} else if (std.mem.eql(u8, interface_str, "xdg_wm_base")) {
if (version < XDG_WM_BASE_VERSION) {
std.log.err("compositor supports only {s} version {}, client expected version >= {}", .{ interface_str, version, XDG_WM_BASE_VERSION });
return error.WaylandInterfaceOutOfDate;
}
xdg_wm_base_id_opt = next_id;
next_id += 1;
try writeRequest(socket, registry_id, WL_REGISTRY_REQUEST_BIND, &[_]u32{
name,
"xdg_wm_base".len + 1,
@bitCast(@as([4]u8, "xdg_".*)),
@bitCast(@as([4]u8, "wm_b".*)),
@bitCast(@as([4]u8, "ase\x00".*)),
XDG_WM_BASE_VERSION,
xdg_wm_base_id_opt.?,
});
}
continue;
}
}
const shm_id = shm_id_opt orelse return error.NeccessaryWaylandExtensionMissing;
const compositor_id = compositor_id_opt orelse return error.NeccessaryWaylandExtensionMissing;
const xdg_wm_base_id = xdg_wm_base_id_opt orelse return error.NeccessaryWaylandExtensionMissing;
std.log.debug("wl_shm client id = {}; wl_compositor client id = {}; xdg_wm_base client id = {}", .{ shm_id, compositor_id, xdg_wm_base_id });
// Create a surface using wl_compositor::create_surface
const surface_id = next_id;
next_id += 1;
// https://wayland.app/protocols/wayland#wl_compositor:request:create_surface
const WL_COMPOSITOR_REQUEST_CREATE_SURFACE = 0;
try writeRequest(socket, compositor_id, WL_COMPOSITOR_REQUEST_CREATE_SURFACE, &[_]u32{
// id: new_id<wl_surface>
surface_id,
});
// Create an xdg_surface
const xdg_surface_id = next_id;
next_id += 1;
// https://wayland.app/protocols/xdg-shell#xdg_wm_base:request:get_xdg_surface
const XDG_WM_BASE_REQUEST_GET_XDG_SURFACE = 2;
try writeRequest(socket, xdg_wm_base_id, XDG_WM_BASE_REQUEST_GET_XDG_SURFACE, &[_]u32{
// id: new_id<xdg_surface>
xdg_surface_id,
// surface: object<wl_surface>
surface_id,
});
// Get the xdg_surface as an xdg_toplevel object
const xdg_toplevel_id = next_id;
next_id += 1;
// https://wayland.app/protocols/xdg-shell#xdg_surface:request:get_toplevel
const XDG_SURFACE_REQUEST_GET_TOPLEVEL = 1;
try writeRequest(socket, xdg_surface_id, XDG_SURFACE_REQUEST_GET_TOPLEVEL, &[_]u32{
// id: new_id<xdg_surface>
xdg_toplevel_id,
});
// Commit the surface. This tells the compositor that the current batch of
// changes is ready, and they can now be applied.
// https://wayland.app/protocols/wayland#wl_surface:request:commit
const WL_SURFACE_REQUEST_COMMIT = 6;
try writeRequest(socket, surface_id, WL_SURFACE_REQUEST_COMMIT, &[_]u32{});
// Wait for the surface to be configured before moving on
while (true) {
const event = try Event.read(socket, &message_buffer);
if (event.header.object_id == xdg_surface_id) {
switch (event.header.opcode) {
// https://wayland.app/protocols/xdg-shell#xdg_surface:event:configure
0 => {
// The configure event acts as a heartbeat. Every once in a while the compositor will send us
// a `configure` event, and if our application doesn't respond with an `ack_configure` response
// it will assume our program has died and destroy the window.
const serial: u32 = @bitCast(event.body[0..4].*);
try writeRequest(socket, xdg_surface_id, XDG_SURFACE_REQUEST_ACK_CONFIGURE, &[_]u32{
// We respond with the number it sent us, so it knows which configure we are responding to.
serial,
});
try writeRequest(socket, surface_id, WL_SURFACE_REQUEST_COMMIT, &[_]u32{});
// The surface has been configured! We can move on
break;
},
else => return error.InvalidOpcode,
}
} else {
std.log.warn("unknown event {{ .object_id = {}, .opcode = {x}, .message = \"{}\" }}", .{ event.header.object_id, event.header.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(event.body)) });
}
}
// allocate a shared memory file, which we will use as a framebuffer to write pixels into
const Pixel = [4]u8;
const framebuffer_size = [2]usize{ 128, 128 };
const shared_memory_pool_len = framebuffer_size[0] * framebuffer_size[1] * @sizeOf(Pixel);
const shared_memory_pool_fd = try std.os.memfd_create("my-wayland-framebuffer", 0);
try std.os.ftruncate(shared_memory_pool_fd, shared_memory_pool_len);
// Create a wl_shm_pool (wayland shared memory pool). This will be used to create framebuffers,
// though in this article we only plan on creating one.
const wl_shm_pool_id = try writeWlShmRequestCreatePool(
socket,
shm_id,
&next_id,
shared_memory_pool_fd,
@intCast(shared_memory_pool_len),
);
// Now we allocate a framebuffer from the shared memory pool
const wl_buffer_id = next_id;
next_id += 1;
// https://wayland.app/protocols/wayland#wl_shm_pool:request:create_buffer
const WL_SHM_POOL_REQUEST_CREATE_BUFFER = 0;
// https://wayland.app/protocols/wayland#wl_shm:enum:format
const WL_SHM_POOL_ENUM_FORMAT_ARGB8888 = 0;
try writeRequest(socket, wl_shm_pool_id, WL_SHM_POOL_REQUEST_CREATE_BUFFER, &[_]u32{
// id: new_id<wl_buffer>,
wl_buffer_id,
// Byte offset of the framebuffer in the pool. In this case we allocate it at the very start of the file.
0,
// Width of the framebuffer.
framebuffer_size[0],
// Height of the framebuffer.
framebuffer_size[1],
// Stride of the framebuffer, or rather, how many bytes are in a single row of pixels.
framebuffer_size[0] * @sizeOf(Pixel),
// The format of the framebuffer. In this case we choose argb8888.
WL_SHM_POOL_ENUM_FORMAT_ARGB8888,
});
// Now we turn the shared memory pool and the framebuffer we just allocated into slices on our side for ease of use.
const shared_memory_pool_bytes = try std.os.mmap(null, shared_memory_pool_len, std.os.PROT.READ | std.os.PROT.WRITE, std.os.MAP.SHARED, shared_memory_pool_fd, 0);
const framebuffer = @as([*]Pixel, @ptrCast(shared_memory_pool_bytes.ptr))[0 .. shared_memory_pool_bytes.len / @sizeOf(Pixel)];
// put some interesting colors into the framebuffer
for (0..framebuffer_size[1]) |y| {
const row = framebuffer[y * framebuffer_size[0] .. (y + 1) * framebuffer_size[0]];
for (row, 0..framebuffer_size[0]) |*pixel, x| {
pixel.* = .{
@truncate(x),
@truncate(y),
0x00,
0xFF,
};
}
}
// Now we attach the framebuffer to the surface at <0, 0>. The x and y MUST be <0, 0> since version 5 of WL_SURFACE,
// which we are using.
// https://wayland.app/protocols/wayland#wl_surface:request:attach
const WL_SURFACE_REQUEST_ATTACH = 1;
try writeRequest(socket, surface_id, WL_SURFACE_REQUEST_ATTACH, &[_]u32{
// buffer: object<wl_buffer>,
wl_buffer_id,
// The x offset of the buffer.
0,
// The y offset of the buffer.
0,
});
// We mark the surface as damaged, meaning that the compositor should update what is rendered on the window.
// You can specify specific damage regions; but in this case we just damage the entire surface.
// https://wayland.app/protocols/wayland#wl_surface:request:damage
const WL_SURFACE_REQUEST_DAMAGE = 2;
try writeRequest(socket, surface_id, WL_SURFACE_REQUEST_DAMAGE, &[_]u32{
// The x offset of the damage region.
0,
// The y offset of the damage region.
0,
// The width of the damage region.
@bitCast(@as(i32, std.math.maxInt(i32))),
// The height of the damage region.
@bitCast(@as(i32, std.math.maxInt(i32))),
});
// Commit the surface. This tells wayland that we are done making changes, and it can display all the changes that have been
// made so far.
// const WL_SURFACE_REQUEST_COMMIT = 6;
try writeRequest(socket, surface_id, WL_SURFACE_REQUEST_COMMIT, &[_]u32{});
// Now we finally, finally, get to the main loop of the program.
var running = true;
while (running) {
const event = try Event.read(socket, &message_buffer);
if (event.header.object_id == xdg_surface_id) {
switch (event.header.opcode) {
// https://wayland.app/protocols/xdg-shell#xdg_surface:event:configure
0 => {
// The configure event acts as a heartbeat. Every once in a while the compositor will send us
// a `configure` event, and if our application doesn't respond with an `ack_configure` response
// it will assume our program has died and destroy the window.
const serial: u32 = @bitCast(event.body[0..4].*);
try writeRequest(socket, xdg_surface_id, XDG_SURFACE_REQUEST_ACK_CONFIGURE, &[_]u32{
// We respond with the number it sent us, so it knows which configure we are responding to.
serial,
});
try writeRequest(socket, surface_id, WL_SURFACE_REQUEST_COMMIT, &[_]u32{});
},
else => return error.InvalidOpcode,
}
} else if (event.header.object_id == xdg_toplevel_id) {
switch (event.header.opcode) {
// https://wayland.app/protocols/xdg-shell#xdg_toplevel:event:configure
0 => {
// The xdg_toplevel:configure event asks us to resize the window. For now, we will ignore it expect to
// log it.
const width: u32 = @bitCast(event.body[0..4].*);
const height: u32 = @bitCast(event.body[4..8].*);
const states_len: u32 = @bitCast(event.body[8..12].*);
const states = @as([*]const u32, @ptrCast(@alignCast(event.body[12..].ptr)))[0..states_len];
std.log.debug("xdg_toplevel:configure({}, {}, {any})", .{ width, height, states });
},
// https://wayland.app/protocols/xdg-shell#xdg_toplevel:event:close
1 => {
// The compositor asked us to close the window.
running = false;
std.log.debug("xdg_toplevel:close()", .{});
},
// https://wayland.app/protocols/xdg-shell#xdg_toplevel:event:configure_bounds
2 => std.log.debug("xdg_toplevel:configure_bounds()", .{}),
// https://wayland.app/protocols/xdg-shell#xdg_toplevel:event:wm_capabilities
3 => std.log.debug("xdg_toplevel:wm_capabilities()", .{}),
else => return error.InvalidOpcode,
}
} else if (event.header.object_id == wl_buffer_id) {
switch (event.header.opcode) {
// https://wayland.app/protocols/wayland#wl_buffer:event:release
0 => {
// The xdg_toplevel:release event let's us know that it is safe to reuse the buffer now.
std.log.debug("wl_buffer:release()", .{});
},
else => return error.InvalidOpcode,
}
} else if (event.header.object_id == display_id) {
switch (event.header.opcode) {
// https://wayland.app/protocols/wayland#wl_display:event:error
0 => {
const object_id: u32 = @bitCast(event.body[0..4].*);
const error_code: u32 = @bitCast(event.body[4..8].*);
const error_message_len: u32 = @bitCast(event.body[8..12].*);
const error_message = event.body[12 .. error_message_len - 1 :0];
std.log.warn("wl_display:error({}, {}, \"{}\")", .{ object_id, error_code, std.zig.fmtEscapes(error_message) });
},
// https://wayland.app/protocols/wayland#wl_display:event:delete_id
1 => {
// wl_display:delete_id tells us that we can reuse an id. In this article we log it, but
// otherwise ignore it.
const name: u32 = @bitCast(event.body[0..4].*);
std.log.debug("wl_display:delete_id({})", .{name});
},
else => return error.InvalidOpcode,
}
} else {
std.log.warn("unknown event {{ .object_id = {}, .opcode = {x}, .message = \"{}\" }}", .{ event.header.object_id, event.header.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(event.body)) });
}
}
}
pub fn getDisplayPath(gpa: std.mem.Allocator) ![]u8 {
const xdg_runtime_dir_path = try std.process.getEnvVarOwned(gpa, "XDG_RUNTIME_DIR");
defer gpa.free(xdg_runtime_dir_path);
const display_name = std.process.getEnvVarOwned(gpa, "WAYLAND_DISPLAY") catch |err| switch (err) {
error.EnvironmentVariableNotFound => return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, "wayland-0" }),
else => return err,
};
defer gpa.free(display_name);
return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, display_name });
}
/// A wayland packet header
const Header = extern struct {
object_id: u32 align(1),
opcode: u16 align(1),
size: u16 align(1),
pub fn read(socket: std.net.Stream) !Header {
var header: Header = undefined;
const header_bytes_read = try socket.readAll(std.mem.asBytes(&header));
if (header_bytes_read < @sizeOf(Header)) {
return error.UnexpectedEOF;
}
return header;
}
};
/// This is the general shape of a Wayland `Event` (a message from the compositor to the client).
const Event = struct {
header: Header,
body: []const u8,
pub fn read(socket: std.net.Stream, body_buffer: *std.ArrayList(u8)) !Event {
const header = try Header.read(socket);
// read bytes until we match the size in the header, not including the bytes in the header.
try body_buffer.resize(header.size - @sizeOf(Header));
const message_bytes_read = try socket.readAll(body_buffer.items);
if (message_bytes_read < body_buffer.items.len) {
return error.UnexpectedEOF;
}
return Event{
.header = header,
.body = body_buffer.items,
};
}
};
/// Handles creating a header and writing the request to the socket.
pub fn writeRequest(socket: std.net.Stream, object_id: u32, opcode: u16, message: []const u32) !void {
const message_bytes = std.mem.sliceAsBytes(message);
const header = Header{
.object_id = object_id,
.opcode = opcode,
.size = @sizeOf(Header) + @as(u16, @intCast(message_bytes.len)),
};
try socket.writeAll(std.mem.asBytes(&header));
try socket.writeAll(message_bytes);
}
/// https://wayland.app/protocols/wayland#wl_shm:request:create_pool
const WL_SHM_REQUEST_CREATE_POOL = 0;
/// This request is more complicated that most other requests, because it has to send the file descriptor to the
/// compositor using a control message.
///
/// Returns the id of the newly create wl_shm_pool
pub fn writeWlShmRequestCreatePool(socket: std.net.Stream, wl_shm_id: u32, next_id: *u32, fd: std.os.fd_t, fd_len: i32) !u32 {
const wl_shm_pool_id = next_id.*;
const message = [_]u32{
// id: new_id<wl_shm_pool>
wl_shm_pool_id,
// size: int
@intCast(fd_len),
};
// If you're paying close attention, you'll notice that our message only has two parameters in it, despite the
// documentation calling for 3: wl_shm_pool_id, fd, and size. This is because `fd` is sent in the control message,
// and so not included in the regular message body.
// Create the message header as usual
const message_bytes = std.mem.sliceAsBytes(&message);
const header = Header{
.object_id = wl_shm_id,
.opcode = WL_SHM_REQUEST_CREATE_POOL,
.size = @sizeOf(Header) + @as(u16, @intCast(message_bytes.len)),
};
const header_bytes = std.mem.asBytes(&header);
// we'll be using `std.os.sendmsg` to send a control message, so we may as well use the vectorized
// IO to send the header and the message body while we're at it.
const msg_iov = [_]std.os.iovec_const{
.{
.iov_base = header_bytes.ptr,
.iov_len = header_bytes.len,
},
.{
.iov_base = message_bytes.ptr,
.iov_len = message_bytes.len,
},
};
// Send the file descriptor through a control message
// This is the control message! It is not a fixed size struct. Instead it varies depending on the message you want to send.
// C uses macros to define it, here we make a comptime function instead.
const control_message = cmsg(std.os.fd_t){
.level = std.os.SOL.SOCKET,
.type = 0x01, // value of SCM_RIGHTS
.data = fd,
};
const control_message_bytes = std.mem.asBytes(&control_message);
const socket_message = std.os.msghdr_const{
.name = null,
.namelen = 0,
.iov = &msg_iov,
.iovlen = msg_iov.len,
.control = control_message_bytes.ptr,
// This is the size of the control message in bytes
.controllen = control_message_bytes.len,
.flags = 0,
};
const bytes_sent = try std.os.sendmsg(socket.handle, &socket_message, 0);
if (bytes_sent < header_bytes.len + message_bytes.len) {
return error.ConnectionClosed;
}
// Wait to increment until we know the message has been sent
next_id.* += 1;
return wl_shm_pool_id;
}
fn cmsg(comptime T: type) type {
const padding_size = (@sizeOf(T) + @sizeOf(c_long) - 1) & ~(@as(usize, @sizeOf(c_long)) - 1);
return extern struct {
len: c_ulong = @sizeOf(@This()) - padding_size,
level: c_int,
type: c_int,
data: T,
_padding: [padding_size]u8 align(1) = [_]u8{0} ** padding_size,
};
}

View File

@ -1,416 +0,0 @@
const std = @import("std");
const wayland = @import("wayland");
pub fn main() !void {
var general_allocator = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = general_allocator.deinit();
const gpa = general_allocator.allocator();
const display_path = try wayland.getDisplayPath(gpa);
defer gpa.free(display_path);
var conn = try wayland.Conn.init(gpa, display_path);
defer conn.deinit();
// Create an id pool to allocate ids for us
var id_pool = wayland.IdPool{};
const ids = try wayland.registerGlobals(gpa, &id_pool, conn.socket, &.{
wayland.core.Shm,
wayland.core.Compositor,
wayland.xdg.WmBase,
wayland.core.Seat,
wayland.zxdg.DecorationManagerV1,
});
const DISPLAY_ID = 1;
const shm_id = ids[0] orelse return error.NeccessaryWaylandExtensionMissing;
const compositor_id = ids[1] orelse return error.NeccessaryWaylandExtensionMissing;
const xdg_wm_base_id = ids[2] orelse return error.NeccessaryWaylandExtensionMissing;
const wl_seat_id = ids[3] orelse return error.NeccessaryWaylandExtensionMissing;
const surface_id = id_pool.create();
try conn.send(
wayland.core.Compositor.Request,
compositor_id,
.{ .create_surface = .{
.new_id = surface_id,
} },
);
const xdg_surface_id = id_pool.create();
try conn.send(
wayland.xdg.WmBase.Request,
xdg_wm_base_id,
.{ .get_xdg_surface = .{
.id = xdg_surface_id,
.surface = surface_id,
} },
);
const xdg_toplevel_id = id_pool.create();
try conn.send(
wayland.xdg.Surface.Request,
xdg_surface_id,
.{ .get_toplevel = .{
.id = xdg_toplevel_id,
} },
);
var zxdg_toplevel_decoration_id_opt: ?u32 = null;
if (ids[4]) |zxdg_decoration_manager_id| {
zxdg_toplevel_decoration_id_opt = id_pool.create();
try conn.send(
wayland.zxdg.DecorationManagerV1.Request,
zxdg_decoration_manager_id,
.{ .get_toplevel_decoration = .{
.new_id = zxdg_toplevel_decoration_id_opt.?,
.toplevel = xdg_toplevel_id,
} },
);
}
try conn.send(
wayland.core.Surface.Request,
surface_id,
wayland.core.Surface.Request.commit,
);
const registry_done_id = id_pool.create();
try conn.send(
wayland.core.Display.Request,
DISPLAY_ID,
.{ .sync = .{ .callback = registry_done_id } },
);
var done = false;
var surface_configured = false;
var seat_capabilties: ?wayland.core.Seat.Capability = null;
while (!done or !surface_configured) {
const header, const body = try conn.recv();
if (header.object_id == xdg_surface_id) {
const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body);
switch (event) {
.configure => |conf| {
try conn.send(
wayland.xdg.Surface.Request,
xdg_surface_id,
.{ .ack_configure = .{
.serial = conf.serial,
} },
);
surface_configured = true;
},
}
} else if (zxdg_toplevel_decoration_id_opt != null and header.object_id == zxdg_toplevel_decoration_id_opt.?) {
const event = try wayland.deserialize(wayland.zxdg.ToplevelDecorationV1.Event, header, body);
std.debug.print("<- zxdg_toplevel_decoration@{}\n", .{event});
} else if (header.object_id == xdg_toplevel_id) {
const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body);
std.debug.print("<- {}\n", .{event});
} else if (header.object_id == registry_done_id) {
done = true;
} else if (header.object_id == shm_id) {
const event = try wayland.deserialize(wayland.core.Shm.Event, header, body);
switch (event) {
.format => |format| std.debug.print("<- format {} {}\n", .{ format.format, std.zig.fmtEscapes(std.mem.asBytes(&format.format)) }),
}
} else if (header.object_id == wl_seat_id) {
const event = try wayland.deserialize(wayland.core.Seat.Event, header, body);
switch (event) {
.capabilities => |capabilities| {
const cap: wayland.core.Seat.Capability = @bitCast(capabilities.capability);
std.debug.print("<- wl_seat.capabilties = {}\n", .{cap});
seat_capabilties = cap;
},
.name => |name| {
std.debug.print("<- wl_seat.name = {s}\n", .{name.name});
},
}
} else if (header.object_id == DISPLAY_ID) {
const event = try wayland.deserialize(wayland.core.Display.Event, header, body);
switch (event) {
.@"error" => |err| std.debug.print("<- error({}): {} {s}\n", .{ err.object_id, err.code, err.message }),
.delete_id => |id| {
std.debug.print("id {} deleted\n", .{id});
id_pool.destroy(id.id);
},
}
} else {
std.debug.print("{} {x} \"{}\"\n", .{ header.object_id, header.size_and_opcode.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(body)) });
}
}
var wl_pointer_id_opt: ?u32 = null;
var wl_keyboard_id_opt: ?u32 = null;
if (seat_capabilties) |caps| {
if (caps.pointer) {
wl_pointer_id_opt = id_pool.create();
std.debug.print("wl pointer id: {}\n", .{wl_pointer_id_opt.?});
try conn.send(
wayland.core.Seat.Request,
wl_seat_id,
.{ .get_pointer = .{
.new_id = wl_pointer_id_opt.?,
} },
);
}
if (caps.keyboard) {
wl_keyboard_id_opt = id_pool.create();
std.debug.print("wl keyboard id: {}\n", .{wl_keyboard_id_opt.?});
try conn.send(
wayland.core.Seat.Request,
wl_seat_id,
.{ .get_keyboard = .{
.new_id = wl_keyboard_id_opt.?,
} },
);
}
}
const wl_pointer_id = wl_pointer_id_opt orelse return error.MissingPointer;
const wl_keyboard_id = wl_keyboard_id_opt orelse return error.MissingKeyboard;
// allocate a shared memory file for display purposes
const Pixel = [4]u8;
const framebuffer_size = [2]usize{ 128, 128 };
const pool_file_len = 1024 * framebuffer_size[0] * framebuffer_size[1] * @sizeOf(Pixel);
const pool_fd = try std.os.memfd_create("my-wayland-framebuffer", 0);
try std.os.ftruncate(pool_fd, pool_file_len);
const pool_bytes = try std.os.mmap(null, pool_file_len, std.os.PROT.READ | std.os.PROT.WRITE, std.os.MAP.SHARED, pool_fd, 0);
var pool_fixed_buffer_allocator = std.heap.FixedBufferAllocator.init(pool_bytes);
var pool_general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){ .backing_allocator = pool_fixed_buffer_allocator.allocator() };
const pool_alloc = pool_general_purpose_allocator.allocator();
const framebuffer = try pool_alloc.alloc(Pixel, framebuffer_size[0] * framebuffer_size[1]);
// put some interesting colors into the framebuffer
for (0..framebuffer_size[1]) |y| {
const row = framebuffer[y * framebuffer_size[0] .. (y + 1) * framebuffer_size[0]];
for (row, 0..framebuffer_size[0]) |*pixel, x| {
pixel.* = .{
@truncate(x),
@truncate(y),
0x00,
0xFF,
};
}
}
const wl_shm_pool_id = id_pool.create();
{
std.debug.print("framebuffer_fd: {}\n", .{pool_fd});
try conn.send(
wayland.core.Shm.Request,
shm_id,
.{ .create_pool = .{
.new_id = wl_shm_pool_id,
.fd = @enumFromInt(pool_fd),
.size = pool_file_len,
} },
);
}
var framebuffers = std.AutoHashMap(u32, []Pixel).init(gpa);
defer framebuffers.deinit();
try framebuffers.put(wl_shm_pool_id, framebuffer);
const wl_buffer_id = id_pool.create();
try conn.send(
wayland.core.ShmPool.Request,
wl_shm_pool_id,
.{ .create_buffer = .{
.new_id = wl_buffer_id,
.offset = 0,
.width = framebuffer_size[0],
.height = framebuffer_size[1],
.stride = framebuffer_size[0] * @sizeOf([4]u8),
.format = .argb8888,
} },
);
try conn.send(
wayland.core.Surface.Request,
surface_id,
.{ .attach = .{
.buffer = wl_buffer_id,
.x = 0,
.y = 0,
} },
);
try conn.send(
wayland.core.Surface.Request,
surface_id,
.{ .damage = .{
.x = 0,
.y = 0,
.width = std.math.maxInt(i32),
.height = std.math.maxInt(i32),
} },
);
try conn.send(
wayland.core.Surface.Request,
surface_id,
wayland.core.Surface.Request.commit,
);
var window_size: [2]u32 = [2]u32{ @intCast(framebuffer_size[0]), @intCast(framebuffer_size[1]) };
var running = true;
while (running) {
const header, const body = try conn.recv();
if (header.object_id == xdg_surface_id) {
const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body);
switch (event) {
.configure => |conf| {
try conn.send(
wayland.xdg.Surface.Request,
xdg_surface_id,
.{ .ack_configure = .{
.serial = conf.serial,
} },
);
const new_buffer_id = id_pool.create();
const new_framebuffer = try pool_alloc.alloc(Pixel, window_size[0] * window_size[1]);
try framebuffers.put(new_buffer_id, new_framebuffer);
// put some interesting colors into the new_framebuffer
for (0..window_size[1]) |y| {
const row = new_framebuffer[y * window_size[0] .. (y + 1) * window_size[0]];
for (row, 0..window_size[0]) |*pixel, x| {
pixel.* = .{
@truncate(x),
@truncate(y),
0x00,
0xFF,
};
}
}
try conn.send(
wayland.core.ShmPool.Request,
wl_shm_pool_id,
.{ .create_buffer = .{
.new_id = new_buffer_id,
.offset = @intCast(@intFromPtr(new_framebuffer.ptr) - @intFromPtr(pool_bytes.ptr)),
.width = @intCast(window_size[0]),
.height = @intCast(window_size[1]),
.stride = @as(i32, @intCast(window_size[0])) * @sizeOf([4]u8),
.format = .argb8888,
} },
);
try conn.send(
wayland.core.Surface.Request,
surface_id,
.{ .attach = .{
.buffer = new_buffer_id,
.x = 0,
.y = 0,
} },
);
try conn.send(
wayland.core.Surface.Request,
surface_id,
.{ .damage = .{
.x = 0,
.y = 0,
.width = std.math.maxInt(i32),
.height = std.math.maxInt(i32),
} },
);
try conn.send(
wayland.core.Surface.Request,
surface_id,
wayland.core.Surface.Request.commit,
);
// commit the configuration
try conn.send(
wayland.core.Surface.Request,
surface_id,
wayland.core.Surface.Request.commit,
);
},
}
} else if (header.object_id == xdg_toplevel_id) {
const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body);
switch (event) {
.configure => |conf| {
std.debug.print("<- xdg_toplevel@{} configure <{}, {}> {any}\n", .{ header.object_id, conf.width, conf.height, conf.states });
window_size = .{
@intCast(conf.width),
@intCast(conf.height),
};
},
.close => running = false,
else => |tag| std.debug.print("<- xdg_toplevel@{} {s} {}\n", .{ header.object_id, @tagName(tag), event }),
}
} else if (header.object_id == xdg_wm_base_id) {
const event = try wayland.deserialize(wayland.xdg.WmBase.Event, header, body);
switch (event) {
.ping => |ping| {
try conn.send(
wayland.xdg.WmBase.Request,
xdg_wm_base_id,
.{ .pong = .{
.serial = ping.serial,
} },
);
},
}
} else if (header.object_id == wl_pointer_id) {
const event = try wayland.deserialize(wayland.core.Pointer.Event, header, body);
std.debug.print("<- wl_pointer@{}\n", .{event});
} else if (header.object_id == wl_keyboard_id) {
const event = try wayland.deserialize(wayland.core.Keyboard.Event, header, body);
switch (event) {
.keymap => |keymap| {
const fd = conn.fd_queue.orderedRemove(0);
std.debug.print("keymap format={}, size={}, fd={}\n", .{
keymap.format,
keymap.size,
fd,
});
},
else => {
std.debug.print("<- wl_keyboard@{}\n", .{event});
},
}
} else if (framebuffers.get(header.object_id)) |framebuffer_slice| {
const event = try wayland.deserialize(wayland.core.Buffer.Event, header, body);
switch (event) {
.release => {
_ = framebuffers.remove(header.object_id);
pool_alloc.free(framebuffer_slice);
},
}
} else if (header.object_id == DISPLAY_ID) {
const event = try wayland.deserialize(wayland.core.Display.Event, header, body);
switch (event) {
.@"error" => |err| std.debug.print("<- error({}): {} {s}\n", .{ err.object_id, err.code, err.message }),
.delete_id => |id| id_pool.destroy(id.id),
}
} else {
std.debug.print("{} {x} \"{}\"\n", .{ header.object_id, header.size_and_opcode.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(body)) });
}
}
}
fn cmsg(comptime T: type) type {
const padding_size = (@sizeOf(T) + @sizeOf(c_long) - 1) & ~(@as(usize, @sizeOf(c_long)) - 1);
return extern struct {
len: c_ulong = @sizeOf(@This()) - padding_size,
level: c_int,
type: c_int,
data: T,
_padding: [padding_size]u8 align(1) = [_]u8{0} ** padding_size,
};
}

View File

@ -1,4 +1,5 @@
const types = @import("types.zig"); const types = @import("types.zig");
const Conn = @import("root.zig").Conn;
pub const Display = struct { pub const Display = struct {
pub const Request = union(enum) { pub const Request = union(enum) {
@ -29,9 +30,44 @@ pub const Display = struct {
no_memory, no_memory,
implementation, implementation,
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Compositor {
return .{ .conn = conn, .id = id };
}
// TODO: take callback
pub fn sync(display: Display) !u32 {
const new_id = display.conn.id_pool.create();
try display.conn.send(
Request,
display.id,
.{ .sync = .{
.callback = new_id,
} },
);
return new_id;
}
pub fn get_registry(display: Display) Registry {
const new_id = display.conn.id_pool.create();
try display.conn.send(
Request,
display.id,
.{ .get_registry = .{
.registery = new_id,
} },
);
return Registry.init(display.conn, new_id);
}
}; };
pub const Registry = struct { pub const Registry = struct {
pub const INTERFACE = "wl_registry";
pub const VERSION = 1;
pub const Event = union(enum) { pub const Event = union(enum) {
global: struct { global: struct {
name: u32, name: u32,
@ -51,6 +87,13 @@ pub const Registry = struct {
new_id: u32, new_id: u32,
}, },
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Registry {
return .{ .conn = conn, .id = id };
}
}; };
pub const Compositor = struct { pub const Compositor = struct {
@ -65,6 +108,33 @@ pub const Compositor = struct {
new_id: u32, new_id: u32,
}, },
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Compositor {
return .{ .conn = conn, .id = id };
}
pub fn create_surface(compositor: *const Compositor) !Surface {
const new_id = compositor.conn.id_pool.create();
try compositor.conn.send(
Request,
compositor.id,
.{ .create_surface = .{ .new_id = new_id } },
);
return Surface.init(compositor.conn, new_id);
}
// pub fn create_region(compositor: *Compositor) Region {
// const new_id = compositor.conn.id_pool.create();
// try compositor.conn.send(
// Request,
// compositor.id,
// .{ .create_region = .{.new_id = new_id} },
// );
// return Region.init(compositor.conn, new_id);
// }
}; };
pub const ShmPool = struct { pub const ShmPool = struct {
@ -82,6 +152,30 @@ pub const ShmPool = struct {
size: i32, size: i32,
}, },
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) ShmPool {
return .{ .conn = conn, .id = id };
}
pub fn create_buffer(shm_pool: ShmPool, offset: i32, width: i32, height: i32, stride: i32, format: Shm.Format) u32 {
const new_id = shm_pool.conn.id_pool.create();
try shm_pool.conn.send(
Request,
shm_pool.id,
.{ .create_buffer = .{
.new_id = new_id,
.offset = offset,
.width = width,
.height = height,
.stride = stride,
.format = format,
} },
);
return Surface.init(shm_pool.conn, new_id);
}
}; };
pub const Shm = struct { pub const Shm = struct {
@ -114,6 +208,23 @@ pub const Shm = struct {
xrgb8888, xrgb8888,
_, _,
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Shm {
return .{ .conn = conn, .id = id };
}
pub fn create_pool(shm: *const Shm, pool_fd: u32, size: u32) ShmPool {
const new_id = shm.conn.id_pool.create();
try shm.conn.send(
Request,
shm.id,
.{ .create_pool = .{ .new_id = new_id, .fd = @enumFromInt(pool_fd), .size = size } },
);
return ShmPool.init(shm.conn, new_id);
}
}; };
pub const Surface = struct { pub const Surface = struct {
@ -165,6 +276,21 @@ pub const Surface = struct {
invalid_offset, invalid_offset,
defunct_role_object, defunct_role_object,
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Surface {
return .{ .conn = conn, .id = id };
}
pub fn commit(surface: Surface) !void {
try surface.conn.send(
Request,
surface.id,
.commit,
);
}
}; };
pub const Buffer = struct { pub const Buffer = struct {
@ -211,6 +337,13 @@ pub const Seat = struct {
}; };
pub const Error = enum(u32) {}; pub const Error = enum(u32) {};
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Seat {
return .{ .conn = conn, .id = id };
}
}; };
pub const Pointer = struct { pub const Pointer = struct {
@ -298,6 +431,13 @@ pub const Pointer = struct {
identical, identical,
inverted, inverted,
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Seat {
return .{ .conn = conn, .id = id };
}
}; };
pub const Keyboard = struct { pub const Keyboard = struct {
@ -348,4 +488,57 @@ pub const Keyboard = struct {
released, released,
pressed, pressed,
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Seat {
return .{ .conn = conn, .id = id };
}
};
pub const Touch = struct {
pub const Request = union(enum) {
release: void,
};
pub const Event = union(enum) {
down: struct {
serial: u32,
time: u32,
surface: u32,
id: i32,
x: u32,
y: u32,
},
up: struct {
serial: u32,
time: u32,
id: i32,
},
motion: struct {
time: u32,
id: i32,
x: i32, //i24.8
y: i32, //i24.8
},
frame: void,
cancel: void,
shape: struct {
id: i32,
major: i32, // fixed
minor: i32, // fixed
},
orientation: struct {
id: i32,
orientation: i32,
},
};
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Seat {
return .{ .conn = conn, .id = id };
}
}; };

View File

@ -1,591 +1,132 @@
const std = @import("std"); const std = @import("std");
const testing = std.testing; const wayland = @import("root.zig");
pub const core = @import("./core.zig");
pub const xdg = @import("./xdg.zig");
pub const zxdg = @import("./zxdg.zig");
pub const types = @import("./types.zig");
pub fn getDisplayPath(gpa: std.mem.Allocator) ![]u8 { const Conn = wayland.Conn;
const xdg_runtime_dir_path = try std.process.getEnvVarOwned(gpa, "XDG_RUNTIME_DIR"); const Context = wayland.Context(&.{
defer gpa.free(xdg_runtime_dir_path); // Core protocol
const display_name = std.process.getEnvVarOwned(gpa, "WAYLAND_DISPLAY") catch |err| switch (err) { wayland.core.Registry,
error.EnvironmentVariableNotFound => return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, "wayland-0" }), wayland.core.Compositor,
else => return err, // wayland.core.ShmPool,
}; wayland.core.Shm,
defer gpa.free(display_name); // wayland.core.Surface,
// wayland.core.Buffer,
wayland.core.Seat,
// wayland.core.Pointer,
// wayland.core.Keyboard,
// wayland.core.Touch,
return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, display_name }); // XDG Shell protocol
} wayland.xdg.WmBase,
pub const Header = extern struct { // XDG Decoration
object_id: u32 align(1), // wayland.zxdg.DecorationManagerV1,
size_and_opcode: SizeAndOpcode align(1), // wayland.zxdg.TopLevelDecorationV1,
});
pub const SizeAndOpcode = packed struct(u32) { const font8x8 = @cImport({
opcode: u16, @cInclude("font8x8.h");
size: u16, });
};
const Pixel = [4]u8;
const Theme = struct {
const background = 0x282A36;
const current_line = 0x44475A;
const foreground = 0xF8F8F2;
const comment = 0x6272A4;
const cyan = 0x8BE9FD;
const green = 0x50FA7B;
const orange = 0xFFB86C;
const pink = 0xFF79C6;
const purple = 0xBD93F9;
const red = 0xFF5555;
const yellow = 0xF1FA8C;
}; };
test "[]u32 from header" { fn toPixel(color: u24) Pixel {
try std.testing.expectEqualSlices( return .{
u32, @intCast(color & 0xFF),
&[_]u32{ @intCast(color >> 8 & 0xFF),
1, @intCast(color >> 16 & 0xFF),
(@as(u32, 12) << 16) | (4), 0xFF,
},
&@as([2]u32, @bitCast(Header{
.object_id = 1,
.size_and_opcode = .{
.size = 12,
.opcode = 4,
},
})),
);
}
test "header from []u32" {
try std.testing.expectEqualDeep(
Header{
.object_id = 1,
.size_and_opcode = .{
.size = 12,
.opcode = 4,
},
},
@as(Header, @bitCast([2]u32{
1,
(@as(u32, 12) << 16) | (4),
})),
);
}
pub fn readUInt(buffer: []const u32, parent_pos: *usize) !u32 {
var pos = parent_pos.*;
if (pos >= buffer.len) return error.EndOfStream;
const uint: u32 = @bitCast(buffer[pos]);
pos += 1;
parent_pos.* = pos;
return uint;
}
pub fn readInt(buffer: []const u32, parent_pos: *usize) !i32 {
var pos = parent_pos.*;
if (pos >= buffer.len) return error.EndOfStream;
const int: i32 = @bitCast(buffer[pos]);
pos += 1;
parent_pos.* = pos;
return int;
}
pub fn readString(buffer: []const u32, parent_pos: *usize) ![:0]const u8 {
var pos = parent_pos.*;
const len = try readUInt(buffer, &pos);
const wordlen = std.mem.alignForward(usize, len, @sizeOf(u32)) / @sizeOf(u32);
if (pos + wordlen > buffer.len) return error.EndOfStream;
const string = std.mem.sliceAsBytes(buffer[pos..])[0 .. len - 1 :0];
pos += std.mem.alignForward(usize, len, @sizeOf(u32)) / @sizeOf(u32);
parent_pos.* = pos;
return string;
}
pub fn readArray(comptime T: type, buffer: []const u32, parent_pos: *usize) ![]const T {
var pos = parent_pos.*;
const byte_size = try readUInt(buffer, &pos);
const array = @as([*]const T, @ptrCast(buffer[pos..].ptr))[0 .. byte_size / @sizeOf(T)];
pos += byte_size / @sizeOf(u32);
parent_pos.* = pos;
return array;
}
pub fn deserializeArguments(comptime Signature: type, buffer: []const u32) !Signature {
if (Signature == void) return {};
var result: Signature = undefined;
var pos: usize = 0;
inline for (std.meta.fields(Signature)) |field| {
if (field.type == types.Fd) continue;
switch (@typeInfo(field.type)) {
.Int => |int_info| switch (int_info.signedness) {
.signed => @field(result, field.name) = try readInt(buffer, &pos),
.unsigned => @field(result, field.name) = try readUInt(buffer, &pos),
},
.Enum => |enum_info| if (@sizeOf(enum_info.tag_type) == @sizeOf(u32)) {
@field(result, field.name) = @enumFromInt(try readInt(buffer, &pos));
} else {
@compileError("Unsupported type " ++ @typeName(field.type));
},
.Pointer => |ptr| switch (ptr.size) {
.Slice => if (ptr.child == u8) {
@field(result, field.name) = try readString(buffer, &pos);
} else {
@field(result, field.name) = try readArray(ptr.child, buffer, &pos);
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
}
}
return result;
}
pub fn deserialize(comptime Union: type, header: Header, buffer: []const u32) !Union {
const op = try std.meta.intToEnum(std.meta.Tag(Union), header.size_and_opcode.opcode);
switch (op) {
inline else => |f| {
const Payload = std.meta.TagPayload(Union, f);
const payload = try deserializeArguments(Payload, buffer);
return @unionInit(Union, @tagName(f), payload);
},
}
}
/// Returns the length of the serialized message in `u32` words.
pub fn calculateSerializedWordLen(comptime Signature: type, message: Signature) usize {
var pos: usize = 0;
inline for (std.meta.fields(Signature)) |field| {
switch (@typeInfo(field.type)) {
.Int => pos += 1,
.Pointer => |ptr| switch (ptr.size) {
.Slice => {
// for size of string in bytes
pos += 1;
const str = @field(message, field.name);
pos += std.mem.alignForward(usize, str.len + 1, @sizeOf(u32)) / @sizeOf(u32);
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
}
}
return pos;
}
pub fn countFds(comptime Signature: type) usize {
if (Signature == void) return 0;
var count: usize = 0;
inline for (std.meta.fields(Signature)) |field| {
if (field.type == types.Fd) {
count += 1;
}
}
return count;
}
pub fn extractFds(comptime Signature: type, message: *const Signature) [countFds(Signature)]*const types.Fd {
if (Signature == void) return [_]*const types.Fd{};
var fds: [countFds(Signature)]*const types.Fd = undefined;
var i: usize = 0;
inline for (std.meta.fields(Signature)) |field| {
if (field.type == types.Fd) {
fds[i] = &@field(message, field.name);
i += 1;
}
}
return fds;
}
/// Message must live until the iovec array is written.
pub fn serializeArguments(comptime Signature: type, buffer: []u32, message: Signature) ![]u32 {
if (Signature == void) return buffer[0..0];
var pos: usize = 0;
inline for (std.meta.fields(Signature)) |field| {
if (field.type == types.Fd) continue;
switch (@typeInfo(field.type)) {
.Int => {
if (pos >= buffer.len) return error.OutOfMemory;
buffer[pos] = @bitCast(@field(message, field.name));
pos += 1;
},
.Enum => |enum_info| if (enum_info.tag_type == u32) {
if (pos >= buffer.len) return error.OutOfMemory;
buffer[pos] = @intFromEnum(@field(message, field.name));
pos += 1;
} else {
@compileError("Unsupported type " ++ @typeName(field.type));
},
.Pointer => |ptr| switch (ptr.size) {
.Slice => {
const str = @field(message, field.name);
if (str.len >= std.math.maxInt(u32)) return error.StringTooLong;
buffer[pos] = @intCast(str.len + 1);
pos += 1;
const str_len_aligned = std.mem.alignForward(usize, str.len + 1, @sizeOf(u32));
const padding_len = str_len_aligned - str.len;
if (str_len_aligned / @sizeOf(u32) >= buffer[pos..].len) return error.OutOfMemory;
const buffer_bytes = std.mem.sliceAsBytes(buffer[pos..]);
@memcpy(buffer_bytes[0..str.len], str);
@memset(buffer_bytes[str.len..][0..padding_len], 0);
pos += str_len_aligned / @sizeOf(u32);
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
}
}
return buffer[0..pos];
}
pub fn serialize(comptime Union: type, buffer: []u32, object_id: u32, message: Union) ![]u32 {
const header_wordlen = @sizeOf(Header) / @sizeOf(u32);
const header: *Header = @ptrCast(buffer[0..header_wordlen]);
header.object_id = object_id;
const tag = std.meta.activeTag(message);
header.size_and_opcode.opcode = @intFromEnum(tag);
const arguments = switch (message) {
inline else => |payload| try serializeArguments(@TypeOf(payload), buffer[header_wordlen..], payload),
}; };
header.size_and_opcode.size = @intCast(@sizeOf(Header) + arguments.len * @sizeOf(u32));
return buffer[0 .. header.size_and_opcode.size / @sizeOf(u32)];
} }
test "deserialize Registry.Event.Global" { pub fn main() !void {
const words = [_]u32{ var general_allocator = std.heap.GeneralPurposeAllocator(.{}){};
1, defer _ = general_allocator.deinit();
7, const gpa = general_allocator.allocator();
@bitCast(@as([4]u8, "wl_s".*)),
@bitCast(@as([4]u8, "hm\x00\x00".*)),
3,
};
const parsed = try deserializeArguments(core.Registry.Event.Global, &words);
try std.testing.expectEqualDeep(core.Registry.Event.Global{
.name = 1,
.interface = "wl_shm",
.version = 3,
}, parsed);
}
test "deserialize Registry.Event" { const display_path = try wayland.getDisplayPath(gpa);
const header = Header{ defer gpa.free(display_path);
.object_id = 123,
.size_and_opcode = .{
.size = 28,
.opcode = @intFromEnum(core.Registry.Event.Tag.global),
},
};
const words = [_]u32{
1,
7,
@bitCast(@as([4]u8, "wl_s".*)),
@bitCast(@as([4]u8, "hm\x00\x00".*)),
3,
};
const parsed = try deserialize(core.Registry.Event, header, &words);
try std.testing.expectEqualDeep(
core.Registry.Event{
.global = .{
.name = 1,
.interface = "wl_shm",
.version = 3,
},
},
parsed,
);
const header2 = Header{ var conn = try Conn.init(gpa, display_path);
.object_id = 1, defer conn.deinit();
.size_and_opcode = .{
.size = 14 * @sizeOf(u32),
.opcode = @intFromEnum(core.Display.Event.Tag.@"error"),
},
};
const words2 = [_]u32{
1,
15,
40,
@bitCast(@as([4]u8, "inva".*)),
@bitCast(@as([4]u8, "lid ".*)),
@bitCast(@as([4]u8, "argu".*)),
@bitCast(@as([4]u8, "ment".*)),
@bitCast(@as([4]u8, "s to".*)),
@bitCast(@as([4]u8, " wl_".*)),
@bitCast(@as([4]u8, "regi".*)),
@bitCast(@as([4]u8, "stry".*)),
@bitCast(@as([4]u8, "@2.b".*)),
@bitCast(@as([4]u8, "ind\x00".*)),
};
const parsed2 = try deserialize(core.Display.Event, header2, &words2);
try std.testing.expectEqualDeep(
core.Display.Event{
.@"error" = .{
.object_id = 1,
.code = 15,
.message = "invalid arguments to wl_registry@2.bind",
},
},
parsed2,
);
}
test "serialize Registry.Event.Global" { var ctx = try Context.init(&conn);
const message = core.Registry.Event.Global{
.name = 1,
.interface = "wl_shm",
.version = 3,
};
var buffer: [5]u32 = undefined;
const serialized = try serializeArguments(core.Registry.Event.Global, &buffer, message);
try std.testing.expectEqualSlices( const compositor = try ctx.getGlobal(wayland.core.Compositor);
u32, const surface = try compositor.create_surface();
&[_]u32{
1,
7,
@bitCast(@as([4]u8, "wl_s".*)),
@bitCast(@as([4]u8, "hm\x00\x00".*)),
3,
},
serialized,
);
}
pub const IdPool = struct { const xdg_wm_base = try ctx.getGlobal(wayland.xdg.WmBase);
next_id: u32 = 2, const xdg_surface = try xdg_wm_base.get_xdg_surface(surface);
free_ids: std.BoundedArray(u32, 1024) = .{}, const xdg_toplevel = try xdg_surface.get_toplevel();
pub fn create(this: *@This()) u32 { try surface.commit();
if (this.free_ids.popOrNull()) |id| {
return id;
}
defer this.next_id += 1; const display = try ctx.getGlobal(wayland.core.Display);
return this.next_id; const registry_done = try display.sync();
}
pub fn destroy(this: *@This(), id: u32) void { const shm = try ctx.getGlobal(wayland.core.Shm);
for (this.free_ids.slice()) |existing_id| { const seat = try ctx.getGlobal(wayland.core.Seat);
if (existing_id == id) return;
}
this.free_ids.append(id) catch {};
}
};
pub fn registerGlobals(alloc: std.mem.Allocator, id_pool: *IdPool, socket: std.net.Stream, comptime T: []const type) ![T.len]?u32 { var done = false;
const Item = struct { version: u32, index: u32 }; var surface_configured = false;
const Pair = struct { []const u8, Item }; var seat_capabilties: ?wayland.core.Seat.Capability = null;
comptime var kvs_list: []const Pair = &[_]Pair{}; while (!done or !surface_configured) {
inline for (T, 0..) |t, i| { const header, const body = try conn.recv();
kvs_list = kvs_list ++ &[_]Pair{.{ t.INTERFACE, .{ .version = t.VERSION, .index = i } }};
}
const map = std.ComptimeStringMap(Item, kvs_list);
const registry_id = id_pool.create(); if (header.object_id == xdg_surface.id) {
{ const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body);
var buffer: [5]u32 = undefined;
const message = try serialize(core.Display.Request, &buffer, 1, .{ .get_registry = .{ .registry = registry_id } });
try socket.writeAll(std.mem.sliceAsBytes(message));
}
const registry_done_id = id_pool.create();
{
var buffer: [5]u32 = undefined;
const message = try serialize(core.Display.Request, &buffer, 1, .{ .sync = .{ .callback = registry_done_id } });
try socket.writeAll(std.mem.sliceAsBytes(message));
}
var ids: [T.len]?u32 = [_]?u32{null} ** T.len;
var message_buffer = std.ArrayList(u32).init(alloc);
defer message_buffer.deinit();
while (true) {
var header: Header = undefined;
const header_bytes_read = try socket.readAll(std.mem.asBytes(&header));
if (header_bytes_read < @sizeOf(Header)) break;
try message_buffer.resize((header.size_and_opcode.size - @sizeOf(Header)) / @sizeOf(u32));
const bytes_read = try socket.readAll(std.mem.sliceAsBytes(message_buffer.items));
message_buffer.shrinkRetainingCapacity(bytes_read / @sizeOf(u32));
if (header.object_id == registry_id) {
const event = try deserialize(core.Registry.Event, header, message_buffer.items);
switch (event) { switch (event) {
.global => |global| { .configure => |conf| {
var buffer: [20]u32 = undefined; try xdg_surface.ack_configure(conf.serial);
if (map.get(global.interface)) |item| { surface_configured = true;
if (global.version < item.version) {
// TODO: Add diagnostics API
return error.OutdatedCompositorProtocol;
}
const new_id = id_pool.create();
ids[item.index] = new_id;
const message = try serialize(core.Registry.Request, &buffer, registry_id, .{ .bind = .{
.name = global.name,
.interface = global.interface,
.version = item.version,
.new_id = new_id,
} });
try socket.writeAll(std.mem.sliceAsBytes(message));
}
}, },
.global_remove => {},
} }
} else if (header.object_id == registry_done_id) { } else if (header.object_id == xdg_toplevel.id) {
break; const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body);
} else { std.debug.print("<- {}\n", .{event});
std.log.info("{} {x} \"{}\"", .{ } else if (header.object_id == registry_done) {
header.object_id, done = true;
header.size_and_opcode.opcode, } else if (header.object_id == shm.id) {
std.zig.fmtEscapes(std.mem.sliceAsBytes(message_buffer.items)), const event = try wayland.deserialize(wayland.core.Shm.Event, header, body);
}); switch (event) {
} .format => |format| std.debug.print("<- format {} {}\n", .{ format.format, std.zig.fmtEscapes(std.mem.asBytes(&format.format)) }),
} }
} else if (header.object_id == seat.id) {
return ids; const event = try wayland.deserialize(wayland.core.Seat.Event, header, body);
} switch (event) {
.capabilities => |capabilities| {
fn cmsg(comptime T: type) type { const cap: wayland.core.Seat.Capability = @bitCast(capabilities.capability);
const padding_size = (@sizeOf(T) + @sizeOf(c_long) - 1) & ~(@as(usize, @sizeOf(c_long)) - 1); std.debug.print("<- wl_seat.capabilties = {}\n", .{cap});
return extern struct { seat_capabilties = cap;
len: c_ulong = @sizeOf(@This()) - padding_size,
level: c_int,
type: c_int,
data: T,
_padding: [padding_size]u8 align(1) = [_]u8{0} ** padding_size,
};
}
pub const Conn = struct {
allocator: std.mem.Allocator,
send_buffer: []u32,
recv_buffer: []u32,
fd_queue: std.ArrayListUnmanaged(std.os.fd_t),
socket: std.net.Stream,
pub fn init(alloc: std.mem.Allocator, display_path: []const u8) !Conn {
const send_buffer = try alloc.alloc(u32, 16);
const recv_buffer = try alloc.alloc(u32, 16);
return .{
.allocator = alloc,
.send_buffer = send_buffer,
.recv_buffer = recv_buffer,
.fd_queue = .{},
.socket = try std.net.connectUnixSocket(display_path),
};
}
pub fn deinit(conn: *Conn) void {
conn.allocator.free(conn.send_buffer);
conn.allocator.free(conn.recv_buffer);
conn.fd_queue.deinit(conn.allocator);
conn.socket.close();
}
pub fn send(conn: *Conn, comptime Signature: type, id: u32, message: Signature) !void {
const msg = while (true) {
const msg = serialize(
Signature,
conn.send_buffer,
id,
message,
) catch |e| switch (e) {
error.OutOfMemory => {
conn.send_buffer = try conn.allocator.realloc(conn.send_buffer, conn.send_buffer.len * 2);
continue;
}, },
}; .name => |name| {
std.debug.print("<- wl_seat.name = {s}\n", .{name.name});
break msg; },
}; }
const msg_bytes = std.mem.sliceAsBytes(msg); } else if (header.object_id == display.id) {
const msg_iov = [_]std.os.iovec_const{ const event = try wayland.deserialize(wayland.core.Display.Event, header, body);
.{ switch (event) {
.iov_base = msg_bytes.ptr, .@"error" => |err| std.debug.print("<- error({}): {} {s}\n", .{ err.object_id, err.code, err.message }),
.iov_len = msg_bytes.len, .delete_id => |id| {
}, std.debug.print("id {} deleted\n", .{id});
}; conn.id_pool.destroy(id.id);
const fds = switch (message) { },
inline else => |*payload| extractFds(@TypeOf(payload.*), payload), }
}; } else {
var ctrl_msgs: [fds.len]cmsg(std.os.fd_t) = undefined; std.debug.print("{} {x} \"{}\"\n", .{ header.object_id, header.size_and_opcode.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(body)) });
for (fds, 0..) |fdp, i| {
std.debug.print("fd {}: {}\n", .{ i, fdp.* });
ctrl_msgs[i] = .{
.level = std.os.SOL.SOCKET,
.type = 0x01,
.data = @intFromEnum(fdp.*),
};
} }
const socket_msg = std.os.msghdr_const{
.name = null,
.namelen = 0,
.iov = &msg_iov,
.iovlen = msg_iov.len,
.control = &ctrl_msgs,
.controllen = @intCast(@sizeOf(cmsg(std.os.fd_t)) * ctrl_msgs.len),
.flags = 0,
};
_ = try std.os.sendmsg(conn.socket.handle, &socket_msg, 0);
} }
}
pub const Message = struct { Header, []const u32 };
pub fn recv(conn: *Conn) !Message {
// TODO: make this less messy
// Read header
@memset(conn.recv_buffer, 0);
var iov: [1]std.os.iovec = .{.{
.iov_base = std.mem.sliceAsBytes(conn.recv_buffer).ptr,
.iov_len = @sizeOf(Header),
}};
var control_msg: cmsg(std.os.fd_t) = undefined;
const control_bytes = std.mem.asBytes(&control_msg);
var socket_msg = std.os.msghdr{
.name = null,
.namelen = 0,
.iov = &iov,
.iovlen = iov.len,
.control = control_bytes.ptr,
.controllen = @intCast(control_bytes.len),
.flags = 0,
};
const size = std.os.linux.recvmsg(conn.socket.handle, &socket_msg, 0);
if (size < @sizeOf(Header)) return error.SocketClosed;
var header: Header = undefined;
@memcpy(std.mem.asBytes(&header), iov[0].iov_base[0..@sizeOf(Header)]);
if (socket_msg.controllen != 0) {
try conn.fd_queue.append(conn.allocator, control_msg.data);
}
// Read body
const body_size = (header.size_and_opcode.size - @sizeOf(Header)) / @sizeOf(u32);
iov[0] = .{
.iov_base = std.mem.sliceAsBytes(conn.recv_buffer).ptr,
.iov_len = body_size * @sizeOf(u32),
};
socket_msg = std.os.msghdr{
.name = null,
.namelen = 0,
.iov = &iov,
.iovlen = iov.len,
.control = control_bytes.ptr,
.controllen = @intCast(control_bytes.len),
.flags = 0,
};
const size2 = std.os.linux.recvmsg(conn.socket.handle, &socket_msg, 0);
const message = conn.recv_buffer[0 .. size2 / @sizeOf(u32)];
if (socket_msg.controllen != 0) {
try conn.fd_queue.append(conn.allocator, control_msg.data);
}
return .{ header, message };
}
};

648
src/root.zig Normal file
View File

@ -0,0 +1,648 @@
const std = @import("std");
const testing = std.testing;
pub const core = @import("./core.zig");
pub const xdg = @import("./xdg.zig");
pub const zxdg = @import("./zxdg.zig");
pub const zwp = @import("./zwp.zig");
pub const types = @import("./types.zig");
pub fn getDisplayPath(gpa: std.mem.Allocator) ![]u8 {
const xdg_runtime_dir_path = try std.process.getEnvVarOwned(gpa, "XDG_RUNTIME_DIR");
defer gpa.free(xdg_runtime_dir_path);
const display_name = std.process.getEnvVarOwned(gpa, "WAYLAND_DISPLAY") catch |err| switch (err) {
error.EnvironmentVariableNotFound => return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, "wayland-0" }),
else => return err,
};
defer gpa.free(display_name);
return try std.fs.path.join(gpa, &.{ xdg_runtime_dir_path, display_name });
}
pub const Header = extern struct {
object_id: u32 align(1),
size_and_opcode: SizeAndOpcode align(1),
pub const SizeAndOpcode = packed struct(u32) {
opcode: u16,
size: u16,
};
};
test "[]u32 from header" {
try std.testing.expectEqualSlices(
u32,
&[_]u32{
1,
(@as(u32, 12) << 16) | (4),
},
&@as([2]u32, @bitCast(Header{
.object_id = 1,
.size_and_opcode = .{
.size = 12,
.opcode = 4,
},
})),
);
}
test "header from []u32" {
try std.testing.expectEqualDeep(
Header{
.object_id = 1,
.size_and_opcode = .{
.size = 12,
.opcode = 4,
},
},
@as(Header, @bitCast([2]u32{
1,
(@as(u32, 12) << 16) | (4),
})),
);
}
pub fn readUInt(buffer: []const u32, parent_pos: *usize) !u32 {
var pos = parent_pos.*;
if (pos >= buffer.len) return error.EndOfStream;
const uint: u32 = @bitCast(buffer[pos]);
pos += 1;
parent_pos.* = pos;
return uint;
}
pub fn readInt(buffer: []const u32, parent_pos: *usize) !i32 {
var pos = parent_pos.*;
if (pos >= buffer.len) return error.EndOfStream;
const int: i32 = @bitCast(buffer[pos]);
pos += 1;
parent_pos.* = pos;
return int;
}
pub fn readString(buffer: []const u32, parent_pos: *usize) !?[:0]const u8 {
var pos = parent_pos.*;
const len = try readUInt(buffer, &pos);
if (len == 0) return null;
const wordlen = std.mem.alignForward(usize, len, @sizeOf(u32)) / @sizeOf(u32);
if (pos + wordlen > buffer.len) return error.EndOfStream;
const string = std.mem.sliceAsBytes(buffer[pos..])[0 .. len - 1 :0];
pos += std.mem.alignForward(usize, len, @sizeOf(u32)) / @sizeOf(u32);
parent_pos.* = pos;
return string;
}
pub fn readArray(comptime T: type, buffer: []const u32, parent_pos: *usize) ![]const T {
var pos = parent_pos.*;
const byte_size = try readUInt(buffer, &pos);
const array = @as([*]const T, @ptrCast(buffer[pos..].ptr))[0 .. byte_size / @sizeOf(T)];
pos += byte_size / @sizeOf(u32);
parent_pos.* = pos;
return array;
}
pub fn deserializeArguments(comptime Signature: type, buffer: []const u32) !Signature {
if (Signature == void) return {};
var result: Signature = undefined;
var pos: usize = 0;
inline for (std.meta.fields(Signature)) |field| {
if (field.type == types.Fd) continue;
switch (@typeInfo(field.type)) {
.Int => |int_info| switch (int_info.signedness) {
.signed => @field(result, field.name) = try readInt(buffer, &pos),
.unsigned => @field(result, field.name) = try readUInt(buffer, &pos),
},
.Enum => |enum_info| if (@sizeOf(enum_info.tag_type) == @sizeOf(u32)) {
@field(result, field.name) = @enumFromInt(try readInt(buffer, &pos));
} else {
@compileError("Unsupported type " ++ @typeName(field.type));
},
.Pointer => |ptr| switch (ptr.size) {
.Slice => if (ptr.child == u8) {
@field(result, field.name) = try readString(buffer, &pos) orelse return error.UnexpectedNullString;
} else {
@field(result, field.name) = try readArray(ptr.child, buffer, &pos);
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
},
.Optional => |opt| switch (@typeInfo(opt.child)) {
.Pointer => |ptr| switch (ptr.size) {
.Slice => if (ptr.child == u8) {
@field(result, field.name) = try readString(buffer, &pos);
} else @compileError("Unsupported type " ++ @typeName(field.type)),
else => @compileError("Unsupported type " ++ @typeName(field.type)),
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
}
}
return result;
}
pub fn deserialize(comptime Union: type, header: Header, buffer: []const u32) !Union {
const op = try std.meta.intToEnum(std.meta.Tag(Union), header.size_and_opcode.opcode);
switch (op) {
inline else => |f| {
const Payload = std.meta.TagPayload(Union, f);
const payload = try deserializeArguments(Payload, buffer);
return @unionInit(Union, @tagName(f), payload);
},
}
}
/// Returns the length of the serialized message in `u32` words.
pub fn calculateSerializedWordLen(comptime Signature: type, message: Signature) usize {
var pos: usize = 0;
inline for (std.meta.fields(Signature)) |field| {
switch (@typeInfo(field.type)) {
.Int => pos += 1,
.Pointer => |ptr| switch (ptr.size) {
.Slice => {
// for size of string in bytes
pos += 1;
const str = @field(message, field.name);
pos += std.mem.alignForward(usize, str.len + 1, @sizeOf(u32)) / @sizeOf(u32);
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
}
}
return pos;
}
pub fn countFds(comptime Signature: type) usize {
if (Signature == void) return 0;
var count: usize = 0;
inline for (std.meta.fields(Signature)) |field| {
if (field.type == types.Fd) {
count += 1;
}
}
return count;
}
pub fn extractFds(comptime Signature: type, message: *const Signature) [countFds(Signature)]*const types.Fd {
if (Signature == void) return [_]*const types.Fd{};
var fds: [countFds(Signature)]*const types.Fd = undefined;
var i: usize = 0;
inline for (std.meta.fields(Signature)) |field| {
if (field.type == types.Fd) {
fds[i] = &@field(message, field.name);
i += 1;
}
}
return fds;
}
/// Message must live until the iovec array is written.
pub fn serializeArguments(comptime Signature: type, buffer: []u32, message: Signature) ![]u32 {
if (Signature == void) return buffer[0..0];
var pos: usize = 0;
inline for (std.meta.fields(Signature)) |field| {
if (field.type == types.Fd) continue;
switch (@typeInfo(field.type)) {
.Int => {
if (pos >= buffer.len) return error.OutOfMemory;
buffer[pos] = @bitCast(@field(message, field.name));
pos += 1;
},
.Enum => |enum_info| if (enum_info.tag_type == u32) {
if (pos >= buffer.len) return error.OutOfMemory;
buffer[pos] = @intFromEnum(@field(message, field.name));
pos += 1;
} else {
@compileError("Unsupported type " ++ @typeName(field.type));
},
.Pointer => |ptr| switch (ptr.size) {
.Slice => {
const str = @field(message, field.name);
if (str.len >= std.math.maxInt(u32)) return error.StringTooLong;
buffer[pos] = @intCast(str.len + 1);
pos += 1;
const str_len_aligned = std.mem.alignForward(usize, str.len + 1, @sizeOf(u32));
const padding_len = str_len_aligned - str.len;
if (str_len_aligned / @sizeOf(u32) >= buffer[pos..].len) return error.OutOfMemory;
const buffer_bytes = std.mem.sliceAsBytes(buffer[pos..]);
@memcpy(buffer_bytes[0..str.len], str);
@memset(buffer_bytes[str.len..][0..padding_len], 0);
pos += str_len_aligned / @sizeOf(u32);
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
},
.Optional => |opt| switch (@typeInfo(opt.child)) {
.Pointer => |ptr| switch (ptr.size) {
.Slice => if (ptr.child == u8) {
const str = @field(message, field.name);
if (str.len >= std.math.maxInt(u32)) return error.StringTooLong;
buffer[pos] = @intCast(str.len + 1);
pos += 1;
const str_len_aligned = std.mem.alignForward(usize, str.len + 1, @sizeOf(u32));
const padding_len = str_len_aligned - str.len;
if (str_len_aligned / @sizeOf(u32) >= buffer[pos..].len) return error.OutOfMemory;
const buffer_bytes = std.mem.sliceAsBytes(buffer[pos..]);
@memcpy(buffer_bytes[0..str.len], str);
@memset(buffer_bytes[str.len..][0..padding_len], 0);
pos += str_len_aligned / @sizeOf(u32);
} else @compileError("Unsupported type " ++ @typeName(field.type)),
else => @compileError("Unsupported type " ++ @typeName(field.type)),
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
},
else => @compileError("Unsupported type " ++ @typeName(field.type)),
}
}
return buffer[0..pos];
}
pub fn serialize(comptime Union: type, buffer: []u32, object_id: u32, message: Union) ![]u32 {
const header_wordlen = @sizeOf(Header) / @sizeOf(u32);
const header: *Header = @ptrCast(buffer[0..header_wordlen]);
header.object_id = object_id;
const tag = std.meta.activeTag(message);
header.size_and_opcode.opcode = @intFromEnum(tag);
const arguments = switch (message) {
inline else => |payload| try serializeArguments(@TypeOf(payload), buffer[header_wordlen..], payload),
};
header.size_and_opcode.size = @intCast(@sizeOf(Header) + arguments.len * @sizeOf(u32));
return buffer[0 .. header.size_and_opcode.size / @sizeOf(u32)];
}
test "deserialize Registry.Event.Global" {
const words = [_]u32{
1,
7,
@bitCast(@as([4]u8, "wl_s".*)),
@bitCast(@as([4]u8, "hm\x00\x00".*)),
3,
};
const parsed = try deserializeArguments(core.Registry.Event.Global, &words);
try std.testing.expectEqualDeep(core.Registry.Event.Global{
.name = 1,
.interface = "wl_shm",
.version = 3,
}, parsed);
}
test "deserialize Registry.Event" {
const header = Header{
.object_id = 123,
.size_and_opcode = .{
.size = 28,
.opcode = @intFromEnum(core.Registry.Event.Tag.global),
},
};
const words = [_]u32{
1,
7,
@bitCast(@as([4]u8, "wl_s".*)),
@bitCast(@as([4]u8, "hm\x00\x00".*)),
3,
};
const parsed = try deserialize(core.Registry.Event, header, &words);
try std.testing.expectEqualDeep(
core.Registry.Event{
.global = .{
.name = 1,
.interface = "wl_shm",
.version = 3,
},
},
parsed,
);
const header2 = Header{
.object_id = 1,
.size_and_opcode = .{
.size = 14 * @sizeOf(u32),
.opcode = @intFromEnum(core.Display.Event.Tag.@"error"),
},
};
const words2 = [_]u32{
1,
15,
40,
@bitCast(@as([4]u8, "inva".*)),
@bitCast(@as([4]u8, "lid ".*)),
@bitCast(@as([4]u8, "argu".*)),
@bitCast(@as([4]u8, "ment".*)),
@bitCast(@as([4]u8, "s to".*)),
@bitCast(@as([4]u8, " wl_".*)),
@bitCast(@as([4]u8, "regi".*)),
@bitCast(@as([4]u8, "stry".*)),
@bitCast(@as([4]u8, "@2.b".*)),
@bitCast(@as([4]u8, "ind\x00".*)),
};
const parsed2 = try deserialize(core.Display.Event, header2, &words2);
try std.testing.expectEqualDeep(
core.Display.Event{
.@"error" = .{
.object_id = 1,
.code = 15,
.message = "invalid arguments to wl_registry@2.bind",
},
},
parsed2,
);
}
test "serialize Registry.Event.Global" {
const message = core.Registry.Event.Global{
.name = 1,
.interface = "wl_shm",
.version = 3,
};
var buffer: [5]u32 = undefined;
const serialized = try serializeArguments(core.Registry.Event.Global, &buffer, message);
try std.testing.expectEqualSlices(
u32,
&[_]u32{
1,
7,
@bitCast(@as([4]u8, "wl_s".*)),
@bitCast(@as([4]u8, "hm\x00\x00".*)),
3,
},
serialized,
);
}
pub const IdPool = struct {
next_id: u32 = 2,
free_ids: std.BoundedArray(u32, 1024) = .{},
pub fn create(this: *@This()) u32 {
if (this.free_ids.popOrNull()) |id| {
return id;
}
defer this.next_id += 1;
return this.next_id;
}
pub fn destroy(this: *@This(), id: u32) void {
for (this.free_ids.slice()) |existing_id| {
if (existing_id == id) return;
}
this.free_ids.append(id) catch {};
}
};
fn cmsg(comptime T: type) type {
const padding_size = (@sizeOf(T) + @sizeOf(c_long) - 1) & ~(@as(usize, @sizeOf(c_long)) - 1);
return extern struct {
len: c_ulong = @sizeOf(@This()) - padding_size,
level: c_int,
type: c_int,
data: T,
_padding: [padding_size]u8 align(1) = [_]u8{0} ** padding_size,
};
}
pub const Conn = struct {
allocator: std.mem.Allocator,
send_buffer: []u32,
recv_buffer: []u32,
fd_queue: std.ArrayListUnmanaged(std.os.fd_t),
socket: std.net.Stream,
id_pool: IdPool,
pub fn init(alloc: std.mem.Allocator, display_path: []const u8) !Conn {
const send_buffer = try alloc.alloc(u32, 16);
const recv_buffer = try alloc.alloc(u32, 16);
return .{
.allocator = alloc,
.send_buffer = send_buffer,
.recv_buffer = recv_buffer,
.fd_queue = .{},
.socket = try std.net.connectUnixSocket(display_path),
.id_pool = .{},
};
}
pub fn deinit(conn: *Conn) void {
conn.allocator.free(conn.send_buffer);
conn.allocator.free(conn.recv_buffer);
conn.fd_queue.deinit(conn.allocator);
conn.socket.close();
}
pub fn send(conn: *Conn, comptime Signature: type, id: u32, message: Signature) !void {
const msg = while (true) {
const msg = serialize(
Signature,
conn.send_buffer,
id,
message,
) catch |e| switch (e) {
error.OutOfMemory => {
conn.send_buffer = try conn.allocator.realloc(conn.send_buffer, conn.send_buffer.len * 2);
continue;
},
else => return e,
};
break msg;
};
const msg_bytes = std.mem.sliceAsBytes(msg);
const msg_iov = [_]std.os.iovec_const{
.{
.iov_base = msg_bytes.ptr,
.iov_len = msg_bytes.len,
},
};
const fds = switch (message) {
inline else => |*payload| extractFds(@TypeOf(payload.*), payload),
};
var ctrl_msgs: [fds.len]cmsg(std.os.fd_t) = undefined;
for (fds, 0..) |fdp, i| {
std.debug.print("fd {}: {}\n", .{ i, fdp.* });
ctrl_msgs[i] = .{
.level = std.os.SOL.SOCKET,
.type = 0x01,
.data = @intFromEnum(fdp.*),
};
}
const socket_msg = std.os.msghdr_const{
.name = null,
.namelen = 0,
.iov = &msg_iov,
.iovlen = msg_iov.len,
.control = &ctrl_msgs,
.controllen = @intCast(@sizeOf(cmsg(std.os.fd_t)) * ctrl_msgs.len),
.flags = 0,
};
_ = try std.os.sendmsg(conn.socket.handle, &socket_msg, 0);
}
pub const Message = struct { Header, []const u32 };
pub fn recv(conn: *Conn) !Message {
// TODO: make this less messy
// Read header
@memset(conn.recv_buffer, 0);
var iov: [1]std.os.iovec = .{.{
.iov_base = std.mem.sliceAsBytes(conn.recv_buffer).ptr,
.iov_len = @sizeOf(Header),
}};
var control_msg: cmsg(std.os.fd_t) = undefined;
const control_bytes = std.mem.asBytes(&control_msg);
var socket_msg = std.os.msghdr{
.name = null,
.namelen = 0,
.iov = &iov,
.iovlen = iov.len,
.control = control_bytes.ptr,
.controllen = @intCast(control_bytes.len),
.flags = 0,
};
const size = std.os.linux.recvmsg(conn.socket.handle, &socket_msg, 0);
if (size < @sizeOf(Header)) return error.SocketClosed;
var header: Header = undefined;
@memcpy(std.mem.asBytes(&header), iov[0].iov_base[0..@sizeOf(Header)]);
if (socket_msg.controllen != 0) {
try conn.fd_queue.append(conn.allocator, control_msg.data);
}
// Read body
const body_size = (header.size_and_opcode.size - @sizeOf(Header)) / @sizeOf(u32);
iov[0] = .{
.iov_base = std.mem.sliceAsBytes(conn.recv_buffer).ptr,
.iov_len = body_size * @sizeOf(u32),
};
socket_msg = std.os.msghdr{
.name = null,
.namelen = 0,
.iov = &iov,
.iovlen = iov.len,
.control = control_bytes.ptr,
.controllen = @intCast(control_bytes.len),
.flags = 0,
};
const size2 = std.os.linux.recvmsg(conn.socket.handle, &socket_msg, 0);
const message = conn.recv_buffer[0 .. size2 / @sizeOf(u32)];
if (socket_msg.controllen != 0) {
try conn.fd_queue.append(conn.allocator, control_msg.data);
}
return .{ header, message };
}
};
pub fn Context(comptime T: []const type) type {
const Item = struct { version: u32, index: u32 };
const Pair = struct { []const u8, Item };
comptime var kvs_list: []const Pair = &[_]Pair{};
inline for (T, 0..) |t, i| {
kvs_list = kvs_list ++ &[_]Pair{.{ t.INTERFACE, .{ .version = t.VERSION, .index = i } }};
}
const stringToType = std.ComptimeStringMap(Item, kvs_list);
return struct {
conn: *Conn,
global_ids: [T.len]?u32,
pub fn init(conn: *Conn) !@This() {
var ctx = @This(){
.conn = conn,
.global_ids = [_]?u32{null} ** T.len,
};
try ctx.registerGlobals();
return ctx;
}
pub fn getGlobal(ctx: *@This(), comptime G: type) !G {
if (G == core.Display) return .{ .id = 1, .conn = ctx.conn };
const g = stringToType.get(G.INTERFACE) orelse return error.NoSuchGlobal;
const id = ctx.global_ids[g.index] orelse return error.NotAGlobal;
return G.init(ctx.conn, id);
}
pub fn registerGlobals(ctx: *@This()) !void {
// ![T.len]?u32 {
const registry_id = ctx.conn.id_pool.create();
{
var buffer: [5]u32 = undefined;
const message = try serialize(core.Display.Request, &buffer, 1, .{ .get_registry = .{ .registry = registry_id } });
try ctx.conn.socket.writeAll(std.mem.sliceAsBytes(message));
}
const registry_done_id = ctx.conn.id_pool.create();
{
var buffer: [5]u32 = undefined;
const message = try serialize(core.Display.Request, &buffer, 1, .{ .sync = .{ .callback = registry_done_id } });
try ctx.conn.socket.writeAll(std.mem.sliceAsBytes(message));
}
// var ids: [T.len]?u32 = [_]?u32{null} ** T.len;
var message_buffer = std.ArrayList(u32).init(ctx.conn.allocator);
defer message_buffer.deinit();
while (true) {
var header: Header = undefined;
const header_bytes_read = try ctx.conn.socket.readAll(std.mem.asBytes(&header));
if (header_bytes_read < @sizeOf(Header)) break;
try message_buffer.resize((header.size_and_opcode.size - @sizeOf(Header)) / @sizeOf(u32));
const bytes_read = try ctx.conn.socket.readAll(std.mem.sliceAsBytes(message_buffer.items));
message_buffer.shrinkRetainingCapacity(bytes_read / @sizeOf(u32));
if (header.object_id == registry_id) {
const event = try deserialize(core.Registry.Event, header, message_buffer.items);
switch (event) {
.global => |global| {
var buffer: [20]u32 = undefined;
if (stringToType.get(global.interface)) |item| {
if (global.version < item.version) {
// TODO: Add diagnostics API
return error.OutdatedCompositorProtocol;
}
const new_id = ctx.conn.id_pool.create();
ctx.global_ids[item.index] = new_id;
const message = try serialize(core.Registry.Request, &buffer, registry_id, .{ .bind = .{
.name = global.name,
.interface = global.interface,
.version = item.version,
.new_id = new_id,
} });
try ctx.conn.socket.writeAll(std.mem.sliceAsBytes(message));
}
},
.global_remove => {},
}
} else if (header.object_id == registry_done_id) {
break;
} else {
std.log.info("{} {x} \"{}\"", .{
header.object_id,
header.size_and_opcode.opcode,
std.zig.fmtEscapes(std.mem.sliceAsBytes(message_buffer.items)),
});
}
}
}
};
}

View File

@ -1,3 +1,6 @@
const core = @import("core.zig");
const Conn = @import("root.zig").Conn;
pub const WmBase = struct { pub const WmBase = struct {
pub const INTERFACE = "xdg_wm_base"; pub const INTERFACE = "xdg_wm_base";
pub const VERSION = 2; pub const VERSION = 2;
@ -40,6 +43,26 @@ pub const WmBase = struct {
invalid_positioner, invalid_positioner,
unresponsive, unresponsive,
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) WmBase {
return .{ .conn = conn, .id = id };
}
pub fn get_xdg_surface(base: WmBase, surface: core.Surface) !Surface {
const new_id = base.conn.id_pool.create();
try base.conn.send(
Request,
base.id,
.{ .get_xdg_surface = .{
.id = new_id,
.surface = surface.id,
} },
);
return Surface.init(base.conn, new_id);
}
}; };
pub const Surface = struct { pub const Surface = struct {
@ -77,6 +100,35 @@ pub const Surface = struct {
invalid_size, invalid_size,
defunct_role_object, defunct_role_object,
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Surface {
return .{ .conn = conn, .id = id };
}
pub fn get_toplevel(surface: Surface) !Toplevel {
const new_id = surface.conn.id_pool.create();
try surface.conn.send(
Request,
surface.id,
.{ .get_toplevel = .{
.id = new_id,
} },
);
return Toplevel.init(surface.conn, new_id);
}
pub fn ack_configure(surface: Surface, serial: u32) !void {
try surface.conn.send(
Request,
surface.id,
.{ .ack_configure = .{
.serial = serial,
} },
);
}
}; };
pub const Toplevel = struct { pub const Toplevel = struct {
@ -168,4 +220,11 @@ pub const Toplevel = struct {
defunct_role_object, defunct_role_object,
_, _,
}; };
conn: *Conn,
id: u32,
pub fn init(conn: *Conn, id: u32) Toplevel {
return .{ .conn = conn, .id = id };
}
}; };

117
src/zwp.zig Normal file
View File

@ -0,0 +1,117 @@
pub const TextInputManagerV3 = struct {
pub const INTERFACE = "zwp_text_input_manager_v3";
pub const VERSION = 1;
pub const Request = union(Request.Tag) {
destroy: void,
get_text_input: struct {
id: u32,
seat: u32,
},
pub const Tag = enum(u16) {
destroy,
get_text_input,
};
};
};
pub const TextInputV3 = struct {
pub const Request = union(Tag) {
destroy,
enable,
disable,
set_surrounding_text: struct {
text: []const u8,
cursor: i32,
anchor: i32,
},
set_text_change_cause: struct {
cause: ChangeCause,
},
set_content_type: struct {
hint: ContentHint,
purpose: ContentPurpose,
},
set_cursor_rectangle: struct {
x: i32,
y: i32,
width: i32,
height: i32,
},
commit,
pub const Tag = enum(u16) {
destroy,
enable,
disable,
set_surrounding_text,
set_text_change_cause,
set_content_type,
set_cursor_rectangle,
commit,
};
};
pub const Event = union(Tag) {
enter: struct { surface: u32 },
leave: struct { surface: u32 },
preedit_string: struct {
text: ?[]const u8,
cursor_begin: i32,
cursor_end: i32,
},
commit_string: struct {
text: []const u8,
},
delete_surrounding_text: struct {
before_length: usize,
after_length: usize,
},
done: struct {
serial: u32,
},
pub const Tag = enum {
enter,
leave,
preedit_string,
commit_string,
delete_surrounding_text,
done,
};
};
pub const ChangeCause = enum(u32) {
input_method,
other,
};
pub const ContentHint = enum(u32) {
none,
completion,
spellcheck,
auto_capitalization,
lowercase,
uppercase,
titlecase,
hidden_text,
sensitive_data,
latin,
multiline,
};
pub const ContentPurpose = enum(u32) {
normal,
alpha,
digits,
number,
phone,
url,
email,
name,
password,
pin,
date,
time,
datetime,
terminal,
};
};