-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHBOS_HAX_API.html
More file actions
1071 lines (971 loc) · 65.3 KB
/
Copy pathHBOS_HAX_API.html
File metadata and controls
1071 lines (971 loc) · 65.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>HBOS 应用开发手册</title>
<meta name="author" content="HBOS / HIVE">
<meta name="subject" content="HAX 与 HIVE Toolkit API 参考">
<meta name="description" content="HBOS HAX 应用和 HIVE Toolkit API 开发参考">
<style>
@page { size: A4; margin: 2cm 2.2cm; }
* { box-sizing: border-box; }
body { font-family: "Noto Sans CJK SC", "WenQuanYi Zen Hei", "Source Han Sans SC", sans-serif;
color: #1a1a1a; font-size: 10.5pt; line-height: 1.6; }
h1 { font-size: 23pt; font-weight: 700; color: #111; border: none; margin: 0 0 2px; }
h1 .en { display:block; font-size: 13pt; font-weight: 700; color: #0aa6dd; letter-spacing:1px; margin-top:2px; }
h2 { font-size: 14.5pt; font-weight: 700; color: #0c6fa6; margin: 20px 0 6px; border-bottom: 1.5px solid #d0e8f5; padding-bottom: 2px; }
h3 { font-size: 11.5pt; font-weight: 700; color: #1a2a3a; margin: 14px 0 4px;
font-family: ui-monospace, "DejaVu Sans Mono", "Noto Sans Mono", "Noto Sans CJK SC", monospace; }
h4 { font-size: 10.5pt; font-weight: 700; color: #333; margin: 10px 0 3px; }
.chapter { page-break-before: always; }
p { margin: 5px 0 8px; }
code, .mono { font-family: ui-monospace, "DejaVu Sans Mono", "Noto Sans Mono", "Noto Sans CJK SC", monospace; font-size: 9.5pt;
background:#eef2f6; border-radius:2px; padding:0 3px; }
pre { background: #f3f6f9; border-left: 3px solid #14a6e0; padding: 10px 13px; margin: 8px 0 12px;
font-family: ui-monospace, "DejaVu Sans Mono", "Noto Sans Mono", "Noto Sans CJK SC", monospace; font-size: 9pt; line-height:1.5;
white-space: pre-wrap; border-radius: 3px; }
table { border-collapse: collapse; width: 100%; margin: 8px 0 12px; font-size: 9.5pt; }
th { background: #c5d4e0; text-align: left; padding: 5px 9px; font-weight: 700; }
td { border-bottom: 1px solid #dde3e8; padding: 5px 9px; vertical-align: top; }
tr:nth-child(even) td { background: #f6f8fa; }
.toc div { margin: 3px 0; }
.toc .c { font-weight: 700; color: #0c6fa6; margin-top: 12px; font-size: 11pt; }
.toc .s { margin-left: 18px; color:#333; font-size: 10pt; }
.toc .ss { margin-left: 34px; color:#555; font-size: 9.5pt; }
.note { background:#eef7fc; border:1px solid #bfe3f4; padding:8px 11px; border-radius:4px; margin:9px 0 12px; font-size:9.5pt; }
.warn { background:#fff8e6; border:1px solid #f5c842; padding:8px 11px; border-radius:4px; margin:9px 0 12px; font-size:9.5pt; }
.kbd { background:#222; color:#fff; border-radius:3px; padding:1px 6px; font-size:9pt; font-family:ui-monospace,"Noto Sans Mono","Noto Sans CJK SC",monospace; }
.small { color:#666; font-size: 9pt; }
.tag { display:inline-block; background:#14a6e0; color:#fff; border-radius:3px; padding:0 6px; font-size:8.5pt; font-weight:700; }
ul { margin: 5px 0 8px; padding-left: 1.6em; }
li { margin: 3px 0; }
.sig { font-family: ui-monospace,"DejaVu Sans Mono","Noto Sans Mono","Noto Sans CJK SC",monospace; font-size:9.5pt;
background:#eef2f6; border-radius:3px; padding:3px 8px; display:block; margin:4px 0; }
.screenshot { display:block; max-width:92%; margin:8px auto 12px;
border:1px solid #b8c6d2; border-radius:4px; }
/* PDF/打印:紧凑版式 + 少量彩色点缀,不印大面积底色,兼顾省墨。 */
@media print {
@page { size: A4; margin: 12mm 14mm 13mm; }
* { print-color-adjust: economy; -webkit-print-color-adjust: economy;
box-shadow: none !important; text-shadow: none !important; }
body { color:#111; font-size:8.8pt; line-height:1.34; }
body * { color:#111 !important; background-color:transparent !important;
border-color:#777 !important; }
/* 彩色方案:多色标题/代码/提示框/表头,浅色底点缀,兼顾省墨。 */
h1 .en { color:#0a6fa0 !important; }
h1 { color:#0a2a4a !important; }
h2 { color:#0b5d8f !important; border-bottom-color:#4a9bc4 !important; }
h3 { color:#0f7a4d !important; } /* 墨绿 */
h4 { color:#8a3fa0 !important; } /* 紫 */
.toc .c { color:#0b5d8f !important; }
.toc .s { color:#0f7a4d !important; }
.toc .ss { color:#8a3fa0 !important; }
code, .mono { color:#a0322e !important; } /* 经典代码红 */
th { color:#ffffff !important; background-color:#3b7a9e !important;
border-color:#2f6585 !important; }
tr:nth-child(even) td { background-color:#eef4f9 !important; }
.tag { color:#0a6fa0 !important; background-color:#e3f2fb !important;
border-color:#4a9bc4 !important; }
.kbd { color:#ffffff !important; background-color:#3a3f4a !important;
border-color:#3a3f4a !important; }
.note { color:#14394e !important; background-color:#e3f2fb !important;
border-color:#7fb8d8 !important; }
.warn { color:#4a3408 !important; background-color:#fdf3d7 !important;
border-color:#d8a23c !important; }
pre { color:#17324a !important; background-color:#f2f7fb !important;
border-left-color:#4a9bc4 !important; }
.sig { color:#17324a !important; background-color:#e8eef4 !important; }
.small { color:#4a5668 !important; }
.screenshot { border-color:#4a9bc4 !important; }
body > div:first-child > div[style*="color:#0aa6dd"] {
color:#0a6fa0 !important; }
h1 { font-size:18pt; margin:0 0 1px; }
h1 .en { font-size:9.5pt; letter-spacing:.5px; margin-top:0; }
h2 { font-size:11.5pt; margin:10px 0 3px; padding-bottom:1px;
border-bottom-width:1px; }
h3 { font-size:9.3pt; margin:7px 0 2px; }
h4 { font-size:8.8pt; margin:6px 0 2px; }
p { margin:2px 0 4px; }
ul { margin:2px 0 4px; padding-left:1.4em; }
li { margin:1px 0; }
code, .mono { font-size:7.7pt; padding:0 1px; }
pre { font-size:7.25pt; line-height:1.27; padding:5px 7px;
margin:4px 0 6px; border-left-width:1px; }
table { font-size:7.65pt; margin:4px auto 6px; }
th, td { padding:2px 4px; border:1px solid #aaa; }
.toc div { margin:1px 0; }
.toc .c { margin-top:5px; font-size:9pt; }
.toc .s { margin-left:12px; font-size:8.3pt; }
.toc .ss { margin-left:24px; font-size:7.7pt; }
.note, .warn { font-size:7.7pt; padding:4px 6px; margin:4px 0 6px;
border:1px solid #888; border-radius:0; }
.tag, .kbd { color:#111 !important; border:1px solid #777;
padding:0 3px; font-size:7.3pt; }
.small { font-size:7.4pt; }
.sig { font-size:7.6pt; padding:2px 4px; margin:2px 0; }
tr, pre, .note, .warn { break-inside:avoid; }
h1, h2, h3, h4 { break-after:avoid; }
/* 仅封面强制分页;正文各章连续排版,减少半空页面。 */
.chapter { page-break-before:auto !important; break-before:auto !important; }
.chapter > h1 { page-break-before:auto !important; break-before:auto !important;
margin-top:12px; }
/* 封面保留层级但压缩空白;底部恢复品牌蓝实心色块(封面整页深蓝)。 */
body > div:first-child > div[style*="height:5.5cm"] { height:3.2cm !important; }
body > div:first-child > div[style*="height:4.5cm"] { height:2.2cm !important; }
body > div:first-child > div[style*="font-size:40pt"] { font-size:29pt !important; }
body > div:first-child > div[style*="background:#14a6e0"] {
background-color:#14a6e0 !important; color:#ffffff !important;
border:none !important; padding:12px 0 !important;
}
body > div:first-child > div[style*="background:#14a6e0"] * {
color:#ffffff !important; }
}
</style>
</head>
<body>
<!-- ── 封面 ── -->
<div style="page-break-after: always;">
<div style="height:5.5cm;"></div>
<div style="font-size:13pt; font-weight:700; color:#0aa6dd; letter-spacing:2px;">HBOS · 裸机内核操作系统</div>
<div style="font-size:40pt; font-weight:700; color:#111; margin:6px 0 4px;">HBOS 应用开发手册</div>
<div style="font-size:16pt; font-weight:600; color:#333;">HAX Application Development Guide · <span style="color:#0aa6dd;font-weight:700;">.hax / C 版</span></div>
<div style="font-size:10pt; color:#555; margin-top:8px;">本手册随 HBOS 内核源码发布,供第三方应用开发者参考。</div>
<div style="height:4.5cm;"></div>
<div style="background:#14a6e0; color:#fff; padding:22px 18px; font-size:11pt; font-weight:600;">
HBOS APPLICATION DEVELOPER GUIDE<br>
<span style="font-size:9.5pt; font-weight:400;">2026/08/19 · VERSION 2.0 · HBOS v0.1-beta5-pre6 · HIVE 0.1-beta5-gui.4(Toolkit API 1.4 / TUI Kit 1.0)</span>
</div>
</div>
<!-- ── 目录 ── -->
<h1 style="page-break-before:always;">目录<span class="en">CONTENTS</span></h1>
<div class="toc">
<div class="c">第 1 章 — 概述、格式与类型系统</div>
<div class="s">1.1 关于本手册</div>
<div class="s">1.2 什么是 HAX 应用(.hax)</div>
<div class="ss">1.2.1 ELF64 结构</div>
<div class="ss">1.2.2 .haxmeta 元数据段</div>
<div class="s">1.3 自动导入机制</div>
<div class="ss">1.3.1 构建流水线详解(多结构 HAX 构建系统)</div>
<div class="ss">1.3.2 预编译应用的导入</div>
<div class="s">1.4 编译 HAX 应用</div>
<div class="ss">1.4.1 Makefile 自动构建</div>
<div class="ss">1.4.2 手动编译步骤</div>
<div class="ss">1.4.3 链接脚本与地址空间</div>
<div class="s">1.5 专有类型系统</div>
<div class="s">1.6 应用元数据宏 HAX_APP()</div>
<div class="c">第 2 章 — HAX SDK 参考手册</div>
<div class="s">2.1 文本输入输出</div>
<div class="s">2.2 文件操作</div>
<div class="s">2.3 系统服务</div>
<div class="s">2.4 错误处理约定</div>
<div class="s">2.5 直接使用 POSIX libc</div>
<div class="s">2.6 目录遍历(opendir / readdir)</div>
<div class="s">2.7 GUI 全屏画布接口</div>
<div class="s">2.8 并发窗口接口(推荐)</div>
<div class="s">2.9 HIVE 标准窗口控件 API</div>
<div class="s">2.10 TUI 终端控件 API</div>
<div class="s">2.11 多用户身份与文件权限</div>
<div class="ss">2.11.1 用户/组 ID 系统调用</div>
<div class="ss">2.11.2 文件权限</div>
<div class="ss">2.11.3 默认元数据</div>
<div class="ss">2.11.4 Shell 命令</div>
<div class="c">第 3 章 — 运行、发现与调试</div>
<div class="s">3.1 TUI 与 GUI 类型说明</div>
<div class="s">3.2 apps / run 命令</div>
<div class="s">3.3 在图形桌面运行</div>
<div class="s">3.4 参数传递</div>
<div class="s">3.5 退出码约定</div>
<div class="c">第 4 章 — 完整示例</div>
<div class="s">4.1 最小应用(TUI,文档示例)</div>
<div class="s">4.2 输入循环(GUI,文档示例)</div>
<div class="s">4.3 catf —— 读取并显示文件</div>
<div class="s">4.4 ls —— 列出目录内容</div>
<div class="s">4.5 GUI 全屏画布(文档示例)</div>
<div class="s">4.6 HIVE 并发窗口(文档示例)</div>
<div class="s">4.7 HIVE Toolkit API 1.4 控件(文档示例)</div>
<div class="s">4.8 实机运行画面(QEMU 截图)</div>
<div class="c">附录 A — 底层 POSIX 系统调用速查</div>
<div class="c">附录 B — hax_meta_t 二进制布局</div>
<div class="c">附录 C — 常见构建错误排查</div>
</div>
<!-- ── 第 1 章 ── -->
<div class="chapter">
<h1 style="page-break-before:always;">第 1 章<span class="en">OVERVIEW, FORMAT & TYPES</span></h1>
<p>本章介绍 HAX 应用格式的设计目标、内部结构、自动导入机制、编译工具链以及类型系统。</p>
<h2>1.1 关于本手册</h2>
<p>本手册面向希望在 <b>HBOS</b>(裸机内核操作系统)上开发应用的用户。HBOS 提供一套名为
<b>HAX</b>(HBOS Application eXecutable)的应用机制,设计目标是:</p>
<ul>
<li><b>零注册</b>——把源码(<code>.c</code>)或预编译应用(<code>.hax</code>)放进 <code>./app</code> 目录,执行一次 <code>make</code>,应用在系统启动后自动出现,无需修改内核代码或配置文件。</li>
<li><b>标准入口</b>——应用写 <code>main(argc, argv)</code>,与普通 C 程序没有区别。</li>
<li><b>轻量 SDK</b>——一个头文件 <code><hax.h></code> 覆盖 90% 的常用场景;更底层的需求可直接调用 POSIX libc。</li>
<li><b>可移植</b>——<code>.hax</code> 是标准 ELF64,可用任何支持 x86-64 裸机 freestanding 的工具链构建。</li>
</ul>
<div class="note">本手册中所有命令示例默认你已在 HBOS 系统提示符(<code>HBOS></code>)或 HBOS 构建机(宿主 Linux/x86-64)下操作。</div>
<h2>1.2 什么是 HAX 应用(.hax)</h2>
<h4>1.2.1 ELF64 结构</h4>
<p>一个 <code>.hax</code> 文件就是标准 <b>ELF64 可执行程序</b>——ELF Magic、Program Headers、.text/.data/.bss 段均与普通用户态 ELF 完全相同,可用 <code>readelf -h foo.hax</code> 验证。它以静态链接方式包含 HBOS 用户态 libc 与 crt0,运行时加载到地址 <code>0x1000000000</code>(256 GiB),通过 <code>int 0x80</code> 陷入内核进行系统调用。</p>
<h4>1.2.2 .haxmeta 元数据段</h4>
<p>与普通 ELF 的唯一区别是额外携带一个名为 <code>.haxmeta</code> 的只读段,其中存放一个
<code>hax_meta_t</code> 结构(见 1.6)。该段仅供 <code>tools/genhax.py</code> 在构建时读取,内核在运行时通过 blob 清单而非 ELF 段头来定位元数据,因此此段不占用运行时内存。</p>
<p>扩展名 <code>.hax</code> 是构建系统的识别标志,<b>与其他规范中的 .epf/.elf 等扩展名无关</b>。</p>
<h2>1.3 自动导入机制</h2>
<h4>1.3.1 构建流水线详解</h4>
<p>核心逻辑:<b>只要 <code>./app</code> 目录里存在 <code>.hax</code> 文件(编译产物或预编译),它就会被自动加入系统。</b></p>
<p>HAX 构建系统支持三种应用结构:</p>
<ul>
<li><b>单文件应用</b>——<code>app/<name>.c</code>,自动编译为 <code>build/app/<name>.hax</code>(向后兼容)</li>
<li><b>多文件应用</b>——<code>app/<name>/</code> 目录,目录内全部 <code>*.c</code> 链接为一个 <code>.hax</code></li>
<li><b>独立库</b>——<code>app/lib/<lib>/</code> 目录,<code>ld -r</code> 合并一次,供多个应用共享</li>
</ul>
<p>库依赖声明:</p>
<ul>
<li>单文件应用 <code>app/<name>.c</code> → 同名 <code>app/<name>.deps</code>,每行一个库名</li>
<li>多文件应用 <code>app/<name>/</code> → <code>app/<name>/deps</code>,每行一个库名</li>
<li>库之间的依赖 → <code>app/lib/<lib>/deps</code>,每行一个库名</li>
</ul>
<p>(<code>#</code> 开头为注释;库头文件放在 <code>app/lib/<lib>/</code> 下,按 <code><lib/xxx.h></code> 引入,include 路径已自动加上 <code>$(APP_DIR)/lib</code>)</p>
<table>
<tr><th width="8%">步骤</th><th width="22%">触发条件</th><th>具体行为</th></tr>
<tr><td>① 编译</td><td><code>app/**/*.c</code> 有更新</td><td>Makefile 用 HAX 工具链把三类结构的源码编译为 <code>build/app/<name>.hax</code>(ELF64,含 <code>.haxmeta</code>);独立库先 <code>ld -r</code> 合并,再按 deps 链接到应用</td></tr>
<tr><td>② 解析</td><td><code>build/app/*.hax</code> 有更新</td><td><code>tools/genhax.py</code> 依次解析每个 ELF 的 <code>.haxmeta</code> 段(magic 校验 → 提取 kind/name/desc),若无合法元数据则以文件名兜底(kind=TUI)</td></tr>
<tr><td>③ 打包</td><td>同上</td><td>生成器按 16 字节对齐拼接所有 ELF 为 <code>build/hax_blob.bin</code>,并输出 <code>build/hax_manifest.c</code>(含 <code>hax_app_table[]</code> + 计数)</td></tr>
<tr><td>④ 嵌入</td><td>blob 有更新</td><td><code>src/user/hax_blob.asm</code> 用 <code>incbin</code> 把 blob 嵌入内核只读数据段;manifest 编译进内核</td></tr>
<tr><td>⑤ 注册</td><td>系统启动</td><td>内核通过 <code>hax_app_table[]</code> 查表;<code>apps</code> 命令列出,<code>run <名></code> 从 blob 切片后 ELF 加载并生成任务</td></tr>
</table>
<div class="note">步骤 ②③ 由同一次 Python 脚本完成(GNU Make <code>&:</code> grouped target),blob 与 manifest 原子更新,不会出现两者不一致的情况。需 GNU Make ≥ 4.3(Ubuntu 22.04+ 默认满足)。</div>
<h4>1.3.2 预编译应用的导入</h4>
<p>你可以把别处交叉编译好的 <code>.hax</code>(只需是合法 ELF64 + <code>.haxmeta</code>,构建主机不限)直接放进 <code>./app</code>。执行 <code>make</code> 时,生成器会与 <code>./app/*.c</code> 产物一起打包。预编译文件无需改动 Makefile,也无需源码。</p>
<h2>1.4 编译 HAX 应用</h2>
<h4>1.4.1 Makefile 自动构建</h4>
<pre>make # 构建整个系统,含所有 .hax 自动打包
make hax-apps # 仅构建 ./app 下的应用,不重新链接内核</pre>
<p>把源文件加入 <code>./app</code> 后不需要改任何 Makefile——通配符规则会自动发现新文件。支持多文件应用目录和独立库,详见 <code>Makefile</code> 注释。</p>
<h4>1.4.2 手动编译步骤</h4>
<p>Makefile 内部等价命令如下,可用于调试或在其他构建系统中集成:</p>
<pre># 1. 单文件应用
# 编译目标文件
gcc -c -m64 -ffreestanding -fno-stack-protector -fno-pie \
-mno-red-zone -mno-sse -mno-sse2 \
-Iapp/include -Iapp/lib -Isrc/user -Isrc/user/libc \
app/myapp.c -o build/app/myapp.o
# 2. 链接为 .hax(ELF64 静态)
ld -m elf_x86_64 -static -nostdlib -T src/user/user.ld \
build/user/libc/*.o build/user/crt0.o \
build/app/myapp.o -o build/app/myapp.hax
# 多文件应用:目录内全部 *.c 分别编译后一起链接
# 独立库:ld -r 合并一次,应用按 deps 依赖库
# 3. 打包(可选,单独重新打包)
python3 tools/genhax.py \
--blob build/hax_blob.bin \
--manifest build/hax_manifest.c \
build/app/myapp.hax</pre>
<h4>1.4.3 链接脚本与地址空间</h4>
<p>所有 HAX 应用共享链接脚本 <code>src/user/user.ld</code>,加载基地址固定为 <code>0x1000000000</code>(256 GiB)。内核在执行 <code>elf64_load_and_spawn</code> 时把各段映射到用户页表的对应位置;每个应用运行在独立地址空间(独立任务),互不干扰。</p>
<table>
<tr><th>地址范围</th><th>用途</th></tr>
<tr><td><code>0x1000000000+</code></td><td>用户态代码与数据(.text / .data / .bss)</td></tr>
<tr><td>栈顶(由内核分配)</td><td>用户栈(初始 128 KiB)</td></tr>
<tr><td><code>0x0000 – 0x0FFF</code></td><td>保留(NULL 陷阱页,访问触发 #PF 而非挂起)</td></tr>
</table>
<h2>1.5 专有类型系统</h2>
<p><code><hax.h></code> 定义了一组与平台无关的定宽类型,在 32/64 位工具链上都有相同语义:</p>
<table>
<tr><th>类型</th><th>位宽</th><th>有符号</th><th>C 底层类型(x86-64)</th><th>典型用途</th></tr>
<tr><td><code>HI8</code></td><td>8</td><td>是</td><td><code>signed char</code></td><td>字节序列、字符</td></tr>
<tr><td><code>HU8</code></td><td>8</td><td>否</td><td><code>unsigned char</code></td><td>原始字节、颜色分量</td></tr>
<tr><td><code>HI16</code></td><td>16</td><td>是</td><td><code>short</code></td><td>小范围整数</td></tr>
<tr><td><code>HU16</code></td><td>16</td><td>否</td><td><code>unsigned short</code></td><td>端口号、UTF-16 单元</td></tr>
<tr><td><code>HI32</code></td><td>32</td><td>是</td><td><code>int</code></td><td>通用整数、返回值</td></tr>
<tr><td><code>HU32</code></td><td>32</td><td>否</td><td><code>unsigned int</code></td><td>颜色值 0xRRGGBB</td></tr>
<tr><td><code>HI64</code></td><td>64</td><td>是</td><td><code>long long</code></td><td>文件偏移、大整数</td></tr>
<tr><td><code>HU64</code></td><td>64</td><td>否</td><td><code>unsigned long long</code></td><td>位掩码、地址</td></tr>
<tr><td><code>HCOLOR</code></td><td>32</td><td>否</td><td><code>unsigned int</code></td><td>RGB 颜色(0x00RRGGBB)</td></tr>
<tr><td><code>hax_meta_t</code></td><td>—</td><td>—</td><td><code>struct</code> 104 字节</td><td>应用元数据(构建时用)</td></tr>
</table>
<h2>1.6 应用元数据宏 HAX_APP()</h2>
<p>在源文件<b>全局作用域</b>(任意位置,不得在函数内)调用一次 <code>HAX_APP()</code> 宏,声明该应用的名称、描述和类型:</p>
<pre>HAX_APP("myapp", "我的第一个 HBOS 应用", HAX_KIND_TUI);</pre>
<p>宏展开后会把一个 <code>hax_meta_t</code> 常量放进 <code>.haxmeta</code> ELF 段,标记为 <code>__attribute__((used))</code> 防止链接器优化掉。</p>
<table>
<tr><th>参数</th><th>类型</th><th>约束</th><th>说明</th></tr>
<tr><td><code>name</code></td><td>字符串字面量</td><td>≤ 31 字节,UTF-8</td><td>应用唯一标识符,<code>run</code> 命令用此名查找;建议全小写英文,无空格</td></tr>
<tr><td><code>desc</code></td><td>字符串字面量</td><td>≤ 63 字节,UTF-8</td><td>一句话描述,显示在 <code>apps</code> 列表中</td></tr>
<tr><td><code>kind</code></td><td>常量</td><td>见下表</td><td>应用类型,影响启动器中的分类标签</td></tr>
</table>
<table>
<tr><th>kind 常量</th><th>值</th><th>含义</th></tr>
<tr><td><code>HAX_KIND_TUI</code></td><td>1</td><td>终端文本界面应用(命令行运行)</td></tr>
<tr><td><code>HAX_KIND_GUI</code></td><td>2</td><td>图形桌面应用(出现在桌面启动器)</td></tr>
<tr><td><code>HAX_KIND_BOTH</code></td><td>3</td><td>两种入口均注册</td></tr>
<tr><td><code>HAX_KIND_GUI_WIN</code></td><td>4(标志位)</td><td>与 GUI kind 按位 OR,声明应用使用可并发窗口、应非阻塞启动</td></tr>
</table>
<div class="note">每个 <code>.hax</code> 文件只能有一个 <code>HAX_APP()</code>。若检测到多个 <code>.haxmeta</code> 符号,构建将报错。</div>
</div>
<!-- ── 第 2 章 ── -->
<div class="chapter">
<h1 style="page-break-before:always;">第 2 章<span class="en">HAX SDK REFERENCE</span></h1>
<p>SDK 是对 HBOS 用户态 libc 与 <code>int 0x80</code> 系统调用的轻量包装,让常见操作一行搞定。所有声明在 <code><hax.h></code>,无需额外链接标志。</p>
<h2>2.1 文本输入输出</h2>
<h3>void hax_print(const char *s);</h3>
<p>把字符串 <code>s</code> 写到标准输出,<b>不</b>追加换行。<code>s</code> 为 <code>NULL</code> 时行为未定义。</p>
<h3>void hax_println(const char *s);</h3>
<p>输出 <code>s</code> 后追加一个换行符(<code>\n</code>)。等价于 <code>hax_print(s); hax_print("\n");</code></p>
<h3>void hax_printf(const char *fmt, ...);</h3>
<p>格式化输出,语义同 C99 <code>printf</code>。支持格式说明符:</p>
<table>
<tr><th>说明符</th><th>类型</th><th>输出</th></tr>
<tr><td><code>%d / %i</code></td><td><code>int</code></td><td>有符号十进制</td></tr>
<tr><td><code>%u</code></td><td><code>unsigned int</code></td><td>无符号十进制</td></tr>
<tr><td><code>%x / %X</code></td><td><code>unsigned int</code></td><td>十六进制(小写/大写)</td></tr>
<tr><td><code>%ld / %lu</code></td><td><code>long / unsigned long</code></td><td>64 位十进制</td></tr>
<tr><td><code>%s</code></td><td><code>char *</code></td><td>字符串</td></tr>
<tr><td><code>%c</code></td><td><code>int</code></td><td>单字符</td></tr>
<tr><td><code>%p</code></td><td><code>void *</code></td><td>指针(十六进制,<code>0x</code> 前缀)</td></tr>
<tr><td><code>%%</code></td><td>—</td><td>字面量 <code>%</code></td></tr>
</table>
<pre>hax_printf("进程 PID=%d,名称=%s\n", hax_pid(), "myapp");
hax_printf("地址 0x%p,计数=%lu\n", ptr, count);</pre>
<h3>int hax_input(char *buf, int cap);</h3>
<p>从标准输入读取一行到 <code>buf</code>(含 NUL 终止符,<b>不含</b>结尾换行)。</p>
<ul>
<li>最多写入 <code>cap - 1</code> 个有效字符,第 <code>cap</code> 字节写 <code>'\0'</code>。</li>
<li>返回实际字符数(不含 NUL);遇到 EOF 或错误返回 <code>-1</code>。</li>
<li><code>buf</code> 为 <code>NULL</code> 或 <code>cap <= 0</code> 时直接返回 <code>-1</code>。</li>
</ul>
<pre>char line[128];
hax_print("请输入指令: ");
int n = hax_input(line, sizeof(line));
if (n < 0) { hax_println("读取失败"); hax_exit(1); }
hax_printf("你输入了 %d 个字符: %s\n", n, line);</pre>
<h3>int hax_getch(void);</h3>
<p>读取并返回一个字节(0–255);无数据时返回 <code>-1</code>。该函数不回显字符、不等待换行,适合逐键处理。</p>
<pre>hax_println("按任意键继续...");
while (hax_getch() < 0) hax_sleep(0); /* 轮询 */</pre>
<h2>2.2 文件操作</h2>
<h3>long hax_read_file(const char *path, void *buf, long cap);</h3>
<p>打开 <code>path</code>,读取全部内容到 <code>buf</code>(最多 <code>cap</code> 字节),关闭文件。</p>
<ul>
<li>返回实际读到的字节数(文件长度 ≤ <code>cap</code> 时等于文件大小)。</li>
<li>失败(文件不存在、权限不足、路径为 <code>NULL</code>)返回 <code>-1</code>。</li>
<li>不追加 NUL,若需当字符串使用,请自行在返回值处置 <code>'\0'</code>。</li>
</ul>
<pre>char data[4096];
long n = hax_read_file("/etc/hostname", data, sizeof(data) - 1);
if (n >= 0) { data[n] = '\0'; hax_printf("主机名: %s\n", data); }</pre>
<h3>long hax_write_file(const char *path, const void *buf, long len);</h3>
<p>以覆盖创建模式打开 <code>path</code>,把 <code>buf</code> 的 <code>len</code> 字节写入,关闭文件。</p>
<ul>
<li>返回实际写入字节数;失败返回 <code>-1</code>。</li>
<li>若文件不存在则创建;若已存在则截断后写入。</li>
</ul>
<pre>const char *msg = "Hello, HBOS!\n";
long w = hax_write_file("/tmp/test.txt", msg, 13);
hax_printf("写入 %ld 字节\n", w);</pre>
<h2>2.3 系统服务</h2>
<table>
<tr><th>函数签名</th><th>返回值</th><th>说明</th></tr>
<tr><td><code>void hax_sleep(unsigned sec);</code></td><td>无</td><td>休眠整数秒。<code>sec=0</code> 为 yield(让出 CPU,不挂起)</td></tr>
<tr><td><code>void hax_exit(int code);</code></td><td>不返回</td><td>以状态码终止当前应用。<code>code=0</code> 表示成功,非零表示错误</td></tr>
<tr><td><code>int hax_pid(void);</code></td><td>进程 ID</td><td>返回当前任务的内核 PID(≥1 的正整数)</td></tr>
</table>
<h2>2.4 错误处理约定</h2>
<p>SDK 函数遵循以下约定,与 POSIX libc 一致:</p>
<ul>
<li>返回 <code>int</code> 或 <code>long</code> 的函数,失败时返回 <b><code>-1</code></b>(不设 <code>errno</code>,HBOS 当前不暴露全局 <code>errno</code>)。</li>
<li>返回指针的函数,失败时返回 <b><code>NULL</code></b>。</li>
<li><code>hax_exit</code> / <code>hax_sleep</code> 不会失败。</li>
</ul>
<div class="warn">HBOS v0.1 暂不设 <code>errno</code>。若需区分具体错误原因,请使用底层 <code>open/read/write</code> 系统调用(见附录 A),它们通过返回值编码错误码(<code>-ENOENT</code>、<code>-EACCES</code> 等)。</div>
<h2>2.5 直接使用 POSIX libc</h2>
<p>SDK 是轻量封装,满足不了的场景可直接 <code>#include</code> libc 头文件:</p>
<pre>#include <hax.h>
#include <libc/string.h> /* memcpy, strlen, strcmp ... */
#include <libc/stdlib.h> /* atoi, malloc, free ... */
#include <libc/stdio.h> /* printf, fopen, fread ... */
#include <libc/socket.h> /* socket, connect, send ... */</pre>
<p>完整列表见附录 A。</p>
<h2>2.6 目录遍历(opendir / readdir)</h2>
<p>HBOS libc 提供 POSIX 风格的目录遍历接口,声明在 <code><libc/dirent.h></code>。</p>
<table>
<tr><th>函数</th><th>说明</th></tr>
<tr><td><code>DIR *opendir(const char *path);</code></td><td>打开目录,返回目录流;失败返回 <code>NULL</code></td></tr>
<tr><td><code>struct dirent *readdir(DIR *d);</code></td><td>读取下一项,返回内部缓冲指针;无更多项返回 <code>NULL</code></td></tr>
<tr><td><code>int closedir(DIR *d);</code></td><td>关闭目录流,释放资源</td></tr>
<tr><td><code>int getdents(int fd, struct dirent *, unsigned);</code></td><td>底层接口:从目录 fd 一次性读取目录项</td></tr>
</table>
<p><code>struct dirent</code> 关键字段:<code>d_name</code>(文件名,NUL 结尾)、<code>d_type</code>
(<code>DT_DIR</code> 目录 / <code>DT_REG</code> 普通文件)、<code>d_ino</code>(inode 号)。</p>
<pre>#include <libc/dirent.h>
DIR *d = opendir("/");
struct dirent *e;
while ((e = readdir(d)) != 0) {
hax_printf("%s%s\n", e->d_name, e->d_type == DT_DIR ? "/" : "");
}
closedir(d);</pre>
<div class="note">实现细节:内核 <code>getdents</code> 单次调用即返回整个目录(不维护游标),
<code>opendir</code> 因此一次性把目录项读入内部 16 KiB 缓冲,<code>readdir</code> 在其上迭代。
完整示例见 4.4 节。</div>
<h2>2.7 GUI 全屏画布接口</h2>
<p>声明 <code>HAX_KIND_GUI</code>(或 <code>BOTH</code>)的应用可从图形桌面启动并<b>直接绘制到帧缓冲</b>,
而非仅在终端输出文本。本接口为<b>全屏即时模式</b>画布:每帧「清屏 → 绘制 → 提交 → 取输入」,
应用运行期间独占整屏(桌面让位),退出后桌面恢复。适合全屏小游戏。若想要可与桌面/其他应用
<b>同时显示、可拖动的窗口</b>,请用 2.8 节的并发窗口接口。</p>
<table>
<tr><th>函数</th><th>说明</th></tr>
<tr><td><code>int hax_gui_begin(int *w, int *h);</code></td><td>探测 GUI 是否可用;可用返回 1 并写入画布宽高,否则返回 0(应回退文本)</td></tr>
<tr><td><code>void hax_gui_clear(HCOLOR c);</code></td><td>用颜色(0xRRGGBB)填满整个画布</td></tr>
<tr><td><code>void hax_gui_rect(int x,int y,int w,int h,HCOLOR c);</code></td><td>填充矩形</td></tr>
<tr><td><code>void hax_gui_text(int x,int y,const char*s,HCOLOR c,int scale);</code></td><td>绘制文本(UTF-8,scale≥1 整数放大)</td></tr>
<tr><td><code>void hax_gui_present(void);</code></td><td>把画布提交到屏幕(绘制后必须调用才可见)</td></tr>
<tr><td><code>int hax_gui_pollkey(void);</code></td><td>轮询一个按键,返回键值;无按键返回 -1</td></tr>
<tr><td><code>int hax_gui_pollmouse(int *x,int *y);</code></td><td>轮询鼠标,写入绝对坐标,返回按键位掩码(bit0=左键)</td></tr>
</table>
<p>另有一组<b>扩展绘图原语</b>(纯 SDK 实现,基于 <code>hax_gui_rect</code>,无额外系统调用):</p>
<table>
<tr><th>函数</th><th>说明</th></tr>
<tr><td><code>void hax_gui_pixel(int x,int y,HCOLOR c);</code></td><td>画一个像素</td></tr>
<tr><td><code>void hax_gui_frame(int x,int y,int w,int h,int t,HCOLOR c);</code></td><td>矩形描边(线宽 t)</td></tr>
<tr><td><code>void hax_gui_line(int x0,int y0,int x1,int y1,HCOLOR c);</code></td><td>直线(Bresenham)</td></tr>
<tr><td><code>void hax_gui_fill_circle(int cx,int cy,int r,HCOLOR c);</code></td><td>实心圆(水平 span 填充,高效)</td></tr>
<tr><td><code>void hax_gui_circle(int cx,int cy,int r,HCOLOR c);</code></td><td>圆环描边(中点画圆法)</td></tr>
</table>
<pre>int w, h;
if (!hax_gui_begin(&w, &h)) { hax_println("需要从桌面启动"); return 1; }
for (;;) {
hax_gui_clear(0x202830);
hax_gui_rect(20, 20, 120, 48, 0x14A6E0);
hax_gui_text(28, 32, "Hello", 0xFFFFFF, 2);
hax_gui_present();
int k = hax_gui_pollkey();
if (k == 'q' || k == 27) break; /* q 或 ESC 退出 */
hax_sleep(0); /* 让出 CPU */
}</pre>
<div class="note">
全屏画布运行期间,桌面合成器自动让位给应用(应用通过 <code>hax_gui_begin</code> 登记为屏幕所有者);
应用退出后桌面立即恢复。若需与桌面共存的窗口,见 2.8。</div>
<h2>2.8 并发窗口接口(推荐)</h2>
<p>并发窗口接口让应用拥有一个<b>独立窗口</b>,与桌面、其他应用窗口<b>同时显示</b>,可拖动、可关闭。
每个窗口拥有自己的离屏表面,由桌面合成器每帧贴合到屏幕;应用与桌面在 100 Hz 抢占式调度下
<b>并发运行</b>(应用在后台持续刷新时,桌面时钟、其他窗口照常工作)。这是编写图形应用的推荐方式。</p>
<table>
<tr><th>函数</th><th>说明</th></tr>
<tr><td><code>int hax_win_open(const char*title,int w,int h);</code></td><td>打开窗口(内容区 w×h),返回窗口 id(≥0)或 -1</td></tr>
<tr><td><code>int hax_win_active(int*w,int*h);</code></td><td>窗口是否仍活动:是返回 1 并写入当前 w、h;被关闭返回 0</td></tr>
<tr><td><code>void hax_win_clear(HCOLOR c);</code></td><td>用颜色填满窗口</td></tr>
<tr><td><code>void hax_win_fill(int x,int y,int w,int h,HCOLOR c);</code></td><td>窗口内填充矩形(坐标相对内容区)</td></tr>
<tr><td><code>void hax_win_text(int x,int y,const char*s,HCOLOR c);</code></td><td>窗口内绘制文本</td></tr>
<tr><td><code>void hax_win_present(void);</code></td><td>提交一帧(让出 CPU,使合成器尽快显示)</td></tr>
<tr><td><code>int hax_win_poll(int*ev4);</code></td><td>取事件到 ev[4]={type,a,b,c},返回类型(HAX_EV_*,0=无)</td></tr>
<tr><td><code>void hax_win_close(void);</code></td><td>关闭并销毁窗口</td></tr>
</table>
<p>事件类型:<code>HAX_EV_KEY</code>(ev[1]=键值)、<code>HAX_EV_MOUSE</code>(移动/按键;
ev[1]=x ev[2]=y ev[3]=按键位,离开时 x=y=-1)、
<code>HAX_EV_CLOSE</code>(用户点了关闭按钮,应退出)。</p>
<pre>int w, h;
if (hax_win_open("我的应用", 360, 240) < 0) return 1;
while (hax_win_active(&w, &h)) { /* 窗口被关闭时返回 0 */
hax_win_clear(0x202830);
hax_win_text(20, 20, "Hello", 0xFFFFFF);
hax_win_present();
int ev[4];
int t = hax_win_poll(ev);
if (t == HAX_EV_KEY && ev[1] == 'q') break;
if (t == HAX_EV_CLOSE) break;
hax_sleep(0);
}
hax_win_close();</pre>
<div class="note">
<b>实现:</b>HIVE 合成器在桌面合成阶段把每个窗口表面贴到屏幕并加标题栏/边框;输入由桌面路由到聚焦
窗口的事件队列。应用退出后窗口自动回收。最多 8 个并发窗口,单窗口最大 900×640。窗口应用应从
<b>开始菜单</b>或在 GUI 终端用 <code>run</code> 启动(均为非阻塞)。鼠标按下后由内容窗口捕获,
移动和松开事件会继续投递;连续移动事件自动合并,防止事件队列被填满。</div>
<p><b>HIVE 0.1-beta5-gui.4 / Toolkit API 1.4 / 窗口 ABI v2:</b>应用可用 <code>hive_window_query</code> 查询能力,
通过 <code>hive_window_create</code> 创建多个带显式代数句柄的窗口,并独立设置标题、几何和
普通/最小化/最大化状态。<code>hive_window_draw</code> 支持批量 Clear、Fill、Text 与 ARGB
位图上传,<code>hive_window_present_rect</code> 支持脏矩形提交;事件扩展为 Move、Resize、
Focus 与 State。旧 <code>hive_window_open</code> 单窗口接口保持兼容。</p>
<h2>2.9 HIVE 标准窗口控件 API</h2>
<p><b>HIVE</b>(HBOS Interface & Visual Environment)是 HBOS 的桌面环境和 GUI
工具包。HAX 负责稳定的应用格式/系统调用 ABI;HIVE 完全运行在用户态,并在
<code>hax_win_*</code> 上提供控件、布局、主题和事件派发。新 GUI 应用首选
<code>#include <hive.h></code>;旧 <code>hax_ui_*</code> 名称继续兼容。</p>
<table>
<tr><th>控件</th><th>创建函数</th><th>交互</th></tr>
<tr><td>面板</td><td><code>hive_ui_add_panel</code></td><td>父子控件树、相对坐标、级联状态</td></tr>
<tr><td>标签</td><td><code>hive_ui_add_label</code></td><td>只读或动态文本</td></tr>
<tr><td>按钮</td><td><code>hive_ui_add_button</code></td><td>松开触发、Enter、Space → CLICK</td></tr>
<tr><td>文本框</td><td><code>hive_ui_add_textbox</code></td><td>输入、UTF-8 选区、鼠标拖选、Shift+左右、Ctrl+A</td></tr>
<tr><td>复选框</td><td><code>hive_ui_add_checkbox</code></td><td>点击或 Space 切换 → CHANGE</td></tr>
<tr><td>列表</td><td><code>hive_ui_add_list</code></td><td>点击、上下键、PageUp/PageDown → SELECT</td></tr>
<tr><td>进度条</td><td><code>hive_ui_add_progress</code></td><td><code>hive_ui_set_value</code> 更新</td></tr>
<tr><td>滑杆</td><td><code>hive_ui_add_slider</code></td><td>拖动、方向键、Home/End → CHANGE</td></tr>
<tr><td>滚动条</td><td><code>hive_ui_add_scrollbar</code></td><td>横向/纵向拖动、方向键、PageUp/PageDown</td></tr>
<tr><td>菜单</td><td><code>hive_ui_add_menu</code></td><td>鼠标或键盘选择 → SELECT</td></tr>
<tr><td>图片</td><td><code>hive_ui_add_image</code></td><td>上传 0xAARRGGBB 位图</td></tr>
<tr><td>画布</td><td><code>hive_ui_add_canvas</code></td><td>应用回调自定义绘制</td></tr>
<tr><td>单选钮</td><td><code>hive_ui_add_radio</code></td><td>同一父容器内互斥,点击或 Space → CHANGE</td></tr>
<tr><td>开关</td><td><code>hive_ui_add_toggle</code></td><td>点击或 Space 切换 → CHANGE</td></tr>
<tr><td>下拉选择</td><td><code>hive_ui_add_dropdown</code></td><td>鼠标左右半区或方向键切换 → SELECT</td></tr>
<tr><td>数字微调</td><td><code>hive_ui_add_spinbox</code></td><td>加减按钮、方向键、Home/End → CHANGE</td></tr>
<tr><td>分隔线</td><td><code>hive_ui_add_separator</code></td><td>横向或纵向视觉分组</td></tr>
<tr><td>分组框</td><td><code>hive_ui_add_groupbox</code></td><td>带标题的父容器,可组织 Radio 组</td></tr>
</table>
<p>当前 <code>HIVE_API_MAJOR=1</code>、<code>HIVE_API_MINOR=4</code>。单个 UI 最多
48 个控件,不使用动态内存;状态完全属于应用自己的 <code>hive_ui_t</code>。
主题统一提供普通、悬停、按下、禁用、选择和焦点环颜色。</p>
<table>
<tr><th>函数</th><th>说明</th></tr>
<tr><td><code>hive_ui_init(ui)</code></td><td>初始化上下文和默认暗色主题</td></tr>
<tr><td><code>hive_layout_begin / hive_layout_row</code></td><td>建立带内边距和间距的纵向布局</td></tr>
<tr><td><code>hive_grid_cell(row,n,gap,i)</code></td><td>把一行等分成响应式网格</td></tr>
<tr><td><code>hive_ui_poll(ui,event)</code></td><td>读取窗口事件并派发到控件</td></tr>
<tr><td><code>hive_ui_draw(ui)</code></td><td>绘制全部可见控件和交互状态</td></tr>
<tr><td><code>hive_ui_set_enabled / set_visible</code></td><td>启用、禁用、显示或隐藏控件</td></tr>
<tr><td><code>hive_ui_set_text / set_value / get_value</code></td><td>更新或读取控件内容</td></tr>
<tr><td><code>hive_ui_set_parent / parent</code></td><td>建立或查询 Panel/Groupbox 父子关系,子控件使用相对坐标</td></tr>
<tr><td><code>hive_ui_set_rect / get_rect / remove</code></td><td>更新相对位置、读取窗口坐标或删除整棵子树</td></tr>
<tr><td><code>hive_textbox_select / select_all</code></td><td>按 UTF-8 码点边界设置或全选文本</td></tr>
<tr><td><code>hive_textbox_selection / copy_selection</code></td><td>查询选区范围或复制为 NUL 结尾 UTF-8 文本</td></tr>
</table>
<pre>#include <hive.h>
HIVE_APP("hello-ui", "最小 HIVE 应用");
hive_ui_t ui;
hive_ui_init(&ui);
hive_ui_add_button(&ui, 1, hive_rect(20,20,120,36), "确定");
while (hive_window_active(&w, &h)) {
hive_event_t ev;
while (hive_ui_poll(&ui, &ev) != HIVE_EVENT_NONE) {
if (ev.type == HIVE_EVENT_CLOSE) goto done;
if (ev.type == HIVE_EVENT_CLICK && ev.widget_id == 1) {
/* 执行动作 */
}
}
hive_window_clear(ui.theme.window_bg);
hive_ui_draw(&ui);
hive_window_present();
hive_yield();
}
done: hive_window_close();</pre>
<div class="warn">HIVE Toolkit API 1.4 能按 UTF-8 码点移动、鼠标/键盘选择、替换和删除已有文本;
窗口键盘事件目前仍只直接产生可打印 ASCII,完整中文输入法、系统剪贴板和无障碍语义
将在后续版本追加。</div>
<h2>2.10 TUI 终端控件 API</h2>
<p><code>#include <hax.h></code> 随 <code>hax_tui.h</code> 提供一套文本终端控件
(<b>TUI Kit API 1.0</b>,<code>HIVE_TUI_API_MAJOR/MINOR=1/0</code>)。与窗口控件
一样纯用户态、无新增系统调用、无全局变量、无动态内存:输出只依赖 stdout,
输入只依赖 stdin(<code>hax_input</code> 读整行),在内核控制台和图形终端中行为
一致。渲染刻意使用纯 ASCII(<code>+ - | = [ ]</code>),对齐按显示格计算
(CJK/全角字符占 2 格),截断不会切断 UTF-8 码点。</p>
<table>
<tr><th>控件</th><th>函数</th><th>说明</th></tr>
<tr><td>标题分隔线</td><td><code>hax_tui_rule / hax_tui_hline</code></td><td>带标题或纯横线</td></tr>
<tr><td>进度条</td><td><code>hax_tui_progress</code></td><td><code>[====----] 45%</code>;in_place 用 <code>\r</code> 原地刷新</td></tr>
<tr><td>边框盒</td><td><code>hax_tui_box</code></td><td>标题嵌顶边的 ASCII 盒</td></tr>
<tr><td>表格</td><td><code>hax_tui_table</code></td><td>列宽/对齐由 <code>hax_tui_column_t</code> 描述</td></tr>
<tr><td>菜单</td><td><code>hax_tui_menu</code></td><td>数字 1..N 或快捷字母选择,q 取消</td></tr>
<tr><td>确认</td><td><code>hax_tui_confirm</code></td><td><code>[Y/n]</code> 默认值,回车取默认</td></tr>
<tr><td>输入行</td><td><code>hax_tui_input</code></td><td>带提示与默认值,UTF-8 安全截断</td></tr>
<tr><td>复选</td><td><code>hax_tui_checkbox</code></td><td><code>[x]</code> 状态行 + y/n 切换</td></tr>
</table>
<p>交互控件都是行驱动的;空行取默认值,<code>q</code>/<code>Q</code> 取消(返回 -1),
stdin 读到 EOF 同样返回 -1,因此即使被无输入管道启动也不会死循环。
解析逻辑(<code>hax_tui_menu_parse</code> / <code>hax_tui_confirm_parse</code>)与
渲染逻辑(<code>*_to</code> 系列)分离,便于宿主测试。</p>
<pre>#include <hive.h>
HAX_APP("svc", "服务管理", HAX_KIND_TUI);
static const char *const items[] = {"启动服务", "停止服务", "查看状态"};
int main(void) {
hax_tui_rule("服务管理", 40);
HI32 sel = hax_tui_menu("请选择操作", items, 3, NULL);
if (sel < 0) return 0; /* q / EOF */
hax_printf("选择了:%s\n", items[sel]);
hax_tui_progress("执行", 100, 0, 100, 24, 1);
hax_println("");
return 0;
}</pre>
<div class="note"><code>hax_tui_progress</code> 的 in_place 模式依赖终端支持
<code>\r</code> 回车不换行;HBOS 图形终端会丢弃 <code>\r</code>,此时退化为逐行
输出(功能不受影响)。<code>app/tui.c</code> 是完整的控件展示应用。</div>
</div>
<h2>2.11 多用户身份与文件权限</h2>
<p>从 HBOS v0.1-beta5-pre6 起,内核支持<b>多用户身份</b>:每个进程/线程拥有独立的 <code>uid</code>、<code>euid</code>、<code>gid</code>、<code>egid</code> 以及补充组列表,系统调用 <code>getuid</code>/<code>setuid</code> 等从此返回真实值,而非固定写死的 <code>0</code>(root)。</p>
<h4>2.11.1 用户/组 ID 系统调用</h4>
<p>以下 POSIX 系统调用已全部实现:</p>
<table>
<tr><th>函数</th><th>说明</th></tr>
<tr><td><code>uid_t getuid(void)</code></td><td>返回实际用户 ID</td></tr>
<tr><td><code>uid_t geteuid(void)</code></td><td>返回有效用户 ID</td></tr>
<tr><td><code>gid_t getgid(void)</code></td><td>返回实际组 ID</td></tr>
<tr><td><code>gid_t getegid(void)</code></td><td>返回有效组 ID</td></tr>
<tr><td><code>int setuid(uid_t uid)</code></td><td>设置用户 ID(root 可设为任意值;非 root 仅能设为自己的 real uid)</td></tr>
<tr><td><code>int setgid(gid_t gid)</code></td><td>设置组 ID(规则同上)</td></tr>
<tr><td><code>int getgroups(int size, gid_t list[])</code></td><td>获取补充组列表</td></tr>
<tr><td><code>int setgroups(int size, const gid_t list[])</code></td><td>设置补充组列表(仅 root)</td></tr>
<tr><td><code>pid_t getpgid(pid_t pid)</code></td><td>获取进程组 ID</td></tr>
</table>
<h4>2.11.2 文件权限</h4>
<p>每个 VFS 节点(文件、目录、设备、符号链接)都记录了 <code>uid</code>、<code>gid</code> 和 <code>mode</code>(权限位 + 文件类型位)。<code>stat()</code> 和 <code>fstat()</code> 返回真实的 owner 和权限。以下操作会检查权限:</p>
<ul>
<li><code>open()</code>——根据打开模式(读/写)检查读/写权限</li>
<li><code>access()</code>——按 <code>R_OK/W_OK/X_OK</code> 检查</li>
<li><code>unlink()</code>——检查写权限(简化版)</li>
<li><code>rmdir()</code>——检查写权限</li>
<li><code>chmod()</code>——修改文件权限(owner 或 root 可改)</li>
<li><code>chown()</code>——修改文件属主(仅 root)</li>
</ul>
<p>root 绕过读/写权限检查,但 execute 仍要求至少有一个执行位。非 root 进程按 POSIX 规则计算:若 euid 等于文件 uid,则使用用户权限位(rwx 高 3 位);否则若 egid 或补充组匹配文件 gid,则使用组权限位;否则使用其他权限位。</p>
<h4>2.11.3 默认元数据</h4>
<p>新建文件时:</p>
<ul>
<li><code>open()</code> 创建的普通文件 owner = 当前进程 uid/gid,mode = 传入 mode(默认 0644)</li>
<li><code>mkdir()</code> 创建的目录 owner = 当前进程 uid/gid,mode = 传入 mode(默认 0755)</li>
<li><code>symlink()</code> 创建的符号链接 owner = 当前进程 uid/gid,mode = 0777</li>
<li>内置文件系统节点(/dev/null、/proc/uptime 等)owner = root,mode 按用途设置</li>
<li>EXT2 挂载时从 inode 读取 uid/gid/mode</li>
</ul>
<h4>2.11.4 Shell 命令</h4>
<p>新增 <code>id</code> 命令,可查看当前进程的身份信息:</p>
<pre>HBOS> id
uid=0 gid=0 euid=0 egid=0 groups=</pre>
<div class="note">当前限制:尚无 <code>/etc/passwd</code> 用户数据库、登录会话管理、setuid/setgid 位,以及 rename 的父目录写权限细化。HBFS 磁盘上的 owner/mode 仅保存在内存,未持久化到磁盘表项。</div>
<!-- ── 第 3 章 ── -->
<div class="chapter">
<h1 style="page-break-before:always;">第 3 章<span class="en">RUN, DISCOVERY & DEBUG</span></h1>
<h2>3.1 TUI 与 GUI 类型说明</h2>
<table>
<tr><th>kind</th><th>标签</th><th>启动方式</th><th>输入输出</th></tr>
<tr><td><code>HAX_KIND_TUI</code></td><td><span class="tag">TUI</span></td><td><code>run <名></code>,或桌面终端窗口</td><td>stdin/stdout 重定向到终端</td></tr>
<tr><td><code>HAX_KIND_GUI</code></td><td><span class="tag">GUI</span></td><td>桌面启动器图标,或 <code>run <名></code></td><td>当前版本:输出到终端窗口</td></tr>
<tr><td><code>HAX_KIND_BOTH</code></td><td><span class="tag">TUI</span> <span class="tag">GUI</span></td><td>两种入口均可用</td><td>同上</td></tr>
</table>
<div class="note">
<b>GUI 应用绘制有三种方式:</b>(1) 只输出文本(在终端窗口显示);(2) <b>全屏画布</b>
<code>hax_gui_*</code>(见 2.7,独占整屏,适合全屏游戏);(3) <b>并发窗口</b> <code>hax_win_*</code>
(见 2.8,独立窗口,与桌面/其他应用同时显示、可拖动,<b>推荐</b>)。后两者均直接绘制到帧缓冲。
</div>
<h2>3.2 apps / run 命令</h2>
<pre>HBOS> apps
catf - 读取并显示文件内容 [TUI .hax]
leapyear - 闰年判定 [TUI GUI .hax]
HBOS> run leapyear</pre>
<table>
<tr><th>命令</th><th>作用</th></tr>
<tr><td><code>apps</code></td><td>列出所有已注册应用(内建命令 + .hax 应用),显示名称、描述、类型标签</td></tr>
<tr><td><code>run <名> [参数...]</code></td><td>运行应用;优先匹配内建命令,未命中后查找 .hax 表,再未命中报错</td></tr>
</table>
<h2>3.3 在图形桌面运行</h2>
<p>在 HIVE 桌面中,打开“终端”窗口后即可像命令行一样输入 <code>apps</code> 与 <code>run</code> 命令。
此外,<code>HAX_KIND_GUI</code>(或 <code>BOTH</code>)类型的应用会<b>自动追加到开始菜单</b>(已固定应用网格的内置项之后),
点击图标即可启动,等价于 <code>run <名></code>。</p>
<h2>3.4 参数传递</h2>
<p><code>run myapp a b c</code> 等价于 C 程序的 <code>main(4, {"myapp","a","b","c",NULL})</code>:</p>
<ul>
<li><code>argv[0]</code> 始终为应用名(与 <code>HAX_APP</code> 中的 <code>name</code> 相同)。</li>
<li><code>argv[1..argc-1]</code> 为用户传入的额外参数,均为 NUL 结尾 UTF-8 字符串。</li>
<li><code>argv[argc]</code> 保证为 <code>NULL</code>(标准 POSIX 约定)。</li>
<li>参数最多 31 个(含 <code>argv[0]</code>),超出部分截断。</li>
</ul>
<h2>3.5 退出码约定</h2>
<table>
<tr><th>退出码</th><th>含义</th></tr>
<tr><td>0</td><td>成功</td></tr>
<tr><td>1</td><td>一般错误(参数错误、IO 失败等)</td></tr>
<tr><td>2</td><td>使用方法错误(参数数量不对)</td></tr>
<tr><td>127</td><td>命令未找到(由 shell/run 命令返回,非应用本身)</td></tr>
<tr><td>其他非零</td><td>应用自定义错误码</td></tr>
</table>
<p>内核通过 <code>task_wait</code> 获取退出码并传回 <code>run</code> 命令;当前 shell 不打印退出码,可用 <code>hax_printf</code> 在退出前自行输出。</p>
</div>
<h2>3.3 文本编辑器(Nano 风格)</h2>
<p>HBOS 内置一个 Nano 风格的 TUI 编辑器,通过 <code>edit <文件></code> 命令启动:</p>
<pre>HBOS> edit /home/hello.txt</pre>
<p>快捷键:</p>
<table>
<tr><th>快捷键</th><th>功能</th></tr>
<tr><td><code>^X</code></td><td>退出(未保存会提示是否保存)</td></tr>
<tr><td><code>^O</code></td><td>保存文件</td></tr>
<tr><td><code>^W</code></td><td>搜索文本(支持回绕)</td></tr>
<tr><td><code>^K</code></td><td>剪切当前行(整行存入剪贴板)</td></tr>
<tr><td><code>^U</code></td><td>粘贴(插入剪贴板内容)</td></tr>
<tr><td><code>^C</code></td><td>显示光标位置(行/列)</td></tr>
<tr><td><code>^G</code></td><td>显示帮助</td></tr>
<tr><td><code>^_</code></td><td>跳转到指定行</td></tr>
<tr><td>方向键</td><td>移动光标</td></tr>
<tr><td>Home / End</td><td>行首 / 行尾</td></tr>
<tr><td>PageUp / PageDown</td><td>翻页</td></tr>
</table>
<p>编辑器底部有菜单栏显示常用快捷键,顶部状态栏显示文件名和修改标记(<code>"*"</code>)。支持行号显示、水平滚动和自动换行保持。</p>
<!-- ── 第 4 章 ── -->
<div class="chapter">
<h1 style="page-break-before:always;">第 4 章<span class="en">COMPLETE EXAMPLES</span></h1>
<div class="note">本章代码用于讲解 SDK,不作为预装应用打包。当前随系统保留的
HAX 应用只有 <code>catf</code> 与 <code>leapyear</code>。</div>
<h2>4.1 最小应用(TUI,文档示例)</h2>
<p>演示:<code>HAX_APP()</code>、文本输出、参数处理、<code>hax_pid()</code>。</p>
<pre>/* myapp.c */
#include <hax.h>
HAX_APP("myapp", "最小 HAX 应用", HAX_KIND_TUI);
int main(int argc, char **argv) {
hax_println("你好,HBOS!这是一个 .hax 应用。");
hax_printf("我的 PID 是 %d\n", hax_pid());
if (argc > 1) {
hax_print("收到参数:");
for (int i = 1; i < argc; i++)
hax_printf(" %s", argv[i]);
hax_println("");
}
return 0;
}</pre>
<p>把文件放入外部应用目录并构建后,可这样运行:</p>
<pre>HBOS> run myapp 世界
你好,HBOS!这是一个 .hax 应用。
我的 PID 是 5
收到参数: 世界</pre>
<h2>4.2 输入循环(GUI,文档示例)</h2>
<p>演示:<code>HAX_KIND_GUI</code>、循环输入、<code>atoi</code>、<code>hax_exit</code>。</p>
<pre>/* input-loop.c */
#include <hax.h>
#include <libc/stdlib.h> /* atoi */
HAX_APP("input-loop", "输入循环示例", HAX_KIND_GUI);
int main(int argc, char **argv) {
(void)argc; (void)argv;
/* 以 PID 为简易随机种子,避免每次都一样 */
int secret = (hax_pid() * 37 + 11) % 100 + 1;
char line[32];
hax_println("我想了一个 1-100 的整数,猜猜看!(输入 q 退出)");
for (int tries = 1; ; tries++) {
hax_printf("第 %d 次猜测> ", tries);
if (hax_input(line, sizeof(line)) < 0 || line[0] == 'q')
break;
int v = atoi(line);
if (v < 1 || v > 100) {
hax_println("请输入 1-100 之间的整数。");
tries--;
continue;
}
if (v < secret) hax_println("太小了,再大一点。");
else if (v > secret) hax_println("太大了,再小一点。");
else {
hax_printf("猜对了!答案是 %d,你用了 %d 次。\n",
secret, tries);
return 0;
}
}
hax_printf("游戏结束。正确答案是 %d。\n", secret);
return 0;
}</pre>
<h2>4.3 catf —— 读取并显示文件(TUI)</h2>
<p>演示:参数处理、<code>hax_read_file</code>、标准 libc 调用(<code>atoi</code>)、错误处理与退出码。</p>
<pre>/* app/catf.c */
#include <hax.h>
HAX_APP("catf", "读取并显示文件内容", HAX_KIND_TUI);
int main(int argc, char **argv) {
if (argc < 2) {
hax_println("用法: run catf <文件路径>");
return 2; /* 用法错误 */
}
static char buf[8192];
long n = hax_read_file(argv[1], buf, sizeof(buf) - 1);
if (n < 0) {
hax_printf("无法读取文件: %s\n", argv[1]);
return 1; /* 一般错误 */
}
buf[n] = '\0'; /* 当字符串处理 */
hax_print(buf);
if (n > 0 && buf[n - 1] != '\n')
hax_println(""); /* 补一个换行 */
hax_printf("\n[共 %ld 字节]\n", n);
return 0;
}</pre>
<pre>HBOS> run catf /etc/hostname
hbos
[共 5 字节]</pre>
<p class="small">本示例只用了 <code>hax_read_file</code>。目录遍历见下一节。</p>
<h2>4.4 ls —— 列出目录内容(TUI)</h2>
<p>演示:<code>opendir/readdir/closedir</code>、<code>d_type</code> 判断、参数处理。</p>
<pre>/* app/ls.c */
#include <hax.h>
#include <libc/dirent.h>
HAX_APP("ls", "列出目录下的文件与子目录", HAX_KIND_TUI);
int main(int argc, char **argv) {
const char *path = (argc > 1) ? argv[1] : "/";
DIR *d = opendir(path);
if (!d) { hax_printf("无法打开目录: %s\n", path); return 1; }
hax_printf("目录 %s:\n", path);
struct dirent *ent;
int files = 0, dirs = 0;
while ((ent = readdir(d)) != 0) {
if (ent->d_name[0] == '\0') continue;
if (ent->d_type == DT_DIR) { hax_printf(" [DIR] %s\n", ent->d_name); dirs++; }
else { hax_printf(" %s\n", ent->d_name); files++; }
}
closedir(d);
hax_printf("共 %d 个文件,%d 个目录。\n", files, dirs);
return 0;
}</pre>
<h2>4.5 GUI 全屏画布(文档示例)</h2>
<p>演示:<code>hax_gui_begin</code> 探测、鼠标轮询绘制、按键退出、无 GUI 时回退。
从图形桌面启动;按住左键涂鸦,<span class="kbd">c</span> 清屏,<span class="kbd">q</span> / <span class="kbd">ESC</span> 退出。</p>
<pre>/* canvas-example.c */
#include <hax.h>
HAX_APP("canvas-example", "GUI 全屏画布示例", HAX_KIND_GUI);
int main(int argc, char **argv) {
(void)argc; (void)argv;
int w, h;
if (!hax_gui_begin(&w, &h)) {
hax_println("此示例需要从图形桌面启动。");
return 1;
}
hax_gui_clear(0x10141A);
hax_gui_rect(0, 0, w, 28, 0x14A6E0);
hax_gui_text(8, 6, "HBOS Paint - 左键涂鸦 c 清屏 q 退出", 0xFFFFFF, 1);
hax_gui_present();
for (;;) {
int mx, my;
int btn = hax_gui_pollmouse(&mx, &my);
if ((btn & 1) && my > 28) { /* 左键拖动绘制 */
hax_gui_rect(mx - 4, my - 4, 8, 8, 0xF5C842);
hax_gui_present();
}
int k = hax_gui_pollkey();
if (k == 'q' || k == 27) break;
hax_sleep(0);
}
return 0;
}</pre>
<h2>4.6 HIVE 并发窗口(文档示例)</h2>
<p>以下片段演示按钮、复选框、
滑杆、进度条、响应式布局及完整键盘操作。</p>
<pre>/* window-example.c(节选) */
#include <hive.h>
HIVE_APP("window-example", "HIVE 并发窗口示例");
int main(int argc, char **argv) {
hive_ui_t ui;
hive_ui_init(&ui);
hive_ui_add_checkbox(&ui, ID_AUTO, hive_rect(20,80,140,28),
"自动计数", 1);
hive_ui_add_slider(&ui, ID_SPEED, hive_rect(180,80,140,28),
1, 10, 6, 1);
hive_ui_add_button(&ui, ID_ADD, hive_rect(20,140,140,36), "增加 10");
while (hive_window_active(&w, &h)) {
hive_event_t ev;
while (hive_ui_poll(&ui, &ev) != HIVE_EVENT_NONE) {
if (ev.type == HIVE_EVENT_CLOSE) goto done;
if (ev.type == HIVE_EVENT_CLICK && ev.widget_id == ID_ADD)
count += 10;
}
hive_window_clear(ui.theme.window_bg);
hive_ui_draw(&ui);
hive_window_present();
hive_yield();
}
done:
hive_window_close();
return 0;
}</pre>
<h2>4.7 HIVE Toolkit API 1.4 控件(文档示例)</h2>
<p>以下片段覆盖响应式布局,以及 1.4 新增的分组框、单选钮、开关、下拉选择、
数字微调和分隔线。Radio 通过共同父容器形成互斥组:</p>
<pre>hive_layout_t layout;
hive_layout_begin(&layout, hive_rect(0,0,width,height), 20, 10);
hive_rect_t form = hive_layout_row(&layout, 32);
hive_ui_widget(&ui, ID_NAME)->rect =
hive_grid_cell(form, 3, 12, 0);
hive_ui_widget(&ui, ID_ENABLED)->rect =
hive_grid_cell(form, 3, 12, 2);
hive_ui_add_slider(&ui, ID_SLIDER, hive_layout_row(&layout, 28),
0, 100, 35, 5);
static const char *const scales[] = {"100%", "125%", "150%"};
hive_ui_add_groupbox(&ui, ID_GROUP, hive_rect(20,160,300,90), "渲染模式");
hive_ui_add_radio(&ui, ID_BASIC, hive_rect(32,182,120,22), "标准", 1);
hive_ui_add_radio(&ui, ID_PRO, hive_rect(32,210,120,22), "增强", 0);
hive_ui_set_parent(&ui, ID_BASIC, ID_GROUP);
hive_ui_set_parent(&ui, ID_PRO, ID_GROUP);
hive_ui_add_toggle(&ui, ID_AUTO, hive_rect(180,182,120,22), "自动", 1);
hive_ui_add_dropdown(&ui, ID_SCALE, hive_rect(20,270,140,28), scales,3,0);
hive_ui_add_spinbox(&ui, ID_COUNT, hive_rect(180,270,120,28), 0,100,10,5);
hive_ui_add_separator(&ui, ID_SEP, hive_rect(20,310,280,4), 0);
while (hive_window_active(&width, &height)) {
hive_event_t ev;
while (hive_ui_poll(&ui, &ev) != HIVE_EVENT_NONE) {
if (ev.type == HIVE_EVENT_CHANGE &&
ev.widget_id == ID_SLIDER)
hive_ui_set_value(&ui, ID_PROGRESS, ev.value);
}
hive_window_clear(ui.theme.window_bg);
hive_ui_draw(&ui);
hive_window_present();
hive_yield();
}</pre>
<p class="small">提示:这些片段只用于 API 讲解;如需试验,请放入外部应用目录构建。</p>
</div>
<!-- ── 4.8 实机运行画面 ── -->
<h2>4.8 实机运行画面(QEMU 截图)</h2>
<p>以下画面来自 HBOS <code>v0.1-beta5-pre6</code> 在 QEMU(q35、1024M、1920×1080)