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 ofpackage.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-desktopsigil.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 schedulingtest/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"))))