Commit2e955d72Recorded20 Sep 2026Repositorysigil-app

Release 0.10.1: the rest of the keyboard, and key-code

Message

The key symbol table gains f1..f25, page-up/page-down, home/end, insert/delete, the right modifiers, caps-lock, the punctuation row and the keypad; every 0.10.0 name is unchanged. (key-code key) answers the GLFW code a symbol names or #f without a window, so the table is testable: test/test-keys.sgl (44 checks) through new sigil-test dev-dependencies.

Changed
 CHANGELOG.md          | 17 +++++++++++++++++
 package.sgl           |  8 +++++++-
 sigil.lock            | 27 +++++++++++++++++++++++++++
 src/c/desktop.c       | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/sigil/desktop.sgl |  2 +-
 test/test-keys.sgl    | 36 ++++++++++++++++++++++++++++++++++++
 6 files changed, 151 insertions(+), 5 deletions(-)
Diff
CHANGELOG.mdmodified
@@ -7,6 +7,23 @@ All notable changes to this project will be documented in this file.
7
The format is based on [Keep a Changelog](https://keepachangelog.com/),
8
and this project adheres to [Semantic Versioning](https://semver.org/).
9
+10
## [0.10.1] - 2026-09-20
+11
+12
### Added
+13
+14
- **The rest of the keyboard.** `key-down?`, `key-pressed?` and
+15
`key-released?` now know `f1`..`f25`, `page-up`/`page-down`, `home`/`end`,
+16
`insert`/`delete`, `right-shift`/`right-ctrl`/`right-alt`, `caps-lock`,
+17
the punctuation row (`comma` `period` `slash` `semicolon` `apostrophe`
+18
`minus` `equal` `left-bracket` `right-bracket` `backslash` `grave`, or the
+19
character itself as a symbol) and the keypad (`kp-0`..`kp-9`, `kp-enter`,
+20
`kp-add`, `kp-subtract`). The 0.10.0 names are unchanged. Wanted by
+21
Crash The Stack's tracker, whose FastTracker key model needs the
+22
function keys, paging and the punctuation row natively.
+23
- `(key-code key)`: the GLFW key code a symbol names, or `#f` for a name
+24
that is no key; needs no window, so a program can check a binding
+25
before it is used and the symbol table is testable (`test/test-keys.sgl`).
+26
27
## [0.10.0] - 2026-09-20
28
29
The package is now **sigil-desktop**, and the window is GLFW instead of
package.sglmodified
@@ -124,7 +124,7 @@
124
125
(package
126
name: "sigil-desktop"
−127
version: "0.10.0"
+127
version: "0.10.1"
128
sigil: "0.20"
129
description: "Desktop host layer for Sigil (window, GL context, input, frame clock) over GLFW"
130
;; The repository was sigil/sigil-app on Codeberg through 0.9.x and is
@@ -162,6 +162,12 @@
162
dependencies: (list
163
(from-git url: "codeberg:sigil/sigil" package: "sigil-stdlib" version: ">=0.20"))
164
+165
;; The key table test (test/test-keys.sgl) needs a harness that links the
+166
;; native module; built only when tests are built.
+167
dev-dependencies: (list
+168
(from-git url: "codeberg:sigil/sigil" package: "sigil-test" version: ">=0.20")
+169
(from-git url: "codeberg:sigil/sigil" package: "sigil-test-runner" version: ">=0.20"))
+170
171
libraries: (list
172
(library
173
name: 'sigil-desktop
sigil.lockmodified
@@ -12,4 +12,31 @@
12
sha: "8643e9abf19038c5f4961d41748427dbd623cb15"
13
package-selector: "sigil-stdlib"
14
version: "0.21.0")
+15
(package name: "sigil-test"
+16
url: "codeberg:sigil/sigil"
+17
ref: ">=0.20"
+18
sha: "5b494e16de01635a1aa3dbcfac5abffd1acdc2ed"
+19
package-selector: "sigil-test"
+20
version: "0.22.2")
+21
(package name: "sigil-test-runner"
+22
url: "codeberg:sigil/sigil"
+23
ref: ">=0.20"
+24
sha: "5b494e16de01635a1aa3dbcfac5abffd1acdc2ed"
+25
package-selector: "sigil-test-runner"
+26
version: "0.22.2")
+27
(package name: "sigil-json"
+28
url: "codeberg:sigil/sigil-json"
+29
ref: "^0.16.0"
+30
sha: "55c9e74712b5f79b45d41c65cfe0aef2c8f7e155"
+31
version: "0.16.0")
+32
(package name: "sigil-crypto"
+33
url: "codeberg:sigil/sigil-crypto"
+34
ref: "^0.16.0"
+35
sha: "71362137db0596e0695791b32c4f6e8faf11b39e"
+36
version: "0.16.6")
+37
(package name: "sigil-sqlite"
+38
url: "codeberg:sigil/sigil-sqlite"
+39
ref: "^0.16.0"
+40
sha: "569ef1063dda8f5989968644af31fc09c64b4bfe"
+41
version: "0.16.3")
42
)
src/c/desktop.cmodified
@@ -77,9 +77,13 @@ int sigil_desktop_framebuffer_height(void)
77
78
/*
79
* Convert Scheme key symbol to a GLFW key code (GLFW_KEY_UNKNOWN when the
−80
* symbol names no key). The symbol set is the one sokol_app had: letters,
−81
* digits, space, return/enter, escape/esc, tab, backspace, the arrows,
−82
* shift, ctrl/control, alt.
+80
* symbol names no key). Letters, digits, space, return/enter, escape/esc,
+81
* tab, backspace, the arrows, shift/ctrl/alt (left), and since 0.10.1 f1..f25,
+82
* page-up/page-down, home/end, insert/delete, right-shift/ctrl/alt,
+83
* caps-lock, the punctuation row (comma period slash semicolon apostrophe
+84
* minus equal left-bracket right-bracket backslash grave, or the character
+85
* itself as a symbol) and the keypad (kp-0..kp-9, kp-enter, kp-add,
+86
* kp-subtract).
87
*/
88
int sigil_desktop_key_code_from_symbol(SigilVM *vm, Value sym)
89
{
@@ -117,6 +121,44 @@ int sigil_desktop_key_code_from_symbol(SigilVM *vm, Value sym)
121
if (strcmp(name, "ctrl") == 0 || strcmp(name, "control") == 0) return GLFW_KEY_LEFT_CONTROL;
122
if (strcmp(name, "alt") == 0) return GLFW_KEY_LEFT_ALT;
123
+124
/* 0.10.1: the rest of a keyboard an editor needs (a tracker's FT2
+125
* table: function keys, paging, the punctuation row, the right
+126
* modifiers). The state arrays already span every GLFW code; only
+127
* these names were missing. */
+128
if (s->length >= 2 && s->length <= 3 && name[0] == 'f' && name[1] >= '1' && name[1] <= '9'
+129
&& (s->length == 2 || (name[2] >= '0' && name[2] <= '9'))) {
+130
int n = (name[1] - '0');
+131
if (s->length == 3) n = n * 10 + (name[2] - '0');
+132
if (n >= 1 && n <= 25) return GLFW_KEY_F1 + (n - 1);
+133
}
+134
if (strcmp(name, "page-up") == 0 || strcmp(name, "pageup") == 0) return GLFW_KEY_PAGE_UP;
+135
if (strcmp(name, "page-down") == 0 || strcmp(name, "pagedown") == 0) return GLFW_KEY_PAGE_DOWN;
+136
if (strcmp(name, "home") == 0) return GLFW_KEY_HOME;
+137
if (strcmp(name, "end") == 0) return GLFW_KEY_END;
+138
if (strcmp(name, "insert") == 0) return GLFW_KEY_INSERT;
+139
if (strcmp(name, "delete") == 0) return GLFW_KEY_DELETE;
+140
if (strcmp(name, "right-shift") == 0 || strcmp(name, "rshift") == 0) return GLFW_KEY_RIGHT_SHIFT;
+141
if (strcmp(name, "right-ctrl") == 0 || strcmp(name, "rctrl") == 0) return GLFW_KEY_RIGHT_CONTROL;
+142
if (strcmp(name, "right-alt") == 0 || strcmp(name, "ralt") == 0) return GLFW_KEY_RIGHT_ALT;
+143
if (strcmp(name, "caps-lock") == 0) return GLFW_KEY_CAPS_LOCK;
+144
if (strcmp(name, "comma") == 0 || strcmp(name, ",") == 0) return GLFW_KEY_COMMA;
+145
if (strcmp(name, "period") == 0 || strcmp(name, ".") == 0) return GLFW_KEY_PERIOD;
+146
if (strcmp(name, "slash") == 0 || strcmp(name, "/") == 0) return GLFW_KEY_SLASH;
+147
if (strcmp(name, "semicolon") == 0 || strcmp(name, ";") == 0) return GLFW_KEY_SEMICOLON;
+148
if (strcmp(name, "apostrophe") == 0 || strcmp(name, "'") == 0) return GLFW_KEY_APOSTROPHE;
+149
if (strcmp(name, "minus") == 0 || strcmp(name, "-") == 0) return GLFW_KEY_MINUS;
+150
if (strcmp(name, "equal") == 0 || strcmp(name, "=") == 0) return GLFW_KEY_EQUAL;
+151
if (strcmp(name, "left-bracket") == 0 || strcmp(name, "[") == 0) return GLFW_KEY_LEFT_BRACKET;
+152
if (strcmp(name, "right-bracket") == 0 || strcmp(name, "]") == 0) return GLFW_KEY_RIGHT_BRACKET;
+153
if (strcmp(name, "backslash") == 0 || strcmp(name, "\\") == 0) return GLFW_KEY_BACKSLASH;
+154
if (strcmp(name, "grave") == 0 || strcmp(name, "backquote") == 0 || strcmp(name, "`") == 0) return GLFW_KEY_GRAVE_ACCENT;
+155
if (strcmp(name, "kp-enter") == 0) return GLFW_KEY_KP_ENTER;
+156
if (strcmp(name, "kp-add") == 0) return GLFW_KEY_KP_ADD;
+157
if (strcmp(name, "kp-subtract") == 0) return GLFW_KEY_KP_SUBTRACT;
+158
if (s->length == 4 && strncmp(name, "kp-", 3) == 0 && name[3] >= '0' && name[3] <= '9') {
+159
return GLFW_KEY_KP_0 + (name[3] - '0');
+160
}
+161
162
return GLFW_KEY_UNKNOWN;
163
}
164
@@ -613,6 +655,21 @@ static Value native_key_released(SigilVM *vm, int argc, Value *args)
655
return g_desktop->keys_released[key] ? SIGIL_TRUE : SIGIL_FALSE;
656
}
657
+658
/*
+659
* (key-code key) -> integer | #f
+660
*
+661
* The GLFW key code a key symbol names, or #f when the symbol names no
+662
* key. Needs no window: the one way to test the symbol table, and how a
+663
* program can ask whether a binding will ever fire before it is used.
+664
*/
+665
static Value native_key_code(SigilVM *vm, int argc, Value *args)
+666
{
+667
if (argc < 1) return SIGIL_FALSE;
+668
int key = sigil_desktop_key_code_from_symbol(vm, args[0]);
+669
if (key == GLFW_KEY_UNKNOWN) return SIGIL_FALSE;
+670
return sigil_fixnum(key);
+671
}
+672
673
/*
674
* (quit-requested?) -> boolean
675
*/
@@ -681,6 +738,8 @@ void sigil__init_sigil_desktop_module(SigilVM *vm)
738
SIGIL_ARITY_EXACT(1), "Was key just pressed?");
739
sigil_module_register_native(vm, "key-released?", native_key_released,
740
SIGIL_ARITY_EXACT(1), "Was key just released?");
+741
sigil_module_register_native(vm, "key-code", native_key_code,
+742
SIGIL_ARITY_EXACT(1), "GLFW key code of a key symbol, or #f");
743
744
/* Quit handling */
745
sigil_module_register_native(vm, "quit-requested?", native_quit_requested,
@@ -702,6 +761,7 @@ void sigil__init_sigil_desktop_module(SigilVM *vm)
761
sigil_module_export(vm, "key-down?");
762
sigil_module_export(vm, "key-pressed?");
763
sigil_module_export(vm, "key-released?");
+764
sigil_module_export(vm, "key-code");
765
sigil_module_export(vm, "quit-requested?");
766
sigil_module_export(vm, "request-quit");
767
src/sigil/desktop.sglmodified
@@ -18,7 +18,7 @@
18
frame-time time-elapsed
19
mouse-x mouse-y
20
mouse-down? mouse-pressed? mouse-released?
−21
key-down? key-pressed? key-released?
+21
key-down? key-pressed? key-released? key-code
22
quit-requested? request-quit
23
24
;; Cooperative frame scheduling
test/test-keys.sgladded
@@ -0,0 +1,36 @@
+1
;;; The key symbol table: every name a program can bind resolves to a GLFW
+2
;;; code without a window, and a name that is no key answers #f. The
+3
;;; codes are GLFW's (glfw3.h): F1 290, PAGE_UP 266, HOME 268, INSERT 260,
+4
;;; DELETE 261, RIGHT_SHIFT 344, COMMA 44, PERIOD 46, GRAVE_ACCENT 96,
+5
;;; KP_0 320, KP_ENTER 335.
+6
+7
(import (sigil test)
+8
(sigil desktop))
+9
+10
(define (check group pairs)
+11
(test-group group
+12
(for-each (lambda (p)
+13
(test (car p)
+14
(assert-equal (cdr p) (key-code (string->symbol (car p))))))
+15
pairs)))
+16
+17
(check "key-code: the 0.10.0 names"
+18
'(("a" . 65) ("0" . 48) ("space" . 32) ("return" . 257) ("escape" . 256)
+19
("left" . 263) ("shift" . 340) ("ctrl" . 341)))
+20
+21
(check "key-code: the 0.10.1 names"
+22
'(("f1" . 290) ("f7" . 296) ("f12" . 301)
+23
("page-up" . 266) ("page-down" . 267) ("home" . 268) ("end" . 269)
+24
("insert" . 260) ("delete" . 261)
+25
("right-shift" . 344) ("right-ctrl" . 345) ("right-alt" . 346)
+26
("comma" . 44) ("," . 44) ("period" . 46) ("." . 46) ("slash" . 47)
+27
("semicolon" . 59) ("apostrophe" . 39) ("minus" . 45) ("equal" . 61)
+28
("left-bracket" . 91) ("right-bracket" . 93) ("backslash" . 92)
+29
("grave" . 96) ("`" . 96)
+30
("kp-0" . 320) ("kp-9" . 329) ("kp-enter" . 335)))
+31
+32
(check "key-code: no key"
+33
'(("bogus" . #f) ("f0" . #f) ("f26" . #f) ("f1x" . #f) ("kp-x" . #f) ("page" . #f)))
+34
+35
(test-group "key-code: not a symbol"
+36
(test "a string is no key" (assert-equal #f (key-code "a"))))