Compare commits
10 Commits
e39f8e6ec8
...
adadca6827
Author | SHA1 | Date |
---|---|---|
Louis Pearson | adadca6827 | |
Louis Pearson | 18a8befacd | |
Louis Pearson | 76365322f2 | |
LeRoyce Pearson | 025ab53055 | |
Louis Pearson | ab10b7292c | |
Louis Pearson | e87f718f5d | |
Louis Pearson | dad4b23cf5 | |
Louis Pearson | fcd58ad7e6 | |
Louis Pearson | ccc90c80ca | |
Louis Pearson | b77c6302ac |
30
build.zig
30
build.zig
|
@ -4,18 +4,6 @@ pub fn build(b: *std.Build) void {
|
|||
const target = b.standardTargetOptions(.{});
|
||||
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(.{
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
|
@ -27,20 +15,14 @@ pub fn build(b: *std.Build) void {
|
|||
const test_step = b.step("test", "Run library tests");
|
||||
test_step.dependOn(&run_main_tests.step);
|
||||
|
||||
const client_connect_raw_exe = b.addExecutable(.{
|
||||
.name = "00_client_connect",
|
||||
.root_source_file = .{ .path = "examples/00_client_connect.zig" },
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "pinephone-test",
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
b.installArtifact(client_connect_raw_exe);
|
||||
|
||||
const client_connect_exe = b.addExecutable(.{
|
||||
.name = "01_client_connect",
|
||||
.root_source_file = .{ .path = "examples/01_client_connect.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
client_connect_exe.root_module.addImport("wayland", module);
|
||||
b.installArtifact(client_connect_exe);
|
||||
exe.addIncludePath(.{ .path = "deps/font8x8/" });
|
||||
|
||||
b.installArtifact(exe);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
;
|
||||
|
||||
|
|
@ -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"
|
|
@ -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
|
||||
};
|
|
@ -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)
|
||||
};
|
|
@ -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)
|
||||
};
|
|
@ -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
|
||||
};
|
|
@ -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)
|
||||
};
|
|
@ -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)
|
||||
};
|
|
@ -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
|
||||
};
|
|
@ -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"
|
|
@ -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)
|
||||
};
|
|
@ -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)
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
193
src/core.zig
193
src/core.zig
|
@ -1,4 +1,5 @@
|
|||
const types = @import("types.zig");
|
||||
const Conn = @import("root.zig").Conn;
|
||||
|
||||
pub const Display = struct {
|
||||
pub const Request = union(enum) {
|
||||
|
@ -29,9 +30,44 @@ pub const Display = struct {
|
|||
no_memory,
|
||||
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 INTERFACE = "wl_registry";
|
||||
pub const VERSION = 1;
|
||||
|
||||
pub const Event = union(enum) {
|
||||
global: struct {
|
||||
name: u32,
|
||||
|
@ -51,6 +87,13 @@ pub const Registry = struct {
|
|||
new_id: u32,
|
||||
},
|
||||
};
|
||||
|
||||
conn: *Conn,
|
||||
id: u32,
|
||||
|
||||
pub fn init(conn: *Conn, id: u32) Registry {
|
||||
return .{ .conn = conn, .id = id };
|
||||
}
|
||||
};
|
||||
|
||||
pub const Compositor = struct {
|
||||
|
@ -65,6 +108,33 @@ pub const Compositor = struct {
|
|||
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 {
|
||||
|
@ -82,6 +152,30 @@ pub const ShmPool = struct {
|
|||
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 {
|
||||
|
@ -114,6 +208,23 @@ pub const Shm = struct {
|
|||
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 {
|
||||
|
@ -165,6 +276,21 @@ pub const Surface = struct {
|
|||
invalid_offset,
|
||||
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 {
|
||||
|
@ -211,6 +337,13 @@ pub const Seat = struct {
|
|||
};
|
||||
|
||||
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 {
|
||||
|
@ -298,6 +431,13 @@ pub const Pointer = struct {
|
|||
identical,
|
||||
inverted,
|
||||
};
|
||||
|
||||
conn: *Conn,
|
||||
id: u32,
|
||||
|
||||
pub fn init(conn: *Conn, id: u32) Seat {
|
||||
return .{ .conn = conn, .id = id };
|
||||
}
|
||||
};
|
||||
|
||||
pub const Keyboard = struct {
|
||||
|
@ -348,4 +488,57 @@ pub const Keyboard = struct {
|
|||
released,
|
||||
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 };
|
||||
}
|
||||
};
|
||||
|
|
671
src/main.zig
671
src/main.zig
|
@ -1,591 +1,132 @@
|
|||
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 types = @import("./types.zig");
|
||||
const wayland = @import("root.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);
|
||||
const Conn = wayland.Conn;
|
||||
const Context = wayland.Context(&.{
|
||||
// Core protocol
|
||||
wayland.core.Registry,
|
||||
wayland.core.Compositor,
|
||||
// wayland.core.ShmPool,
|
||||
wayland.core.Shm,
|
||||
// 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 {
|
||||
object_id: u32 align(1),
|
||||
size_and_opcode: SizeAndOpcode align(1),
|
||||
// XDG Decoration
|
||||
// wayland.zxdg.DecorationManagerV1,
|
||||
// wayland.zxdg.TopLevelDecorationV1,
|
||||
});
|
||||
|
||||
pub const SizeAndOpcode = packed struct(u32) {
|
||||
opcode: u16,
|
||||
size: u16,
|
||||
};
|
||||
const font8x8 = @cImport({
|
||||
@cInclude("font8x8.h");
|
||||
});
|
||||
|
||||
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" {
|
||||
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);
|
||||
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),
|
||||
fn toPixel(color: u24) Pixel {
|
||||
return .{
|
||||
@intCast(color & 0xFF),
|
||||
@intCast(color >> 8 & 0xFF),
|
||||
@intCast(color >> 16 & 0xFF),
|
||||
0xFF,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
pub fn main() !void {
|
||||
var general_allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = general_allocator.deinit();
|
||||
const gpa = general_allocator.allocator();
|
||||
|
||||
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 display_path = try wayland.getDisplayPath(gpa);
|
||||
defer gpa.free(display_path);
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
var conn = try Conn.init(gpa, display_path);
|
||||
defer conn.deinit();
|
||||
|
||||
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);
|
||||
var ctx = try Context.init(&conn);
|
||||
|
||||
try std.testing.expectEqualSlices(
|
||||
u32,
|
||||
&[_]u32{
|
||||
1,
|
||||
7,
|
||||
@bitCast(@as([4]u8, "wl_s".*)),
|
||||
@bitCast(@as([4]u8, "hm\x00\x00".*)),
|
||||
3,
|
||||
},
|
||||
serialized,
|
||||
);
|
||||
}
|
||||
const compositor = try ctx.getGlobal(wayland.core.Compositor);
|
||||
const surface = try compositor.create_surface();
|
||||
|
||||
pub const IdPool = struct {
|
||||
next_id: u32 = 2,
|
||||
free_ids: std.BoundedArray(u32, 1024) = .{},
|
||||
const xdg_wm_base = try ctx.getGlobal(wayland.xdg.WmBase);
|
||||
const xdg_surface = try xdg_wm_base.get_xdg_surface(surface);
|
||||
const xdg_toplevel = try xdg_surface.get_toplevel();
|
||||
|
||||
pub fn create(this: *@This()) u32 {
|
||||
if (this.free_ids.popOrNull()) |id| {
|
||||
return id;
|
||||
}
|
||||
try surface.commit();
|
||||
|
||||
defer this.next_id += 1;
|
||||
return this.next_id;
|
||||
}
|
||||
const display = try ctx.getGlobal(wayland.core.Display);
|
||||
const registry_done = try display.sync();
|
||||
|
||||
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 {};
|
||||
}
|
||||
};
|
||||
const shm = try ctx.getGlobal(wayland.core.Shm);
|
||||
const seat = try ctx.getGlobal(wayland.core.Seat);
|
||||
|
||||
pub fn registerGlobals(alloc: std.mem.Allocator, id_pool: *IdPool, socket: std.net.Stream, comptime T: []const type) ![T.len]?u32 {
|
||||
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 map = std.ComptimeStringMap(Item, kvs_list);
|
||||
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();
|
||||
|
||||
const registry_id = id_pool.create();
|
||||
{
|
||||
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);
|
||||
if (header.object_id == xdg_surface.id) {
|
||||
const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body);
|
||||
switch (event) {
|
||||
.global => |global| {
|
||||
var buffer: [20]u32 = undefined;
|
||||
if (map.get(global.interface)) |item| {
|
||||
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));
|
||||
}
|
||||
.configure => |conf| {
|
||||
try xdg_surface.ack_configure(conf.serial);
|
||||
surface_configured = true;
|
||||
},
|
||||
.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)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
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;
|
||||
} 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) {
|
||||
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 == 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;
|
||||
},
|
||||
};
|
||||
|
||||
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.*),
|
||||
};
|
||||
.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});
|
||||
conn.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)) });
|
||||
}
|
||||
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 };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
59
src/xdg.zig
59
src/xdg.zig
|
@ -1,3 +1,6 @@
|
|||
const core = @import("core.zig");
|
||||
const Conn = @import("root.zig").Conn;
|
||||
|
||||
pub const WmBase = struct {
|
||||
pub const INTERFACE = "xdg_wm_base";
|
||||
pub const VERSION = 2;
|
||||
|
@ -40,6 +43,26 @@ pub const WmBase = struct {
|
|||
invalid_positioner,
|
||||
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 {
|
||||
|
@ -77,6 +100,35 @@ pub const Surface = struct {
|
|||
invalid_size,
|
||||
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 {
|
||||
|
@ -168,4 +220,11 @@ pub const Toplevel = struct {
|
|||
defunct_role_object,
|
||||
_,
|
||||
};
|
||||
|
||||
conn: *Conn,
|
||||
id: u32,
|
||||
|
||||
pub fn init(conn: *Conn, id: u32) Toplevel {
|
||||
return .{ .conn = conn, .id = id };
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue