Compare commits

...

549 commits

Author SHA1 Message Date
AnErrupTion
a22805d44b
Auth: Handle symlinks for session log
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-31 23:52:19 +02:00
AnErrupTion
b6fba46b08
GPA: Add more tracking in Debug
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-28 01:38:48 +02:00
AnErrupTion
7c2ae2738c
positionSingleWidget: Resist integer overflows
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-10 13:02:58 +02:00
AnErrupTion
84950136c9
positionWidgets: Less integer overflows possible
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-10 00:15:07 +02:00
AnErrupTion
d00780d6ce
zig: Remove usage of [*c]
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-08 01:29:57 +02:00
AnErrupTion
014a00d33e
Improve templates
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-07 22:35:06 +02:00
AnErrupTion
fb544b3357
Change default box_position_v to 0.5
This can potentially be confusing to users.

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-07 12:22:43 +02:00
AnErrupTion
5ccd91c132
Prepare for Zig 0.17.0
Still requires Zig 0.16.x for now.

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-07 11:51:05 +02:00
AnErrupTion
78c7c2e2e8
Decouple save file path from config directory (closes #1025)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 22:11:52 +02:00
AnErrupTion
a41ebe8f6f
Remove cornersContain()
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 21:33:30 +02:00
AnErrupTion
e4e399cd07
Allow toggling visibility of shutdown, restart & show password keybinds
(closes #993)

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 21:18:49 +02:00
AnErrupTion
018d797ed6
Brother what am I doing
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 20:19:40 +02:00
AnErrupTion
54f1ff14ee
corners: Break up "keys" into individual keys
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 20:17:46 +02:00
AnErrupTion
7e6474924c
config: Order battery_id alphabetically
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 19:57:29 +02:00
AnErrupTion
26377fd319
FreeBSD: Fix compilation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 19:56:24 +02:00
AnErrupTion
5fc5fd62a0
Remove shutdown_cmd & restart_cmd + sleep & hibernate
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 19:44:10 +02:00
AnErrupTion
acb532b5d4
migrator: Free old save path later
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 18:33:13 +02:00
AnErrupTion
e1696a4e73
config: Better document corner customisation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 17:32:49 +02:00
AnErrupTion
954e90b3bd
Remove options superseded by corner customisation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 17:20:05 +02:00
AnErrupTion
7eae544418
systemd: Add more conflicts, better kmscon service
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 12:43:10 +02:00
AnErrupTion
32f11cdbe5
Modify link to black hole animation (closes #1006)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 11:47:37 +02:00
AnErrupTion
9b8046e37c
auth: chown & chmod TTY (closes #944)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 11:10:26 +02:00
AnErrupTion
4513033bcb
ly@.service: Use agetty
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 02:22:40 +02:00
AnErrupTion
102d1c9a9b
Unset Shift and Ctrl for arrow keys (closes #1015)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-06 00:22:51 +02:00
Luna
e0c287306e feature: add corner customization (closes #961) (#963)
## What are the changes about?

Add corner customization (what is in which corner of the screen)

## What existing issue does this resolve?

Issue #961

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have run `zig fmt` throughout my changes

Co-authored-by: AnErrupTion <anerruption@disroot.org>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/963
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-07-05 23:22:03 +02:00
HigherOrderLogic
25fad28c34 Add type_username option (closes #891) (#1012)
## What are the changes about?

Add an option to allow user manually input username, instead of selecting from a list of discovered users.

I have not tested the changes yet since I wasnt able to get it build locally. Will try to resolve this asap.

## What existing issue does this resolve?

#891

## Pre-requisites

- [ ] I have tested & confirmed the changes work locally
- [x] I have run `zig fmt` throughout my changes

Co-authored-by: HigherOrderLogic <73709188+HigherOrderLogic@users.noreply.github.com>
Co-authored-by: AnErrupTion <anerruption@disroot.org>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1012
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-07-05 22:43:15 +02:00
AnErrupTion
006749b2ec
Format README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-05 21:20:02 +02:00
Lars Engels
cf731dbce1 Add battery capacity support on FreeBSD (#988)
## What are the changes about?

So far ly only supported showing battery capacity on Linux. This adds support for FreeBSD as well using `sysctlbyname()` instead of reading from `/sys/...`

## What existing issue does this resolve?

No battery shown on FreeBSD.

_Replace this with a reference to an existing issue, or N/A if there is none_

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have run `zig fmt` throughout my changes

Co-authored-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: AnErrupTion <anerruption@disroot.org>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/988
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-07-05 21:19:30 +02:00
dumbsplash
1d272ff74f Updated readme to address SELinux problems (closes #494) (#1019)
PR is an edit of the repo readme. See commit message.

Co-authored-by: AnErrupTion <anerruption+codeberg@disroot.org>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1019
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-07-05 20:59:40 +02:00
Tim132
63f0f4931c Fix authentication error with ly-freebsd-autologin (#1020)
## What are the changes about?

Remove the line "auth include login" from ly-freebsd-autologin

## What existing issue does this resolve?

The autologin at the beginning and further logins with password always failed with this message in the log: "failed to authenticate: PamAuthError". When I removed the line "auth include login" from ly-freebsd-autologin, autologin and login worked great.

## Pre-requisites

- [X] I have tested & confirmed the changes work locally
- [X] I have read and fully adhere to the rules set in the contributing guidelines found in `CONTRIBUTING.md`

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1020
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-07-03 18:37:40 +02:00
AnErrupTion
8f4ca8d3f0
Autologin: Don't draw info line (closes #1002)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-07-03 18:33:58 +02:00
AnErrupTion
4d882f7997
Fix example.lua doc + add ly-community in README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-06-19 20:18:45 +02:00
RadsammyT
eeccb7421b feat: LuaJIT Animations (#1001)
## What are the changes about?
TL;DR:
![ly_meme](/attachments/f4c3a93b-6ede-42ab-a351-292e2105d4e2)

Slaps the entire LuaJIT runtime onto Ly, allowing for the creation of custom dynamic animations like GameOfLife, ColorWave, Doom, etc.
This PR adds the [ziglua](https://github.com/natecraddock/ziglua?ref=zig-0.16) dependency for its zig bindings and considerable buildtime config (mainly lua version selection).

### Example
 <video src="/attachments/3f91cf72-ae24-459c-8ef6-099f71e866fd" title="Screencast_20260519_172320" controls></video>
```lua
ly.frame_delay = 5
local timer = 0
local clock = os.clock()
local clock_diff = 0
function draw()
	timer = timer + 1
	byte = string.byte(' ')
	clock_diff = os.clock() - clock
	clock = os.clock()
	timer = timer + clock_diff
	for x = 0, ly.width-1 do
		for y = 0, ly.height-1 do
			local xc = 0xFF
			if x < 255 then xc = ((x + math.floor(timer / 2)) * 3) % 255 else xc = 0 end
			local yc = 0xFF
			if y < 255 then yc = ((y) * 3) % 255 else yc = 0 end
			ly.putCell(byte, xc, bit.bor(xc, yc), x, y)
		end
	end
end
```

### The API

The API that Ly gives to the user is minimal. A table is globally available, named `ly`, which provides the following:

| Member  | Purpose  |
|---------|---------|
| `ly.width` & `ly.height` | Respective Width/Height from the `TerminalBuffer` |
| `ly.putCell(byte, fg, bg, x, y)` | Literally `Cell.init(byte, fg, bg).put(x, y)`.|
| `ly.clock()` | The current real-time, in microseconds. |

### Error Handling

On a Lua Error, Ly won't quit but will instead paint the entire background red. The lua error in question can be found in the Ly log file and on-screen.
```log
2026-05-19 16:13:40 [err/Lua] Error (Cannot call draw()): attempt to call a nil value
2026-05-19 11:05:51 [err/Lua] Lua Error: ...dsammyt/programming/probe/ly/scratch/testConfig/test.lua:30: bad argument #1 to 'ipairs' (table expected, got number)
```

## Pre-requisites

- [X] I have tested & confirmed the changes work locally
- [X] I have run `zig fmt` throughout my changes

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1001
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-06-19 20:05:00 +02:00
AnErrupTion
e833c4bc1d
auth: Create XDG_RUNTIME_DIR if /run/user/UID exists
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-06-18 20:16:25 +02:00
AnErrupTion
e138563874
README: Specify Alpine for /etc/inittab
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-06-18 19:51:18 +02:00
AnErrupTion
8942e30a9e
Merge branch 'master' of codeberg.org:fairyglade/ly
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-06-18 00:05:51 +02:00
AnErrupTion
a801d1a569
Clean up save code
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-06-17 22:23:12 +02:00
AnErrupTion
aa00f7c8c7
Replace ArrayListUnmanaged with ArrayList
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-06-15 16:53:24 +02:00
Yuri dos Santos
ed783b5a23 Minor pt_BR translation correction to sound more natural (#1018)
## What are the changes about?

Changed two sentences to sound more natural to Brazilian Portuguese speakers.

- Token to masculine adj. Token is often (if not always) described as masculine subject (e.g [Wikipedia](https://pt.wikipedia.org/wiki/Token_(an%C3%A1lise_l%C3%A9xica)))
- Moved sentence to be written using "falhou" always in the end of a phrase to follow the same pattern as the other similar sentences. Also, using "falhou" at the beginning of the sentence is not wrong, but sounds a bit weird and it's not common in formal texts

## What existing issue does this resolve?

Just translation related issues

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have read and fully adhere to the rules set in the contributing guidelines found in `CONTRIBUTING.md`

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1018
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-06-14 21:59:15 +02:00
oliebe
f683a82f56 Fix small typos in readme.md (#1017)
## What are the changes about?

I fixed some incorrect inflections and typos in the readme

## What existing issue does this resolve?
N/A

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have read and fully adhere to the rules set in the contributing guidelines found in `CONTRIBUTING.md`

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1017
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-06-14 21:03:41 +02:00
Дмитрий
008d413295 Fix user detection if /etc/login.defs does not contain both UID_MIN and UID_MAX (#1014)
## What are the changes about?

I modified getUserIdRange(), so it checks if either of UID_MIN and UID_MAX is set in /etc/login.defs. If any, but not both, is not set, it uses fallback value. I don't know zig build system, so I added ugly fallback values passthrough.

## What existing issue does this resolve?

#1013

## Pre-requisites

- [X] I have tested & confirmed the changes work locally
- [X] I have read and fully adhere to the rules set in the contributing guidelines found in `CONTRIBUTING.md`

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1014
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-06-12 12:09:37 +02:00
AnErrupTion
a07ca88c74
build.zig: Fix new locales not being installed
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-06-11 20:35:42 +02:00
AnErrupTion
d8c3aec668
Update PR template to reflect new contributing guidelines
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-06-10 14:49:39 +02:00
AnErrupTion
5b8ebeba6e
Create CONTRIBUTING.md
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-06-10 14:45:48 +02:00
koru
32436d438e Update err_crawl for Russian lang (#1011)
See: https://codeberg.org/fairyglade/ly/pulls/997#issuecomment-15674144
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/1011
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-06-06 11:54:29 +02:00
Wicin-134
ace79f76b5 Overhaul language files (#997)
- Added sr_Cyrl.ini as new Cyrillic  variant for the Serbian Language
- Added zh_TW.ini (Traditional Chinese)
- Completed ALL missing keys in EVERY translation

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/997
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-06-06 11:48:31 +02:00
AnErrupTion
35be66e66f
termbox2: Log init errors
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-30 12:17:25 +02:00
AnErrupTion
0ac11065f4
ly-kmsconvt: Set TERM=linux
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-30 12:17:06 +02:00
AnErrupTion
9d8ccf6709
Fix termbox already being initialised when reclaiming
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-23 12:31:05 +02:00
AnErrupTion
6f3fdc4708
Merge branch 'master' of codeberg.org:fairyglade/ly
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-22 17:16:53 +02:00
AnErrupTion
0cee1c039a
ly-kmsconvt: Remove usage of gone --seats argument
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-22 17:15:17 +02:00
Louis Pate
f03aca0379 Be specific about zig version in readme (#996)
## What are the changes about?

There have been quite a few issues about the required zig version which
have appeared on codeberg recently. Largely these issues are opened
(like mine, #995) because individuals attempt to build using development
versions of zig and not the tagged releases.

The lack of a locked version (only a minimum) creates these issues.
Barring a locking mechanism, which I do not feel is my place to
implement, we should be clear to newer developers in Zig that you need
this specific version, not a development/master versioned release.

This is WIP mainly because I feel that a version file would be a better solution
than a README note, and I don't have enough zig experience or knowledge of
this repo to make a guess at what is best for this scenario.

## What existing issue does this resolve?

N/A

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have run `zig fmt` throughout my changes

Co-authored-by: Louis Pate <louispate@gmail.com>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/996
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-05-20 20:17:11 +02:00
AnErrupTion
0cd8b2ebfc
README: Add section for testing config changes (closes #994)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-18 19:46:28 +02:00
AnErrupTion
4c066ce564
Handle termbox2 errors
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-17 13:26:46 +02:00
AnErrupTion
05c1d4bece
Improve errors for lock state
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-16 10:33:06 +02:00
AnErrupTion
78794b3e10
Stream dur reading instead of reading all at once
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-15 23:22:50 +02:00
AnErrupTion
afee1d9194
Remove TODO and stop forcing LLVM usage (again)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-15 21:46:20 +02:00
AnErrupTion
9b1965a3d8
Always build translate-c in Debug
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-15 21:42:13 +02:00
AnErrupTion
741e9e0345
Use correct naming convention for functions in DurFile.zig
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-12 20:35:17 +02:00
AnErrupTion
de8579854c
Use $EXECUTABLE_NAME in kmscon service
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-11 21:03:01 +02:00
AnErrupTion
ee3196bab8
Fix labels_max_length calculation (closes #984)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-11 21:00:52 +02:00
AnErrupTion
9ff4ddd129
Improve keyboard handling (closes #982)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-10 13:55:19 +02:00
Titanium Brain
b3830d5bb6 fix(dur): apply correct offset for animations bigger than the terminal (#966)
Animations bigger than the rendering area would have an alignment to the
top left instead of center.

## What are the changes about?

Fixes incorrect offset calculations for dur movies bigger than the screen.

## What existing issue does this resolve?
N/A

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have run `zig fmt` throughout my changes

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/966
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-05-09 21:06:45 +02:00
Titanium Brain
b8ae126623 Apply the typestate pattern to DurFormat (#972) 2026-05-04 12:34:32 +02:00
AnErrupTion
4db9295102
Fix building without X11
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-03 20:05:44 +02:00
AnErrupTion
864f5f2892
Implement syslog functionality (closes #
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-01 21:51:27 +02:00
AnErrupTion
c50af66407
Fix log file race condition
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-01 20:54:54 +02:00
MartorSkull
fdf241bed5 Add option to move the box relative to the screen size (#964)
## What are the changes about?

Added a new option in the configuration file for moving the box relative to the screen size.

```
box_h_position = 0.5
box_v_position = 0.5
```

The big clock is centered relative to the box. In the cases where it would be outside of the screen, it moves the box to fit in the screen.

## What existing issue does this resolve?

Add more options for personalization

## Examples

Normal usage:
```
box_h_position = 0.15
box_v_position = 0.35
```
![image](/attachments/6595dfa9-aade-45f4-887c-e5db7f8d5a89)

Clock would be outside of the screen vertically:
```
box_h_position = 0.15
box_v_position = -1.0
```
![image](/attachments/0d6bdcc4-e9dd-4671-a65d-b8b9f063ffb6)

Clock would be outside of the screen horizontally and vertically:
```
box_h_position = -1.0
box_v_position = -1.0
input_len = 3
```
![image](/attachments/630b07fd-b400-4e71-8a67-1baf3a6700a0)

Clock would be outside of the screen horizontally and vertically on the bottom left of the screen:
```
box_h_position = 2.0
box_v_position = 2.0
input_len = 3
```
![image](/attachments/28902967-11a8-4c02-a4c9-1b92f9a728ee)

## What existing issue does this resolve?

_Replace this with a reference to an existing issue, or N/A if there is none_

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have run `zig fmt` throughout my changes

Co-authored-by: AnErrupTion <anerruption+codeberg@disroot.org>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/964
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-05-01 20:09:34 +02:00
AnErrupTion
79eebd8ee0
Prefer std.log instead of stderr directly
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-01 17:43:28 +02:00
AnErrupTion
3869bfd2f9
Add config validation argument (closes #969)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-01 17:40:04 +02:00
AnErrupTion
5905e054c5
Start Ly v1.5.0 development cycle
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-05-01 07:47:48 +02:00
AnErrupTion
807f6d249a
Remove further & all @cImport() usage in interop
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-04-30 22:48:29 +02:00
AnErrupTion
15cd0c4779
Use SIGINT instead of SIGCHILD for TTY control transfer
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-04-30 14:38:56 +02:00
AnErrupTion
51c5c3ee0b
Fix waitpid() being interrupted by SIGCHLD
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-04-29 22:52:58 +02:00
AnErrupTion
59c07aa3ba
Fix xauth log not being flushed
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-04-28 19:35:21 +02:00
AnErrupTion
80d4b114f3
Fix 32-bit issues on Ly's side
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-04-26 18:37:33 +02:00
RadsammyT
4f45d92ea8 Fix: battery label positioning, custom keybinds not disappearing on hide_key_hints = false (#970)
## What are the changes about?

Fixes battery label positioning in regards to custom binds.
Since the label was on the top-left, it only accounted for the first line of built-in keybinds, and it didn't account for the other lines of custom ones.

This also fixes the custom keybinds not disappearing on `hide_key_hints = false`, which is my bad. whoops.

Also, with https://codeberg.org/fairyglade/ly/pulls/963 being a thing, we should probably think about deprecating this hardcoded battery label in favor of a custom label command, top-left by default.

## What existing issue does this resolve?

N/A

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have run `zig fmt` throughout my changes

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/970
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
2026-04-26 09:37:39 +02:00
AnErrupTion
5edf5251f6 Update to Zig 0.16.0 (#962)
Signed-off-by: AnErrupTion <anerruption@disroot.org>

## What are the changes about?

Ports the code base to Zig 0.16.0.

## What existing issue does this resolve?

N/A

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have run `zig fmt` throughout my changes

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/962
2026-04-25 17:37:34 +02:00
Jackson Delahunt
eec83179b9 config: add shell option to hide the shell session (#955)
The shell session is unconditionally added to the session list with no way to hide it. This is inconsistent with `xinitrc`, which is omitted from the list when set to `null`.

This change adds a `shell` boolean config option (default `true`). Setting it to `false` hides the shell session from the list, following the same pattern as `xinitrc`.

Co-authored-by: Jackson Delahunt <jackson@stemn.com>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/955
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: Jackson Delahunt <sabrehagen@noreply.codeberg.org>
Co-committed-by: Jackson Delahunt <sabrehagen@noreply.codeberg.org>
2026-04-01 19:00:37 +02:00
Jackson Delahunt
b8048234d9 config: add show_tty option to display active TTY in top right corner (#956)
When running multiple ly instances across different TTYs there is no way to tell which TTY a given login screen belongs to at a glance.

This change adds a `show_tty` boolean config option (default `false`) that displays the active TTY number (e.g. `tty3`) in the top right corner. When the clock is also enabled the TTY label sits immediately to its right on the same row. When the clock is disabled it occupies the top right corner on its own.

I'm open to advice from the maintainers on the placement of the TTY label — positioning it next to the clock is simply my personal preference and it doesn't need to stay there if a different position is more appropriate.

Co-authored-by: Jackson Delahunt <jackson@stemn.com>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/956
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: Jackson Delahunt <sabrehagen@noreply.codeberg.org>
Co-committed-by: Jackson Delahunt <sabrehagen@noreply.codeberg.org>
2026-04-01 18:51:37 +02:00
Jackson Delahunt
10a873acb9 config: allow waylandsessions and xsessions to be set to null (#954)
`waylandsessions` and `xsessions` are currently non-optional string fields, so there is no clean way to disable session type discovery for users who do not use Wayland or X11. Setting them to a nonexistent path works but produces log errors on every startup.

This change makes both fields optional (`?[]const u8`), consistent with other nullable config fields such as `xinitrc`. Setting either to `null` in `config.ini` cleanly skips crawling for that session type with no side effects.

Co-authored-by: Jackson Delahunt <jackson@stemn.com>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/954
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: Jackson Delahunt <sabrehagen@noreply.codeberg.org>
Co-committed-by: Jackson Delahunt <sabrehagen@noreply.codeberg.org>
2026-03-29 08:32:27 +02:00
AnErrupTion
142476041d
Update French translation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-28 12:09:41 +01:00
RacerBG
fad683e035 Update the Bulgarian translation (#952)
## What are the changes about?

As the title says.

## What existing issue does this resolve?

N/A

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/952
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: RacerBG <racerbg@noreply.codeberg.org>
Co-committed-by: RacerBG <racerbg@noreply.codeberg.org>
2026-03-28 12:07:10 +01:00
AnErrupTion
e882eea22a
Improve bug report template
Notably, don't make the issue reproduction on a fresh install required.
This'll likely filter out the honest people who have actually done it
from the others who haven't.

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-27 23:18:16 +01:00
RadsammyT
fe6942d406 fix: custom label and bind ordering (#951)
## What are the changes about?

Fixes the order of custom labels and binds because of a HashMap shenanigan (no guaranteed order), so we use `ArrayHashMap` instead which preserves insertion order.  They should now be shown in the order they are declared in the config.

![image](/attachments/7a928c5f-fbbe-4a60-b120-3feddbcdfdb6)

![image](/attachments/22afe011-00a0-4a29-90ab-060e1d059c75)

## What existing issue does this resolve?

!950

## Pre-requisites

- [x] I have tested & confirmed the changes work locally
- [x] I have run `zig fmt` throughout my changes

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/951
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: RadsammyT <radsammyt@gmail.com>
Co-committed-by: RadsammyT <radsammyt@gmail.com>
2026-03-27 22:46:37 +01:00
AnErrupTion
5b7c7dfdf5
migrator.zig: Run zig fmt
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-27 21:34:32 +01:00
AnErrupTion
074bb0a68a
Improve custom command sample config readability (closes #949)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-27 20:34:39 +01:00
AnErrupTion
e0a3364169
Merge branch 'master' of codeberg.org:fairyglade/ly
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-27 17:17:18 +01:00
RadsammyT
7a8d913531 Feature: Add custom command & label support (#945)
## What are the changes about?

Adds customizable commands and labels to ly.
Solves https://codeberg.org/fairyglade/ly/issues/905.

Since Ly doesn't use INI headers. I use them exclusively for declarations of custom commands and labels.

### Commands

Bind a keybind to a command, and add a hint to the HUD. Useful for use cases like display brightness, switching between GPUs, etc.

Supports localization in the `name` field only. ex: where `lang = es`: `$brightness_up` => `bajar brillo`

Declared in config.ini with the following:

```ini
[cmd:F8]
name = custom command 2
cmd = touch /tmp/ly.gaming
```

### Labels

Add a label to the HUD. As specified in #905.
The text of the label corresponds to the output of the command specified in `[lbl:NAME]`.
Only shows the first line of the output.

Declared in config.ini with the following:

```ini
[lbl:kernel]
cmd = uname -srn
refresh = 0
```

Example to add to the config.ini:
```ini
# Declare a command with the F8 binding.
[cmd:F8]
#The name of the command to show up in Ly.
name = custom command
cmd = touch /tmp/ly.gaming

# Declare a label with an ID. This ID should be unique across all labels.
[lbl:kernel]
cmd = uname -srn
# In frames, the time to re-run the command and update the label. If 0, only run once- do not refresh.
refresh = 0

# Once you're done setting up labels and commands, add an empty header
# below to continue configurating the rest of Ly.
# Put other settings not belonging to custom commands/labels below here.
[]

```

## Pre-requisites

- [x] I have tested & confirmed the changes work locally

![image](/attachments/f9373ac9-567e-4f47-987c-1df6f4ee0d84)

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/945
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: RadsammyT <radsammyt@gmail.com>
Co-committed-by: RadsammyT <radsammyt@gmail.com>
2026-03-27 17:15:49 +01:00
AnErrupTion
984ac596af
Group for loops in event loop
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-26 21:55:35 +01:00
AnErrupTion
ed486c29d2
Add xauth file as X server argument
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-26 21:10:50 +01:00
AnErrupTion
a6fc5d67e8
Use upstream zigini library
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-25 22:43:05 +01:00
AnErrupTion
549576aa3e
Make box widget not position-dependent
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-25 22:06:57 +01:00
AnErrupTion
3758b5da1b
Mention -quiet argument for X11 server (closes #722)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-25 21:10:09 +01:00
AnErrupTion
5e1c681385
Update screenshot (closes #948)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-25 20:49:53 +01:00
AnErrupTion
ac78ccc398
Update Kawaii-Ash's GitHub username
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-25 20:06:59 +01:00
Tom
e548333473 Improve README.md (#946)
Signed-off-by: Tom <tom@helix.me.uk>

## What are the changes about?

Sentence structure updates and converting text to md syntax

## What existing issue does this resolve?

Fixes: #943

- Updated a few sentences to make them more understandable
- Converted the **Note**'s etc to md formatting.

## Pre-requisites

- [✓] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/946
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: Tom <tripplehelix@noreply.codeberg.org>
Co-committed-by: Tom <tripplehelix@noreply.codeberg.org>
2026-03-21 19:30:23 +01:00
AnErrupTion
60e3380375
Switch to single-instance Widget model
And make widget() functions return pointers to widgets instead of just
widgets

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-21 16:19:33 +01:00
AnErrupTion
aa392837bc
Require "zig fmt" to be run in a PR
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-21 16:01:03 +01:00
AnErrupTion
abe72c74ff
Optimise event loop initialisation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-18 20:26:00 +01:00
Mr. Cat
dda56eab37 Fix compilation error when building without X11 support (#947)
## What are the changes about?

Add an argument to info log function to fix compiler error.

## What existing issue does this resolve?

If building with `zig build -Denable_x11_support=false` there is a compiler error about a missing argument.
```
install
└─ install ly
   └─ compile exe ly Debug native 1 errors
src/main.zig:730:27: error: member function expected 3 argument(s), found 2
        try state.log_file.info(
            ~~~~~~~~~~~~~~^~~~~
ly-core/src/LogFile.zig:26:5: note: function declared here
pub fn info(self: *LogFile, category: []const u8, comptime message: []const u8, args: anytype) !void {
~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
referenced by:
    callMain [inlined]: /home/oskar/.cache/zig/p/N-V-__8AACFNhBSaulceFT2Wx6Mx-ycGtZh7CEztyVdtX2jW/lib/std/start.zig:627:37
    callMainWithArgs [inlined]: /home/oskar/.cache/zig/p/N-V-__8AACFNhBSaulceFT2Wx6Mx-ycGtZh7CEztyVdtX2jW/lib/std/start.zig:587:20
    main: /home/oskar/.cache/zig/p/N-V-__8AACFNhBSaulceFT2Wx6Mx-ycGtZh7CEztyVdtX2jW/lib/std/start.zig:602:28
    1 reference(s) hidden; use '-freference-trace=4' to see all references
```

## Pre-requisites

- [x] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/947
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: Mr. Cat <mrcat@posteo.com>
Co-committed-by: Mr. Cat <mrcat@posteo.com>
2026-03-18 20:18:45 +01:00
AnErrupTion
9c50297059
Fix insert mode hack + fix bugs
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-17 23:58:06 +01:00
AnErrupTion
acac884cfe
Add support for local keybinds
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-17 22:58:39 +01:00
AnErrupTion
a89c918c5d
Move back custom widgets into main project
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-17 21:59:24 +01:00
AnErrupTion
64539f4342
Split UI code into ly-ui library
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-17 21:44:33 +01:00
AnErrupTion
93696a6b30
Remove unused import + add TODO
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-17 21:03:59 +01:00
AnErrupTion
4f26eeada0
Fix double spacing issue in labels
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-17 20:10:47 +01:00
AnErrupTion
83e98a185f
Add TODO in main.zig
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-17 20:01:47 +01:00
AnErrupTion
128dcb16f8
Merge branch 'master' of codeberg.org:fairyglade/ly
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-03-17 19:58:58 +01:00
Luna
3a4109eb2d Add toggle visibility to password (#938)
## What are the changes about?

I have add a keybinding to toggle the visibility of the password

## What existing issue does this resolve?

N/A

## Pre-requisites

- [x] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/938
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: Luna <luna-39@noreply.codeberg.org>
Co-committed-by: Luna <luna-39@noreply.codeberg.org>
2026-03-17 12:27:23 +01:00
GalaxyShard
7cefff4570 Add Esperanto translation (#942)
Title.

- [x] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/942
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: GalaxyShard <dominic.adragna@byteroach.com>
Co-committed-by: GalaxyShard <dominic.adragna@byteroach.com>
2026-03-16 23:16:43 +01:00
OSVidYapan
f31c55b562 Improve README.md (#940)
For anyone who doesn't know see this thread;
https://codeberg.org/fairyglade/ly/pulls/934
This is the original post i have fixed the commit log issue
(by recreating entire new fork)
Note that is is also grammarly fixed version.
(mostly please advise if i still have those mistakes)

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/940
Reviewed-by: AnErrupTion <anerruption+codeberg@disroot.org>
Co-authored-by: OSVidYapan <osvidyapan@noreply.codeberg.org>
Co-committed-by: OSVidYapan <osvidyapan@noreply.codeberg.org>
2026-03-16 21:13:08 +01:00
AnErrupTion
9cde291ac7
Remove unused termbox alias
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-27 22:19:09 +01:00
AnErrupTion
03d976171a
Fix battery level overlapping shutdown label (fixes #935)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-27 20:28:11 +01:00
AnErrupTion
b01e4afc79
Make default startup script more compatible
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-12 17:10:52 +01:00
AnErrupTion
5a4605ffb6
Add layering system for widgets
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-12 11:29:14 +01:00
AnErrupTion
01dcfa207e
Show UI errors in info line again
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-12 11:20:54 +01:00
AnErrupTion
32d5330efb
Move the event loop to a separate function
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-12 01:19:19 +01:00
AnErrupTion
5564fed664
Add Widget.calculateTimeout function
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-12 00:27:07 +01:00
AnErrupTion
7c7aed9cb2
Make animation timeout independent of event loop
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-11 23:51:32 +01:00
AnErrupTion
57c96a3478
Fix animation timeout bug + remove redundant check
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-11 22:55:10 +01:00
AnErrupTion
6773f74788
Add widget display name to improve logging
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-11 21:51:07 +01:00
AnErrupTion
b389e379fa
Make handling inputs widget-independent
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-11 21:00:00 +01:00
hynak
4a72e41e44 Fix default startup script (#929)
## What are the changes about?

Discussed in !920. Adds fixes to the startup script by removing the array usage (some shells use arrays different/unsupported) and adds stdout_behavior Inherit flag to the child process to propagate the echos to the TTY.

## What existing issue does this resolve?

N/A

## Pre-requisites

- [x] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/929
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: hynak <hynak@noreply.codeberg.org>
Co-committed-by: hynak <hynak@noreply.codeberg.org>
2026-02-10 22:20:30 +01:00
AnErrupTion
d268d5bb45
Add the cascade animation as a separate widget
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-10 17:43:55 +01:00
Cyaxares
f320d3f666 Add Kurdish translation (#930)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/930
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: Cyaxares <cyaxares@noreply.codeberg.org>
Co-committed-by: Cyaxares <cyaxares@noreply.codeberg.org>
2026-02-10 10:47:50 +01:00
AnErrupTion
70e95f094a
Fix incorrect key parsing
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-10 10:41:08 +01:00
AnErrupTion
207b352888
Add central Widget struct + clean up code
In particular, move all termbox2 usage to TerminalBuffer.zig &
keyboard.zig

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-10 00:22:27 +01:00
AnErrupTion
99dba44e46
Rename min_refresh_delta option to animation_frame_delay (closes #925)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-09 11:38:56 +01:00
AnErrupTion
d1810d8c98
Fix numlock & capslock positioning
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-09 11:20:27 +01:00
AnErrupTion
852a602032
Support more configurable keybindings (closes #679)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-09 11:11:00 +01:00
AnErrupTion
cf5f62661c
Remove resolution_changed bool
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-08 22:23:07 +01:00
AnErrupTion
1db780c7a7
Better handle screen resizes
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-08 22:18:00 +01:00
AnErrupTion
941b7e0dae
Properly calculate string lengths
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-08 22:04:09 +01:00
AnErrupTion
e9e2d51261
Better bigclock positioning
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-08 21:36:18 +01:00
AnErrupTion
769aefd6e9
Add separate big label widget for bigclock
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-08 21:14:49 +01:00
AnErrupTion
f678e3bb28
Add update function for Label
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-08 19:50:32 +01:00
AnErrupTion
f22593f828
Add Label component & make colors custom
This commit also makes Ly more resilient to (impossible) screen
resolutions.

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-08 17:40:50 +01:00
AnErrupTion
7bbdebe58b
Fix center offset error
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-08 15:09:55 +01:00
AnErrupTion
bca38856b1
Completely refactor widget placement code
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-08 14:53:36 +01:00
laur
8c08359e51 custom-sessions/README: remove extraneous comma (#927)
## What are the changes about?

Fix a typo.

## What existing issue does this resolve?

N/A

Co-authored-by: laur <laur.aliste@gmail.com>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/927
Co-authored-by: laur <laur@noreply.codeberg.org>
Co-committed-by: laur <laur@noreply.codeberg.org>
2026-02-08 13:16:25 +01:00
AnErrupTion
fd81da7cbd
Organise imports
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-07 17:01:11 +01:00
AnErrupTion
950eeed3ee
Log which VT is used
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-07 10:48:05 +01:00
AnErrupTion
ce0d00771d
Use the unifont engine for KMSCON
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-07 10:43:08 +01:00
WinuxVidYapan
b032c9b296 Update Turkish translations (#917)
So i am sorry for unintended behavior i am new to Codeberg and pull requests
But here are the words.

- [x] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/917
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: WinuxVidYapan <winuxvidyapan@noreply.codeberg.org>
Co-committed-by: WinuxVidYapan <winuxvidyapan@noreply.codeberg.org>
2026-02-05 20:19:41 +01:00
AnErrupTion
7744745f09
Don't try to switch TTY with KMSCON
It can do it automatically (see the corresponding service file).

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-05 19:55:40 +01:00
AnErrupTion
11735290b8
Fix active TTY detection for KMSCON
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-05 19:50:48 +01:00
AnErrupTion
21fca058e7
Don't install startup.sh with installnoconf
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-05 15:03:05 +01:00
AnErrupTion
2b46a81796
Add basic KMSCON support (closes #886)
It's not perfect yet, but at least it works!

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-05 15:01:45 +01:00
hynak
72f43fbc56 Add shell script showing how change TTY colors (#920)
## What are the changes about?

What was discussed in !912 before I accidentally caused it to auto merge (still not sure how that happened). I assume this is what was meant when asking for it to be in the startup script commented out.

## What existing issue does this resolve?

N/A

## Pre-requisites

- [x] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/920
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: hynak <hynak@noreply.codeberg.org>
Co-committed-by: hynak <hynak@noreply.codeberg.org>
2026-02-04 20:36:55 +01:00
AnErrupTion
b00d6899e5
Fix buffer not resizing with no animation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-04 20:16:53 +01:00
AnErrupTion
7ce8ff61fe
Unify ini parsing logic
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-02-01 10:41:06 +01:00
AnErrupTion
1a04a608a1
Remove dummy animation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-31 22:51:44 +01:00
AnErrupTion
b0dcc12785
Move UI drawing to separate function
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-31 22:35:28 +01:00
AnErrupTion
b672d04dc6
Improve authentication logging
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-31 11:46:55 +01:00
AnErrupTion
2e04ea4d79
Add time, severity & category in logs
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-31 11:17:36 +01:00
AnErrupTion
0c12008327
Move more termbox usage into TerminalBuffer
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-31 10:36:08 +01:00
AnErrupTion
7934060d3b
Credit Kawaii-Ash in the README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-29 21:31:45 +01:00
AnErrupTion
a158098df0
Add config.x_vt option (fixes #910)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-26 20:45:33 +01:00
AnErrupTion
5bfa1670cc
Better systemd-homed user detection (fixes #913)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-26 20:36:44 +01:00
hynak
a4076b83da [dur] Add support for alignments and negative offsets + Ly logo (#893)
## What are the changes about?
Add support for letting a user use a negative offset (#880), alignment, and logo. Below is example of the logo file, I hope it is what was request :). It has no padding so a user can move the alignment and offset to get it how they want on screen.

This technically is good to go, except I didn't upload the logo file as I'm not sure where to add the animation file to get it to here: $CONFIG_DIRECTORY/ly/example.dur

![logo-preview](/attachments/5a829dbd-7708-4d0a-9841-d024902ede68)

## What existing issue does this resolve?

#880

## Pre-requisites

- [x] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/893
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: hynak <hynak@noreply.codeberg.org>
Co-committed-by: hynak <hynak@noreply.codeberg.org>
2026-01-25 23:08:42 +01:00
AnErrupTion
2eea683078
Fix wrong session being chosen in autologin (closes #911)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-18 21:35:49 +01:00
AnErrupTion
d7f64676ee
Split core code into ly-core library
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-18 21:07:53 +01:00
AnErrupTion
456916f059
Remove unused import in auth.zig
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-18 20:49:32 +01:00
AnErrupTion
2b1e4dc6c9
Fix undefined value in XCB connection check
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-18 18:03:21 +01:00
AnErrupTion
94c306758a
Update Matrix space link (envs.net migration)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-17 16:58:05 +01:00
AnErrupTion
82d24d7725
Add donation links
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-17 10:48:12 +01:00
AnErrupTion
5a51d5ced5
Fix building on musl (closes #760)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-06 22:27:36 +01:00
AnErrupTion
135d1e40f6
Update termbox2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2026-01-06 22:18:31 +01:00
AnErrupTion
c0c400e0b6
Recursively create xauth file directory if non-existent
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-30 19:35:40 +01:00
AnErrupTion
9e4147bfb4
Fix invalid XDG_RUNTIME_DIR if D-Bus isn't used
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-30 19:29:47 +01:00
AnErrupTion
8e893932f2
Clamp user session index if invalid
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-30 18:09:46 +01:00
AnErrupTion
26e7585b0b
Don't forget to allocate :D
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-30 17:43:00 +01:00
AnErrupTion
b1cb576f67
Check for session file name in autologin (closes #895)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-30 17:38:22 +01:00
AnErrupTion
add7f25f0d
Create session log directory if non-existent (closes #896)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-30 17:20:04 +01:00
Theo Gaige
2e7bb3eb58 Use buf_height and buf_width in Matrix.draw() (#903)
## What are the changes about?

buf_height is declared at the start of the draw() function of the
Matrix animation but both buf_height and self.terminal_buffer.height are
used in the function. replace every occurence of
self.terminal_buffer.height by buf_height for consistency.

The same goes for buf_width and self.terminal_buffer.width.

no functionnal changes

## What existing issue does this resolve?

N/A

## Pre-requisites

- [x] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/903
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: Theo Gaige <theo.gaige@free.fr>
Co-committed-by: Theo Gaige <theo.gaige@free.fr>
2025-12-29 23:39:10 +01:00
ViSzKe
e57de5172e Update Swedish translation (#899)
## What are the changes about?

Adding proper Swedish translation

## What existing issue does this resolve?

The sub-standard Swedish translation

## Pre-requisites

- [x] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/899
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: ViSzKe <vilgot.sz.k@gmail.com>
Co-committed-by: ViSzKe <vilgot.sz.k@gmail.com>
2025-12-29 23:36:47 +01:00
qbe
04d4447273 Escape TTY in systemd service (closes #889) (#890)
## What are the changes about?

* fix templated systemd dependencies: %I -> %i
* amend systemd-specific documentation in readme with section specific to systemd-logind / autovt

## What existing issue does this resolve?

[issue #889](https://codeberg.org/fairyglade/ly/issues/889)

## Pre-requisites

- [x] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/890
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: qbe <public.github.c@hannen.at>
Co-committed-by: qbe <public.github.c@hannen.at>
2025-12-18 12:17:00 +01:00
AnErrupTion
ced8f9bee3
Fix session not being saved correctly
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-17 17:32:16 +01:00
AnErrupTion
c6446db3e2
Start Ly v1.4.0 development cycle
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-06 15:58:39 +01:00
AnErrupTion
e0692885c5
Make delay floating point in dur format
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-06 14:34:47 +01:00
AnErrupTion
b6d3aa0e1c
Merge changes
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-06 14:25:57 +01:00
AnErrupTion
e6966a628c
Fix wrong session index + save file corruption
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-06 14:25:01 +01:00
RacerBG
cab3f1bfb5 Added the Latest Changes to the Bulgarian Translation (#879)
Signed-off-by: RacerBG <racerbg@noreply.codeberg.org>

## What are the changes about?

N/A

## What existing issue does this resolve?

N/A

## Pre-requisites

- [ x ] I have tested & confirmed the changes work locally

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/879
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: RacerBG <racerbg@noreply.codeberg.org>
Co-committed-by: RacerBG <racerbg@noreply.codeberg.org>
2025-12-06 13:39:52 +01:00
AnErrupTion
a9ff0a6d07
Only support dur format v7, set -1 color to black
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-06 10:05:41 +01:00
AnErrupTion
92beb24c80
Fix save file initialisation & incorrect saved session index
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-05 19:49:27 +01:00
hynak
7e18d906c4 [Feature] Add support for .dur file format and animations (closes #719) (#833)
Adds support for durdraw's .dur file format. Supports ascii, animations, and 16/256 color display.

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/833
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: hynak <hynak@noreply.codeberg.org>
Co-committed-by: hynak <hynak@noreply.codeberg.org>
2025-12-05 19:46:42 +01:00
AnErrupTion
3bfdc75a70
Add option to run command after inactivity delay (closes #747)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-03 20:39:27 +01:00
AnErrupTion
6cce221cbf
Improve issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-02 22:37:03 +01:00
AnErrupTion
1c99574f73
Add option to run command before UI is initialised (closes #798)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-02 22:23:10 +01:00
AnErrupTion
b249dba092
Support multiple TTYs with systemd service (closes #102)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-02 22:11:39 +01:00
AnErrupTion
3365b33d6d
Fix zig-clap dependency URL
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-12-01 22:09:41 +01:00
radsammyt
6cb53b6e38 Refactor active_input field-jumping logic (#873)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/873
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: radsammyt <radsammyt@noreply.codeberg.org>
Co-committed-by: radsammyt <radsammyt@noreply.codeberg.org>
2025-12-01 20:07:59 +01:00
AnErrupTion
d82fa82a87
Always add hostname last in the info line
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 23:50:39 +01:00
AnErrupTion
c2b3d794e8
Fix fallback UID range + add error if UID range not found
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 23:36:47 +01:00
AnErrupTion
e29bda3250
Add systemd-homed UID range
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 23:02:23 +01:00
AnErrupTion
4df2382698
Add possibility to disable auth_fails animation (closes #835)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 22:19:38 +01:00
AnErrupTion
3dfb86fe69
Merge changes
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 21:59:09 +01:00
AnErrupTion
f9a001b160
Update zigini dependency & use same Git URL format everywhere
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 21:57:42 +01:00
RacerBG
5235ca47c5 Add Bulgarian Translation (#872)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/872
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: RacerBG <racerbg@noreply.codeberg.org>
Co-committed-by: RacerBG <racerbg@noreply.codeberg.org>
2025-11-30 21:52:46 +01:00
AnErrupTion
a94abf2e69
Improve bug report template & add PR template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 12:08:10 +01:00
AnErrupTion
fe354a4809
Add fallback UID range options at compile-time
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 09:47:50 +01:00
AnErrupTion
8c964d9ce5
Don't crash when failing to crawl a session directory (closes #870)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 09:30:33 +01:00
AnErrupTion
392ea6ea63
Update French locale
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 09:18:29 +01:00
AnErrupTion
10854e643a
Don't crash when failing to parse arguments
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-30 09:14:10 +01:00
ebits
1980b2e479 Feature: Added option for hibernate between sleep and brightness down (#867) (closes #866)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/867
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: ebits <tspamiitesh@gmail.com>
Co-committed-by: ebits <tspamiitesh@gmail.com>
2025-11-28 19:05:17 +01:00
AnErrupTion
816be7449f
Update Repology URL
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-19 22:19:12 +01:00
AnErrupTion
f0758d812e
Fix potential out of bounds issue when automatically changing session
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-11-19 22:15:07 +01:00
AnErrupTion
3c0c84d067
Fix build error
I really should test PRs before merging them, shouldn't I?
2025-11-15 20:02:59 +01:00
notiant
cc07c4870a Add option to hide CapsLock and NumLock states (#864)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/864
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: notiant <codeberg.cizj4@simplelogin.com>
Co-committed-by: notiant <codeberg.cizj4@simplelogin.com>
2025-11-15 16:32:42 +01:00
RomanPro100
68ec85f412 Add missing Russian translations (#863)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/863
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: RomanPro100 <romanpro100@disroot.org>
Co-committed-by: RomanPro100 <romanpro100@disroot.org>
2025-11-09 17:35:26 +01:00
Corey Newton
1537addd67 Change default session log directory to ~/.local/state (#859)
This addresses #808 and #823 conclusively for all parties.
- It requires no additional directory creation.
- It does not impact users who do not have a problem.
- It adds additional documentation detail in both the bug template and
the readme to help users who *are* having a problem.
- Fulfills the spirit of the [XDG Spec(https://specifications.freedesktop.org/basedir-spec/latest)
but not in practice as `$XDG_CONFIG_HOME` and `$HOME` and the associated
logic are not implemented. `~/.local/state` is the fallback location.

In particular, the spec indicates:
> It may contain: actions history (**logs**, history, …)

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/859
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: Corey Newton <corey.newton@zenoprax.com>
Co-committed-by: Corey Newton <corey.newton@zenoprax.com>
2025-11-05 21:57:10 +01:00
Piotr Ginał
2da3648179 Add missing Polish translations (#861)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/861
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: Piotr Ginał <ginal.piotr11@gmail.com>
Co-committed-by: Piotr Ginał <ginal.piotr11@gmail.com>
2025-11-03 19:45:59 +01:00
ferreo
0cf752f3b8 [Feature] Add autologin support (#841) (closes #200)
This is the simplest change I could come up with to add working autologin, only bit I really dislike is the event but it seemed like the cleanest way without refactoring.

Co-authored-by: ferreo <harderthanfire@gmail.com>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/841
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: ferreo <ferreo@noreply.codeberg.org>
Co-committed-by: ferreo <ferreo@noreply.codeberg.org>
2025-10-25 23:09:24 +02:00
AnErrupTion
15c78c838a
Update screenshot (for real this time)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-25 20:51:51 +02:00
AnErrupTion
4a486bd876
Update screenshot
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-25 20:49:56 +02:00
AnErrupTion
38173d8557
Add edge margin to numlock & capslock
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-25 20:39:34 +02:00
AnErrupTion
95d1d9378c
Add edge margin to top-right clock
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-23 22:53:58 +02:00
AnErrupTion
6f62106fcc
Merge changes
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-23 22:16:24 +02:00
AnErrupTion
4171e29995
Fix build error when runit service symlink already exists
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-23 22:15:43 +02:00
AnErrupTion
80c27224e9
Create xauth directory if it doesn't exist
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-23 22:15:16 +02:00
ebits
106f157a2c [Feature] Add edge margin option (#856) (closes #848)
Allows setting a balanced margin on all sides.

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/856
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: ebits <tspamiitesh@gmail.com>
Co-committed-by: ebits <tspamiitesh@gmail.com>
2025-10-23 19:07:54 +02:00
AnErrupTion
ec16ad5dfc
Remove mention of config.tty option in README (fixes #854)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-21 17:36:42 +02:00
AnErrupTion
5f22173b91
Mention only Ly v1.2.0 and above are supported in issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 19:10:44 +02:00
AnErrupTion
4bc405f239
Start Ly v1.3.0 development cycle
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 18:12:16 +02:00
AnErrupTion
c4b68364ef
Add FreeBSD-specific PAM file
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 17:34:44 +02:00
AnErrupTion
1c05664c85
Fix xauth file name UB
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 16:18:05 +02:00
AnErrupTion
1fbcb10110
Open new log file after every fork() where necessary
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 15:29:47 +02:00
AnErrupTion
bfb3f925d9
Use shutdown -p now on FreeBSD, update instructions
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 13:47:52 +02:00
AnErrupTion
44c8acff16
Open new log file handle after fork()
This would also need to be done in the
nested fork() calls.

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 12:03:48 +02:00
AnErrupTion
541eae5311
Update init service for FreeBSD
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 11:23:57 +02:00
AnErrupTion
3faf3dec42
Fix login issue
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 08:41:36 +02:00
AnErrupTion
52d29bbd47
Fix FreeBSD installation in README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 00:46:56 +02:00
AnErrupTion
a3a8f11575
Add basic FreeBSD service, change default fallback TTY to 2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 00:46:09 +02:00
AnErrupTion
02f5aa702d
Implement /etc/login.defs in interop, TODO for FreeBSD
We should be able to parse the "minuid" and "maxuid"
values in /etc/rc.conf to get the UID range of the
system, with default values of 1000 to 32000 (as
they don't seem to be present by default).

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 00:28:12 +02:00
AnErrupTion
ed88458efd
Fix platform-specific bugs for FreeBSD compilation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-18 00:05:53 +02:00
AnErrupTion
8df9603188
Install SysVinit service as an executable
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-17 23:12:21 +02:00
AnErrupTion
412994775b
Add SysVinit service (closes #224)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-17 23:10:52 +02:00
AnErrupTion
bb669c239c
Remove config TTY options in OpenRC & runit services
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-17 23:08:15 +02:00
AnErrupTion
657daafec8
Fix crash after reading saved credentials for first time
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-17 22:58:50 +02:00
AnErrupTion
e36872baa6
Don't spam battery status error if already tried once
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-15 16:32:57 +02:00
AnErrupTion
4738ca81a7
Merge changes
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-15 14:10:00 +02:00
Matthew Rothlisberger
1f2453f0fb Disable battery status display by default (#852)
A couple things to fix in the new battery status display configuration.

I think this should be disabled by default. My reasoning:
- Historically a conservative approach is taken with new capabilities in Ly; even the clock is disabled by default
- The existing default creates a regression (error message) for anyone without `/sys/class/power_supply/BAT0` on their system (all non-portable PCs, and laptops that use a different identifier)
- The battery status check causes animations to momentarily hang at a regular interval

Other changes:
- Comment for `battery_id` aligned with similar config switch comments (description / useful information / effect of null setting)
- `battery_id` moved to its correct alphabetical position in the config file
- Setting aligned between `Config.zig` and `config.ini` (the prototypical config file should reflect the actual default)
- Configurations prefixed with `hide_` alphabetized

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/852
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: Matthew Rothlisberger <mattjrothlis@gmail.com>
Co-committed-by: Matthew Rothlisberger <mattjrothlis@gmail.com>
2025-10-15 13:58:04 +02:00
AnErrupTion
76da16904f
Update French translation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-14 21:17:58 +02:00
AnErrupTion
e577c454da
Fix merge conflict
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-14 21:10:17 +02:00
AnErrupTion
09c2cfb74d
Remove file that was added by mistake
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-14 21:07:42 +02:00
AnErrupTion
b3f1e91cf6
Remember last session for each user (closes #619)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-14 21:05:54 +02:00
AnErrupTion
aef1dd9c1a
Add more logs when logging into an X11 session
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-14 20:09:39 +02:00
AnErrupTion
3d977d2ff7
Fix compatibility with Zig 0.15.2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-14 20:05:50 +02:00
AnErrupTion
a34a5a97bd
Execute shell in case exec_cmd is null
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-14 20:02:55 +02:00
AnErrupTion
1839e4cb44
Use LLVM in Debug (closes #832)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-14 19:42:16 +02:00
Galtrhan
b2f51e5bc8 Adjust Latvian translation & add missing file entries to build.zig (#850)
Change Latvian literal translation that did not quite fit the role of
username to more appropriate.

Added missing locale files to build.zig

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/850
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: Galtrhan <galtrhan@gmail.com>
Co-committed-by: Galtrhan <galtrhan@gmail.com>
2025-10-13 21:43:46 +02:00
AnErrupTion
bd335c8c91
Add missing locales in build.zig
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-13 21:39:44 +02:00
AnErrupTion
4f4855b5e9
Implement dummy active TTY getter for FreeBSD
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-10-12 22:47:03 +02:00
Galtrhan
1e2faad0f8 Add Latvian language translation (#847)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/847
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: Galtrhan <galtrhan@gmail.com>
Co-committed-by: Galtrhan <galtrhan@gmail.com>
2025-10-12 20:57:28 +02:00
nyraa
cb4f1952cd Move version string to bottom-left corner (#846)
In #834, it was decided to move the version string to the bottom-left corner, and a new PR was opened to address it.

Co-authored-by: nyraa <112930946+nyraa@users.noreply.github.com>
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/846
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: nyraa <nyraa@noreply.codeberg.org>
Co-committed-by: nyraa <nyraa@noreply.codeberg.org>
2025-10-11 09:24:15 +02:00
ebits
81a17f2904 Fix: Default battery status to row 1 when hide_key_hints and hide_version_string is true (fixes #844) (#845)
Default battery status behaviour to usize 0 when hide_key_hints and hide_version_string is true as a fix to issue [#844](https://codeberg.org/fairyglade/ly/issues/844)

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/845
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: ebits <tspamiitesh@gmail.com>
Co-committed-by: ebits <tspamiitesh@gmail.com>
2025-10-11 08:47:13 +02:00
ebits
339e39d496 Adding the battery status for the top bar alongside brightness controls (closes #821) (#826)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/826
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: ebits <tspamiitesh@gmail.com>
Co-committed-by: ebits <tspamiitesh@gmail.com>
2025-10-09 18:48:46 +02:00
mctaylors
44faa263b1 change(config): add -n flag to prevent accidental backlight disabling (#840)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/840
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: mctaylors <cantsendmails@mctaylors.ru>
Co-committed-by: mctaylors <cantsendmails@mctaylors.ru>
2025-10-05 19:26:09 +02:00
AnErrupTion
7a0520687d
Add fallback TTY option (closes #838)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-09-29 21:30:51 +02:00
AnErrupTion
cee0e0ca4b
Log more detailed config error messages (closes #801)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-09-26 09:54:38 +02:00
AnErrupTion
3edd1ff1be
Log error name when zigini fails to parse config
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-09-25 13:35:28 +02:00
AnErrupTion
145ad5142c
Clean up redundant authentication & session code
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-09-25 08:32:30 +02:00
AnErrupTion
d8b0ae34f3
Remove semi-colons in default locale
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-09-25 08:30:13 +02:00
AnErrupTion
a6535b9152
Update custom sessions' README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-09-25 08:29:36 +02:00
AnErrupTion
ee97f3b5e1
Automatically detect TTY (closes #795)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-09-07 17:44:50 +02:00
AnErrupTion
5924db58e1
Use std.mem.span + remove useless dupeZ()
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-30 13:17:19 +02:00
AnErrupTion
36e220e2ff
Remove usage of std.c.stat() for xauth code
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-30 02:09:51 +02:00
AnErrupTion
0a9ceca822
Don't dupeZ() in main
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-30 01:09:12 +02:00
AnErrupTion
f9553655a3
Separate platform code from C interop code
Also, don't use isBSD() because we'll only support FreeBSD for
now. Other BSDs may not necessarily support Unicode characters
or the same ioctl constants as we do (or even ioctl at all).

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-30 00:27:04 +02:00
AnErrupTion
230874abd1
Don't forget to flush... :)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-29 22:54:05 +02:00
AnErrupTion
38c3ecd089
Remove unused import
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-29 22:42:34 +02:00
AnErrupTion
fec0815161
Always copy an example config file (partially addresses #801)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-29 22:18:50 +02:00
AnErrupTion
a7d6b06d21
Add partial logging in authentication process (closes #822)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-29 09:28:29 +02:00
AnErrupTion
1ee8010c24
Fix dinit & s6 service + don't hardcode paths in runit service
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-29 02:18:11 +02:00
AnErrupTion
f988bd334b
Update termbox2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-29 00:45:28 +02:00
AnErrupTion
aa0222948a
Update config migrator
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-29 00:35:11 +02:00
AnErrupTion
69d39dc035
Remove config.load
config.save already makes it redundant. Besides, who would want
to save the current username & session, but not want to load it
at the next boot?

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-29 00:32:49 +02:00
AnErrupTion
6d7dbb9f27
Fix typo & remove unused import
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-29 00:07:32 +02:00
AnErrupTion
7cfb947187
Reduce libc usage & move more stuff to interop
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-28 01:39:19 +02:00
AnErrupTion
336847d418
Merge changes from master
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-27 23:45:52 +02:00
AnErrupTion
ff9b6279d3
Update to Zig 0.15.0 (closes #829)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-27 23:44:39 +02:00
KaiJan57
c7dea3c178 [dinit] fix deprecated loginready, replaced by login.target (#828)
Fixes [corresponding issue](https://codeberg.org/fairyglade/ly/issues/827#issue-2234461)

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/828
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: KaiJan57 <kaijan57@noreply.codeberg.org>
Co-committed-by: KaiJan57 <kaijan57@noreply.codeberg.org>
2025-08-26 18:59:43 +02:00
AnErrupTion
d7a4535007
Update screenshot
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-11 12:32:10 +02:00
djsigmann
73ecac67bf Prevent Ly from zombifying when X.org is terminated (#807) (fixes #787)
If a compositor is running when exiting Xorg, the user is met with an unresponsive black screen without the ability to switch to a different TTY (the usual `Ctrl+Alt+F{1..6}`  chord doesn't do anything).
In addition, ly is displayed as a zombie process under `ps` and cannot be killed (observed by utilizing a preexisting ssh connection to the host).

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/807
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: djsigmann <djsigmann@protonmail.com>
Co-committed-by: djsigmann <djsigmann@protonmail.com>
2025-08-05 21:45:09 +02:00
AnErrupTion
7353e75d78
Merge branch 'master' of codeberg.org:fairyglade/ly 2025-08-05 08:26:38 +02:00
AnErrupTion
cd0accfb28
Show error name instead of error if shutdown/reboot fails
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-05 08:26:13 +02:00
João Lucas
b71789912d Add enable_session_log option to control session logging (#809) (fixes #808)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/809
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: João Lucas <jlucaso@hotmail.com>
Co-committed-by: João Lucas <jlucaso@hotmail.com>
2025-08-04 00:00:18 +02:00
AnErrupTion
5bacc8870b
Update repository link in README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-03 23:42:35 +02:00
Matthew Rothlisberger
a7ff18aa16 Add option for eight-color terminal output (#802)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/802
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: Matthew Rothlisberger <mattjrothlis@gmail.com>
Co-committed-by: Matthew Rothlisberger <mattjrothlis@gmail.com>
2025-08-03 23:37:53 +02:00
AnErrupTion
c3d180c213
Add basic general log file
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-03 15:59:47 +02:00
AnErrupTion
3f891d7f0d
Workaround for session process not exiting immediately
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-03 14:56:16 +02:00
AnErrupTion
e404d5bdb3
Clean-up: std.posix.kill() returns nothing
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-03 14:55:50 +02:00
AnErrupTion
dee055748c
Format code properly
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-03 13:47:53 +02:00
AnErrupTion
bd2d1142b2
Don't enable bigclock by default
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-03 13:05:28 +02:00
RadsammyT
b382d74969 bigclock: add 12-hour & seconds support (#805)
Added P,A,M characters to bigclock and added 12hr and seconds support to bigclock via `bigclock_12hr` and `bigclock_seconds` in the config.
![image](/attachments/e95accff-4822-4801-8159-94411a6c644f)
Image has bigclock_12hr and bigclock_seconds enabled.

Farsi characters for P,A,M are blank since I don't know what it would look like in their language. (should i have just used the english characters as a placeholder?)

Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/805
Reviewed-by: AnErrupTion <anerruption@disroot.org>
Co-authored-by: RadsammyT <radsammyt@gmail.com>
Co-committed-by: RadsammyT <radsammyt@gmail.com>
2025-08-03 11:16:04 +02:00
AnErrupTion
4fbbb6f0f2
Reduce nesting a bit
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-02 22:59:34 +02:00
AnErrupTion
ef64029795
Only clear TTY under certain circumstances
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-02 22:57:47 +02:00
AnErrupTion
71c694e575
Correct mention of TTY modification in README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-02 22:22:18 +02:00
AnErrupTion
c37aa6957a
Improve README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-02 20:26:31 +02:00
AnErrupTion
8377f14509
Exclude unsupported distributions in packaging status
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-02 19:45:40 +02:00
AnErrupTion
b35c055e7b
Fix clock string length issues (fixes #716)
Co-authored-by: Plash <plash@noreply.codeberg.org>
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-02 13:08:07 +02:00
AnErrupTion
c05c32c5be
Fix possible overflow with 5-digit+ UIDs (c.f. #684)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-02 00:57:22 +02:00
AnErrupTion
1f0274e797
Add packaging status in readme.md through Repology
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-08-02 00:02:30 +02:00
AnErrupTion
f97d0c27d3 Merge pull request 'Russian translation update' (#810) from tyusha/ly:LangUpdate into master
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/810
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-08-01 23:52:53 +02:00
AnErrupTion
d36147a376 Merge pull request 'fix: Reorder default PATH to prioritize /usr/local directories to solve problem with archlinux' (#814) from jlucaso/ly:fix-reorder-path into master
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/814
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-08-01 23:51:18 +02:00
João Lucas
8030cf5244 fix: Reorder default PATH to prioritize /usr/local directories to solve
problem with archlinux
2025-07-26 19:27:36 +02:00
AnErrupTion
933e5bdd2d Merge pull request 'fix: duplicated entry waylandsessions' (#813) from jlucaso/ly:fix-duplicate-waylandsessions-entry into master
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/813
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-07-26 19:26:39 +02:00
João Lucas
a9d85a6925 fix: duplicated entry waylandsessions 2025-07-26 13:55:48 -03:00
tyusha
3d3cf84292 fix 2025-07-25 16:41:06 +03:00
tyusha
19c879a201 update russian translation 2025-07-25 16:36:06 +03:00
AnErrupTion
98f59a69cc Merge pull request 'Refactor: Pull termbox2 in as a Zig dependency' (#799) from jlucaso/ly:refactor/termbox2-dependency into master
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/799
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-07-25 10:48:28 +02:00
João Lucas
5fb40899e5 refactor: remove termbox_extras in favor of the fork 2025-07-25 01:33:57 -03:00
jlucaso
6933c4db02 Merge branch 'master' into refactor/termbox2-dependency 2025-07-25 06:31:10 +02:00
João Lucas
c3d0864e62 fix: update termbox2 dependency URL 2025-07-25 01:29:57 -03:00
AnErrupTion
e8cb8970fd Merge pull request 'Feat: Add Japanese (ja_JP) localization' (#793) from darallium/ly:master into master
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/793
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-07-19 22:11:14 +02:00
darallium
c11194332c Add Japanese lang file 2025-07-19 20:44:47 +09:00
AnErrupTion
35265f5e77 Merge pull request 'fix: confined labels/box title cutting off' (#806) from radsammyt/ly:fix-confined-labels into master
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/806
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-07-17 10:39:08 +02:00
RadsammyT
5c3da10386 fix: confined labels cutting off
`drawConfinedLabel` didn't take into account the starting x axis
when checking to break for exceeding `max_length`.

This should fix the box title not appearing on
terminals with larger column counts.
2025-07-16 22:03:51 -04:00
AnErrupTion
1d4e32ba82
List all users in the system (fixes #373)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-07-10 10:06:19 +02:00
AnErrupTion
97efac0cd1
Add Matrix space link to README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-07-09 21:38:45 +02:00
AnErrupTion
04920e1b1b
Implement custom session support (fixes #757)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-07-08 22:39:14 +02:00
AnErrupTion
48e5369f56
Fix character width calculation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-07-08 16:32:20 +02:00
AnErrupTion
918e9ad5ac
Only show lang.err_lock_state once
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-07-08 12:47:24 +02:00
AnErrupTion
ce17d346e8
Remove lang.err_console_dev
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-07-08 12:45:47 +02:00
AnErrupTion
ce0b05fd6e Merge pull request 'Fix Doom fire animation; add flame height control' (#792) from asterane/ly:doom-fire-refresh into master (fixes #784)
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/792
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-07-08 10:26:37 +02:00
Matthew Rothlisberger
99f3ab96ba changes fire parameters 2025-07-07 21:02:58 -04:00
Matthew Rothlisberger
2a8e221e80 improves fire gradient in true color mode 2025-07-07 21:02:58 -04:00
Matthew Rothlisberger
1c5686ea54 further improves fire behavior 2025-07-07 21:02:58 -04:00
Matthew Rothlisberger
ab23631e66 reimplements PSX Doom fire animation; adds flame height control 2025-07-07 21:02:58 -04:00
AnErrupTion
1bcbb08202
Add config.console_dev as a removed property in migrator
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-07-07 21:46:49 +02:00
AnErrupTion
d08b9a916e
Remove config.console_dev option + handle ioctl errors
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-07-07 21:45:54 +02:00
AnErrupTion
e5eb8b7f6f
Merge branch 'master' of codeberg.org:fairyglade/ly 2025-07-06 09:26:38 +02:00
AnErrupTion
41f4378bfe
Fix XDG_RUNTIME_DIR not being set properly (fixes #781)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-07-06 09:25:06 +02:00
João Lucas
7b81336761 refactor: Use zig fetch for termbox2 dependency
Replaced the local `termbox2.h` header file with a proper dependency managed by the Zig package manager. This improves maintainability and makes it easier to track and update the library in the future.

The previous `termbox2.h` contained a custom `tb_get_cell` function that is not present in the upstream repository. This function has been re-implemented in Zig (`src/tui/termbox_extras.zig`) to maintain compatibility, especially for the failed-login "cascade" animation.

This change also involved updating `build.zig` and `build.zig.zon` to use the new dependency.
2025-07-04 08:32:49 -03:00
AnErrupTion
3ad0c00380 Merge pull request 'Update German translation' (#796) from Ireozar/ly:master into master
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/796
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-06-24 17:00:30 +02:00
Ireozar
7182d91b37 removed some special characters 2025-06-24 13:25:45 +02:00
Ireozar
de11ac8972 improved/added German localization 2025-06-24 12:34:41 +02:00
AnErrupTion
2df2c44cdf Merge pull request 'Add GameOfLife Animation' (#766) from thoxy/ly:master into master
Reviewed-on: https://codeberg.org/fairyglade/ly/pulls/766
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-06-22 15:03:32 +02:00
AnErrupTion
9a3d62333a Merge branch 'master' into master 2025-06-22 15:03:04 +02:00
AnErrupTion
2ae86a695b
Finish merge
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-06-17 22:29:03 +02:00
AnErrupTion
ef78ac28a4
Remove big header about Codeberg migration
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-06-17 22:28:00 +02:00
thoxy
e7aad8de88 remove gameoflife_randomize_colors argument in main for gameoflife 2025-06-17 12:40:49 +02:00
thoxy
9b00df4337 Merge branch 'master' into master 2025-06-17 12:36:17 +02:00
thoxy
a5e38e2ce5 Remove color randomization from Game of Life animation 2025-06-17 12:35:22 +02:00
AnErrupTion
e156414062 Merge pull request 'Add option to hide version string' (#785) from dusan/ly:add-option-to-hide-version-string into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/785
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-06-13 09:28:25 +02:00
Dusan
14aae40fda Use existing label value for x 2025-06-10 14:34:20 +02:00
Dusan
3504180e95 Option to hide version string 2025-06-10 06:12:48 +02:00
thoxy
b4952cdc51 Merge branch 'master' into master 2025-05-31 18:04:56 +02:00
thoxy
5e8e0af59c Remove redundant comments and type annotations 2025-05-31 17:56:16 +02:00
AnErrupTion
a8b8292318
Add name of unknown error instead of generic string
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-31 11:04:46 +02:00
AnErrupTion
36a27f6167
Fix big clock UB in ReleaseSafe
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-31 10:32:03 +02:00
thoxy
9d4c4a3a59 add mention of the new animation in the config file 2025-05-30 21:07:38 +02:00
thoxy
78ed67b35f Merge branch 'master' into master 2025-05-30 20:07:32 +02:00
thoxy
fa46155f72 make gameoflife more configurable and fix pull reviews 2025-05-30 20:04:52 +02:00
AnErrupTion
063b3145ed
Start v1.2.0 development cycle
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 19:29:04 +02:00
AnErrupTion
fa0d8c509d Merge pull request 'Update readme.md' (#777) from a1e5Term-rf-ln/ly:new1 into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/777
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 17:49:20 +02:00
AnErrupTion
b42953fd7e
Update screenshot
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 17:39:44 +02:00
B P
11d9cf8b71 Update readme.md 2025-05-30 18:35:46 +03:00
AnErrupTion
67a4dd8f9d
Rewrite DOOM animation (fixes #692)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 16:11:14 +02:00
AnErrupTion
cedb7a3b02
Fix TTY not being cleared sometimes (closes #696)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 14:18:29 +02:00
AnErrupTion
02729cce21
Add brightnessctl to runtime dependency list
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 14:17:41 +02:00
AnErrupTion
d9204131aa
Fix brightness up key not working (closes #763)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-30 09:24:07 +02:00
AnErrupTion
e90cf40e5b
Update termbox2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-05-29 23:52:58 +02:00
AnErrupTion
b0395ef103 Merge pull request 'Update Polish translation' (#758) from Bluudek/ly:pl_lang_update into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/758
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-05-29 23:13:27 +02:00
AnErrupTion
692e7265e3 Merge pull request 'Added dwm and gnome on Supported X11 Environments' (#753) from ManogyaDahal/ly:manogya/update_readme_supported_device into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/753
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-05-29 22:49:44 +02:00
thoxy
98af3a98c8 Add GameOfLife Animation 2025-05-27 07:07:37 +02:00
Bluudek
732888fd94 Remove invisible Unicode characters 2025-05-08 00:16:14 +02:00
Bluudek
71a06e13a6 Update Polish phrases 2025-05-08 00:12:50 +02:00
Bluudek
f74c4e92a9 Update Polish missing messages 2025-05-07 23:31:31 +02:00
ManogyaDahal
555e72a388 Updae: Added dwm in Readme 2025-04-28 14:18:12 +05:45
AnErrupTion
a1b38188d0 Merge pull request 'Add Arabic Translation' (#745) from MohamedAAbdallah/ly:master into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/745
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-04-05 07:37:04 +00:00
Mohamed A. Abdallah
751d31cae2
Update Arabic error messages for errors 2025-03-28 23:26:17 +02:00
Mohamed A. Abdallah
c6a3223a03
Update Arabic localization for actions 2025-03-28 23:10:09 +02:00
Mohamed A. Abdallah
d2803194f3
Add Arabic localization file 2025-03-28 22:57:13 +02:00
AnErrupTion
4345a99c02 Merge pull request 'Support specifying multiple directories to crawl for sessions' (#744) from jack-avery/ly:multiple_session_dirs into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/744
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-03-28 16:57:15 +00:00
jack-avery
fecc688418 snake case 2025-03-28 11:01:33 -04:00
jack-avery
b7e37ce1b7 update config comments 2025-03-28 11:00:47 -04:00
jack-avery
be5a68dd1d allow specify multiple dirs 2025-03-28 00:47:12 -04:00
AnErrupTion
622303d150 Merge pull request 'Separate Wayland/X11 environments in README' (#742) from Chiron8/ly:master into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/742
Reviewed-by: AnErrupTion <anerruption@disroot.org>
2025-03-20 18:20:20 +00:00
Chiron8
7747b27f9d Update readme.md 2025-03-20 13:08:13 +00:00
Chiron8
585ca5f0da Update readme.md 2025-03-20 08:24:25 +00:00
Chiron8
c624c35ac7 Update readme.md 2025-03-17 16:25:06 +00:00
Chiron8
beb6d04c58 Update readme.md 2025-03-17 16:19:43 +00:00
Chiron8
32793bcfeb Update readme.md 2025-03-17 16:18:51 +00:00
AnErrupTion
9ded9fd765
Remove use of deprecated aliases/types + use upstream zigini
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 22:45:46 +01:00
AnErrupTion
13ba52319c
Clean termbox2 usage + fix animation bug
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 11:40:27 +01:00
AnErrupTion
1672d4a9ec
Make main code less directly dependent on termbox2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 11:17:38 +01:00
AnErrupTion
e0ed1b4eb1
Add animation framework
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 10:54:06 +01:00
AnErrupTion
fa0748ead2
Remove unused valgrind file
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 09:13:01 +01:00
AnErrupTion
86ea38f460
Split session crawling from TUI component
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-16 00:24:56 +01:00
AnErrupTion
9efb734fd5
Show Ly version string at top-left
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-13 13:27:18 +01:00
AnErrupTion
5f52063835
Remove duplicate information in README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-12 08:00:42 +01:00
AnErrupTion
548fa210c1
Update README with new build commands
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-12 07:58:37 +01:00
AnErrupTion
75975ea301
Add Niri to tested WM list
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-12 07:48:38 +01:00
AnErrupTion
8fa6b2cec9
Improve build system commands
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-07 11:03:45 +01:00
AnErrupTion
4dcef65b1c
Fix X11 session logout
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 23:32:23 +01:00
AnErrupTion
1eca889e45
Confirm LeftWM support (closes #164)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 22:57:24 +01:00
AnErrupTion
ac1d828a5f
Stop spamming err_console_dev when first time didn't work
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 22:02:01 +01:00
AnErrupTion
92845268af
Add option to allow empty password or not (closes #577)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 22:00:46 +01:00
AnErrupTion
6504cd0209
Mention OS in issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:51:17 +01:00
AnErrupTion
a058a81ec9
Use proper input text box in issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:48:40 +01:00
AnErrupTion
c6db79b873
Fix issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:48:03 +01:00
AnErrupTion
932c751ac2
Stop redirecting X11 output to session log (closes #693, #688)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:46:40 +01:00
AnErrupTion
4599654398
Update issue template for bugs
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:41:28 +01:00
AnErrupTion
e19a23b54c
Allow using up to a UTF-32 codepoint as a password asterisk (closes #715)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:16:24 +01:00
AnErrupTion
f2ca72eace
Add (incomplete) Chinese translation, thanks @eonun! (closes #194)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 21:08:05 +01:00
AnErrupTion
4e859e56cb
Allow modifying DOOM animation fire colors (closes #239)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 20:40:53 +01:00
AnErrupTion
78d64ad2a7
Possibly fix .Xresources not being loaded (closes #600)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 20:06:28 +01:00
AnErrupTion
d80ec8fd1f
Allow changing matrix animation min/max codepoints (closes #615)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 19:48:11 +01:00
AnErrupTion
d12fa27168
Added new error and updated French translation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 19:41:05 +01:00
AnErrupTion
593a775148
Use unsigned integers only in Matrix animation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 19:36:42 +01:00
AnErrupTion
6079c01a4b
Allow disabling the brightness control commands (closes #664)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 19:16:47 +01:00
AnErrupTion
9c79137c9f
Remove all deprecated calls to tb_cell_buffer()
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 15:42:33 +01:00
AnErrupTion
9168266cca
Don't shutdown termbox2 if authentication fails
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 15:29:56 +01:00
AnErrupTion
6cb102257c
Fix Doom & Matrix animation + bug in migrator
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 15:27:38 +01:00
AnErrupTion
55abc4d7f1
Decouple TerminalBuffer and auth from Config
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 14:32:05 +01:00
AnErrupTion
f013af0dde
Fix segmentation fault when using color mix after auth
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 14:31:11 +01:00
AnErrupTion
973d8fe120
Don't dynamically allocate color strings
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 13:23:47 +01:00
AnErrupTion
3e6d7a1b3b
Forgot a file lol
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 13:09:19 +01:00
AnErrupTion
0c69e0412c
Don't make errors blink by default
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 13:08:50 +01:00
AnErrupTion
d0ccaa4d69
Enable true color output (closes #705)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 12:47:28 +01:00
AnErrupTion
a766dc2b9c
Update termbox2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-06 11:42:19 +01:00
AnErrupTion
f54657432a
Don't set XDG_CURRENT_DESKTOP and XDG_SESSION_DESKTOP if they're empty (closes #702)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 22:50:09 +01:00
AnErrupTion
87503367e9
Try to create /etc/pam.d and /usr/bin everytime when installing
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 22:36:17 +01:00
AnErrupTion
85e071a60a
Code style changes in build.zig
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 22:07:18 +01:00
AnErrupTion
24599368df
Minor changes in color mix animation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 22:06:56 +01:00
AnErrupTion
d5bc6aab24 Merge pull request 'Color mix animation' (#724) from winlith/colormix into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/724
Reviewed-by: AnErrupTion <anerruption@noreply.codeberg.org>
2025-03-05 20:54:48 +00:00
AnErrupTion
c9a416b8a2 Merge branch 'master' into winlith/colormix 2025-03-05 20:53:53 +00:00
AnErrupTion
15c2b19371 Merge pull request 'Make language files uniform' (#704) from moritz-reinel/03_uniform-lang-files into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/704
Reviewed-by: AnErrupTion <anerruption@noreply.codeberg.org>
2025-03-05 20:28:57 +00:00
AnErrupTion
142073c362 Merge branch 'master' into moritz-reinel/03_uniform-lang-files 2025-03-05 20:26:01 +00:00
AnErrupTion
514931b486 Merge pull request 'fix: don't include dest_dir in config_dir' (#728) from villamorrd/master into master
Reviewed-on: https://codeberg.org/AnErrupTion/ly/pulls/728
Reviewed-by: AnErrupTion <anerruption@noreply.codeberg.org>
2025-03-05 19:53:54 +00:00
AnErrupTion
50e9e912e0 Merge branch 'master' into villamorrd/master 2025-03-05 19:52:09 +00:00
AnErrupTion
895edaf904
Update README to only include Zig 0.14.0
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 19:43:02 +01:00
AnErrupTion
bebccf4d5a
Support Zig 0.14.0 only (with 1 downstream dependency fork)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 19:42:00 +01:00
AnErrupTion
346a614ba0 Mention Codeberg in the README
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2025-03-05 02:03:40 +00:00
villamorrd
d171634f9b
fix: don't include dest_dir in config_dir
This should fix $CONFIG_DIRECTORY including the installation directory in its path.
2025-02-01 20:58:52 +08:00
winlith
f5f7422d82 color mix animation 2025-01-17 22:37:48 +01:00
Moritz Reinel
117eccd65a rename language normalizer script 2024-10-26 13:19:33 +02:00
Moritz Reinel
b6726a76c6 refactored some variables + added comments 2024-10-26 13:09:30 +02:00
Moritz Reinel
0dea19c8db remove bash version of script 2024-10-21 19:43:33 +02:00
Moritz Reinel
7a82b51ac5 Revert "remove python version of lang script"
This reverts commit 7a1fce660c.
2024-10-21 19:42:57 +02:00
Moritz Reinel
fe36879dfb added note to Lang.zig and updated lang script to not process the comment 2024-10-12 22:07:29 +02:00
Moritz Reinel
47ebe641d9 remove necessity for temp file 2024-10-12 21:51:50 +02:00
Moritz Reinel
7a1fce660c remove python version of lang script 2024-10-12 21:27:19 +02:00
Moritz Reinel
06e283961d add helper script for keeping lang files in sync with Lang.zig 2024-10-12 21:26:31 +02:00
Moritz
e125d8f1aa
Add error when sleep command fails (#703) 2024-10-12 20:13:25 +02:00
Ch1llyB1lly
e885a5e776
Add Hyprland in README (#701)
Adds hyprland to the list of tested desktop environments
2024-10-09 07:17:38 +02:00
Moritz
aea95b7724
Prevent output of brightness/sleep command showing on screen (fixing #695) (#699)
* Prevent output of brightness cmds showing on screen (fixing #695)

* Prevent output of sleep cmd showing on screen
2024-10-01 20:32:30 +02:00
ttsenturk
215ca5edc7
Update readme.md (#698) 2024-10-01 20:29:58 +02:00
Claudi Lleyda Moltó
022d146f76
Update Catalan translation (#689) 2024-08-21 21:19:33 +02:00
S41G0N
87ceba4de8
Add installation instructions for Gentoo (#685)
* Adding installation section for Gentoo Linux (ly was recently added into Gentoo's official GURU project)

* replace '$ sudo' with '#'
2024-08-12 00:18:53 +02:00
AnErrupTion
b80c276dad
Redirect X11 output to file via shell
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-11 18:27:58 +02:00
llc0930
b84158e1c0
Add option to center env name (#683) 2024-08-09 18:44:49 +02:00
DoctorKnowsBetter
c87d5b4e7a
Fix OpenRC service (#682)
Co-authored-by: Your Name <you@example.com>
2024-08-07 17:50:55 +02:00
AnErrupTion
028cb9496a
Fix session logging for X11 (somewhat)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-07 17:06:14 +02:00
Kian A.
00c94f8ffd
Add Farsi Bigclock (#673)
* Change config type for bigclock

* Seprates big clock's hard codes from its logic

* Adds Farsi (fa) to big clock langs

* Minor changes

* Makes requested changes
2024-08-07 16:47:27 +02:00
AnErrupTion
2bd0d0d4f3
Don't mention log file yet in issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-07 13:41:28 +02:00
AnErrupTion
767bdaf166
Add session logging support
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-07 12:03:34 +02:00
AnErrupTion
c033f5bd03
Use hexadecimal numbers for colors in config
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-06 19:38:02 +02:00
AnErrupTion
096b1a7d44
Refactor brightness handling code
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-06 18:40:24 +02:00
AnErrupTion
f0869f0e13
Add numlock set error + handle 2 more errors
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-06 15:32:31 +02:00
AnErrupTion
1ca53f661e
Fix drawn position of sleep key hint
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-06 14:44:48 +02:00
AnErrupTion
8562cf4e29
Update feature request template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-06 11:11:38 +02:00
AnErrupTion
b5b3317dd8
Add login & logout script support
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-06 11:06:20 +02:00
AnErrupTion
2901b408dc
Arrange config alphabetically
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-06 10:07:23 +02:00
AnErrupTion
4e40e32f59
Slightly refactor resolution check
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-05 18:33:22 +02:00
AnErrupTion
5e85618730
Conditionally import login_cap.h with pwd.h
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-05 15:16:58 +02:00
AnErrupTion
2c428f5537
Reduce dependence on tb_cell and tb_cell_buffer()
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-05 11:08:51 +02:00
AnErrupTion
071b7a2182
Make all colors u16
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-05 10:41:34 +02:00
AnErrupTion
1075c923ef
Make shell login use setup script
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-05 01:03:11 +02:00
AnErrupTion
391f86f602
Delete old setup scripts
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-05 00:59:29 +02:00
AnErrupTion
6fbbb4eff0
Consolidate xsetup.sh & wsetup.sh into one file
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-05 00:58:29 +02:00
AnErrupTion
c7f70ac78f
Handle termbox2 outside of authentication
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-04 20:42:00 +02:00
AnErrupTion
ef86ea19ac
Update termbox2
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-04 19:40:26 +02:00
AnErrupTion
37061269a4
Remove config.save_file
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-04 17:08:50 +02:00
AnErrupTion
7b9f03176d
FreeBSD fixes
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-04 11:04:23 +02:00
AnErrupTion
b73c78d2fb
Remove tput dependency & use termbox2 instead
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-03 21:48:38 +02:00
AnErrupTion
0bbe9c78dd
Reduce heap allocations a bit
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-03 15:17:02 +02:00
AnErrupTion
b18f29a81a
Load logind PAM modules before required ones
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-03 13:39:50 +02:00
AnErrupTion
cab3a7d214
Delete old save file if it's been migrated
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-03 10:34:13 +02:00
AnErrupTion
8995c590eb
Fix CLI note & OpenRC service not using config directory option
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-03 08:36:26 +02:00
AnErrupTion
57d5d7497b
Fix PAM module order
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-03 07:17:06 +02:00
AnErrupTion
ce3b310e58
Make authentication fail count configurable
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-02 22:31:35 +02:00
AnErrupTion
5d3cd62434
Swap /usr/bin and /usr/sbin in PATH
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-01 17:02:08 +02:00
AnErrupTion
d40ec873a7
Retrieve gettimeofday() from sys/time.h
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-01 14:23:52 +02:00
AnErrupTion
61f3fadfbf
Make code more portable + remove mcookie usage
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-01 13:15:54 +02:00
AnErrupTion
1314c57796
Fix config.brightnessctl missing + bugs
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-01 00:54:00 +02:00
AnErrupTion
872b15c0d4
Fix authentication
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-01 00:39:00 +02:00
AnErrupTion
9b4d381f1e
Update zigini (fixes an escaping bug)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-08-01 00:18:46 +02:00
AnErrupTion
bacbacd5fb
Merge branch 'master' of https://github.com/fairyglade/ly 2024-07-31 22:35:55 +02:00
AnErrupTion
ee0c00574a
Patch resource files + add prefix directory
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-31 22:35:36 +02:00
ello
6df91cac12
Add an animation timeout (#659)
* added animation timeout

* Updated animation timeout to u12

* updated config comment to reflect the new range for animation timeout
2024-07-31 21:49:19 +02:00
AnErrupTion
598fa6a505
I need to stop doing this
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-31 14:32:39 +02:00
AnErrupTion
548a411ae2
Remove maximum length config options + don't localize config parse error
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-31 14:31:28 +02:00
AnErrupTion
48f28e40c4
Fix an oopsie
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-31 14:02:43 +02:00
AnErrupTion
46f9ddd5fc
Add translatable string for config parse error
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-31 14:01:44 +02:00
AnErrupTion
a393525212
Support multiple info lines in UI
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-31 13:58:49 +02:00
AnErrupTion
961018e753
Add generic cyclable label & base session component off it
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-31 13:21:54 +02:00
Joaquín Guerra
8b12ade372
Update spanish translation (#668) 2024-07-31 13:14:29 +02:00
AnErrupTion
a64d7efc69
Make asterisk optional (hides password if so)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-31 10:34:27 +02:00
AnErrupTion
b592a11fb0
Improve the config migrator
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-31 09:44:25 +02:00
AnErrupTion
3fedb59fdb
Make setting numlock work on BSD + less homebrew interop
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-30 18:38:21 +02:00
AnErrupTion
b1bf89a4cf
Only shutdown or restart after deinitializing everything
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-30 11:56:21 +02:00
AnErrupTion
2dec2e0b7f
Print data directory deletion in build.zig
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-30 10:54:30 +02:00
AnErrupTion
5f2f21620a
Update zigini (fixes incorrect comment parsing)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-30 09:43:56 +02:00
AnErrupTion
48185bdfe0
More verbose output in build.zig
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-29 14:44:56 +02:00
AnErrupTion
f646dddd02
Fix clock & bigclock not updating without input
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-29 14:18:23 +02:00
AnErrupTion
c1f1c8f5c1
Use usize instead of u64 in most places for better 32-bit compatibility
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-29 13:46:14 +02:00
AnErrupTion
ee488ba36e
Revert "Redirect stderr to systemd journal in service (#621)"
This reverts commit 3d8d8d67df.

Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-29 12:25:44 +02:00
AnErrupTion
56c210372d
Better handle info line messages internally
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-28 23:05:12 +02:00
AnErrupTion
04a0ad3b33
Move Gentoo/OpenRC installation tip to the OpenRC section
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-28 18:17:14 +02:00
AnErrupTion
2dd83b41e8
Incorporate some FreeBSD authentication patches
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-28 13:15:49 +02:00
Moabeat
19d4b195f3
Display error messages differently in info line (#661)
* Add list of error messages to InfoLine.zig

* Change info and error cases according to review

* Add changes from review for width calculation
2024-07-28 13:02:42 +02:00
AnErrupTion
93554d9ba3
Add missing supervise symlink on runit (fixes #610)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-28 11:34:33 +02:00
AnErrupTion
075bf67cef
Fix missing brightness localized strings
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-28 00:09:59 +02:00
AnErrupTion
33791a2844
Remove changelog.md
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-28 00:06:08 +02:00
AnErrupTion
8333f7ea77
Update screenshot
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-28 00:05:37 +02:00
AnErrupTion
2bc12549a1
Switch to utmpx
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 23:39:09 +02:00
AnErrupTion
1df890b238
Set PAM_TTY (fixes #248)
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 22:31:56 +02:00
AnErrupTion
92c6a38835
Fix ~/.profile not being loaded with Fish
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 22:21:50 +02:00
AnErrupTion
a7e8b55c6e
Add COSMIC in the list of tested DEs
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 21:13:55 +02:00
AnErrupTion
b7e1c81ad1
Re-arrange keyring PAM files + integrate with pam_systemd & elogind
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 20:36:45 +02:00
mietinen
dc310ca80e
Unlock GNOME Keyring & KWallet on login (#496) 2024-07-27 20:35:24 +02:00
AnErrupTion
da2ea08078
Clarify compile-time dependencies
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 18:38:07 +02:00
AnErrupTion
8c69472065
Allow building without X11 support
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 18:35:58 +02:00
AnErrupTion
0ee28927cf
Update French translation
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 18:15:34 +02:00
AnErrupTion
b7934e42d1
Add dinit support
Co-authored-by: Simon Pflaumer <s.pflaumer@murena.io>
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 15:59:02 +02:00
AnErrupTion
e775827c8b
Fix possible overflow with TTY ID
Co-authored-by: Kevin Morris <kevr@0cost.org>
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 15:20:42 +02:00
AnErrupTion
9cd58123c4
Fix silly mistake
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 15:02:01 +02:00
AnErrupTion
0cead672da
Add s6 support
Co-authored-by: userbook <userbook@devuan>
Co-authored-by: TerminalJunki <158248817+TerminalJunki@users.noreply.github.com>
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 15:00:07 +02:00
AnErrupTion
ce90f91bbf
Add issue template for feature requests
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 14:16:40 +02:00
AnErrupTion
23c7175528
Make pre-requisites required
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 14:11:42 +02:00
AnErrupTion
5ea780f806
Continue merge
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 14:09:19 +02:00
AnErrupTion
2eaa473144
Add issue template
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-27 14:06:28 +02:00
Varun Vasan V
a939b82179
Add configurable default vi mode (#660)
- `vi_default_mode` added
- supports `normal` and `insert`
2024-07-27 11:31:55 +02:00
AnErrupTion
2b0301c1d0
Make runit run and finish scripts executable
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-26 21:58:18 +02:00
AnErrupTion
b84e6c9eed
Use default PRNG and retrieve better seed
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-26 21:39:27 +02:00
AnErrupTion
49b8697546
Use octal prefix for file modes in build.zig
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-26 18:54:27 +02:00
0xNiffin
1d7a001a0b
Add customizable foreground color to CMatrix (#652)
* Final fixes, adding fg_ini as field

* Capital B changed to lowercase
2024-07-22 16:44:49 +02:00
0xNiffin
3dc1482603
fixed 'std' has no member 'ChildProcess' error when building with zig build command. (#651) 2024-07-17 13:27:54 +02:00
jinzhongjia
e4abf79ad5
Support Zig 0.13.0 and setting default TTY at build time (#632)
* feat: support zig `0.13.0`

* 12 compatible

* update clap

* feat: add default tty to build option

* little fix

* update `zigini`
2024-07-12 22:40:01 +02:00
simonfogliato
5f8fbe381c
Fix documentation issue about DOOM animation (#647) 2024-07-12 22:37:18 +02:00
liesen
c6d7d177b7
Fix incorrect shebang in xsetup.sh (#640) 2024-07-12 22:32:46 +02:00
tubi16
dc8d143fac
Add brightness control support with brightnessctl (#626)
* Added key binds to control brightness

I added keybinds to control brightness with brightnessctl. 

F5 to decrease brightness.
f6 to increase brightness.

* Update src/main.zig

Co-authored-by: ShiningLea <anerruption@disroot.org>

* added proper keybinds and configs for brightness control

* Update src/main.zig

Co-authored-by: ShiningLea <anerruption@disroot.org>

* code improvement and changes

* updated en.ini

---------

Co-authored-by: ShiningLea <anerruption@disroot.org>
2024-07-04 13:17:56 +02:00
AnErrupTion
cbe7b37564
Fix dest_directory embedded in binary
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-03 09:50:56 +02:00
AnErrupTion
4eb4e15e43
Start v1.1.0 development cycle
Signed-off-by: AnErrupTion <anerruption@disroot.org>
2024-07-03 09:44:23 +02:00
jinzhongjia
08f6ce5184
Add minimum_zig_version to build.zig.zon (#630) 2024-07-03 09:22:18 +02:00
アシュ
291e0d836b
Various bug fixes and small features (#606)
* Fix stray cursor, integer overflows and other bugs

* check for getenvlist error, shorten code

* fix cascade, set info_line before auth, make code clearer and a bug fix

* Add option to turn on numlock at startup

* Fix setting numlock

* Update build.zig

* Custom info text

* Shift+Tab for previous input

* update changelog and res/config

* Some fixes

* update build.zig

* update build.zig again

* Fix xauth command for some shells and fix building in ReleaseSafe

* Use git describe to get dev version str

* revert change to getLockState (it broke the doom animation)

* add new ly error messages. Only try to make path for pam/exe during install when dest_directory is defined + print warning on error.

* add warning message for workaround
2024-07-02 10:52:38 +02:00
134 changed files with 12753 additions and 6345 deletions

2
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,2 @@
github: AnErrupTion
liberapay: ShiningLea

84
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View file

@ -0,0 +1,84 @@
name: Bug report
description: File a bug report.
title: "[Bug] "
labels: ["bug"]
body:
- type: checkboxes
id: prerequisites
attributes:
label: Pre-requisites
description: By submitting this issue, you agree to have done the following.
options:
- label: I have looked for any other duplicate issues
required: true
- label: I have reproduced the issue with the default config of Ly
required: true
- label: I have reproduced the issue on a fresh install of Ly **as well as** my OS and my desktop environment/window manager, all with **default settings**, except ones I will mention
required: false
- label: I have confirmed this issue also occurs on the latest development version (found in the `master` branch)
required: true
- label: I understand that this issue **will** be closed if requirements are not or not properly met
required: true
- type: input
id: version
attributes:
label: Ly version
description: The output of `ly --version`. Please note that only the latest release and the current version in development are supported.
placeholder: 1.1.0-dev.12+2b0301c
validations:
required: true
- type: textarea
id: observed
attributes:
label: Observed behavior
description: What happened?
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: What did you expect to happen instead?
validations:
required: true
- type: input
id: desktop
attributes:
label: OS + Desktop environment/Window manager
description: Which operating system and DE (or WM) did you use when observing the problem?
validations:
required: true
- type: input
id: osimage
attributes:
label: OS version/snapshot used to reproduce
description: Which version (or snapshot if rolling release) of the OS did you use to reproduce the issue? This is only required if you have reproduced the issue on a fresh copy of the OS.
validations:
required: false
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: What **exactly** can someone else do in order to observe the problem you observed?
placeholder: |
1. Authenticate with ...
2. Go to ...
3. Create file ...
4. Log out and log back in
5. Observe error
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant logs
description: |
Please copy and paste (or attach) any relevant logs, error messages or any other output. This will be automatically formatted into code, so no need for backticks. Screenshots are accepted if they make life easier for you. The log files (located as specified by `/etc/ly/config.ini`) usually contain relevant information about the problem:
- The session log is located at `~/.local/state/ly-session.log` by default.
- The system log is located at `/var/log/ly.log` by default.
render: shell
- type: textarea
id: moreinfo
attributes:
label: Additional information
description: If you have any additional information that might be helpful in reproducing the problem, please provide it here.

29
.github/ISSUE_TEMPLATE/feature.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Feature request
description: Request a new feature or enhancement.
title: "[Feature] "
labels: ["feature"]
body:
- type: checkboxes
id: prerequisites
attributes:
label: Pre-requisites
description: By submitting this issue, you agree to have done the following.
options:
- label: I have looked for any other duplicate issues
required: true
- label: I have confirmed the requested feature doesn't exist in the latest version in development
required: true
- type: textarea
id: wanted
attributes:
label: Wanted behavior
description: What do you want to be added? Describe the behavior clearly.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives
description: Are there any alternatives to the new behavior you wish to see being added?
validations:
required: true

12
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,12 @@
## What are the changes about?
_Replace this with a brief description of your changes_
## What existing issue(s) does this resolve?
_Replace this with a reference to (an) existing issue(s), or N/A if there is none_
## Pre-requisites
- [ ] I have tested & confirmed the changes work locally
- [ ] I have read and fully adhere to the rules set in the contributing guidelines found in `CONTRIBUTING.md`

BIN
.github/screenshot.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

4
.gitignore vendored
View file

@ -1,4 +1,6 @@
.idea/
zig-cache/
zig-out/
valgrind.log
valgrind.log
.zig-cache
zig-pkg

377
build.zig
View file

@ -1,221 +1,244 @@
const std = @import("std");
const builtin = @import("builtin");
const ly_version = std.SemanticVersion{ .major = 1, .minor = 0, .patch = 0, .build = "dev" };
const InitSystem = enum {
systemd,
openrc,
runit,
s6,
dinit,
sysvinit,
freebsd,
};
const InstallType = enum {
installexe,
installnoconf,
uninstallexe,
uninstallnoconf,
};
pub fn build(b: *std.Build) void {
const data_directory = b.option([]const u8, "data_directory", "Specify a default data directory (default is /etc/ly)");
const min_zig_string = "0.16.0";
const current_zig = builtin.zig_version;
// Implementing zig version detection through compile time
comptime {
const min_zig = std.SemanticVersion.parse(min_zig_string) catch unreachable;
if (current_zig.order(min_zig) == .lt) {
@compileError(std.fmt.comptimePrint("Your Zig version v{} does not meet the minimum build requirement of v{}", .{ current_zig, min_zig }));
}
}
const ly_version = std.SemanticVersion{ .major = 1, .minor = 5, .patch = 0 };
fn InstallStep(
b: *std.Build,
target: std.Build.ResolvedTarget,
comptime install_type: InstallType,
dest_directory: []const u8,
config_directory: []const u8,
prefix_directory: []const u8,
executable_name: []const u8,
init_system: InitSystem,
default_tty_str: []const u8,
) *std.Build.Step.Run {
const step = b.addRunArtifact(b.addExecutable(.{
.name = "install",
.root_module = b.createModule(.{
.root_source_file = b.path("install.zig"),
.target = target,
}),
}));
step.step.dependOn(b.getInstallStep());
step.addArgs(&.{
std.enums.tagName(InstallType, install_type).?,
dest_directory,
config_directory,
prefix_directory,
executable_name,
std.enums.tagName(InitSystem, init_system).?,
default_tty_str,
});
return step;
}
pub fn build(b: *std.Build) !void {
const dest_directory = b.option([]const u8, "dest_directory", "Specify a destination directory for installation") orelse "";
const config_directory = b.option([]const u8, "config_directory", "Specify a default config directory (default is /etc). This path gets embedded into the binary") orelse "/etc";
const prefix_directory = b.option([]const u8, "prefix_directory", "Specify a default prefix directory (default is /usr)") orelse "/usr";
const executable_name = b.option([]const u8, "name", "Specify installed executable file name (default is ly)") orelse "ly";
const init_system = b.option(InitSystem, "init_system", "Specify the target init system (default is systemd)") orelse .systemd;
const build_options = b.addOptions();
build_options.addOption([]const u8, "data_directory", data_directory orelse "/etc/ly");
const version_str = b.fmt("{d}.{d}.{d}-{s}", .{ ly_version.major, ly_version.minor, ly_version.patch, ly_version.build.? });
const version_str = try getVersionStr(b, "ly", ly_version);
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support (default is on)") orelse true;
const default_tty = b.option(u8, "default_tty", "Set the TTY (default is 2)") orelse 2;
const fallback_tty = b.option(u8, "fallback_tty", "Set the fallback TTY (default is 2). This value gets embedded into the binary") orelse 2;
const fallback_uid_min = b.option(std.posix.uid_t, "fallback_uid_min", "Set the fallback minimum UID (default is 1000). This value gets embedded into the binary") orelse 1000;
const fallback_uid_max = b.option(std.posix.uid_t, "fallback_uid_max", "Set the fallback maximum UID (default is 60000). This value gets embedded into the binary") orelse 60000;
const default_tty_str = try std.fmt.allocPrint(b.allocator, "{d}", .{default_tty});
build_options.addOption([]const u8, "config_directory", config_directory);
build_options.addOption([]const u8, "prefix_directory", prefix_directory);
build_options.addOption([]const u8, "version", version_str);
build_options.addOption(u8, "tty", default_tty);
build_options.addOption(u8, "fallback_tty", fallback_tty);
build_options.addOption(std.posix.uid_t, "fallback_uid_min", fallback_uid_min);
build_options.addOption(std.posix.uid_t, "fallback_uid_max", fallback_uid_max);
build_options.addOption(bool, "enable_x11_support", enable_x11_support);
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const c_args = [_][]const u8{
"-std=c99",
"-pedantic",
"-g",
"-Wall",
"-Wextra",
"-Werror=vla",
"-Wno-unused-parameter",
"-D_DEFAULT_SOURCE",
"-D_POSIX_C_SOURCE=200809L",
"-D_XOPEN_SOURCE",
};
const exe = b.addExecutable(.{
.name = "ly",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("zigini", zigini.module("zigini"));
const zlua = b.dependency("zlua", .{
.target = target,
.optimize = optimize,
.lang = .luajit,
});
exe.root_module.addImport("zlua", zlua.module("zlua"));
const ly_ui = b.dependency("ly_ui", .{
.target = target,
.optimize = optimize,
.enable_x11_support = enable_x11_support,
.fallback_uid_min = fallback_uid_min,
.fallback_uid_max = fallback_uid_max,
});
exe.root_module.addImport("ly-ui", ly_ui.module("ly-ui"));
exe.root_module.addOptions("build_options", build_options);
const clap = b.dependency("clap", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("clap", clap.module("clap"));
exe.linkSystemLibrary("pam");
exe.linkSystemLibrary("xcb");
exe.linkLibC();
exe.addIncludePath(.{ .path = "dep/termbox_next/src" });
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/input.c" }, .flags = &c_args });
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/memstream.c" }, .flags = &c_args });
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/ringbuffer.c" }, .flags = &c_args });
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/term.c" }, .flags = &c_args });
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/termbox.c" }, .flags = &c_args });
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/utf8.c" }, .flags = &c_args });
exe.root_module.linkSystemLibrary("pam", .{});
if (enable_x11_support) exe.root_module.linkSystemLibrary("xcb", .{});
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const installexe_step = b.step("installexe", "Install Ly");
installexe_step.makeFn = installexe;
installexe_step.dependOn(b.getInstallStep());
const installexe_step = b.step("installexe", "Install Ly and the selected init system service");
installexe_step.dependOn(&InstallStep(
b,
target,
.installexe,
dest_directory,
config_directory,
prefix_directory,
executable_name,
init_system,
default_tty_str,
).step);
const installnoconf_step = b.step("installnoconf", "Install Ly without its configuration file");
installnoconf_step.makeFn = installnoconf;
installnoconf_step.dependOn(b.getInstallStep());
const installnoconf_step = b.step("installnoconf", "Install Ly and the selected init system service, but not the configuration file");
installnoconf_step.dependOn(&InstallStep(
b,
target,
.installnoconf,
dest_directory,
config_directory,
prefix_directory,
executable_name,
init_system,
default_tty_str,
).step);
const installsystemd_step = b.step("installsystemd", "Install the Ly systemd service");
installsystemd_step.makeFn = installsystemd;
installsystemd_step.dependOn(installexe_step);
const uninstallexe_step = b.step("uninstallexe", "Uninstall Ly and remove the selected init system service");
uninstallexe_step.dependOn(&InstallStep(
b,
target,
.uninstallexe,
dest_directory,
config_directory,
prefix_directory,
executable_name,
init_system,
default_tty_str,
).step);
const installopenrc_step = b.step("installopenrc", "Install the Ly openrc service");
installopenrc_step.makeFn = installopenrc;
installopenrc_step.dependOn(installexe_step);
const installrunit_step = b.step("installrunit", "Install the Ly runit service");
installrunit_step.makeFn = installrunit;
installrunit_step.dependOn(installexe_step);
const uninstallall_step = b.step("uninstallall", "Uninstall Ly and all services");
uninstallall_step.makeFn = uninstallall;
const uninstallnoconf_step = b.step("uninstallnoconf", "Uninstall Ly and remove the selected init system service, but keep the configuration directory");
uninstallnoconf_step.dependOn(&InstallStep(
b,
target,
.uninstallnoconf,
dest_directory,
config_directory,
prefix_directory,
executable_name,
init_system,
default_tty_str,
).step);
}
fn installexe(self: *std.Build.Step, progress: *std.Progress.Node) !void {
_ = progress;
_ = self;
fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion) ![]const u8 {
const version_str = b.fmt("{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
try install_ly(true);
}
fn installnoconf(self: *std.Build.Step, progress: *std.Progress.Node) !void {
_ = progress;
_ = self;
try install_ly(false);
}
fn installsystemd(self: *std.Build.Step, progress: *std.Progress.Node) !void {
_ = progress;
_ = self;
var service_dir = std.fs.openDirAbsolute("/usr/lib/systemd/system", .{}) catch unreachable;
defer service_dir.close();
try std.fs.cwd().copyFile("res/ly.service", service_dir, "ly.service", .{ .override_mode = 644 });
}
fn installopenrc(self: *std.Build.Step, progress: *std.Progress.Node) !void {
_ = progress;
_ = self;
var service_dir = std.fs.openDirAbsolute("/etc/init.d", .{}) catch unreachable;
defer service_dir.close();
try std.fs.cwd().copyFile("res/ly-openrc", service_dir, "ly", .{ .override_mode = 755 });
}
fn installrunit(self: *std.Build.Step, progress: *std.Progress.Node) !void {
_ = progress;
_ = self;
var service_dir = std.fs.openDirAbsolute("/etc/sv", .{}) catch unreachable;
defer service_dir.close();
std.fs.makeDirAbsolute("/etc/sv/ly") catch {
std.debug.print("warn: /etc/sv/ly already exists as a directory.\n", .{});
var status: u8 = undefined;
const git_describe_raw = b.runAllowFail(&[_][]const u8{
"git",
"-C",
b.build_root.path orelse ".",
"describe",
"--match",
"*.*.*",
"--tags",
}, &status, .ignore) catch {
return version_str;
};
var git_describe = std.mem.trim(u8, git_describe_raw, " \n\r");
git_describe = std.mem.trimStart(u8, git_describe, "v");
var ly_service_dir = std.fs.openDirAbsolute("/etc/sv/ly", .{}) catch unreachable;
defer ly_service_dir.close();
switch (std.mem.count(u8, git_describe, "-")) {
0 => {
if (!std.mem.eql(u8, version_str, git_describe)) {
std.debug.print("{s} version '{s}' does not match git tag: '{s}'\n", .{ name, version_str, git_describe });
std.process.exit(1);
}
return version_str;
},
2 => {
// Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
var it = std.mem.splitScalar(u8, git_describe, '-');
const tagged_ancestor = std.mem.trimStart(u8, it.first(), "v");
const commit_height = it.next().?;
const commit_id = it.next().?;
try std.fs.cwd().copyFile("res/ly-runit-service/conf", ly_service_dir, "conf", .{});
try std.fs.cwd().copyFile("res/ly-runit-service/finish", ly_service_dir, "finish", .{});
try std.fs.cwd().copyFile("res/ly-runit-service/run", ly_service_dir, "run", .{});
}
const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
if (version.order(ancestor_ver) != .gt) {
std.debug.print("{s} version '{f}' must be greater than tagged ancestor '{f}'\n", .{ name, version, ancestor_ver });
std.process.exit(1);
}
fn uninstallall(self: *std.Build.Step, progress: *std.Progress.Node) !void {
_ = progress;
_ = self;
// Check that the commit hash is prefixed with a 'g' (a Git convention).
if (commit_id.len < 1 or commit_id[0] != 'g') {
std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
return version_str;
}
try std.fs.deleteTreeAbsolute("/etc/ly");
try std.fs.deleteFileAbsolute("/usr/bin/ly");
try std.fs.deleteFileAbsolute("/etc/pam.d/ly");
std.fs.deleteFileAbsolute("/usr/lib/systemd/system/ly.service") catch {
std.debug.print("warn: systemd service not found.\n", .{});
};
std.fs.deleteFileAbsolute("/etc/init.d/ly") catch {
std.debug.print("warn: openrc service not found.\n", .{});
};
std.fs.deleteTreeAbsolute("/etc/sv/ly") catch {
std.debug.print("warn: runit service not found.\n", .{});
};
}
fn install_ly(install_config: bool) !void {
std.fs.makeDirAbsolute("/etc/ly") catch {
std.debug.print("warn: /etc/ly already exists as a directory.\n", .{});
};
std.fs.makeDirAbsolute("/etc/ly/lang") catch {
std.debug.print("warn: /etc/ly/lang already exists as a directory.\n", .{});
};
var current_dir = std.fs.cwd();
{
var executable_dir = std.fs.openDirAbsolute("/usr/bin", .{}) catch unreachable;
defer executable_dir.close();
try current_dir.copyFile("zig-out/bin/ly", executable_dir, "ly", .{});
}
{
var config_dir = std.fs.openDirAbsolute("/etc/ly", .{}) catch unreachable;
defer config_dir.close();
if (install_config) {
try current_dir.copyFile("res/config.ini", config_dir, "config.ini", .{});
}
try current_dir.copyFile("res/xsetup.sh", config_dir, "xsetup.sh", .{});
try current_dir.copyFile("res/wsetup.sh", config_dir, "wsetup.sh", .{});
}
{
var lang_dir = std.fs.openDirAbsolute("/etc/ly/lang", .{}) catch unreachable;
defer lang_dir.close();
try current_dir.copyFile("res/lang/cat.ini", lang_dir, "cat.ini", .{});
try current_dir.copyFile("res/lang/cs.ini", lang_dir, "cs.ini", .{});
try current_dir.copyFile("res/lang/de.ini", lang_dir, "de.ini", .{});
try current_dir.copyFile("res/lang/en.ini", lang_dir, "en.ini", .{});
try current_dir.copyFile("res/lang/es.ini", lang_dir, "es.ini", .{});
try current_dir.copyFile("res/lang/fr.ini", lang_dir, "fr.ini", .{});
try current_dir.copyFile("res/lang/it.ini", lang_dir, "it.ini", .{});
try current_dir.copyFile("res/lang/pl.ini", lang_dir, "pl.ini", .{});
try current_dir.copyFile("res/lang/pt.ini", lang_dir, "pt.ini", .{});
try current_dir.copyFile("res/lang/pt_BR.ini", lang_dir, "pt_BR.ini", .{});
try current_dir.copyFile("res/lang/ro.ini", lang_dir, "ro.ini", .{});
try current_dir.copyFile("res/lang/ru.ini", lang_dir, "ru.ini", .{});
try current_dir.copyFile("res/lang/sr.ini", lang_dir, "sr.ini", .{});
try current_dir.copyFile("res/lang/sv.ini", lang_dir, "sv.ini", .{});
try current_dir.copyFile("res/lang/tr.ini", lang_dir, "tr.ini", .{});
try current_dir.copyFile("res/lang/uk.ini", lang_dir, "uk.ini", .{});
}
{
var pam_dir = std.fs.openDirAbsolute("/etc/pam.d", .{}) catch unreachable;
defer pam_dir.close();
try current_dir.copyFile("res/pam.d/ly", pam_dir, "ly", .{ .override_mode = 644 });
// The version is reformatted in accordance with the https://semver.org specification.
return b.fmt("{s}-dev.{s}+{s}", .{ version_str, commit_height, commit_id[1..] });
},
else => {
std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
return version_str;
},
}
}

View file

@ -1,15 +1,24 @@
.{
.name = "ly",
.version = "1.0.0",
.name = .ly,
.version = "1.5.0",
.fingerprint = 0xa148ffcc5dc2cb59,
.minimum_zig_version = "0.16.0",
.dependencies = .{
.clap = .{
.url = "https://github.com/Hejsil/zig-clap/archive/8c98e6404b22aafc0184e999d8f068b81cc22fa1.tar.gz",
.hash = "122014e73fd712190e109950837b97f6143f02d7e2b6986e1db70b6f4aadb5ba6a0d",
.ly_ui = .{
.path = "ly-ui",
},
.zigini = .{
.url = "https://github.com/Kawaii-Ash/zigini/archive/ce1f322482099db058f5d9fdd05fbfa255d79723.tar.gz",
.hash = "1220e7a99793a0430e0a7c0b938cb3c98321035bc297e21cd0e2413cf740b4923b9f",
.clap = .{
.url = "git+https://github.com/Hejsil/zig-clap#fc1e5cc3f6d9d3001112385ee6256d694e959d2f",
.hash = "clap-0.11.0-oBajB7foAQC3Iyn4IVCkUdYaOVVng5IZkSncySTjNig1",
},
.zlua = .{
.url = "git+https://github.com/natecraddock/ziglua?ref=zig-0.16#8f271c82baa5fc43aa02a72f6da020c2025d9436",
.hash = "zlua-0.1.0-hGRpC2aABQD4D9PBVH3wAW8k32-I4969MRQ0CpOwoley",
},
},
.paths = .{""},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View file

@ -1,47 +0,0 @@
# Zig Rewrite (Version 1.0.0)
## Config Options
res/config.ini contains all of the available config options and their default values.
### Additions
+ `border_fg` has been introduced to change the color of the borders.
+ `term_restore_cursor_cmd` should restore the cursor to it's usual state.
+ `vi_mode` to enable vi keybindings.
+ `sleep_key` and `sleep_cmd`.
Note: `sleep_cmd` is unset by default, meaning it's hidden and has no effect.
### Changes
+ xinitrc can be set to null to hide it.
+ `blank_password` has been renamed to `clear_password`.
+ `save_file` has been deprecated and will be removed in a future version.
### Removals
+ `wayland_specifier` has been removed.
## Save File
The save file is now in .ini format and stored in the same directory as the config.
Older save files will be migrated to the new format.
Example:
```ini
user = ash
session_index = 0
```
## Misc
+ Display server name added next to selected session.
+ getty@tty2 has been added as a conflict in res/ly.service, so if it is running, ly should still be able to start.
+ `XDG_CURRENT_DESKTOP` is now set by ly.
+ LANG is no longer set by ly.
+ X Server PID is fetched from /tmp/X{d}.lock to be able to kill the process since it detaches.
+ Non .desktop files are now ignored in sessions directory.
+ PAM auth is now done in a child process. (Fixes some issues with logging out and back in).
+ When ly receives SIGTERM, the terminal is now cleared and existing child processes are cleaned up.

29
contributing.md Normal file
View file

@ -0,0 +1,29 @@
# Contributing to Ly
To contribute to Ly, you have to open a pull request on [Codeberg](https://codeberg.org/fairyglade/ly), as [GitHub](https://github.com/fairyglade/ly) is just a mirror. However, you also have to respect the following rules, otherwise your PR may end up being rejected:
## AI usage
While we cannot control your usage of any LLM whatsoever, we do heavily discourage their use for environmental, ethical & moral reasons that you can learn more about on the Internet. However, if you do end up still using them, here are a couple rules you **must absolutely** respect for your PR not to get instantly shot down. Of course, it goes without saying that **all** the other rules in the other sections below **must** be adhered to, even more so when AI is used.
1. **Communicate through you**, not the AI; as in, responses to reviews or other comments in the PR must be written by you and not an LLM. If English isn't your native language and you have a lot of trouble speaking it, you _may_ use AI, but responses must not be your typical, alienating AI-generated text: https://github.com/realrossmanngroup/no_ai_slop_writing_rules
2. Control the code, as in, **don't vibecode**, especially if you don't know Zig. Don't waste the maintainers's time, and **heavily test your code**, as AI-generated code is typically more error-prone in subtle ways (even if they can be better than humans in some other aspects). Finally, don't generate any "summary" or whatever the AI may come up with during the process. **If you cannot understand the generated code, do not contribute it**. Maintainers are busy and are often simple volunteers, so the less work they have to do, the better.
3. **Be transparent about AI usage**. Precise what AI model was used in the process, and preferably, how you went about creating your changes (i.e. how did you use AI to make the pull request). The more honest you are about it, the more likely your PR will be accepted and merged into the code base.
If all the above rules are respected, your changes have much more likely to be accepted into the code base.
## Code style
You must follow Zig's [style guide](https://ziglang.org/documentation/master/#Style-Guide). In most cases, all you'll have to do is run `zig fmt` after you have completed your changes, though it does not fix everything, notably variable, function & field naming, as well as the maximum length of aline.
For the former, please refer to the aforementioned style guide in that case. For the latter, you must respect a maximum line length of **80 characters**. For function calls with many parameters or anything similar that may overflow this limit, consider adding a trailing comma to the list so that `zig fmt` can split it into multiple lines. Ideally, few or no lines end up having to get soft wrapped by an editor having this limit set.
## Commit names & descriptions
1. **Commit names must be descriptive**, resorting to the commit description if they are too long. In such cases, the commit name **must** be shortened while still making sense. With that said, no particular convention (emojis, prefixes & suffixes, etc.) is mandated for them, and you are free to adopt any one you think is best or that you are used to.
2. **Do not force push**, as PRs will get squashed into a single commit anyway. This erases history and makes it impossible to see the changes done over time and by a particular commit.
3. While not a requirement per se, **consider signing your commits** with an SSH or GPG key for security purposes. This ensures to a lesser extent that you are the person who you claim to be, and reduces the potential amount of malicious contributions that could infiltrate the code base.
## Additional requirements
Other requirements (such as testing your code) for merging the changes will be available when you open your pull request via the template (which you **must** use), however, these are not stated here because they may not be mandatory in some cases (e.g. if the changes are still work-in-progress), so they may temporarily be ommitted for a certain period of time until the changes are fully ready to be reviewed, for instance.

2
create_vendor_tarball.sh Executable file
View file

@ -0,0 +1,2 @@
#!/bin/sh
tar --zstd -cvf vendor.tar.zst zig-pkg ly-ui/zig-pkg ly-core/zig-pkg

View file

@ -1,7 +0,0 @@
bin
obj
src/demo/*.o
src/demo/keyboard
src/demo/output
src/demo/paint
src/demo/truecolor

View file

@ -1,19 +0,0 @@
Copyright (C) 2010-2013 nsf <no.smile.face@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -1,57 +0,0 @@
# Termbox
[Termbox](https://github.com/nsf/termbox)
was a promising Text User Interface (TUI) library.
Unfortunately, its original author
[changed his mind](https://github.com/nsf/termbox/issues/37#issuecomment-261075481)
about consoles and despite the
[community's efforts](https://github.com/nsf/termbox/pull/104#issuecomment-300308156)
to keep the library's development going, preferred to let it die. Before it happened,
[some people](https://wiki.musl-libc.org/alternatives.html)
already noticed the robustness of the initial architecture
[became compromised](https://github.com/nsf/termbox/commit/66c3f91b14e24510319bce6b5cc2fecf8cf5abff#commitcomment-3790714)
in a nonsensical refactoring frenzy. Now, the author refuses to merge features
like true-color support, invoking some
[spurious correlations](https://github.com/nsf/termbox/pull/104#issuecomment-300292223)
we will discuss no further.
## The new Termbox-next
This fork was made to restore the codebase to its original quality (before
[66c3f91](https://github.com/nsf/termbox/commit/66c3f91b14e24510319bce6b5cc2fecf8cf5abff))
while providing all the functionnalities of the current implementation.
This was achieved by branching at
[a2e217f](https://github.com/nsf/termbox/commit/a2e217f0fb97e6bbd589136ea3945f9d5a123d81)
and cherry-picking all the commits up to
[d63b83a](https://github.com/nsf/termbox/commit/d63b83af04e0fd6da836bb8f37e5cec72a1dc95a)
if they weren't harmful.
## Changes
A lot of things changed during the process:
- *waf*, the original build system, was completely removed from the
project and replaced by make.
- anything related to python was removed as well
## Getting started
Termbox's interface only consists of 12 functions:
```
tb_init() // initialization
tb_shutdown() // shutdown
tb_width() // width of the terminal screen
tb_height() // height of the terminal screen
tb_clear() // clear buffer
tb_present() // sync internal buffer with terminal
tb_put_cell()
tb_change_cell()
tb_blit() // drawing functions
tb_select_input_mode() // change input mode
tb_peek_event() // peek a keyboard event
tb_poll_event() // wait for a keyboard event
```
See src/termbox.h header file for full detail.
## TL;DR
`make` to build a static version of the lib under bin/termbox.a
`cd src/demo && make` to build the example programs in src/demo/

View file

@ -1,827 +0,0 @@
#include <assert.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdio.h>
#include "termbox.h"
struct key
{
unsigned char x;
unsigned char y;
uint32_t ch;
};
#define STOP {0,0,0}
struct key K_ESC[] = {{1, 1, 'E'}, {2, 1, 'S'}, {3, 1, 'C'}, STOP};
struct key K_F1[] = {{6, 1, 'F'}, {7, 1, '1'}, STOP};
struct key K_F2[] = {{9, 1, 'F'}, {10, 1, '2'}, STOP};
struct key K_F3[] = {{12, 1, 'F'}, {13, 1, '3'}, STOP};
struct key K_F4[] = {{15, 1, 'F'}, {16, 1, '4'}, STOP};
struct key K_F5[] = {{19, 1, 'F'}, {20, 1, '5'}, STOP};
struct key K_F6[] = {{22, 1, 'F'}, {23, 1, '6'}, STOP};
struct key K_F7[] = {{25, 1, 'F'}, {26, 1, '7'}, STOP};
struct key K_F8[] = {{28, 1, 'F'}, {29, 1, '8'}, STOP};
struct key K_F9[] = {{33, 1, 'F'}, {34, 1, '9'}, STOP};
struct key K_F10[] = {{36, 1, 'F'}, {37, 1, '1'}, {38, 1, '0'}, STOP};
struct key K_F11[] = {{40, 1, 'F'}, {41, 1, '1'}, {42, 1, '1'}, STOP};
struct key K_F12[] = {{44, 1, 'F'}, {45, 1, '1'}, {46, 1, '2'}, STOP};
struct key K_PRN[] = {{50, 1, 'P'}, {51, 1, 'R'}, {52, 1, 'N'}, STOP};
struct key K_SCR[] = {{54, 1, 'S'}, {55, 1, 'C'}, {56, 1, 'R'}, STOP};
struct key K_BRK[] = {{58, 1, 'B'}, {59, 1, 'R'}, {60, 1, 'K'}, STOP};
struct key K_LED1[] = {{66, 1, '-'}, STOP};
struct key K_LED2[] = {{70, 1, '-'}, STOP};
struct key K_LED3[] = {{74, 1, '-'}, STOP};
struct key K_TILDE[] = {{1, 4, '`'}, STOP};
struct key K_TILDE_SHIFT[] = {{1, 4, '~'}, STOP};
struct key K_1[] = {{4, 4, '1'}, STOP};
struct key K_1_SHIFT[] = {{4, 4, '!'}, STOP};
struct key K_2[] = {{7, 4, '2'}, STOP};
struct key K_2_SHIFT[] = {{7, 4, '@'}, STOP};
struct key K_3[] = {{10, 4, '3'}, STOP};
struct key K_3_SHIFT[] = {{10, 4, '#'}, STOP};
struct key K_4[] = {{13, 4, '4'}, STOP};
struct key K_4_SHIFT[] = {{13, 4, '$'}, STOP};
struct key K_5[] = {{16, 4, '5'}, STOP};
struct key K_5_SHIFT[] = {{16, 4, '%'}, STOP};
struct key K_6[] = {{19, 4, '6'}, STOP};
struct key K_6_SHIFT[] = {{19, 4, '^'}, STOP};
struct key K_7[] = {{22, 4, '7'}, STOP};
struct key K_7_SHIFT[] = {{22, 4, '&'}, STOP};
struct key K_8[] = {{25, 4, '8'}, STOP};
struct key K_8_SHIFT[] = {{25, 4, '*'}, STOP};
struct key K_9[] = {{28, 4, '9'}, STOP};
struct key K_9_SHIFT[] = {{28, 4, '('}, STOP};
struct key K_0[] = {{31, 4, '0'}, STOP};
struct key K_0_SHIFT[] = {{31, 4, ')'}, STOP};
struct key K_MINUS[] = {{34, 4, '-'}, STOP};
struct key K_MINUS_SHIFT[] = {{34, 4, '_'}, STOP};
struct key K_EQUALS[] = {{37, 4, '='}, STOP};
struct key K_EQUALS_SHIFT[] = {{37, 4, '+'}, STOP};
struct key K_BACKSLASH[] = {{40, 4, '\\'}, STOP};
struct key K_BACKSLASH_SHIFT[] = {{40, 4, '|'}, STOP};
struct key K_BACKSPACE[] = {{44, 4, 0x2190}, {45, 4, 0x2500}, {46, 4, 0x2500}, STOP};
struct key K_INS[] = {{50, 4, 'I'}, {51, 4, 'N'}, {52, 4, 'S'}, STOP};
struct key K_HOM[] = {{54, 4, 'H'}, {55, 4, 'O'}, {56, 4, 'M'}, STOP};
struct key K_PGU[] = {{58, 4, 'P'}, {59, 4, 'G'}, {60, 4, 'U'}, STOP};
struct key K_K_NUMLOCK[] = {{65, 4, 'N'}, STOP};
struct key K_K_SLASH[] = {{68, 4, '/'}, STOP};
struct key K_K_STAR[] = {{71, 4, '*'}, STOP};
struct key K_K_MINUS[] = {{74, 4, '-'}, STOP};
struct key K_TAB[] = {{1, 6, 'T'}, {2, 6, 'A'}, {3, 6, 'B'}, STOP};
struct key K_q[] = {{6, 6, 'q'}, STOP};
struct key K_Q[] = {{6, 6, 'Q'}, STOP};
struct key K_w[] = {{9, 6, 'w'}, STOP};
struct key K_W[] = {{9, 6, 'W'}, STOP};
struct key K_e[] = {{12, 6, 'e'}, STOP};
struct key K_E[] = {{12, 6, 'E'}, STOP};
struct key K_r[] = {{15, 6, 'r'}, STOP};
struct key K_R[] = {{15, 6, 'R'}, STOP};
struct key K_t[] = {{18, 6, 't'}, STOP};
struct key K_T[] = {{18, 6, 'T'}, STOP};
struct key K_y[] = {{21, 6, 'y'}, STOP};
struct key K_Y[] = {{21, 6, 'Y'}, STOP};
struct key K_u[] = {{24, 6, 'u'}, STOP};
struct key K_U[] = {{24, 6, 'U'}, STOP};
struct key K_i[] = {{27, 6, 'i'}, STOP};
struct key K_I[] = {{27, 6, 'I'}, STOP};
struct key K_o[] = {{30, 6, 'o'}, STOP};
struct key K_O[] = {{30, 6, 'O'}, STOP};
struct key K_p[] = {{33, 6, 'p'}, STOP};
struct key K_P[] = {{33, 6, 'P'}, STOP};
struct key K_LSQB[] = {{36, 6, '['}, STOP};
struct key K_LCUB[] = {{36, 6, '{'}, STOP};
struct key K_RSQB[] = {{39, 6, ']'}, STOP};
struct key K_RCUB[] = {{39, 6, '}'}, STOP};
struct key K_ENTER[] =
{
{43, 6, 0x2591}, {44, 6, 0x2591}, {45, 6, 0x2591}, {46, 6, 0x2591},
{43, 7, 0x2591}, {44, 7, 0x2591}, {45, 7, 0x21B5}, {46, 7, 0x2591},
{41, 8, 0x2591}, {42, 8, 0x2591}, {43, 8, 0x2591}, {44, 8, 0x2591},
{45, 8, 0x2591}, {46, 8, 0x2591}, STOP
};
struct key K_DEL[] = {{50, 6, 'D'}, {51, 6, 'E'}, {52, 6, 'L'}, STOP};
struct key K_END[] = {{54, 6, 'E'}, {55, 6, 'N'}, {56, 6, 'D'}, STOP};
struct key K_PGD[] = {{58, 6, 'P'}, {59, 6, 'G'}, {60, 6, 'D'}, STOP};
struct key K_K_7[] = {{65, 6, '7'}, STOP};
struct key K_K_8[] = {{68, 6, '8'}, STOP};
struct key K_K_9[] = {{71, 6, '9'}, STOP};
struct key K_K_PLUS[] = {{74, 6, ' '}, {74, 7, '+'}, {74, 8, ' '}, STOP};
struct key K_CAPS[] = {{1, 8, 'C'}, {2, 8, 'A'}, {3, 8, 'P'}, {4, 8, 'S'}, STOP};
struct key K_a[] = {{7, 8, 'a'}, STOP};
struct key K_A[] = {{7, 8, 'A'}, STOP};
struct key K_s[] = {{10, 8, 's'}, STOP};
struct key K_S[] = {{10, 8, 'S'}, STOP};
struct key K_d[] = {{13, 8, 'd'}, STOP};
struct key K_D[] = {{13, 8, 'D'}, STOP};
struct key K_f[] = {{16, 8, 'f'}, STOP};
struct key K_F[] = {{16, 8, 'F'}, STOP};
struct key K_g[] = {{19, 8, 'g'}, STOP};
struct key K_G[] = {{19, 8, 'G'}, STOP};
struct key K_h[] = {{22, 8, 'h'}, STOP};
struct key K_H[] = {{22, 8, 'H'}, STOP};
struct key K_j[] = {{25, 8, 'j'}, STOP};
struct key K_J[] = {{25, 8, 'J'}, STOP};
struct key K_k[] = {{28, 8, 'k'}, STOP};
struct key K_K[] = {{28, 8, 'K'}, STOP};
struct key K_l[] = {{31, 8, 'l'}, STOP};
struct key K_L[] = {{31, 8, 'L'}, STOP};
struct key K_SEMICOLON[] = {{34, 8, ';'}, STOP};
struct key K_PARENTHESIS[] = {{34, 8, ':'}, STOP};
struct key K_QUOTE[] = {{37, 8, '\''}, STOP};
struct key K_DOUBLEQUOTE[] = {{37, 8, '"'}, STOP};
struct key K_K_4[] = {{65, 8, '4'}, STOP};
struct key K_K_5[] = {{68, 8, '5'}, STOP};
struct key K_K_6[] = {{71, 8, '6'}, STOP};
struct key K_LSHIFT[] = {{1, 10, 'S'}, {2, 10, 'H'}, {3, 10, 'I'}, {4, 10, 'F'}, {5, 10, 'T'}, STOP};
struct key K_z[] = {{9, 10, 'z'}, STOP};
struct key K_Z[] = {{9, 10, 'Z'}, STOP};
struct key K_x[] = {{12, 10, 'x'}, STOP};
struct key K_X[] = {{12, 10, 'X'}, STOP};
struct key K_c[] = {{15, 10, 'c'}, STOP};
struct key K_C[] = {{15, 10, 'C'}, STOP};
struct key K_v[] = {{18, 10, 'v'}, STOP};
struct key K_V[] = {{18, 10, 'V'}, STOP};
struct key K_b[] = {{21, 10, 'b'}, STOP};
struct key K_B[] = {{21, 10, 'B'}, STOP};
struct key K_n[] = {{24, 10, 'n'}, STOP};
struct key K_N[] = {{24, 10, 'N'}, STOP};
struct key K_m[] = {{27, 10, 'm'}, STOP};
struct key K_M[] = {{27, 10, 'M'}, STOP};
struct key K_COMMA[] = {{30, 10, ','}, STOP};
struct key K_LANB[] = {{30, 10, '<'}, STOP};
struct key K_PERIOD[] = {{33, 10, '.'}, STOP};
struct key K_RANB[] = {{33, 10, '>'}, STOP};
struct key K_SLASH[] = {{36, 10, '/'}, STOP};
struct key K_QUESTION[] = {{36, 10, '?'}, STOP};
struct key K_RSHIFT[] = {{42, 10, 'S'}, {43, 10, 'H'}, {44, 10, 'I'}, {45, 10, 'F'}, {46, 10, 'T'}, STOP};
struct key K_ARROW_UP[] = {{54, 10, '('}, {55, 10, 0x2191}, {56, 10, ')'}, STOP};
struct key K_K_1[] = {{65, 10, '1'}, STOP};
struct key K_K_2[] = {{68, 10, '2'}, STOP};
struct key K_K_3[] = {{71, 10, '3'}, STOP};
struct key K_K_ENTER[] = {{74, 10, 0x2591}, {74, 11, 0x2591}, {74, 12, 0x2591}, STOP};
struct key K_LCTRL[] = {{1, 12, 'C'}, {2, 12, 'T'}, {3, 12, 'R'}, {4, 12, 'L'}, STOP};
struct key K_LWIN[] = {{6, 12, 'W'}, {7, 12, 'I'}, {8, 12, 'N'}, STOP};
struct key K_LALT[] = {{10, 12, 'A'}, {11, 12, 'L'}, {12, 12, 'T'}, STOP};
struct key K_SPACE[] =
{
{14, 12, ' '}, {15, 12, ' '}, {16, 12, ' '}, {17, 12, ' '}, {18, 12, ' '},
{19, 12, 'S'}, {20, 12, 'P'}, {21, 12, 'A'}, {22, 12, 'C'}, {23, 12, 'E'},
{24, 12, ' '}, {25, 12, ' '}, {26, 12, ' '}, {27, 12, ' '}, {28, 12, ' '},
STOP
};
struct key K_RALT[] = {{30, 12, 'A'}, {31, 12, 'L'}, {32, 12, 'T'}, STOP};
struct key K_RWIN[] = {{34, 12, 'W'}, {35, 12, 'I'}, {36, 12, 'N'}, STOP};
struct key K_RPROP[] = {{38, 12, 'P'}, {39, 12, 'R'}, {40, 12, 'O'}, {41, 12, 'P'}, STOP};
struct key K_RCTRL[] = {{43, 12, 'C'}, {44, 12, 'T'}, {45, 12, 'R'}, {46, 12, 'L'}, STOP};
struct key K_ARROW_LEFT[] = {{50, 12, '('}, {51, 12, 0x2190}, {52, 12, ')'}, STOP};
struct key K_ARROW_DOWN[] = {{54, 12, '('}, {55, 12, 0x2193}, {56, 12, ')'}, STOP};
struct key K_ARROW_RIGHT[] = {{58, 12, '('}, {59, 12, 0x2192}, {60, 12, ')'}, STOP};
struct key K_K_0[] = {{65, 12, ' '}, {66, 12, '0'}, {67, 12, ' '}, {68, 12, ' '}, STOP};
struct key K_K_PERIOD[] = {{71, 12, '.'}, STOP};
struct combo
{
struct key* keys[6];
};
struct combo combos[] =
{
{{K_TILDE, K_2, K_LCTRL, K_RCTRL, 0}},
{{K_A, K_LCTRL, K_RCTRL, 0}},
{{K_B, K_LCTRL, K_RCTRL, 0}},
{{K_C, K_LCTRL, K_RCTRL, 0}},
{{K_D, K_LCTRL, K_RCTRL, 0}},
{{K_E, K_LCTRL, K_RCTRL, 0}},
{{K_F, K_LCTRL, K_RCTRL, 0}},
{{K_G, K_LCTRL, K_RCTRL, 0}},
{{K_H, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}},
{{K_I, K_TAB, K_LCTRL, K_RCTRL, 0}},
{{K_J, K_LCTRL, K_RCTRL, 0}},
{{K_K, K_LCTRL, K_RCTRL, 0}},
{{K_L, K_LCTRL, K_RCTRL, 0}},
{{K_M, K_ENTER, K_K_ENTER, K_LCTRL, K_RCTRL, 0}},
{{K_N, K_LCTRL, K_RCTRL, 0}},
{{K_O, K_LCTRL, K_RCTRL, 0}},
{{K_P, K_LCTRL, K_RCTRL, 0}},
{{K_Q, K_LCTRL, K_RCTRL, 0}},
{{K_R, K_LCTRL, K_RCTRL, 0}},
{{K_S, K_LCTRL, K_RCTRL, 0}},
{{K_T, K_LCTRL, K_RCTRL, 0}},
{{K_U, K_LCTRL, K_RCTRL, 0}},
{{K_V, K_LCTRL, K_RCTRL, 0}},
{{K_W, K_LCTRL, K_RCTRL, 0}},
{{K_X, K_LCTRL, K_RCTRL, 0}},
{{K_Y, K_LCTRL, K_RCTRL, 0}},
{{K_Z, K_LCTRL, K_RCTRL, 0}},
{{K_LSQB, K_ESC, K_3, K_LCTRL, K_RCTRL, 0}},
{{K_4, K_BACKSLASH, K_LCTRL, K_RCTRL, 0}},
{{K_RSQB, K_5, K_LCTRL, K_RCTRL, 0}},
{{K_6, K_LCTRL, K_RCTRL, 0}},
{{K_7, K_SLASH, K_MINUS_SHIFT, K_LCTRL, K_RCTRL, 0}},
{{K_SPACE, 0}},
{{K_1_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_DOUBLEQUOTE, K_LSHIFT, K_RSHIFT, 0}},
{{K_3_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_4_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_5_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_7_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_QUOTE, 0}},
{{K_9_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_0_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_8_SHIFT, K_K_STAR, K_LSHIFT, K_RSHIFT, 0}},
{{K_EQUALS_SHIFT, K_K_PLUS, K_LSHIFT, K_RSHIFT, 0}},
{{K_COMMA, 0}},
{{K_MINUS, K_K_MINUS, 0}},
{{K_PERIOD, K_K_PERIOD, 0}},
{{K_SLASH, K_K_SLASH, 0}},
{{K_0, K_K_0, 0}},
{{K_1, K_K_1, 0}},
{{K_2, K_K_2, 0}},
{{K_3, K_K_3, 0}},
{{K_4, K_K_4, 0}},
{{K_5, K_K_5, 0}},
{{K_6, K_K_6, 0}},
{{K_7, K_K_7, 0}},
{{K_8, K_K_8, 0}},
{{K_9, K_K_9, 0}},
{{K_PARENTHESIS, K_LSHIFT, K_RSHIFT, 0}},
{{K_SEMICOLON, 0}},
{{K_LANB, K_LSHIFT, K_RSHIFT, 0}},
{{K_EQUALS, 0}},
{{K_RANB, K_LSHIFT, K_RSHIFT, 0}},
{{K_QUESTION, K_LSHIFT, K_RSHIFT, 0}},
{{K_2_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_A, K_LSHIFT, K_RSHIFT, 0}},
{{K_B, K_LSHIFT, K_RSHIFT, 0}},
{{K_C, K_LSHIFT, K_RSHIFT, 0}},
{{K_D, K_LSHIFT, K_RSHIFT, 0}},
{{K_E, K_LSHIFT, K_RSHIFT, 0}},
{{K_F, K_LSHIFT, K_RSHIFT, 0}},
{{K_G, K_LSHIFT, K_RSHIFT, 0}},
{{K_H, K_LSHIFT, K_RSHIFT, 0}},
{{K_I, K_LSHIFT, K_RSHIFT, 0}},
{{K_J, K_LSHIFT, K_RSHIFT, 0}},
{{K_K, K_LSHIFT, K_RSHIFT, 0}},
{{K_L, K_LSHIFT, K_RSHIFT, 0}},
{{K_M, K_LSHIFT, K_RSHIFT, 0}},
{{K_N, K_LSHIFT, K_RSHIFT, 0}},
{{K_O, K_LSHIFT, K_RSHIFT, 0}},
{{K_P, K_LSHIFT, K_RSHIFT, 0}},
{{K_Q, K_LSHIFT, K_RSHIFT, 0}},
{{K_R, K_LSHIFT, K_RSHIFT, 0}},
{{K_S, K_LSHIFT, K_RSHIFT, 0}},
{{K_T, K_LSHIFT, K_RSHIFT, 0}},
{{K_U, K_LSHIFT, K_RSHIFT, 0}},
{{K_V, K_LSHIFT, K_RSHIFT, 0}},
{{K_W, K_LSHIFT, K_RSHIFT, 0}},
{{K_X, K_LSHIFT, K_RSHIFT, 0}},
{{K_Y, K_LSHIFT, K_RSHIFT, 0}},
{{K_Z, K_LSHIFT, K_RSHIFT, 0}},
{{K_LSQB, 0}},
{{K_BACKSLASH, 0}},
{{K_RSQB, 0}},
{{K_6_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_MINUS_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_TILDE, 0}},
{{K_a, 0}},
{{K_b, 0}},
{{K_c, 0}},
{{K_d, 0}},
{{K_e, 0}},
{{K_f, 0}},
{{K_g, 0}},
{{K_h, 0}},
{{K_i, 0}},
{{K_j, 0}},
{{K_k, 0}},
{{K_l, 0}},
{{K_m, 0}},
{{K_n, 0}},
{{K_o, 0}},
{{K_p, 0}},
{{K_q, 0}},
{{K_r, 0}},
{{K_s, 0}},
{{K_t, 0}},
{{K_u, 0}},
{{K_v, 0}},
{{K_w, 0}},
{{K_x, 0}},
{{K_y, 0}},
{{K_z, 0}},
{{K_LCUB, K_LSHIFT, K_RSHIFT, 0}},
{{K_BACKSLASH_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_RCUB, K_LSHIFT, K_RSHIFT, 0}},
{{K_TILDE_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
{{K_8, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}}
};
struct combo func_combos[] =
{
{{K_F1, 0}},
{{K_F2, 0}},
{{K_F3, 0}},
{{K_F4, 0}},
{{K_F5, 0}},
{{K_F6, 0}},
{{K_F7, 0}},
{{K_F8, 0}},
{{K_F9, 0}},
{{K_F10, 0}},
{{K_F11, 0}},
{{K_F12, 0}},
{{K_INS, 0}},
{{K_DEL, 0}},
{{K_HOM, 0}},
{{K_END, 0}},
{{K_PGU, 0}},
{{K_PGD, 0}},
{{K_ARROW_UP, 0}},
{{K_ARROW_DOWN, 0}},
{{K_ARROW_LEFT, 0}},
{{K_ARROW_RIGHT, 0}}
};
void print_tb(const char* str, int x, int y, uint32_t fg, uint32_t bg)
{
while (*str)
{
uint32_t uni;
str += utf8_char_to_unicode(&uni, str);
tb_change_cell(x, y, uni, fg, bg);
x++;
}
}
void printf_tb(int x, int y, uint32_t fg, uint32_t bg, const char* fmt, ...)
{
char buf[4096];
va_list vl;
va_start(vl, fmt);
vsnprintf(buf, sizeof(buf), fmt, vl);
va_end(vl);
print_tb(buf, x, y, fg, bg);
}
void draw_key(struct key* k, uint32_t fg, uint32_t bg)
{
while (k->x)
{
tb_change_cell(k->x + 2, k->y + 4, k->ch, fg, bg);
k++;
}
}
void draw_keyboard()
{
int i;
tb_change_cell(0, 0, 0x250C, TB_WHITE, TB_DEFAULT);
tb_change_cell(79, 0, 0x2510, TB_WHITE, TB_DEFAULT);
tb_change_cell(0, 23, 0x2514, TB_WHITE, TB_DEFAULT);
tb_change_cell(79, 23, 0x2518, TB_WHITE, TB_DEFAULT);
for (i = 1; i < 79; ++i)
{
tb_change_cell(i, 0, 0x2500, TB_WHITE, TB_DEFAULT);
tb_change_cell(i, 23, 0x2500, TB_WHITE, TB_DEFAULT);
tb_change_cell(i, 17, 0x2500, TB_WHITE, TB_DEFAULT);
tb_change_cell(i, 4, 0x2500, TB_WHITE, TB_DEFAULT);
}
for (i = 1; i < 23; ++i)
{
tb_change_cell(0, i, 0x2502, TB_WHITE, TB_DEFAULT);
tb_change_cell(79, i, 0x2502, TB_WHITE, TB_DEFAULT);
}
tb_change_cell(0, 17, 0x251C, TB_WHITE, TB_DEFAULT);
tb_change_cell(79, 17, 0x2524, TB_WHITE, TB_DEFAULT);
tb_change_cell(0, 4, 0x251C, TB_WHITE, TB_DEFAULT);
tb_change_cell(79, 4, 0x2524, TB_WHITE, TB_DEFAULT);
for (i = 5; i < 17; ++i)
{
tb_change_cell(1, i, 0x2588, TB_YELLOW, TB_YELLOW);
tb_change_cell(78, i, 0x2588, TB_YELLOW, TB_YELLOW);
}
draw_key(K_ESC, TB_WHITE, TB_BLUE);
draw_key(K_F1, TB_WHITE, TB_BLUE);
draw_key(K_F2, TB_WHITE, TB_BLUE);
draw_key(K_F3, TB_WHITE, TB_BLUE);
draw_key(K_F4, TB_WHITE, TB_BLUE);
draw_key(K_F5, TB_WHITE, TB_BLUE);
draw_key(K_F6, TB_WHITE, TB_BLUE);
draw_key(K_F7, TB_WHITE, TB_BLUE);
draw_key(K_F8, TB_WHITE, TB_BLUE);
draw_key(K_F9, TB_WHITE, TB_BLUE);
draw_key(K_F10, TB_WHITE, TB_BLUE);
draw_key(K_F11, TB_WHITE, TB_BLUE);
draw_key(K_F12, TB_WHITE, TB_BLUE);
draw_key(K_PRN, TB_WHITE, TB_BLUE);
draw_key(K_SCR, TB_WHITE, TB_BLUE);
draw_key(K_BRK, TB_WHITE, TB_BLUE);
draw_key(K_LED1, TB_WHITE, TB_BLUE);
draw_key(K_LED2, TB_WHITE, TB_BLUE);
draw_key(K_LED3, TB_WHITE, TB_BLUE);
draw_key(K_TILDE, TB_WHITE, TB_BLUE);
draw_key(K_1, TB_WHITE, TB_BLUE);
draw_key(K_2, TB_WHITE, TB_BLUE);
draw_key(K_3, TB_WHITE, TB_BLUE);
draw_key(K_4, TB_WHITE, TB_BLUE);
draw_key(K_5, TB_WHITE, TB_BLUE);
draw_key(K_6, TB_WHITE, TB_BLUE);
draw_key(K_7, TB_WHITE, TB_BLUE);
draw_key(K_8, TB_WHITE, TB_BLUE);
draw_key(K_9, TB_WHITE, TB_BLUE);
draw_key(K_0, TB_WHITE, TB_BLUE);
draw_key(K_MINUS, TB_WHITE, TB_BLUE);
draw_key(K_EQUALS, TB_WHITE, TB_BLUE);
draw_key(K_BACKSLASH, TB_WHITE, TB_BLUE);
draw_key(K_BACKSPACE, TB_WHITE, TB_BLUE);
draw_key(K_INS, TB_WHITE, TB_BLUE);
draw_key(K_HOM, TB_WHITE, TB_BLUE);
draw_key(K_PGU, TB_WHITE, TB_BLUE);
draw_key(K_K_NUMLOCK, TB_WHITE, TB_BLUE);
draw_key(K_K_SLASH, TB_WHITE, TB_BLUE);
draw_key(K_K_STAR, TB_WHITE, TB_BLUE);
draw_key(K_K_MINUS, TB_WHITE, TB_BLUE);
draw_key(K_TAB, TB_WHITE, TB_BLUE);
draw_key(K_q, TB_WHITE, TB_BLUE);
draw_key(K_w, TB_WHITE, TB_BLUE);
draw_key(K_e, TB_WHITE, TB_BLUE);
draw_key(K_r, TB_WHITE, TB_BLUE);
draw_key(K_t, TB_WHITE, TB_BLUE);
draw_key(K_y, TB_WHITE, TB_BLUE);
draw_key(K_u, TB_WHITE, TB_BLUE);
draw_key(K_i, TB_WHITE, TB_BLUE);
draw_key(K_o, TB_WHITE, TB_BLUE);
draw_key(K_p, TB_WHITE, TB_BLUE);
draw_key(K_LSQB, TB_WHITE, TB_BLUE);
draw_key(K_RSQB, TB_WHITE, TB_BLUE);
draw_key(K_ENTER, TB_WHITE, TB_BLUE);
draw_key(K_DEL, TB_WHITE, TB_BLUE);
draw_key(K_END, TB_WHITE, TB_BLUE);
draw_key(K_PGD, TB_WHITE, TB_BLUE);
draw_key(K_K_7, TB_WHITE, TB_BLUE);
draw_key(K_K_8, TB_WHITE, TB_BLUE);
draw_key(K_K_9, TB_WHITE, TB_BLUE);
draw_key(K_K_PLUS, TB_WHITE, TB_BLUE);
draw_key(K_CAPS, TB_WHITE, TB_BLUE);
draw_key(K_a, TB_WHITE, TB_BLUE);
draw_key(K_s, TB_WHITE, TB_BLUE);
draw_key(K_d, TB_WHITE, TB_BLUE);
draw_key(K_f, TB_WHITE, TB_BLUE);
draw_key(K_g, TB_WHITE, TB_BLUE);
draw_key(K_h, TB_WHITE, TB_BLUE);
draw_key(K_j, TB_WHITE, TB_BLUE);
draw_key(K_k, TB_WHITE, TB_BLUE);
draw_key(K_l, TB_WHITE, TB_BLUE);
draw_key(K_SEMICOLON, TB_WHITE, TB_BLUE);
draw_key(K_QUOTE, TB_WHITE, TB_BLUE);
draw_key(K_K_4, TB_WHITE, TB_BLUE);
draw_key(K_K_5, TB_WHITE, TB_BLUE);
draw_key(K_K_6, TB_WHITE, TB_BLUE);
draw_key(K_LSHIFT, TB_WHITE, TB_BLUE);
draw_key(K_z, TB_WHITE, TB_BLUE);
draw_key(K_x, TB_WHITE, TB_BLUE);
draw_key(K_c, TB_WHITE, TB_BLUE);
draw_key(K_v, TB_WHITE, TB_BLUE);
draw_key(K_b, TB_WHITE, TB_BLUE);
draw_key(K_n, TB_WHITE, TB_BLUE);
draw_key(K_m, TB_WHITE, TB_BLUE);
draw_key(K_COMMA, TB_WHITE, TB_BLUE);
draw_key(K_PERIOD, TB_WHITE, TB_BLUE);
draw_key(K_SLASH, TB_WHITE, TB_BLUE);
draw_key(K_RSHIFT, TB_WHITE, TB_BLUE);
draw_key(K_ARROW_UP, TB_WHITE, TB_BLUE);
draw_key(K_K_1, TB_WHITE, TB_BLUE);
draw_key(K_K_2, TB_WHITE, TB_BLUE);
draw_key(K_K_3, TB_WHITE, TB_BLUE);
draw_key(K_K_ENTER, TB_WHITE, TB_BLUE);
draw_key(K_LCTRL, TB_WHITE, TB_BLUE);
draw_key(K_LWIN, TB_WHITE, TB_BLUE);
draw_key(K_LALT, TB_WHITE, TB_BLUE);
draw_key(K_SPACE, TB_WHITE, TB_BLUE);
draw_key(K_RCTRL, TB_WHITE, TB_BLUE);
draw_key(K_RPROP, TB_WHITE, TB_BLUE);
draw_key(K_RWIN, TB_WHITE, TB_BLUE);
draw_key(K_RALT, TB_WHITE, TB_BLUE);
draw_key(K_ARROW_LEFT, TB_WHITE, TB_BLUE);
draw_key(K_ARROW_DOWN, TB_WHITE, TB_BLUE);
draw_key(K_ARROW_RIGHT, TB_WHITE, TB_BLUE);
draw_key(K_K_0, TB_WHITE, TB_BLUE);
draw_key(K_K_PERIOD, TB_WHITE, TB_BLUE);
printf_tb(33, 1, TB_MAGENTA | TB_BOLD, TB_DEFAULT, "Keyboard demo!");
printf_tb(21, 2, TB_MAGENTA, TB_DEFAULT,
"(press CTRL+X and then CTRL+Q to exit)");
printf_tb(15, 3, TB_MAGENTA, TB_DEFAULT,
"(press CTRL+X and then CTRL+C to change input mode)");
int inputmode = tb_select_input_mode(0);
char inputmode_str[64];
if (inputmode & TB_INPUT_ESC)
{
sprintf(inputmode_str, "TB_INPUT_ESC");
}
if (inputmode & TB_INPUT_ALT)
{
sprintf(inputmode_str, "TB_INPUT_ALT");
}
if (inputmode & TB_INPUT_MOUSE)
{
sprintf(inputmode_str + 12, " | TB_INPUT_MOUSE");
}
printf_tb(3, 18, TB_WHITE, TB_DEFAULT, "Input mode: %s", inputmode_str);
}
const char* funckeymap(int k)
{
static const char* fcmap[] =
{
"CTRL+2, CTRL+~",
"CTRL+A",
"CTRL+B",
"CTRL+C",
"CTRL+D",
"CTRL+E",
"CTRL+F",
"CTRL+G",
"CTRL+H, BACKSPACE",
"CTRL+I, TAB",
"CTRL+J",
"CTRL+K",
"CTRL+L",
"CTRL+M, ENTER",
"CTRL+N",
"CTRL+O",
"CTRL+P",
"CTRL+Q",
"CTRL+R",
"CTRL+S",
"CTRL+T",
"CTRL+U",
"CTRL+V",
"CTRL+W",
"CTRL+X",
"CTRL+Y",
"CTRL+Z",
"CTRL+3, ESC, CTRL+[",
"CTRL+4, CTRL+\\",
"CTRL+5, CTRL+]",
"CTRL+6",
"CTRL+7, CTRL+/, CTRL+_",
"SPACE"
};
static const char* fkmap[] =
{
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"INSERT",
"DELETE",
"HOME",
"END",
"PGUP",
"PGDN",
"ARROW UP",
"ARROW DOWN",
"ARROW LEFT",
"ARROW RIGHT"
};
if (k == TB_KEY_CTRL_8)
{
return "CTRL+8, BACKSPACE 2"; // 0x7F
}
else if (k >= TB_KEY_ARROW_RIGHT && k <= 0xFFFF)
{
return fkmap[0xFFFF - k];
}
else if (k <= TB_KEY_SPACE)
{
return fcmap[k];
}
return "UNKNOWN";
}
void pretty_print_press(struct tb_event* ev)
{
char buf[7];
buf[utf8_unicode_to_char(buf, ev->ch)] = '\0';
printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Key: ");
printf_tb(8, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->key);
printf_tb(8, 20, TB_GREEN, TB_DEFAULT, "hex: 0x%X", ev->key);
printf_tb(8, 21, TB_CYAN, TB_DEFAULT, "octal: 0%o", ev->key);
printf_tb(8, 22, TB_RED, TB_DEFAULT, "string: %s", funckeymap(ev->key));
printf_tb(54, 19, TB_WHITE, TB_DEFAULT, "Char: ");
printf_tb(60, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->ch);
printf_tb(60, 20, TB_GREEN, TB_DEFAULT, "hex: 0x%X", ev->ch);
printf_tb(60, 21, TB_CYAN, TB_DEFAULT, "octal: 0%o", ev->ch);
printf_tb(60, 22, TB_RED, TB_DEFAULT, "string: %s", buf);
printf_tb(54, 18, TB_WHITE, TB_DEFAULT, "Modifier: %s",
(ev->mod) ? "TB_MOD_ALT" : "none");
}
void pretty_print_resize(struct tb_event* ev)
{
printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Resize event: %d x %d", ev->w, ev->h);
}
int counter = 0;
void pretty_print_mouse(struct tb_event* ev)
{
printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Mouse event: %d x %d", ev->x, ev->y);
char* btn = "";
switch (ev->key)
{
case TB_KEY_MOUSE_LEFT:
btn = "MouseLeft: %d";
break;
case TB_KEY_MOUSE_MIDDLE:
btn = "MouseMiddle: %d";
break;
case TB_KEY_MOUSE_RIGHT:
btn = "MouseRight: %d";
break;
case TB_KEY_MOUSE_WHEEL_UP:
btn = "MouseWheelUp: %d";
break;
case TB_KEY_MOUSE_WHEEL_DOWN:
btn = "MouseWheelDown: %d";
break;
case TB_KEY_MOUSE_RELEASE:
btn = "MouseRelease: %d";
}
counter++;
printf_tb(43, 19, TB_WHITE, TB_DEFAULT, "Key: ");
printf_tb(48, 19, TB_YELLOW, TB_DEFAULT, btn, counter);
}
void dispatch_press(struct tb_event* ev)
{
if (ev->mod & TB_MOD_ALT)
{
draw_key(K_LALT, TB_WHITE, TB_RED);
draw_key(K_RALT, TB_WHITE, TB_RED);
}
struct combo* k = 0;
if (ev->key >= TB_KEY_ARROW_RIGHT)
{
k = &func_combos[0xFFFF - ev->key];
}
else if (ev->ch < 128)
{
if (ev->ch == 0 && ev->key < 128)
{
k = &combos[ev->key];
}
else
{
k = &combos[ev->ch];
}
}
if (!k)
{
return;
}
struct key** keys = k->keys;
while (*keys)
{
draw_key(*keys, TB_WHITE, TB_RED);
keys++;
}
}
int main(int argc, char** argv)
{
(void) argc;
(void) argv;
int ret;
ret = tb_init();
if (ret)
{
fprintf(stderr, "tb_init() failed with error code %d\n", ret);
return 1;
}
tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
struct tb_event ev;
tb_clear();
draw_keyboard();
tb_present();
int inputmode = 0;
int ctrlxpressed = 0;
while (tb_poll_event(&ev))
{
switch (ev.type)
{
case TB_EVENT_KEY:
if (ev.key == TB_KEY_CTRL_Q && ctrlxpressed)
{
tb_shutdown();
return 0;
}
if (ev.key == TB_KEY_CTRL_C && ctrlxpressed)
{
static int chmap[] =
{
TB_INPUT_ESC | TB_INPUT_MOUSE, // 101
TB_INPUT_ALT | TB_INPUT_MOUSE, // 110
TB_INPUT_ESC, // 001
TB_INPUT_ALT, // 010
};
inputmode++;
if (inputmode >= 4)
{
inputmode = 0;
}
tb_select_input_mode(chmap[inputmode]);
}
if (ev.key == TB_KEY_CTRL_X)
{
ctrlxpressed = 1;
}
else
{
ctrlxpressed = 0;
}
tb_clear();
draw_keyboard();
dispatch_press(&ev);
pretty_print_press(&ev);
tb_present();
break;
case TB_EVENT_RESIZE:
tb_clear();
draw_keyboard();
pretty_print_resize(&ev);
tb_present();
break;
case TB_EVENT_MOUSE:
tb_clear();
draw_keyboard();
pretty_print_mouse(&ev);
tb_present();
break;
default:
break;
}
}
tb_shutdown();
return 0;
}

View file

@ -1,30 +0,0 @@
CC=gcc
FLAGS=-std=c99 -pedantic -Wall -Werror -g -static
INCL=-I../
BIND=../../bin
%.o:%.c
@echo "building source object $@"
@$(CC) $(INCL) $(FLAGS) -c -o $@ $<
all:keyboard output paint truecolor
keyboard:keyboard.o
@echo "compiling $@"
@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
output:output.o
@echo "compiling $@"
@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
paint:paint.o
@echo "compiling $@"
@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
truecolor:truecolor.o
@echo "compiling $@"
@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
clean:
@echo "cleaning workspace"
@rm -rf *.o keyboard output paint truecolor

View file

@ -1,156 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "../termbox.h"
static const char chars[] = "nnnnnnnnnbbbbbbbbbuuuuuuuuuBBBBBBBBB";
static const uint32_t all_attrs[] =
{
0,
TB_BOLD,
TB_UNDERLINE,
TB_BOLD | TB_UNDERLINE,
};
static int next_char(int current)
{
current++;
if (!chars[current])
{
current = 0;
}
return current;
}
static void draw_line(int x, int y, uint32_t bg)
{
int a, c;
int current_char = 0;
for (a = 0; a < 4; a++)
{
for (c = TB_DEFAULT; c <= TB_WHITE; c++)
{
uint32_t fg = all_attrs[a] | c;
tb_change_cell(x, y, chars[current_char], fg, bg);
current_char = next_char(current_char);
x++;
}
}
}
static void print_combinations_table(int sx, int sy, const uint32_t* attrs,
int attrs_n)
{
int i, c;
for (i = 0; i < attrs_n; i++)
{
for (c = TB_DEFAULT; c <= TB_WHITE; c++)
{
uint32_t bg = attrs[i] | c;
draw_line(sx, sy, bg);
sy++;
}
}
}
static void draw_all()
{
tb_clear();
tb_select_output_mode(TB_OUTPUT_NORMAL);
static const uint32_t col1[] = {0, TB_BOLD};
static const uint32_t col2[] = {TB_REVERSE};
print_combinations_table(1, 1, col1, 2);
print_combinations_table(2 + strlen(chars), 1, col2, 1);
tb_present();
tb_select_output_mode(TB_OUTPUT_GRAYSCALE);
int c, x, y;
for (x = 0, y = 23; x < 24; ++x)
{
tb_change_cell(x, y, '@', x, 0);
tb_change_cell(x + 25, y, ' ', 0, x);
}
tb_present();
tb_select_output_mode(TB_OUTPUT_216);
y++;
for (c = 0, x = 0; c < 216; ++c, ++x)
{
if (!(x % 24))
{
x = 0;
++y;
}
tb_change_cell(x, y, '@', c, 0);
tb_change_cell(x + 25, y, ' ', 0, c);
}
tb_present();
tb_select_output_mode(TB_OUTPUT_256);
y++;
for (c = 0, x = 0; c < 256; ++c, ++x)
{
if (!(x % 24))
{
x = 0;
++y;
}
tb_change_cell(x, y, '+', c | ((y & 1) ? TB_UNDERLINE : 0), 0);
tb_change_cell(x + 25, y, ' ', 0, c);
}
tb_present();
}
int main(int argc, char** argv)
{
(void)argc;
(void)argv;
int ret = tb_init();
if (ret)
{
fprintf(stderr, "tb_init() failed with error code %d\n", ret);
return 1;
}
draw_all();
struct tb_event ev;
while (tb_poll_event(&ev))
{
switch (ev.type)
{
case TB_EVENT_KEY:
switch (ev.key)
{
case TB_KEY_ESC:
goto done;
break;
}
break;
case TB_EVENT_RESIZE:
draw_all();
break;
}
}
done:
tb_shutdown();
return 0;
}

View file

@ -1,183 +0,0 @@
#include "../termbox.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static int curCol = 0;
static int curRune = 0;
static struct tb_cell* backbuf;
static int bbw = 0, bbh = 0;
static const uint32_t runes[] =
{
0x20, // ' '
0x2591, // '░'
0x2592, // '▒'
0x2593, // '▓'
0x2588, // '█'
};
#define len(a) (sizeof(a)/sizeof(a[0]))
static const uint32_t colors[] =
{
TB_BLACK,
TB_RED,
TB_GREEN,
TB_YELLOW,
TB_BLUE,
TB_MAGENTA,
TB_CYAN,
TB_WHITE,
};
void updateAndDrawButtons(int* current, int x, int y, int mx, int my, int n,
void (*attrFunc)(int, uint32_t*, uint32_t*, uint32_t*))
{
int lx = x;
int ly = y;
for (int i = 0; i < n; i++)
{
if (lx <= mx && mx <= lx + 3 && ly <= my && my <= ly + 1)
{
*current = i;
}
uint32_t r;
uint32_t fg, bg;
(*attrFunc)(i, &r, &fg, &bg);
tb_change_cell(lx + 0, ly + 0, r, fg, bg);
tb_change_cell(lx + 1, ly + 0, r, fg, bg);
tb_change_cell(lx + 2, ly + 0, r, fg, bg);
tb_change_cell(lx + 3, ly + 0, r, fg, bg);
tb_change_cell(lx + 0, ly + 1, r, fg, bg);
tb_change_cell(lx + 1, ly + 1, r, fg, bg);
tb_change_cell(lx + 2, ly + 1, r, fg, bg);
tb_change_cell(lx + 3, ly + 1, r, fg, bg);
lx += 4;
}
lx = x;
ly = y;
for (int i = 0; i < n; i++)
{
if (*current == i)
{
uint32_t fg = TB_RED | TB_BOLD;
uint32_t bg = TB_DEFAULT;
tb_change_cell(lx + 0, ly + 2, '^', fg, bg);
tb_change_cell(lx + 1, ly + 2, '^', fg, bg);
tb_change_cell(lx + 2, ly + 2, '^', fg, bg);
tb_change_cell(lx + 3, ly + 2, '^', fg, bg);
}
lx += 4;
}
}
void runeAttrFunc(int i, uint32_t* r, uint32_t* fg, uint32_t* bg)
{
*r = runes[i];
*fg = TB_DEFAULT;
*bg = TB_DEFAULT;
}
void colorAttrFunc(int i, uint32_t* r, uint32_t* fg, uint32_t* bg)
{
*r = ' ';
*fg = TB_DEFAULT;
*bg = colors[i];
}
void updateAndRedrawAll(int mx, int my)
{
tb_clear();
if (mx != -1 && my != -1)
{
backbuf[bbw * my + mx].ch = runes[curRune];
backbuf[bbw * my + mx].fg = colors[curCol];
}
memcpy(tb_cell_buffer(), backbuf, sizeof(struct tb_cell)*bbw * bbh);
int h = tb_height();
updateAndDrawButtons(&curRune, 0, 0, mx, my, len(runes), runeAttrFunc);
updateAndDrawButtons(&curCol, 0, h - 3, mx, my, len(colors), colorAttrFunc);
tb_present();
}
void reallocBackBuffer(int w, int h)
{
bbw = w;
bbh = h;
if (backbuf)
{
free(backbuf);
}
backbuf = calloc(sizeof(struct tb_cell), w * h);
}
int main(int argv, char** argc)
{
(void)argc;
(void)argv;
int code = tb_init();
if (code < 0)
{
fprintf(stderr, "termbox init failed, code: %d\n", code);
return -1;
}
tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
int w = tb_width();
int h = tb_height();
reallocBackBuffer(w, h);
updateAndRedrawAll(-1, -1);
for (;;)
{
struct tb_event ev;
int mx = -1;
int my = -1;
int t = tb_poll_event(&ev);
if (t == -1)
{
tb_shutdown();
fprintf(stderr, "termbox poll event error\n");
return -1;
}
switch (t)
{
case TB_EVENT_KEY:
if (ev.key == TB_KEY_ESC)
{
tb_shutdown();
return 0;
}
break;
case TB_EVENT_MOUSE:
if (ev.key == TB_KEY_MOUSE_LEFT)
{
mx = ev.x;
my = ev.y;
}
break;
case TB_EVENT_RESIZE:
reallocBackBuffer(ev.w, ev.h);
break;
}
updateAndRedrawAll(mx, my);
}
}

View file

@ -1,69 +0,0 @@
#include "termbox.h"
int main()
{
tb_init();
tb_select_output_mode(TB_OUTPUT_TRUECOLOR);
int w = tb_width();
int h = tb_height();
uint32_t bg = 0x000000, fg = 0x000000;
tb_clear();
int z = 0;
for (int y = 1; y < h; y++)
{
for (int x = 1; x < w; x++)
{
uint32_t ch;
utf8_char_to_unicode(&ch, "x");
fg = 0;
if (z % 2 == 0)
{
fg |= TB_BOLD;
}
if (z % 3 == 0)
{
fg |= TB_UNDERLINE;
}
if (z % 5 == 0)
{
fg |= TB_REVERSE;
}
tb_change_cell(x, y, ch, fg, bg);
bg += 0x000101;
z++;
}
bg += 0x080000;
if (bg > 0xFFFFFF)
{
bg = 0;
}
}
tb_present();
while (1)
{
struct tb_event ev;
int t = tb_poll_event(&ev);
if (t == -1)
{
break;
}
if (t == TB_EVENT_KEY)
{
break;
}
}
tb_shutdown();
return 0;
}

View file

@ -1,319 +0,0 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "term.h"
#define BUFFER_SIZE_MAX 16
// if s1 starts with s2 returns 1, else 0
static int starts_with(const char* s1, const char* s2)
{
// nice huh?
while (*s2)
{
if (*s1++ != *s2++)
{
return 0;
}
}
return 1;
}
static int parse_mouse_event(struct tb_event* event, const char* buf, int len)
{
if ((len >= 6) && starts_with(buf, "\033[M"))
{
// X10 mouse encoding, the simplest one
// \033 [ M Cb Cx Cy
int b = buf[3] - 32;
switch (b & 3)
{
case 0:
if ((b & 64) != 0)
{
event->key = TB_KEY_MOUSE_WHEEL_UP;
}
else
{
event->key = TB_KEY_MOUSE_LEFT;
}
break;
case 1:
if ((b & 64) != 0)
{
event->key = TB_KEY_MOUSE_WHEEL_DOWN;
}
else
{
event->key = TB_KEY_MOUSE_MIDDLE;
}
break;
case 2:
event->key = TB_KEY_MOUSE_RIGHT;
break;
case 3:
event->key = TB_KEY_MOUSE_RELEASE;
break;
default:
return -6;
}
event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default
if ((b & 32) != 0)
{
event->mod |= TB_MOD_MOTION;
}
// the coord is 1,1 for upper left
event->x = (uint8_t)buf[4] - 1 - 32;
event->y = (uint8_t)buf[5] - 1 - 32;
return 6;
}
else if (starts_with(buf, "\033[<") || starts_with(buf, "\033["))
{
// xterm 1006 extended mode or urxvt 1015 extended mode
// xterm: \033 [ < Cb ; Cx ; Cy (M or m)
// urxvt: \033 [ Cb ; Cx ; Cy M
int i, mi = -1, starti = -1;
int isM, isU, s1 = -1, s2 = -1;
int n1 = 0, n2 = 0, n3 = 0;
for (i = 0; i < len; i++)
{
// We search the first (s1) and the last (s2) ';'
if (buf[i] == ';')
{
if (s1 == -1)
{
s1 = i;
}
s2 = i;
}
// We search for the first 'm' or 'M'
if ((buf[i] == 'm' || buf[i] == 'M') && mi == -1)
{
mi = i;
break;
}
}
if (mi == -1)
{
return 0;
}
// whether it's a capital M or not
isM = (buf[mi] == 'M');
if (buf[2] == '<')
{
isU = 0;
starti = 3;
}
else
{
isU = 1;
starti = 2;
}
if (s1 == -1 || s2 == -1 || s1 == s2)
{
return 0;
}
n1 = strtoul(&buf[starti], NULL, 10);
n2 = strtoul(&buf[s1 + 1], NULL, 10);
n3 = strtoul(&buf[s2 + 1], NULL, 10);
if (isU)
{
n1 -= 32;
}
switch (n1 & 3)
{
case 0:
if ((n1 & 64) != 0)
{
event->key = TB_KEY_MOUSE_WHEEL_UP;
}
else
{
event->key = TB_KEY_MOUSE_LEFT;
}
break;
case 1:
if ((n1 & 64) != 0)
{
event->key = TB_KEY_MOUSE_WHEEL_DOWN;
}
else
{
event->key = TB_KEY_MOUSE_MIDDLE;
}
break;
case 2:
event->key = TB_KEY_MOUSE_RIGHT;
break;
case 3:
event->key = TB_KEY_MOUSE_RELEASE;
break;
default:
return mi + 1;
}
if (!isM)
{
// on xterm mouse release is signaled by lowercase m
event->key = TB_KEY_MOUSE_RELEASE;
}
event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default
if ((n1 & 32) != 0)
{
event->mod |= TB_MOD_MOTION;
}
event->x = (uint8_t)n2 - 1;
event->y = (uint8_t)n3 - 1;
return mi + 1;
}
return 0;
}
// convert escape sequence to event, and return consumed bytes on success (failure == 0)
static int parse_escape_seq(struct tb_event* event, const char* buf, int len)
{
int mouse_parsed = parse_mouse_event(event, buf, len);
if (mouse_parsed != 0)
{
return mouse_parsed;
}
// it's pretty simple here, find 'starts_with' match and return success, else return failure
int i;
for (i = 0; keys[i]; i++)
{
if (starts_with(buf, keys[i]))
{
event->ch = 0;
event->key = 0xFFFF - i;
return strlen(keys[i]);
}
}
return 0;
}
bool extract_event(struct tb_event* event, struct ringbuffer* inbuf,
int inputmode)
{
char buf[BUFFER_SIZE_MAX + 1];
int nbytes = ringbuffer_data_size(inbuf);
if (nbytes > BUFFER_SIZE_MAX)
{
nbytes = BUFFER_SIZE_MAX;
}
if (nbytes == 0)
{
return false;
}
ringbuffer_read(inbuf, buf, nbytes);
buf[nbytes] = '\0';
if (buf[0] == '\033')
{
int n = parse_escape_seq(event, buf, nbytes);
if (n != 0)
{
bool success = true;
if (n < 0)
{
success = false;
n = -n;
}
ringbuffer_pop(inbuf, 0, n);
return success;
}
else
{
// it's not escape sequence, then it's ALT or ESC, check inputmode
if (inputmode & TB_INPUT_ESC)
{
// if we're in escape mode, fill ESC event, pop buffer, return success
event->ch = 0;
event->key = TB_KEY_ESC;
event->mod = 0;
ringbuffer_pop(inbuf, 0, 1);
return true;
}
else if (inputmode & TB_INPUT_ALT)
{
// if we're in alt mode, set ALT modifier to event and redo parsing
event->mod = TB_MOD_ALT;
ringbuffer_pop(inbuf, 0, 1);
return extract_event(event, inbuf, inputmode);
}
assert(!"never got here");
}
}
// if we're here, this is not an escape sequence and not an alt sequence
// so, it's a FUNCTIONAL KEY or a UNICODE character
// first of all check if it's a functional key*/
if ((unsigned char)buf[0] <= TB_KEY_SPACE ||
(unsigned char)buf[0] == TB_KEY_BACKSPACE2)
{
// fill event, pop buffer, return success
event->ch = 0;
event->key = (uint16_t)buf[0];
ringbuffer_pop(inbuf, 0, 1);
return true;
}
// feh... we got utf8 here
// check if there is all bytes
if (nbytes >= utf8_char_length(buf[0]))
{
// everything ok, fill event, pop buffer, return success
utf8_char_to_unicode(&event->ch, buf);
event->key = 0;
ringbuffer_pop(inbuf, 0, utf8_char_length(buf[0]));
return true;
}
return false;
}

View file

@ -1,36 +0,0 @@
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "memstream.h"
void memstream_init(struct memstream* s, int fd, void* buffer, size_t len)
{
s->file = fd;
s->data = buffer;
s->pos = 0;
s->capa = len;
}
void memstream_flush(struct memstream* s)
{
write(s->file, s->data, s->pos);
s->pos = 0;
}
void memstream_write(struct memstream* s, void* source, size_t len)
{
unsigned char* data = source;
if (s->pos + len > s->capa)
{
memstream_flush(s);
}
memcpy(s->data + s->pos, data, len);
s->pos += len;
}
void memstream_puts(struct memstream* s, const char* str)
{
memstream_write(s, (void*) str, strlen(str));
}

View file

@ -1,20 +0,0 @@
#ifndef H_MEMSTREAM
#define H_MEMSTREAM
#include <stddef.h>
#include <stdio.h>
struct memstream
{
size_t pos;
size_t capa;
int file;
unsigned char* data;
};
void memstream_init(struct memstream* s, int fd, void* buffer, size_t len);
void memstream_flush(struct memstream* s);
void memstream_write(struct memstream* s, void* source, size_t len);
void memstream_puts(struct memstream* s, const char* str);
#endif

View file

@ -1,195 +0,0 @@
#include "ringbuffer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h> // for ptrdiff_t
int init_ringbuffer(struct ringbuffer* r, size_t size)
{
r->buf = (char*)malloc(size);
if (!r->buf)
{
return ERINGBUFFER_ALLOC_FAIL;
}
r->size = size;
clear_ringbuffer(r);
return 0;
}
void free_ringbuffer(struct ringbuffer* r)
{
free(r->buf);
}
void clear_ringbuffer(struct ringbuffer* r)
{
r->begin = 0;
r->end = 0;
}
size_t ringbuffer_free_space(struct ringbuffer* r)
{
if (r->begin == 0 && r->end == 0)
{
return r->size;
}
if (r->begin < r->end)
{
return r->size - (r->end - r->begin) - 1;
}
else
{
return r->begin - r->end - 1;
}
}
size_t ringbuffer_data_size(struct ringbuffer* r)
{
if (r->begin == 0 && r->end == 0)
{
return 0;
}
if (r->begin <= r->end)
{
return r->end - r->begin + 1;
}
else
{
return r->size - (r->begin - r->end) + 1;
}
}
void ringbuffer_push(struct ringbuffer* r, const void* data, size_t size)
{
if (ringbuffer_free_space(r) < size)
{
return;
}
if (r->begin == 0 && r->end == 0)
{
memcpy(r->buf, data, size);
r->begin = r->buf;
r->end = r->buf + size - 1;
return;
}
r->end++;
if (r->begin < r->end)
{
if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size)
{
// we can fit without cut
memcpy(r->end, data, size);
r->end += size - 1;
}
else
{
// make a cut
size_t s = r->buf + r->size - r->end;
memcpy(r->end, data, s);
size -= s;
memcpy(r->buf, (char*)data + s, size);
r->end = r->buf + size - 1;
}
}
else
{
memcpy(r->end, data, size);
r->end += size - 1;
}
}
void ringbuffer_pop(struct ringbuffer* r, void* data, size_t size)
{
if (ringbuffer_data_size(r) < size)
{
return;
}
int need_clear = 0;
if (ringbuffer_data_size(r) == size)
{
need_clear = 1;
}
if (r->begin < r->end)
{
if (data)
{
memcpy(data, r->begin, size);
}
r->begin += size;
}
else
{
if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size)
{
if (data)
{
memcpy(data, r->begin, size);
}
r->begin += size;
}
else
{
size_t s = r->buf + r->size - r->begin;
if (data)
{
memcpy(data, r->begin, s);
}
size -= s;
if (data)
{
memcpy((char*)data + s, r->buf, size);
}
r->begin = r->buf + size;
}
}
if (need_clear)
{
clear_ringbuffer(r);
}
}
void ringbuffer_read(struct ringbuffer* r, void* data, size_t size)
{
if (ringbuffer_data_size(r) < size)
{
return;
}
if (r->begin < r->end)
{
memcpy(data, r->begin, size);
}
else
{
if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size)
{
memcpy(data, r->begin, size);
}
else
{
size_t s = r->buf + r->size - r->begin;
memcpy(data, r->begin, s);
size -= s;
memcpy((char*)data + s, r->buf, size);
}
}
}

View file

@ -1,26 +0,0 @@
#ifndef H_RINGBUFFER
#define H_RINGBUFFER
#include <stddef.h>
#define ERINGBUFFER_ALLOC_FAIL -1
struct ringbuffer
{
char* buf;
size_t size;
char* begin;
char* end;
};
int init_ringbuffer(struct ringbuffer* r, size_t size);
void free_ringbuffer(struct ringbuffer* r);
void clear_ringbuffer(struct ringbuffer* r);
size_t ringbuffer_free_space(struct ringbuffer* r);
size_t ringbuffer_data_size(struct ringbuffer* r);
void ringbuffer_push(struct ringbuffer* r, const void* data, size_t size);
void ringbuffer_pop(struct ringbuffer* r, void* data, size_t size);
void ringbuffer_read(struct ringbuffer* r, void* data, size_t size);
#endif

View file

@ -1,412 +0,0 @@
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "term.h"
#define ENTER_MOUSE_SEQ "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
#define EXIT_MOUSE_SEQ "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
#define EUNSUPPORTED_TERM -1
// rxvt-256color
static const char* rxvt_256color_keys[] =
{
"\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~",
"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
"\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~",
"\033[A", "\033[B", "\033[D", "\033[C", NULL
};
static const char* rxvt_256color_funcs[] =
{
"\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l",
"\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
"\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
};
// Eterm
static const char* eterm_keys[] =
{
"\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~",
"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
"\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~",
"\033[A", "\033[B", "\033[D", "\033[C", NULL
};
static const char* eterm_funcs[] =
{
"\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l",
"\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
"", "", "", "",
};
// screen
static const char* screen_keys[] =
{
"\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~",
"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
"\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~",
"\033OA", "\033OB", "\033OD", "\033OC", NULL
};
static const char* screen_funcs[] =
{
"\033[?1049h", "\033[?1049l", "\033[34h\033[?25h", "\033[?25l",
"\033[H\033[J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
"\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
};
// rxvt-unicode
static const char* rxvt_unicode_keys[] =
{
"\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~",
"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
"\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~",
"\033[A", "\033[B", "\033[D", "\033[C", NULL
};
static const char* rxvt_unicode_funcs[] =
{
"\033[?1049h", "\033[r\033[?1049l", "\033[?25h", "\033[?25l",
"\033[H\033[2J", "\033[m\033(B", "\033[4m", "\033[1m", "\033[5m",
"\033[7m", "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
};
// linux
static const char* linux_keys[] =
{
"\033[[A", "\033[[B", "\033[[C", "\033[[D", "\033[[E", "\033[17~",
"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
"\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~",
"\033[A", "\033[B", "\033[D", "\033[C", NULL
};
static const char* linux_funcs[] =
{
"", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J",
"\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "",
};
// xterm
static const char* xterm_keys[] =
{
"\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~", "\033[18~",
"\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~",
"\033[3~", "\033OH", "\033OF", "\033[5~", "\033[6~", "\033OA", "\033OB",
"\033OD", "\033OC", NULL
};
static const char* xterm_funcs[] =
{
"\033[?1049h", "\033[?1049l", "\033[?12l\033[?25h", "\033[?25l",
"\033[H\033[2J", "\033(B\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
"\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
};
struct term
{
const char* name;
const char** keys;
const char** funcs;
};
static struct term terms[] =
{
{"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs},
{"Eterm", eterm_keys, eterm_funcs},
{"screen", screen_keys, screen_funcs},
{"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs},
{"linux", linux_keys, linux_funcs},
{"xterm", xterm_keys, xterm_funcs},
{0, 0, 0},
};
static int init_from_terminfo = 0;
const char** keys;
const char** funcs;
static int try_compatible(const char* term, const char* name,
const char** tkeys, const char** tfuncs)
{
if (strstr(term, name))
{
keys = tkeys;
funcs = tfuncs;
return 0;
}
return EUNSUPPORTED_TERM;
}
static int init_term_builtin(void)
{
int i;
const char* term = getenv("TERM");
if (term)
{
for (i = 0; terms[i].name; i++)
{
if (!strcmp(terms[i].name, term))
{
keys = terms[i].keys;
funcs = terms[i].funcs;
return 0;
}
}
// let's do some heuristic, maybe it's a compatible terminal
if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0)
{
return 0;
}
if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0)
{
return 0;
}
if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0)
{
return 0;
}
if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0)
{
return 0;
}
if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0)
{
return 0;
}
// let's assume that 'cygwin' is xterm compatible
if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0)
{
return 0;
}
}
return EUNSUPPORTED_TERM;
}
// terminfo
static char* read_file(const char* file)
{
FILE* f = fopen(file, "rb");
if (!f)
{
return 0;
}
struct stat st;
if (fstat(fileno(f), &st) != 0)
{
fclose(f);
return 0;
}
char* data = malloc(st.st_size);
if (!data)
{
fclose(f);
return 0;
}
if (fread(data, 1, st.st_size, f) != (size_t)st.st_size)
{
fclose(f);
free(data);
return 0;
}
fclose(f);
return data;
}
static char* terminfo_try_path(const char* path, const char* term)
{
char tmp[4096];
snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
tmp[sizeof(tmp) - 1] = '\0';
char* data = read_file(tmp);
if (data)
{
return data;
}
// fallback to darwin specific dirs structure
snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
tmp[sizeof(tmp) - 1] = '\0';
return read_file(tmp);
}
static char* load_terminfo(void)
{
char tmp[4096];
const char* term = getenv("TERM");
if (!term)
{
return 0;
}
// if TERMINFO is set, no other directory should be searched
const char* terminfo = getenv("TERMINFO");
if (terminfo)
{
return terminfo_try_path(terminfo, term);
}
// next, consider ~/.terminfo
const char* home = getenv("HOME");
if (home)
{
snprintf(tmp, sizeof(tmp), "%s/.terminfo", home);
tmp[sizeof(tmp) - 1] = '\0';
char* data = terminfo_try_path(tmp, term);
if (data)
{
return data;
}
}
// next, TERMINFO_DIRS
const char* dirs = getenv("TERMINFO_DIRS");
if (dirs)
{
snprintf(tmp, sizeof(tmp), "%s", dirs);
tmp[sizeof(tmp) - 1] = '\0';
char* dir = strtok(tmp, ":");
while (dir)
{
const char* cdir = dir;
if (strcmp(cdir, "") == 0)
{
cdir = "/usr/share/terminfo";
}
char* data = terminfo_try_path(cdir, term);
if (data)
{
return data;
}
dir = strtok(0, ":");
}
}
// fallback to /usr/share/terminfo
return terminfo_try_path("/usr/share/terminfo", term);
}
#define TI_MAGIC 0432
#define TI_ALT_MAGIC 542
#define TI_HEADER_LENGTH 12
#define TB_KEYS_NUM 22
static const char* terminfo_copy_string(char* data, int str, int table)
{
const int16_t off = *(int16_t*)(data + str);
const char* src = data + table + off;
int len = strlen(src);
char* dst = malloc(len + 1);
strcpy(dst, src);
return dst;
}
const int16_t ti_funcs[] =
{
28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88,
};
const int16_t ti_keys[] =
{
// apparently not a typo; 67 is F10 for whatever reason
66, 68, 69, 70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82,
81, 87, 61, 79, 83,
};
int init_term(void)
{
int i;
char* data = load_terminfo();
if (!data)
{
init_from_terminfo = 0;
return init_term_builtin();
}
int16_t* header = (int16_t*)data;
const int number_sec_len = header[0] == TI_ALT_MAGIC ? 4 : 2;
if ((header[1] + header[2]) % 2)
{
// old quirk to align everything on word boundaries
header[2] += 1;
}
const int str_offset = TI_HEADER_LENGTH +
header[1] + header[2] + number_sec_len * header[3];
const int table_offset = str_offset + 2 * header[4];
keys = malloc(sizeof(const char*) * (TB_KEYS_NUM + 1));
for (i = 0; i < TB_KEYS_NUM; i++)
{
keys[i] = terminfo_copy_string(data,
str_offset + 2 * ti_keys[i], table_offset);
}
keys[i] = NULL;
funcs = malloc(sizeof(const char*) * T_FUNCS_NUM);
// the last two entries are reserved for mouse. because the table offset is
// not there, the two entries have to fill in manually
for (i = 0; i < T_FUNCS_NUM - 2; i++)
{
funcs[i] = terminfo_copy_string(data,
str_offset + 2 * ti_funcs[i], table_offset);
}
funcs[T_FUNCS_NUM - 2] = ENTER_MOUSE_SEQ;
funcs[T_FUNCS_NUM - 1] = EXIT_MOUSE_SEQ;
init_from_terminfo = 1;
free(data);
return 0;
}
void shutdown_term(void)
{
if (init_from_terminfo)
{
int i;
for (i = 0; i < TB_KEYS_NUM; i++)
{
free((void*)keys[i]);
}
// the last two entries are reserved for mouse. because the table offset
// is not there, the two entries have to fill in manually and do not
// need to be freed.
for (i = 0; i < T_FUNCS_NUM - 2; i++)
{
free((void*)funcs[i]);
}
free(keys);
free(funcs);
}
}

View file

@ -1,38 +0,0 @@
#ifndef H_TERM
#define H_TERM
#include "termbox.h"
#include "ringbuffer.h"
#include <stdbool.h>
#define EUNSUPPORTED_TERM -1
enum
{
T_ENTER_CA,
T_EXIT_CA,
T_SHOW_CURSOR,
T_HIDE_CURSOR,
T_CLEAR_SCREEN,
T_SGR0,
T_UNDERLINE,
T_BOLD,
T_BLINK,
T_REVERSE,
T_ENTER_KEYPAD,
T_EXIT_KEYPAD,
T_ENTER_MOUSE,
T_EXIT_MOUSE,
T_FUNCS_NUM,
};
extern const char** keys;
extern const char** funcs;
// true on success, false on failure
bool extract_event(struct tb_event* event, struct ringbuffer* inbuf,
int inputmode);
int init_term(void);
void shutdown_term(void);
#endif

View file

@ -1,885 +0,0 @@
#include "term.h"
#include "termbox.h"
#include "memstream.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <termios.h>
#include <unistd.h>
#include <wchar.h>
struct cellbuf
{
int width;
int height;
struct tb_cell* cells;
};
#define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)]
#define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1)
#define LAST_COORD_INIT -1
static struct termios orig_tios;
static struct cellbuf back_buffer;
static struct cellbuf front_buffer;
static unsigned char write_buffer_data[32 * 1024];
static struct memstream write_buffer;
static int termw = -1;
static int termh = -1;
static int inputmode = TB_INPUT_ESC;
static int outputmode = TB_OUTPUT_NORMAL;
static struct ringbuffer inbuf;
static int out;
static FILE* in;
static int out_fileno;
static int in_fileno;
static int winch_fds[2];
static int lastx = LAST_COORD_INIT;
static int lasty = LAST_COORD_INIT;
static int cursor_x = -1;
static int cursor_y = -1;
static uint32_t background = TB_DEFAULT;
static uint32_t foreground = TB_DEFAULT;
static void write_cursor(int x, int y);
static void write_sgr(uint32_t fg, uint32_t bg);
static void cellbuf_init(struct cellbuf* buf, int width, int height);
static void cellbuf_resize(struct cellbuf* buf, int width, int height);
static void cellbuf_clear(struct cellbuf* buf);
static void cellbuf_free(struct cellbuf* buf);
static void update_size(void);
static void update_term_size(void);
static void send_attr(uint32_t fg, uint32_t bg);
static void send_char(int x, int y, uint32_t c);
static void send_clear(void);
static void sigwinch_handler(int xxx);
static int wait_fill_event(struct tb_event* event, struct timeval* timeout);
// may happen in a different thread
static volatile int buffer_size_change_request;
int tb_init_file(const char* name)
{
out = open(name, O_WRONLY);
in = fopen(name, "r");
if (out == -1 || !in)
{
if (out != -1)
{
close(out);
}
if (in)
{
fclose(in);
}
return TB_EFAILED_TO_OPEN_TTY;
}
out_fileno = out;
in_fileno = fileno(in);
if (init_term() < 0)
{
close(out);
fclose(in);
return TB_EUNSUPPORTED_TERMINAL;
}
if (pipe(winch_fds) < 0)
{
close(out);
fclose(in);
return TB_EPIPE_TRAP_ERROR;
}
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigwinch_handler;
sa.sa_flags = 0;
sigaction(SIGWINCH, &sa, 0);
tcgetattr(out_fileno, &orig_tios);
struct termios tios;
memcpy(&tios, &orig_tios, sizeof(tios));
tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
tios.c_oflag &= ~OPOST;
tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tios.c_cflag &= ~(CSIZE | PARENB);
tios.c_cflag |= CS8;
tios.c_cc[VMIN] = 0;
tios.c_cc[VTIME] = 0;
tcsetattr(out_fileno, TCSAFLUSH, &tios);
memstream_init(&write_buffer, out_fileno, write_buffer_data,
sizeof(write_buffer_data));
memstream_puts(&write_buffer, funcs[T_ENTER_CA]);
memstream_puts(&write_buffer, funcs[T_ENTER_KEYPAD]);
memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]);
send_clear();
update_term_size();
cellbuf_init(&back_buffer, termw, termh);
cellbuf_init(&front_buffer, termw, termh);
cellbuf_clear(&back_buffer);
cellbuf_clear(&front_buffer);
init_ringbuffer(&inbuf, 4096);
return 0;
}
int tb_init(void)
{
return tb_init_file("/dev/tty");
}
void tb_shutdown(void)
{
if (termw == -1)
{
fputs("tb_shutdown() should not be called twice.", stderr);
abort();
}
memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]);
memstream_puts(&write_buffer, funcs[T_SGR0]);
memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]);
memstream_puts(&write_buffer, funcs[T_EXIT_CA]);
memstream_puts(&write_buffer, funcs[T_EXIT_KEYPAD]);
memstream_puts(&write_buffer, funcs[T_EXIT_MOUSE]);
memstream_flush(&write_buffer);
tcsetattr(out_fileno, TCSAFLUSH, &orig_tios);
shutdown_term();
close(out);
fclose(in);
close(winch_fds[0]);
close(winch_fds[1]);
cellbuf_free(&back_buffer);
cellbuf_free(&front_buffer);
free_ringbuffer(&inbuf);
termw = termh = -1;
}
void tb_present(void)
{
int x, y, w, i;
struct tb_cell* back, *front;
// invalidate cursor position
lastx = LAST_COORD_INIT;
lasty = LAST_COORD_INIT;
if (buffer_size_change_request)
{
update_size();
buffer_size_change_request = 0;
}
for (y = 0; y < front_buffer.height; ++y)
{
for (x = 0; x < front_buffer.width;)
{
back = &CELL(&back_buffer, x, y);
front = &CELL(&front_buffer, x, y);
w = wcwidth(back->ch);
if (w < 1)
{
w = 1;
}
if (memcmp(back, front, sizeof(struct tb_cell)) == 0)
{
x += w;
continue;
}
memcpy(front, back, sizeof(struct tb_cell));
send_attr(back->fg, back->bg);
if (w > 1 && x >= front_buffer.width - (w - 1))
{
// Not enough room for wide ch, so send spaces
for (i = x; i < front_buffer.width; ++i)
{
send_char(i, y, ' ');
}
}
else
{
send_char(x, y, back->ch);
for (i = 1; i < w; ++i)
{
front = &CELL(&front_buffer, x + i, y);
front->ch = 0;
front->fg = back->fg;
front->bg = back->bg;
}
}
x += w;
}
}
if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
{
write_cursor(cursor_x, cursor_y);
}
memstream_flush(&write_buffer);
}
void tb_set_cursor(int cx, int cy)
{
if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy))
{
memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]);
}
if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy))
{
memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]);
}
cursor_x = cx;
cursor_y = cy;
if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
{
write_cursor(cursor_x, cursor_y);
}
}
void tb_put_cell(int x, int y, const struct tb_cell* cell)
{
if ((unsigned)x >= (unsigned)back_buffer.width)
{
return;
}
if ((unsigned)y >= (unsigned)back_buffer.height)
{
return;
}
CELL(&back_buffer, x, y) = *cell;
}
void tb_change_cell(int x, int y, uint32_t ch, uint32_t fg, uint32_t bg)
{
struct tb_cell c = {ch, fg, bg};
tb_put_cell(x, y, &c);
}
void tb_blit(int x, int y, int w, int h, const struct tb_cell* cells)
{
if (x + w < 0 || x >= back_buffer.width)
{
return;
}
if (y + h < 0 || y >= back_buffer.height)
{
return;
}
int xo = 0, yo = 0, ww = w, hh = h;
if (x < 0)
{
xo = -x;
ww -= xo;
x = 0;
}
if (y < 0)
{
yo = -y;
hh -= yo;
y = 0;
}
if (ww > back_buffer.width - x)
{
ww = back_buffer.width - x;
}
if (hh > back_buffer.height - y)
{
hh = back_buffer.height - y;
}
int sy;
struct tb_cell* dst = &CELL(&back_buffer, x, y);
const struct tb_cell* src = cells + yo * w + xo;
size_t size = sizeof(struct tb_cell) * ww;
for (sy = 0; sy < hh; ++sy)
{
memcpy(dst, src, size);
dst += back_buffer.width;
src += w;
}
}
struct tb_cell* tb_cell_buffer(void)
{
return back_buffer.cells;
}
int tb_poll_event(struct tb_event* event)
{
return wait_fill_event(event, 0);
}
int tb_peek_event(struct tb_event* event, int timeout)
{
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
return wait_fill_event(event, &tv);
}
int tb_width(void)
{
return termw;
}
int tb_height(void)
{
return termh;
}
void tb_clear(void)
{
if (buffer_size_change_request)
{
update_size();
buffer_size_change_request = 0;
}
cellbuf_clear(&back_buffer);
}
int tb_select_input_mode(int mode)
{
if (mode)
{
if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0)
{
mode |= TB_INPUT_ESC;
}
// technically termbox can handle that, but let's be nice
// and show here what mode is actually used
if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT))
{
mode &= ~TB_INPUT_ALT;
}
inputmode = mode;
if (mode & TB_INPUT_MOUSE)
{
memstream_puts(&write_buffer, funcs[T_ENTER_MOUSE]);
memstream_flush(&write_buffer);
}
else
{
memstream_puts(&write_buffer, funcs[T_EXIT_MOUSE]);
memstream_flush(&write_buffer);
}
}
return inputmode;
}
int tb_select_output_mode(int mode)
{
if (mode)
{
outputmode = mode;
}
return outputmode;
}
void tb_set_clear_attributes(uint32_t fg, uint32_t bg)
{
foreground = fg;
background = bg;
}
static unsigned convertnum(uint32_t num, char* buf)
{
unsigned i, l = 0;
int ch;
do
{
buf[l++] = '0' + (num % 10);
num /= 10;
}
while (num);
for (i = 0; i < l / 2; i++)
{
ch = buf[i];
buf[i] = buf[l - 1 - i];
buf[l - 1 - i] = ch;
}
return l;
}
#define WRITE_LITERAL(X) memstream_write(&write_buffer, (X), sizeof(X) -1)
#define WRITE_INT(X) memstream_write(&write_buffer, buf, convertnum((X), buf))
static void write_cursor(int x, int y)
{
char buf[32];
WRITE_LITERAL("\033[");
WRITE_INT(y + 1);
WRITE_LITERAL(";");
WRITE_INT(x + 1);
WRITE_LITERAL("H");
}
static void write_sgr(uint32_t fg, uint32_t bg)
{
char buf[32];
if (outputmode != TB_OUTPUT_TRUECOLOR && fg == TB_DEFAULT && bg == TB_DEFAULT)
{
return;
}
switch (outputmode)
{
case TB_OUTPUT_TRUECOLOR:
WRITE_LITERAL("\033[38;2;");
WRITE_INT(fg >> 16 & 0xFF);
WRITE_LITERAL(";");
WRITE_INT(fg >> 8 & 0xFF);
WRITE_LITERAL(";");
WRITE_INT(fg & 0xFF);
WRITE_LITERAL(";48;2;");
WRITE_INT(bg >> 16 & 0xFF);
WRITE_LITERAL(";");
WRITE_INT(bg >> 8 & 0xFF);
WRITE_LITERAL(";");
WRITE_INT(bg & 0xFF);
WRITE_LITERAL("m");
break;
case TB_OUTPUT_256:
case TB_OUTPUT_216:
case TB_OUTPUT_GRAYSCALE:
WRITE_LITERAL("\033[");
if (fg != TB_DEFAULT)
{
WRITE_LITERAL("38;5;");
WRITE_INT(fg);
if (bg != TB_DEFAULT)
{
WRITE_LITERAL(";");
}
}
if (bg != TB_DEFAULT)
{
WRITE_LITERAL("48;5;");
WRITE_INT(bg);
}
WRITE_LITERAL("m");
break;
case TB_OUTPUT_NORMAL:
default:
WRITE_LITERAL("\033[");
if (fg != TB_DEFAULT)
{
WRITE_LITERAL("3");
WRITE_INT(fg - 1);
if (bg != TB_DEFAULT)
{
WRITE_LITERAL(";");
}
}
if (bg != TB_DEFAULT)
{
WRITE_LITERAL("4");
WRITE_INT(bg - 1);
}
WRITE_LITERAL("m");
break;
}
}
static void cellbuf_init(struct cellbuf* buf, int width, int height)
{
buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height);
assert(buf->cells);
buf->width = width;
buf->height = height;
}
static void cellbuf_resize(struct cellbuf* buf, int width, int height)
{
if (buf->width == width && buf->height == height)
{
return;
}
int oldw = buf->width;
int oldh = buf->height;
struct tb_cell* oldcells = buf->cells;
cellbuf_init(buf, width, height);
cellbuf_clear(buf);
int minw = (width < oldw) ? width : oldw;
int minh = (height < oldh) ? height : oldh;
int i;
for (i = 0; i < minh; ++i)
{
struct tb_cell* csrc = oldcells + (i * oldw);
struct tb_cell* cdst = buf->cells + (i * width);
memcpy(cdst, csrc, sizeof(struct tb_cell) * minw);
}
free(oldcells);
}
static void cellbuf_clear(struct cellbuf* buf)
{
int i;
int ncells = buf->width * buf->height;
for (i = 0; i < ncells; ++i)
{
buf->cells[i].ch = ' ';
buf->cells[i].fg = foreground;
buf->cells[i].bg = background;
}
}
static void cellbuf_free(struct cellbuf* buf)
{
free(buf->cells);
}
static void get_term_size(int* w, int* h)
{
struct winsize sz;
memset(&sz, 0, sizeof(sz));
ioctl(out_fileno, TIOCGWINSZ, &sz);
if (w)
{
*w = sz.ws_col;
}
if (h)
{
*h = sz.ws_row;
}
}
static void send_attr(uint32_t fg, uint32_t bg)
{
#define LAST_ATTR_INIT 0xFFFFFFFF
static uint32_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT;
if (fg != lastfg || bg != lastbg)
{
memstream_puts(&write_buffer, funcs[T_SGR0]);
uint32_t fgcol;
uint32_t bgcol;
switch (outputmode)
{
case TB_OUTPUT_TRUECOLOR:
fgcol = fg;
bgcol = bg;
break;
case TB_OUTPUT_256:
fgcol = fg & 0xFF;
bgcol = bg & 0xFF;
break;
case TB_OUTPUT_216:
fgcol = fg & 0xFF;
if (fgcol > 215)
{
fgcol = 7;
}
bgcol = bg & 0xFF;
if (bgcol > 215)
{
bgcol = 0;
}
fgcol += 0x10;
bgcol += 0x10;
break;
case TB_OUTPUT_GRAYSCALE:
fgcol = fg & 0xFF;
if (fgcol > 23)
{
fgcol = 23;
}
bgcol = bg & 0xFF;
if (bgcol > 23)
{
bgcol = 0;
}
fgcol += 0xe8;
bgcol += 0xe8;
break;
case TB_OUTPUT_NORMAL:
default:
fgcol = fg & 0x0F;
bgcol = bg & 0x0F;
}
if (fg & TB_BOLD)
{
memstream_puts(&write_buffer, funcs[T_BOLD]);
}
if (bg & TB_BOLD)
{
memstream_puts(&write_buffer, funcs[T_BLINK]);
}
if (fg & TB_UNDERLINE)
{
memstream_puts(&write_buffer, funcs[T_UNDERLINE]);
}
if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
{
memstream_puts(&write_buffer, funcs[T_REVERSE]);
}
write_sgr(fgcol, bgcol);
lastfg = fg;
lastbg = bg;
}
}
static void send_char(int x, int y, uint32_t c)
{
char buf[7];
int bw = utf8_unicode_to_char(buf, c);
buf[bw] = '\0';
if (x - 1 != lastx || y != lasty)
{
write_cursor(x, y);
}
lastx = x;
lasty = y;
if (!c)
{
buf[0] = ' '; // replace 0 with whitespace
}
memstream_puts(&write_buffer, buf);
}
static void send_clear(void)
{
send_attr(foreground, background);
memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]);
if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
{
write_cursor(cursor_x, cursor_y);
}
memstream_flush(&write_buffer);
// we need to invalidate cursor position too and these two vars are
// used only for simple cursor positioning optimization, cursor
// actually may be in the correct place, but we simply discard
// optimization once and it gives us simple solution for the case when
// cursor moved
lastx = LAST_COORD_INIT;
lasty = LAST_COORD_INIT;
}
static void sigwinch_handler(int xxx)
{
(void) xxx;
const int zzz = 1;
write(winch_fds[1], &zzz, sizeof(int));
}
static void update_size(void)
{
update_term_size();
cellbuf_resize(&back_buffer, termw, termh);
cellbuf_resize(&front_buffer, termw, termh);
cellbuf_clear(&front_buffer);
send_clear();
}
static void update_term_size(void)
{
struct winsize sz;
memset(&sz, 0, sizeof(sz));
ioctl(out_fileno, TIOCGWINSZ, &sz);
termw = sz.ws_col;
termh = sz.ws_row;
}
static int wait_fill_event(struct tb_event* event, struct timeval* timeout)
{
#define ENOUGH_DATA_FOR_INPUT_PARSING 128
int result;
char buf[ENOUGH_DATA_FOR_INPUT_PARSING];
fd_set events;
memset(event, 0, sizeof(struct tb_event));
// try to extract event from input buffer, return on success
event->type = TB_EVENT_KEY;
if (extract_event(event, &inbuf, inputmode))
{
return event->type;
}
// it looks like input buffer is incomplete, let's try the short path
size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in);
if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in))
{
clearerr(in);
}
if (r > 0)
{
if (ringbuffer_free_space(&inbuf) < r)
{
return -1;
}
ringbuffer_push(&inbuf, buf, r);
if (extract_event(event, &inbuf, inputmode))
{
return event->type;
}
}
// no stuff in FILE's internal buffer, block in select
while (1)
{
FD_ZERO(&events);
FD_SET(in_fileno, &events);
FD_SET(winch_fds[0], &events);
int maxfd = (winch_fds[0] > in_fileno) ? winch_fds[0] : in_fileno;
result = select(maxfd + 1, &events, 0, 0, timeout);
if (!result)
{
return 0;
}
if (FD_ISSET(in_fileno, &events))
{
event->type = TB_EVENT_KEY;
size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in);
if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in))
{
clearerr(in);
}
if (r == 0)
{
continue;
}
// if there is no free space in input buffer, return error
if (ringbuffer_free_space(&inbuf) < r)
{
return -1;
}
// fill buffer
ringbuffer_push(&inbuf, buf, r);
if (extract_event(event, &inbuf, inputmode))
{
return event->type;
}
}
if (FD_ISSET(winch_fds[0], &events))
{
event->type = TB_EVENT_RESIZE;
int zzz = 0;
read(winch_fds[0], &zzz, sizeof(int));
buffer_size_change_request = 1;
get_term_size(&event->w, &event->h);
return TB_EVENT_RESIZE;
}
}
}

View file

@ -1,307 +0,0 @@
#ifndef H_TERMBOX
#define H_TERMBOX
#include <stdint.h>
// shared objects
#if __GNUC__ >= 4
#define SO_IMPORT __attribute__((visibility("default")))
#else
#define SO_IMPORT
#endif
// c++
#ifdef __cplusplus
extern "C" {
#endif
// Key constants. See also struct tb_event's key field.
// These are a safe subset of terminfo keys, which exist on all popular
// terminals. Termbox uses only them to stay truly portable.
#define TB_KEY_F1 (0xFFFF-0)
#define TB_KEY_F2 (0xFFFF-1)
#define TB_KEY_F3 (0xFFFF-2)
#define TB_KEY_F4 (0xFFFF-3)
#define TB_KEY_F5 (0xFFFF-4)
#define TB_KEY_F6 (0xFFFF-5)
#define TB_KEY_F7 (0xFFFF-6)
#define TB_KEY_F8 (0xFFFF-7)
#define TB_KEY_F9 (0xFFFF-8)
#define TB_KEY_F10 (0xFFFF-9)
#define TB_KEY_F11 (0xFFFF-10)
#define TB_KEY_F12 (0xFFFF-11)
#define TB_KEY_INSERT (0xFFFF-12)
#define TB_KEY_DELETE (0xFFFF-13)
#define TB_KEY_HOME (0xFFFF-14)
#define TB_KEY_END (0xFFFF-15)
#define TB_KEY_PGUP (0xFFFF-16)
#define TB_KEY_PGDN (0xFFFF-17)
#define TB_KEY_ARROW_UP (0xFFFF-18)
#define TB_KEY_ARROW_DOWN (0xFFFF-19)
#define TB_KEY_ARROW_LEFT (0xFFFF-20)
#define TB_KEY_ARROW_RIGHT (0xFFFF-21)
#define TB_KEY_MOUSE_LEFT (0xFFFF-22)
#define TB_KEY_MOUSE_RIGHT (0xFFFF-23)
#define TB_KEY_MOUSE_MIDDLE (0xFFFF-24)
#define TB_KEY_MOUSE_RELEASE (0xFFFF-25)
#define TB_KEY_MOUSE_WHEEL_UP (0xFFFF-26)
#define TB_KEY_MOUSE_WHEEL_DOWN (0xFFFF-27)
// These are all ASCII code points below SPACE character and a BACKSPACE key.
#define TB_KEY_CTRL_TILDE 0x00
#define TB_KEY_CTRL_2 0x00 // clash with 'CTRL_TILDE'
#define TB_KEY_CTRL_A 0x01
#define TB_KEY_CTRL_B 0x02
#define TB_KEY_CTRL_C 0x03
#define TB_KEY_CTRL_D 0x04
#define TB_KEY_CTRL_E 0x05
#define TB_KEY_CTRL_F 0x06
#define TB_KEY_CTRL_G 0x07
#define TB_KEY_BACKSPACE 0x08
#define TB_KEY_CTRL_H 0x08 // clash with 'CTRL_BACKSPACE'
#define TB_KEY_TAB 0x09
#define TB_KEY_CTRL_I 0x09 // clash with 'TAB'
#define TB_KEY_CTRL_J 0x0A
#define TB_KEY_CTRL_K 0x0B
#define TB_KEY_CTRL_L 0x0C
#define TB_KEY_ENTER 0x0D
#define TB_KEY_CTRL_M 0x0D // clash with 'ENTER'
#define TB_KEY_CTRL_N 0x0E
#define TB_KEY_CTRL_O 0x0F
#define TB_KEY_CTRL_P 0x10
#define TB_KEY_CTRL_Q 0x11
#define TB_KEY_CTRL_R 0x12
#define TB_KEY_CTRL_S 0x13
#define TB_KEY_CTRL_T 0x14
#define TB_KEY_CTRL_U 0x15
#define TB_KEY_CTRL_V 0x16
#define TB_KEY_CTRL_W 0x17
#define TB_KEY_CTRL_X 0x18
#define TB_KEY_CTRL_Y 0x19
#define TB_KEY_CTRL_Z 0x1A
#define TB_KEY_ESC 0x1B
#define TB_KEY_CTRL_LSQ_BRACKET 0x1B // clash with 'ESC'
#define TB_KEY_CTRL_3 0x1B // clash with 'ESC'
#define TB_KEY_CTRL_4 0x1C
#define TB_KEY_CTRL_BACKSLASH 0x1C // clash with 'CTRL_4'
#define TB_KEY_CTRL_5 0x1D
#define TB_KEY_CTRL_RSQ_BRACKET 0x1D // clash with 'CTRL_5'
#define TB_KEY_CTRL_6 0x1E
#define TB_KEY_CTRL_7 0x1F
#define TB_KEY_CTRL_SLASH 0x1F // clash with 'CTRL_7'
#define TB_KEY_CTRL_UNDERSCORE 0x1F // clash with 'CTRL_7'
#define TB_KEY_SPACE 0x20
#define TB_KEY_BACKSPACE2 0x7F
#define TB_KEY_CTRL_8 0x7F // clash with 'BACKSPACE2'
// These are non-existing ones.
// #define TB_KEY_CTRL_1 clash with '1'
// #define TB_KEY_CTRL_9 clash with '9'
// #define TB_KEY_CTRL_0 clash with '0'
// Alt modifier constant, see tb_event.mod field and tb_select_input_mode function.
// Mouse-motion modifier
#define TB_MOD_ALT 0x01
#define TB_MOD_MOTION 0x02
// Colors (see struct tb_cell's fg and bg fields).
#define TB_DEFAULT 0x00
#define TB_BLACK 0x01
#define TB_RED 0x02
#define TB_GREEN 0x03
#define TB_YELLOW 0x04
#define TB_BLUE 0x05
#define TB_MAGENTA 0x06
#define TB_CYAN 0x07
#define TB_WHITE 0x08
// Attributes, it is possible to use multiple attributes by combining them
// using bitwise OR ('|'). Although, colors cannot be combined. But you can
// combine attributes and a single color. See also struct tb_cell's fg and bg
// fields.
#define TB_BOLD 0x01000000
#define TB_UNDERLINE 0x02000000
#define TB_REVERSE 0x04000000
// A cell, single conceptual entity on the terminal screen. The terminal screen
// is basically a 2d array of cells. It has the following fields:
// - 'ch' is a unicode character
// - 'fg' foreground color and attributes
// - 'bg' background color and attributes
struct tb_cell
{
uint32_t ch;
uint32_t fg;
uint32_t bg;
};
#define TB_EVENT_KEY 1
#define TB_EVENT_RESIZE 2
#define TB_EVENT_MOUSE 3
// An event, single interaction from the user. The 'mod' and 'ch' fields are
// valid if 'type' is TB_EVENT_KEY. The 'w' and 'h' fields are valid if 'type'
// is TB_EVENT_RESIZE. The 'x' and 'y' fields are valid if 'type' is
// TB_EVENT_MOUSE. The 'key' field is valid if 'type' is either TB_EVENT_KEY
// or TB_EVENT_MOUSE. The fields 'key' and 'ch' are mutually exclusive; only
// one of them can be non-zero at a time.
struct tb_event
{
uint8_t type;
uint8_t mod; // modifiers to either 'key' or 'ch' below
uint16_t key; // one of the TB_KEY_* constants
uint32_t ch; // unicode character
int32_t w;
int32_t h;
int32_t x;
int32_t y;
};
// Error codes returned by tb_init(). All of them are self-explanatory, except
// the pipe trap error. Termbox uses unix pipes in order to deliver a message
// from a signal handler (SIGWINCH) to the main event reading loop. Honestly in
// most cases you should just check the returned code as < 0.
#define TB_EUNSUPPORTED_TERMINAL -1
#define TB_EFAILED_TO_OPEN_TTY -2
#define TB_EPIPE_TRAP_ERROR -3
// Initializes the termbox library. This function should be called before any
// other functions. Function tb_init is same as tb_init_file("/dev/tty"). After successful initialization, the library must be
// finalized using the tb_shutdown() function.
SO_IMPORT int tb_init(void);
SO_IMPORT int tb_init_file(const char* name);
SO_IMPORT void tb_shutdown(void);
// Returns the size of the internal back buffer (which is the same as
// terminal's window size in characters). The internal buffer can be resized
// after tb_clear() or tb_present() function calls. Both dimensions have an
// unspecified negative value when called before tb_init() or after
// tb_shutdown().
SO_IMPORT int tb_width(void);
SO_IMPORT int tb_height(void);
// Clears the internal back buffer using TB_DEFAULT color or the
// color/attributes set by tb_set_clear_attributes() function.
SO_IMPORT void tb_clear(void);
SO_IMPORT void tb_set_clear_attributes(uint32_t fg, uint32_t bg);
// Synchronizes the internal back buffer with the terminal.
SO_IMPORT void tb_present(void);
#define TB_HIDE_CURSOR -1
// Sets the position of the cursor. Upper-left character is (0, 0). If you pass
// TB_HIDE_CURSOR as both coordinates, then the cursor will be hidden. Cursor
// is hidden by default.
SO_IMPORT void tb_set_cursor(int cx, int cy);
// Changes cell's parameters in the internal back buffer at the specified
// position.
SO_IMPORT void tb_put_cell(int x, int y, const struct tb_cell* cell);
SO_IMPORT void tb_change_cell(int x, int y, uint32_t ch, uint32_t fg,
uint32_t bg);
// Copies the buffer from 'cells' at the specified position, assuming the
// buffer is a two-dimensional array of size ('w' x 'h'), represented as a
// one-dimensional buffer containing lines of cells starting from the top.
// (DEPRECATED: use tb_cell_buffer() instead and copy memory on your own)
SO_IMPORT void tb_blit(int x, int y, int w, int h, const struct tb_cell* cells);
// Returns a pointer to internal cell back buffer. You can get its dimensions
// using tb_width() and tb_height() functions. The pointer stays valid as long
// as no tb_clear() and tb_present() calls are made. The buffer is
// one-dimensional buffer containing lines of cells starting from the top.
SO_IMPORT struct tb_cell* tb_cell_buffer(void);
#define TB_INPUT_CURRENT 0 // 000
#define TB_INPUT_ESC 1 // 001
#define TB_INPUT_ALT 2 // 010
#define TB_INPUT_MOUSE 4 // 100
// Sets the termbox input mode. Termbox has two input modes:
// 1. Esc input mode.
// When ESC sequence is in the buffer and it doesn't match any known
// ESC sequence => ESC means TB_KEY_ESC.
// 2. Alt input mode.
// When ESC sequence is in the buffer and it doesn't match any known
// sequence => ESC enables TB_MOD_ALT modifier for the next keyboard event.
//
// You can also apply TB_INPUT_MOUSE via bitwise OR operation to either of the
// modes (e.g. TB_INPUT_ESC | TB_INPUT_MOUSE). If none of the main two modes
// were set, but the mouse mode was, TB_INPUT_ESC mode is used. If for some
// reason you've decided to use (TB_INPUT_ESC | TB_INPUT_ALT) combination, it
// will behave as if only TB_INPUT_ESC was selected.
//
// If 'mode' is TB_INPUT_CURRENT, it returns the current input mode.
//
// Default termbox input mode is TB_INPUT_ESC.
SO_IMPORT int tb_select_input_mode(int mode);
#define TB_OUTPUT_CURRENT 0
#define TB_OUTPUT_NORMAL 1
#define TB_OUTPUT_256 2
#define TB_OUTPUT_216 3
#define TB_OUTPUT_GRAYSCALE 4
#define TB_OUTPUT_TRUECOLOR 5
// Sets the termbox output mode. Termbox has three output options:
// 1. TB_OUTPUT_NORMAL => [1..8]
// This mode provides 8 different colors:
// black, red, green, yellow, blue, magenta, cyan, white
// Shortcut: TB_BLACK, TB_RED, ...
// Attributes: TB_BOLD, TB_UNDERLINE, TB_REVERSE
//
// Example usage:
// tb_change_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED);
//
// 2. TB_OUTPUT_256 => [0..256]
// In this mode you can leverage the 256 terminal mode:
// 0x00 - 0x07: the 8 colors as in TB_OUTPUT_NORMAL
// 0x08 - 0x0f: TB_* | TB_BOLD
// 0x10 - 0xe7: 216 different colors
// 0xe8 - 0xff: 24 different shades of grey
//
// Example usage:
// tb_change_cell(x, y, '@', 184, 240);
// tb_change_cell(x, y, '@', 0xb8, 0xf0);
//
// 3. TB_OUTPUT_216 => [0..216]
// This mode supports the 3rd range of the 256 mode only.
// But you don't need to provide an offset.
//
// 4. TB_OUTPUT_GRAYSCALE => [0..23]
// This mode supports the 4th range of the 256 mode only.
// But you dont need to provide an offset.
//
// 5. TB_OUTPUT_TRUECOLOR => [0x000000..0xFFFFFF]
// This mode supports 24-bit true color. Format is 0xRRGGBB.
//
// Execute build/src/demo/output to see its impact on your terminal.
//
// If 'mode' is TB_OUTPUT_CURRENT, it returns the current output mode.
//
// Default termbox output mode is TB_OUTPUT_NORMAL.
SO_IMPORT int tb_select_output_mode(int mode);
// Wait for an event up to 'timeout' milliseconds and fill the 'event'
// structure with it, when the event is available. Returns the type of the
// event (one of TB_EVENT_* constants) or -1 if there was an error or 0 in case
// there were no event during 'timeout' period.
SO_IMPORT int tb_peek_event(struct tb_event* event, int timeout);
// Wait for an event forever and fill the 'event' structure with it, when the
// event is available. Returns the type of the event (one of TB_EVENT_
// constants) or -1 if there was an error.
SO_IMPORT int tb_poll_event(struct tb_event* event);
// Utility utf8 functions.
#define TB_EOF -1
SO_IMPORT int utf8_char_length(char c);
SO_IMPORT int utf8_char_to_unicode(uint32_t* out, const char* c);
SO_IMPORT int utf8_unicode_to_char(char* out, uint32_t c);
// c++
#ifdef __cplusplus
}
#endif
#endif

View file

@ -1,106 +0,0 @@
#include "termbox.h"
static const unsigned char utf8_length[256] =
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
};
static const unsigned char utf8_mask[6] =
{
0x7F,
0x1F,
0x0F,
0x07,
0x03,
0x01
};
int utf8_char_length(char c)
{
return utf8_length[(unsigned char)c];
}
int utf8_char_to_unicode(uint32_t* out, const char* c)
{
if (*c == 0)
{
return TB_EOF;
}
int i;
unsigned char len = utf8_char_length(*c);
unsigned char mask = utf8_mask[len - 1];
uint32_t result = c[0] & mask;
for (i = 1; i < len; ++i)
{
result <<= 6;
result |= c[i] & 0x3f;
}
*out = result;
return (int)len;
}
int utf8_unicode_to_char(char* out, uint32_t c)
{
int len = 0;
int first;
int i;
if (c < 0x80)
{
first = 0;
len = 1;
}
else if (c < 0x800)
{
first = 0xc0;
len = 2;
}
else if (c < 0x10000)
{
first = 0xe0;
len = 3;
}
else if (c < 0x200000)
{
first = 0xf0;
len = 4;
}
else if (c < 0x4000000)
{
first = 0xf8;
len = 5;
}
else
{
first = 0xfc;
len = 6;
}
for (i = len - 1; i > 0; --i)
{
out[i] = (c & 0x3f) | 0x80;
c >>= 6;
}
out[0] = c | first;
return len;
}

View file

@ -1,27 +0,0 @@
--style=break
--indent=force-tab=4
--indent-classes
--indent-switches
--indent-namespaces
--indent-after-parens
--indent-continuation=1
--indent-preproc-block
--indent-preproc-define
--indent-preproc-cond
--indent-col1-comments
--min-conditional-indent=0
--max-continuation-indent=40
--break-blocks
--pad-oper
--pad-comma
--pad-header
--unpad-paren
--align-pointer=type
--align-reference=type
--break-one-line-headers
--add-braces
--attach-return-type
--attach-return-type-decl
--remove-comment-prefix
--max-code-length=80
--mode=c

View file

@ -1,108 +0,0 @@
#!/usr/bin/env python
import sys, os, subprocess
def escaped(s):
return s.replace("\033", "\\033")
def tput(term, name):
try:
return subprocess.check_output(['tput', '-T%s' % term, name]).decode()
except subprocess.CalledProcessError as e:
return e.output.decode()
def w(s):
if s == None:
return
sys.stdout.write(s)
terminals = {
'xterm' : 'xterm',
'rxvt-256color' : 'rxvt_256color',
'rxvt-unicode' : 'rxvt_unicode',
'linux' : 'linux',
'Eterm' : 'eterm',
'screen' : 'screen'
}
keys = [
"F1", "kf1",
"F2", "kf2",
"F3", "kf3",
"F4", "kf4",
"F5", "kf5",
"F6", "kf6",
"F7", "kf7",
"F8", "kf8",
"F9", "kf9",
"F10", "kf10",
"F11", "kf11",
"F12", "kf12",
"INSERT", "kich1",
"DELETE", "kdch1",
"HOME", "khome",
"END", "kend",
"PGUP", "kpp",
"PGDN", "knp",
"KEY_UP", "kcuu1",
"KEY_DOWN", "kcud1",
"KEY_LEFT", "kcub1",
"KEY_RIGHT", "kcuf1"
]
funcs = [
"T_ENTER_CA", "smcup",
"T_EXIT_CA", "rmcup",
"T_SHOW_CURSOR", "cnorm",
"T_HIDE_CURSOR", "civis",
"T_CLEAR_SCREEN", "clear",
"T_SGR0", "sgr0",
"T_UNDERLINE", "smul",
"T_BOLD", "bold",
"T_BLINK", "blink",
"T_REVERSE", "rev",
"T_ENTER_KEYPAD", "smkx",
"T_EXIT_KEYPAD", "rmkx"
]
def iter_pairs(iterable):
iterable = iter(iterable)
while True:
yield (next(iterable), next(iterable))
def do_term(term, nick):
w("// %s\n" % term)
w("static const char *%s_keys[] = {\n\t" % nick)
for k, v in iter_pairs(keys):
w('"')
w(escaped(tput(term, v)))
w('",')
w(" 0\n};\n")
w("static const char *%s_funcs[] = {\n\t" % nick)
for k,v in iter_pairs(funcs):
w('"')
if v == "sgr":
w("\\033[3%d;4%dm")
elif v == "cup":
w("\\033[%d;%dH")
else:
w(escaped(tput(term, v)))
w('", ')
w("\n};\n\n")
def do_terms(d):
w("static struct term {\n")
w("\tconst char *name;\n")
w("\tconst char **keys;\n")
w("\tconst char **funcs;\n")
w("} terms[] = {\n")
for k, v in d.items():
w('\t{"%s", %s_keys, %s_funcs},\n' % (k, v, v))
w("\t{0, 0, 0},\n")
w("};\n")
for k,v in terminals.items():
do_term(k, v)
do_terms(terminals)

445
install.zig Normal file
View file

@ -0,0 +1,445 @@
const std = @import("std");
const PatchMap = std.StringHashMap([]const u8);
const InitSystem = enum {
systemd,
openrc,
runit,
s6,
dinit,
sysvinit,
freebsd,
};
const InstallType = enum {
installexe,
installnoconf,
uninstallexe,
uninstallnoconf,
};
var dest_directory: []const u8 = undefined;
var config_directory: []const u8 = undefined;
var prefix_directory: []const u8 = undefined;
var executable_name: []const u8 = undefined;
var init_system: InitSystem = undefined;
pub fn main(init: std.process.Init) !void {
const io = init.io;
const allocator = init.gpa;
var args = init.minimal.args.iterate();
if (!args.skip()) return error.NoProgramName;
const install_type = std.meta.stringToEnum(InstallType, args.next().?).?;
dest_directory = args.next().?;
config_directory = args.next().?;
prefix_directory = args.next().?;
executable_name = args.next().?;
init_system = std.meta.stringToEnum(InitSystem, args.next().?).?;
const default_tty_str = args.next().?;
switch (install_type) {
.installexe, .installnoconf => {
var patch_map = PatchMap.init(allocator);
defer patch_map.deinit();
try patch_map.put("$DEFAULT_TTY", default_tty_str);
try patch_map.put("$CONFIG_DIRECTORY", config_directory);
try patch_map.put("$PREFIX_DIRECTORY", prefix_directory);
try patch_map.put("$EXECUTABLE_NAME", executable_name);
try installLy(allocator, io, patch_map, install_type == .installexe);
try installService(allocator, io, patch_map);
},
.uninstallexe, .uninstallnoconf => {
if (install_type == .uninstallexe) {
try deleteTree(allocator, io, config_directory, "/ly", "ly config directory not found");
}
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ prefix_directory, "/bin/", executable_name });
defer allocator.free(exe_path);
var success = true;
std.Io.Dir.cwd().deleteFile(io, exe_path) catch {
std.debug.print("warn: ly executable not found\n", .{});
success = false;
};
if (success) std.debug.print("info: deleted {s}\n", .{exe_path});
try deleteFile(allocator, io, config_directory, "/pam.d/ly", "ly pam file not found");
switch (init_system) {
.systemd => try deleteFile(allocator, io, prefix_directory, "/lib/systemd/system/ly@.service", "systemd service not found"),
.openrc => try deleteFile(allocator, io, config_directory, "/init.d/ly", "openrc service not found"),
.runit => try deleteTree(allocator, io, config_directory, "/sv/ly", "runit service not found"),
.s6 => {
try deleteTree(allocator, io, config_directory, "/s6/sv/ly-srv", "s6 service not found");
try deleteFile(allocator, io, config_directory, "/s6/adminsv/default/contents.d/ly-srv", "s6 admin service not found");
},
.dinit => try deleteFile(allocator, io, config_directory, "/dinit.d/ly", "dinit service not found"),
.sysvinit => try deleteFile(allocator, io, config_directory, "/init.d/ly", "sysvinit service not found"),
.freebsd => try deleteFile(allocator, io, prefix_directory, "/bin/ly_wrapper", "freebsd wrapper not found"),
}
},
}
}
fn installLy(allocator: std.mem.Allocator, io: std.Io, patch_map: PatchMap, install_config: bool) !void {
const ly_config_directory = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly" });
defer allocator.free(ly_config_directory);
std.Io.Dir.cwd().createDirPath(io, ly_config_directory) catch {
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_config_directory});
};
const ly_custom_sessions_directory = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/custom-sessions" });
defer allocator.free(ly_custom_sessions_directory);
std.Io.Dir.cwd().createDirPath(io, ly_custom_sessions_directory) catch {
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_custom_sessions_directory});
};
const ly_lang_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/lang" });
defer allocator.free(ly_lang_path);
std.Io.Dir.cwd().createDirPath(io, ly_lang_path) catch {
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_lang_path});
};
{
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
defer allocator.free(exe_path);
std.Io.Dir.cwd().createDirPath(io, exe_path) catch {
if (!std.mem.eql(u8, dest_directory, "")) {
std.debug.print("warn: {s} already exists as a directory.\n", .{exe_path});
}
};
var executable_dir = std.Io.Dir.cwd().openDir(io, exe_path, .{}) catch unreachable;
defer executable_dir.close(io);
try installFile(io, "zig-out/bin/ly", executable_dir, exe_path, executable_name, .{});
}
{
var config_dir = std.Io.Dir.cwd().openDir(io, ly_config_directory, .{}) catch unreachable;
defer config_dir.close(io);
if (install_config) {
const patched_config = try patchFile(allocator, io, "res/config.ini", patch_map);
defer allocator.free(patched_config);
try installText(io, patched_config, config_dir, ly_config_directory, "config.ini", .{});
try installFile(io, "res/startup.sh", config_dir, ly_config_directory, "startup.sh", .{ .permissions = .fromMode(0o755) });
}
const patched_example_config = try patchFile(allocator, io, "res/config.ini", patch_map);
defer allocator.free(patched_example_config);
try installText(io, patched_example_config, config_dir, ly_config_directory, "config.ini.example", .{});
const patched_setup = try patchFile(allocator, io, "res/setup.sh", patch_map);
defer allocator.free(patched_setup);
try installText(io, patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .permissions = .fromMode(0o755) });
try installFile(io, "res/example.dur", config_dir, ly_config_directory, "example.dur", .{ .permissions = .fromMode(0o755) });
try installFile(io, "res/example.lua", config_dir, ly_config_directory, "example.lua", .{ .permissions = .fromMode(0o755) });
}
{
var custom_sessions_dir = std.Io.Dir.cwd().openDir(io, ly_custom_sessions_directory, .{}) catch unreachable;
defer custom_sessions_dir.close(io);
const patched_readme = try patchFile(allocator, io, "res/custom-sessions/README", patch_map);
defer allocator.free(patched_readme);
try installText(io, patched_readme, custom_sessions_dir, ly_custom_sessions_directory, "README", .{});
}
{
var lang_dir = std.Io.Dir.cwd().openDir(io, ly_lang_path, .{}) catch unreachable;
defer lang_dir.close(io);
const languages = [_][]const u8{
"ar.ini",
"bg.ini",
"cat.ini",
"cs.ini",
"de.ini",
"en.ini",
"eo.ini",
"es.ini",
"fr.ini",
"it.ini",
"ja_JP.ini",
"ku.ini",
"lv.ini",
"pl.ini",
"pt.ini",
"pt_BR.ini",
"ro.ini",
"ru.ini",
"sr.ini",
"sr_Cyrl.ini",
"sv.ini",
"tr.ini",
"uk.ini",
"zh_CN.ini",
"zh_TW.ini",
};
inline for (languages) |language| {
try installFile(io, "res/lang/" ++ language, lang_dir, ly_lang_path, language, .{});
}
}
{
const pam_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/pam.d" });
defer allocator.free(pam_path);
std.Io.Dir.cwd().createDirPath(io, pam_path) catch {
if (!std.mem.eql(u8, dest_directory, "")) {
std.debug.print("warn: {s} already exists as a directory.\n", .{pam_path});
}
};
var pam_dir = std.Io.Dir.cwd().openDir(io, pam_path, .{}) catch unreachable;
defer pam_dir.close(io);
try installFile(io, if (init_system == .freebsd) "res/pam.d/ly-freebsd" else "res/pam.d/ly-linux", pam_dir, pam_path, "ly", .{ .permissions = .fromMode(0o644) });
try installFile(io, if (init_system == .freebsd) "res/pam.d/ly-freebsd-autologin" else "res/pam.d/ly-linux-autologin", pam_dir, pam_path, "ly-autologin", .{ .permissions = .fromMode(0o644) });
}
}
fn installService(allocator: std.mem.Allocator, io: std.Io, patch_map: PatchMap) !void {
switch (init_system) {
.systemd => {
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/lib/systemd/system" });
defer allocator.free(service_path);
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const patched_service = try patchFile(allocator, io, "res/ly@.service", patch_map);
defer allocator.free(patched_service);
try installText(io, patched_service, service_dir, service_path, "ly@.service", .{ .permissions = .fromMode(0o644) });
const patched_kmsconvt_service = try patchFile(allocator, io, "res/ly-kmsconvt@.service", patch_map);
defer allocator.free(patched_kmsconvt_service);
try installText(io, patched_kmsconvt_service, service_dir, service_path, "ly-kmsconvt@.service", .{ .permissions = .fromMode(0o644) });
},
.openrc => {
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
defer allocator.free(service_path);
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const patched_service = try patchFile(allocator, io, "res/ly-openrc", patch_map);
defer allocator.free(patched_service);
try installText(io, patched_service, service_dir, service_path, executable_name, .{ .permissions = .fromMode(0o755) });
},
.runit => {
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/sv/ly" });
defer allocator.free(service_path);
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const supervise_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ service_path, "supervise" });
defer allocator.free(supervise_path);
const patched_conf = try patchFile(allocator, io, "res/ly-runit-service/conf", patch_map);
defer allocator.free(patched_conf);
try installText(io, patched_conf, service_dir, service_path, "conf", .{});
try installFile(io, "res/ly-runit-service/finish", service_dir, service_path, "finish", .{ .permissions = .fromMode(0o755) });
const patched_run = try patchFile(allocator, io, "res/ly-runit-service/run", patch_map);
defer allocator.free(patched_run);
try installText(io, patched_run, service_dir, service_path, "run", .{ .permissions = .fromMode(0o755) });
std.Io.Dir.cwd().symLink(io, "/run/runit/supervise.ly", supervise_path, .{}) catch |err| {
if (err == error.PathAlreadyExists) {
std.debug.print("warn: /run/runit/supervise.ly already exists as a symbolic link.\n", .{});
} else {
return err;
}
};
std.debug.print("info: installed symlink /run/runit/supervise.ly\n", .{});
},
.s6 => {
const admin_service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/adminsv/default/contents.d" });
std.Io.Dir.cwd().createDirPath(io, admin_service_path) catch {};
defer allocator.free(admin_service_path);
var admin_service_dir = std.Io.Dir.cwd().openDir(io, admin_service_path, .{}) catch unreachable;
defer admin_service_dir.close(io);
const file = try admin_service_dir.createFile(io, "ly-srv", .{});
file.close(io);
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/sv/ly-srv" });
defer allocator.free(service_path);
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const patched_run = try patchFile(allocator, io, "res/ly-s6/run", patch_map);
defer allocator.free(patched_run);
try installText(io, patched_run, service_dir, service_path, "run", .{ .permissions = .fromMode(0o755) });
try installFile(io, "res/ly-s6/type", service_dir, service_path, "type", .{});
},
.dinit => {
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/dinit.d" });
defer allocator.free(service_path);
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const patched_service = try patchFile(allocator, io, "res/ly-dinit", patch_map);
defer allocator.free(patched_service);
try installText(io, patched_service, service_dir, service_path, "ly", .{});
},
.sysvinit => {
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
defer allocator.free(service_path);
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const patched_service = try patchFile(allocator, io, "res/ly-sysvinit", patch_map);
defer allocator.free(patched_service);
try installText(io, patched_service, service_dir, service_path, "ly", .{ .permissions = .fromMode(0o755) });
},
.freebsd => {
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
defer allocator.free(exe_path);
var executable_dir = std.Io.Dir.cwd().openDir(io, exe_path, .{}) catch unreachable;
defer executable_dir.close(io);
const patched_wrapper = try patchFile(allocator, io, "res/ly-freebsd-wrapper", patch_map);
defer allocator.free(patched_wrapper);
try installText(io, patched_wrapper, executable_dir, exe_path, "ly_wrapper", .{ .permissions = .fromMode(0o755) });
},
}
}
fn installFile(
io: std.Io,
source_file: []const u8,
destination_directory: std.Io.Dir,
destination_directory_path: []const u8,
destination_file: []const u8,
options: std.Io.Dir.CopyFileOptions,
) !void {
try std.Io.Dir.cwd().copyFile(source_file, destination_directory, destination_file, io, options);
std.debug.print("info: installed {s}/{s}\n", .{ destination_directory_path, destination_file });
}
fn patchFile(allocator: std.mem.Allocator, io: std.Io, source_file: []const u8, patch_map: PatchMap) ![]const u8 {
var file = try std.Io.Dir.cwd().openFile(io, source_file, .{});
defer file.close(io);
const stat = try file.stat(io);
var buffer: [4096]u8 = undefined;
var reader = file.reader(io, &buffer);
var text = try reader.interface.readAlloc(allocator, @intCast(stat.size));
var iterator = patch_map.iterator();
while (iterator.next()) |kv| {
const new_text = try std.mem.replaceOwned(u8, allocator, text, kv.key_ptr.*, kv.value_ptr.*);
allocator.free(text);
text = new_text;
}
return text;
}
fn installText(
io: std.Io,
text: []const u8,
destination_directory: std.Io.Dir,
destination_directory_path: []const u8,
destination_file: []const u8,
options: std.Io.File.CreateFlags,
) !void {
var file = try destination_directory.createFile(io, destination_file, options);
defer file.close(io);
var buffer: [1024]u8 = undefined;
var writer = file.writer(io, &buffer);
try writer.interface.writeAll(text);
try writer.interface.flush();
std.debug.print("info: installed {s}/{s}\n", .{ destination_directory_path, destination_file });
}
fn deleteFile(
allocator: std.mem.Allocator,
io: std.Io,
prefix: []const u8,
file: []const u8,
warning: []const u8,
) !void {
const path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix, file });
defer allocator.free(path);
std.Io.Dir.cwd().deleteFile(io, path) catch |err| {
if (err == error.FileNotFound) {
std.debug.print("warn: {s}\n", .{warning});
return;
}
return err;
};
std.debug.print("info: deleted {s}\n", .{path});
}
fn deleteTree(
allocator: std.mem.Allocator,
io: std.Io,
prefix: []const u8,
directory: []const u8,
warning: []const u8,
) !void {
const path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix, directory });
defer allocator.free(path);
var dir = std.Io.Dir.cwd().openDir(io, path, .{}) catch |err| {
if (err == error.FileNotFound) {
std.debug.print("warn: {s}\n", .{warning});
return;
}
return err;
};
dir.close(io);
try std.Io.Dir.cwd().deleteTree(io, path);
std.debug.print("info: deleted {s}\n", .{path});
}

82
ly-core/build.zig Normal file
View file

@ -0,0 +1,82 @@
const std = @import("std");
const Translator = @import("translate_c").Translator;
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support") orelse true;
const mod = b.addModule("ly-core", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const fallback_uid_min = b.option(std.posix.uid_t, "fallback_uid_min", "Set the fallback minimum UID (default is 1000). This value gets embedded into the binary").?;
const fallback_uid_max = b.option(std.posix.uid_t, "fallback_uid_max", "Set the fallback maximum UID (default is 60000). This value gets embedded into the binary").?;
const build_options = b.addOptions();
build_options.addOption(std.posix.uid_t, "fallback_uid_min", fallback_uid_min);
build_options.addOption(std.posix.uid_t, "fallback_uid_max", fallback_uid_max);
mod.addOptions("build_options", build_options);
const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize });
mod.addImport("zigini", zigini.module("zigini"));
const translate_c = b.dependency("translate_c", .{
.target = target,
});
addCImport(b, mod, translate_c, target, optimize, "pam", "#include <security/pam_appl.h>");
addCImport(b, mod, translate_c, target, optimize, "utmp", "#include <utmpx.h>");
if (enable_x11_support) {
addCImport(b, mod, translate_c, target, optimize, "xcb", "#include <xcb/xcb.h>");
}
if (target.result.os.tag == .freebsd) {
addCImport(b, mod, translate_c, target, optimize, "pwd",
\\#include <pwd.h>
\\#include <sys/types.h>
\\#include <login_cap.h>
);
} else {
addCImport(b, mod, translate_c, target, optimize, "pwd", "#include <pwd.h>");
}
addCImport(b, mod, translate_c, target, optimize, "stdlib", "#include <stdlib.h>");
addCImport(b, mod, translate_c, target, optimize, "unistd", "#include <unistd.h>");
addCImport(b, mod, translate_c, target, optimize, "grp", "#include <grp.h>");
addCImport(b, mod, translate_c, target, optimize, "system_time", "#include <sys/time.h>");
addCImport(b, mod, translate_c, target, optimize, "time", "#include <time.h>");
if (target.result.os.tag == .linux) {
addCImport(b, mod, translate_c, target, optimize, "kd", "#include <sys/kd.h>");
addCImport(b, mod, translate_c, target, optimize, "vt", "#include <sys/vt.h>");
} else if (target.result.os.tag == .freebsd) {
addCImport(b, mod, translate_c, target, optimize, "kbio", "#include <sys/kbio.h>");
addCImport(b, mod, translate_c, target, optimize, "consio", "#include <sys/consio.h>");
addCImport(b, mod, translate_c, target, optimize, "sysctl", "#include <sys/sysctl.h>");
addCImport(b, mod, translate_c, target, optimize, "reboot", "#include <sys/reboot.h>");
}
const mod_tests = b.addTest(.{
.root_module = mod,
});
const run_mod_tests = b.addRunArtifact(mod_tests);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_mod_tests.step);
}
fn addCImport(
b: *std.Build,
mod: *std.Build.Module,
translate_c: *std.Build.Dependency,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
comptime name: []const u8,
comptime bytes: []const u8,
) void {
const pam: Translator = .init(translate_c, .{
.c_source_file = b.addWriteFiles().add(name ++ ".h", bytes),
.target = target,
.optimize = optimize,
});
mod.addImport(name, pam.mod);
}

21
ly-core/build.zig.zon Normal file
View file

@ -0,0 +1,21 @@
.{
.name = .ly_core,
.version = "1.1.0",
.fingerprint = 0xddda7afda795472,
.minimum_zig_version = "0.16.0",
.dependencies = .{
.zigini = .{
.url = "git+https://github.com/AshAmetrine/zigini?ref=master#a665d081dda42664a96da2840ea09c5ccf9d0692",
.hash = "zigini-0.5.0-BSkB7e9WAACfyCBABNZiWL3gFMw18GKn3qBcPs8L1Ec1",
},
.translate_c = .{
.url = "git+https://codeberg.org/ziglang/translate-c?ref=zig-0.16.x#6fe0ffc4549f15c5f2d9432c2b4460ba90ff85ac",
.hash = "translate_c-1.0.0-Q_BUWo_5BgD4flHdUhA31zOz0XvZk9k7lQv1ouzyNXj2",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

106
ly-core/src/LogFile.zig Normal file
View file

@ -0,0 +1,106 @@
const std = @import("std");
const interop = @import("interop.zig");
const LogFile = @This();
maybe_path: ?[]const u8,
could_open_log_file: bool = undefined,
maybe_file: ?std.Io.File = null,
buffer: []u8,
maybe_file_writer: ?std.Io.File.Writer = null,
pub fn init(io: std.Io, path: ?[]const u8, buffer: []u8) !LogFile {
var log_file = LogFile{
.maybe_path = path,
.buffer = buffer,
};
if (path) |p| {
log_file.could_open_log_file = try openLogFile(io, p, &log_file);
} else {
std.posix.system.openlog("ly", 0, 0);
log_file.could_open_log_file = true;
}
return log_file;
}
pub fn reinit(self: *LogFile, io: std.Io) !void {
if (self.maybe_path) |path| {
self.could_open_log_file = try openLogFile(io, path, self);
} else {
std.posix.system.openlog("ly", 0, 0);
self.could_open_log_file = true;
}
}
pub fn deinit(self: *LogFile, io: std.Io) void {
if (self.maybe_file) |file| {
file.close(io);
} else {
std.posix.system.closelog();
}
}
pub fn info(self: *LogFile, io: std.Io, category: []const u8, comptime message: []const u8, args: anytype) !void {
if (self.maybe_file_writer) |*writer| {
var buffer: [128:0]u8 = undefined;
const time = interop.timeAsString(io, &buffer, "%Y-%m-%d %H:%M:%S");
try writer.interface.print("{s} [info/{s}] ", .{ time, category });
try writer.interface.print(message, args);
try writer.interface.writeByte('\n');
try writer.interface.flush();
} else {
var buffer: [1024]u8 = undefined;
const slice = try std.fmt.bufPrint(&buffer, message, args);
const msg = try std.fmt.bufPrintZ(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice });
std.posix.system.syslog(std.posix.LOG.INFO, msg.ptr);
}
}
pub fn err(self: *LogFile, io: std.Io, category: []const u8, comptime message: []const u8, args: anytype) !void {
if (self.maybe_file_writer) |*writer| {
var buffer: [128:0]u8 = undefined;
const time = interop.timeAsString(io, &buffer, "%Y-%m-%d %H:%M:%S");
try writer.interface.print("{s} [err/{s}] ", .{ time, category });
try writer.interface.print(message, args);
try writer.interface.writeByte('\n');
try writer.interface.flush();
} else {
var buffer: [1024]u8 = undefined;
const slice = try std.fmt.bufPrint(&buffer, message, args);
const msg = try std.fmt.bufPrintZ(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice });
std.posix.system.syslog(std.posix.LOG.ERR, msg.ptr);
}
}
fn openLogFile(io: std.Io, path: []const u8, log_file: *LogFile) !bool {
var could_open_log_file = true;
open_log_file: {
log_file.maybe_file = std.Io.Dir.cwd().openFile(io, path, .{ .mode = .write_only }) catch std.Io.Dir.cwd().createFile(io, path, .{ .permissions = .fromMode(0o666) }) catch {
// If we could neither open an existing log file nor create a new
// one, abort.
could_open_log_file = false;
break :open_log_file;
};
}
if (!could_open_log_file) {
log_file.maybe_file = try std.Io.Dir.openFileAbsolute(io, "/dev/null", .{ .mode = .write_only });
}
var log_file_writer = log_file.maybe_file.?.writer(io, log_file.buffer);
// Seek to the end of the log file
if (could_open_log_file) {
const stat = try log_file.maybe_file.?.stat(io);
try log_file_writer.seekTo(stat.size);
}
log_file.maybe_file_writer = log_file_writer;
return could_open_log_file;
}

View file

@ -0,0 +1,52 @@
const std = @import("std");
const ErrInt = std.meta.Int(.unsigned, @bitSizeOf(anyerror));
const PaddingInt = std.meta.Int(.unsigned, 8 - (@bitSizeOf(ErrInt) + @bitSizeOf(bool)) % 8);
const ErrorHandler = packed struct {
has_error: bool = false,
err_int: ErrInt = 0,
padding: PaddingInt = 0,
};
const SharedError = @This();
data: []align(std.heap.page_size_min) u8,
write_error_event_fn: ?*const fn (anyerror, *anyopaque) anyerror!void,
ctx: ?*anyopaque,
pub fn init(
write_error_event_fn: ?*const fn (anyerror, *anyopaque) anyerror!void,
ctx: ?*anyopaque,
) !SharedError {
const data = try std.posix.mmap(null, @sizeOf(ErrorHandler), .{ .READ = true, .WRITE = true }, .{ .TYPE = .SHARED, .ANONYMOUS = true }, -1, 0);
return .{
.data = data,
.write_error_event_fn = write_error_event_fn,
.ctx = ctx,
};
}
pub fn deinit(self: *SharedError) void {
std.posix.munmap(self.data);
}
pub fn writeError(self: SharedError, err: anyerror) void {
var writer: std.Io.Writer = .fixed(self.data);
writer.writeStruct(ErrorHandler{ .has_error = true, .err_int = @intFromError(err) }, .native) catch {};
if (self.write_error_event_fn) |write_error_event_fn| {
@call(.auto, write_error_event_fn, .{ err, self.ctx.? }) catch {};
}
}
pub fn readError(self: SharedError) ?anyerror {
var reader: std.Io.Reader = .fixed(self.data);
const err_handler = try reader.takeStruct(ErrorHandler, .native);
if (err_handler.has_error)
return @errorFromInt(err_handler.err_int);
return null;
}

6
ly-core/src/UidRange.zig Normal file
View file

@ -0,0 +1,6 @@
const std = @import("std");
// We set both values to 0 by default so that, in case they aren't present in
// the login.defs for some reason, then only the root username will be shown
uid_min: std.posix.uid_t = 0,
uid_max: std.posix.uid_t = 0,

433
ly-core/src/interop.zig Normal file
View file

@ -0,0 +1,433 @@
const std = @import("std");
const builtin = @import("builtin");
const build_options = @import("build_options");
const UidRange = @import("UidRange.zig");
const pwd = @import("pwd");
const stdlib = @import("stdlib");
const unistd = @import("unistd");
const grp = @import("grp");
const system_time = @import("system_time");
const time = @import("time");
pub const pam = @import("pam");
pub const utmp = @import("utmp");
// Exists for X11 support only
pub const xcb = @import("xcb");
// FreeBSD only
pub const sysctl = @import("sysctl");
pub const TimeOfDay = struct {
seconds: i64,
microseconds: i64,
};
pub const UsernameEntry = struct {
username: ?[]const u8,
uid: std.posix.uid_t,
gid: std.posix.gid_t,
home: ?[]const u8,
shell: ?[]const u8,
passwd_struct: [*]pwd.passwd,
};
// Contains the platform-specific code
fn PlatformStruct() type {
return switch (builtin.os.tag) {
.linux => struct {
pub const kd = @import("kd");
pub const vt = @import("vt");
pub const LedState = c_char;
pub const get_led_state = kd.KDGKBLED;
pub const set_led_state = kd.KDSKBLED;
pub const numlock_led = kd.K_NUMLOCK;
pub const capslock_led = kd.K_CAPSLOCK;
pub const vt_activate = vt.VT_ACTIVATE;
pub const vt_waitactive = vt.VT_WAITACTIVE;
pub fn setUserContextImpl(username: [*:0]const u8, entry: UsernameEntry) !void {
const status = grp.initgroups(username, @intCast(entry.gid));
if (status != 0) return error.GroupInitializationFailed;
if (isError(std.posix.system.setgid(@intCast(entry.gid)))) return error.SetUserGidFailed;
if (isError(std.posix.system.setuid(@intCast(entry.uid)))) return error.SetUserUidFailed;
}
// Procedure:
// 1. Open /proc/self/stat to retrieve the tty_nr field
// 2. Parse the tty_nr field to extract the major and minor device
// numbers
// 3. Then, read every /sys/class/tty/[dir]/dev, where [dir] is
// every sub-directory
// 4. Finally, compare the major and minor device numbers with the
// extracted values. If they correspond, parse [dir] to get the
// TTY ID
pub fn getActiveTtyImpl(allocator: std.mem.Allocator, io: std.Io, use_kmscon_vt: bool) !u8 {
var file_buffer: [256]u8 = undefined;
if (use_kmscon_vt) {
var file = try std.Io.Dir.openFileAbsolute(io, "/sys/class/tty/tty0/active", .{});
defer file.close(io);
var reader = file.reader(io, &file_buffer);
var buffer: [16]u8 = undefined;
const read = try readBuffer(&reader.interface, &buffer);
const tty = buffer[0..(read - 1)];
return std.fmt.parseInt(u8, tty["tty".len..], 10);
}
var tty_major: u16 = undefined;
var tty_minor: u16 = undefined;
{
var file = try std.Io.Dir.openFileAbsolute(io, "/proc/self/stat", .{});
defer file.close(io);
var reader = file.reader(io, &file_buffer);
var buffer: [1024]u8 = undefined;
const read = try readBuffer(&reader.interface, &buffer);
var iterator = std.mem.splitScalar(u8, buffer[0..read], ' ');
var fields: [52][]const u8 = undefined;
var index: usize = 0;
while (iterator.next()) |field| {
fields[index] = field;
index += 1;
}
const tty_nr = try std.fmt.parseInt(u16, fields[6], 10);
tty_major = tty_nr / 256;
tty_minor = tty_nr % 256;
}
var directory = try std.Io.Dir.openDirAbsolute(io, "/sys/class/tty", .{ .iterate = true });
defer directory.close(io);
var iterator = directory.iterate();
while (try iterator.next(io)) |entry| {
const path = try std.fmt.allocPrint(allocator, "/sys/class/tty/{s}/dev", .{entry.name});
defer allocator.free(path);
var file = try std.Io.Dir.openFileAbsolute(io, path, .{});
defer file.close(io);
var reader = file.reader(io, &file_buffer);
var buffer: [16]u8 = undefined;
const read = try readBuffer(&reader.interface, &buffer);
var device_iterator = std.mem.splitScalar(u8, buffer[0..(read - 1)], ':');
const device_major_str = device_iterator.next() orelse continue;
const device_minor_str = device_iterator.next() orelse continue;
const device_major = try std.fmt.parseInt(u8, device_major_str, 10);
const device_minor = try std.fmt.parseInt(u8, device_minor_str, 10);
if (device_major == tty_major and device_minor == tty_minor) {
const tty_id_str = entry.name["tty".len..];
return try std.fmt.parseInt(u8, tty_id_str, 10);
}
}
return error.NoTtyFound;
}
// This is very bad parsing, but we only need to get 2 values..
// and the format of the file seems to be standard? So this should
// be fine...
pub fn getUserIdRange(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8) !UidRange {
const login_defs_file = try std.Io.Dir.cwd().openFile(io, file_path, .{});
defer login_defs_file.close(io);
var buffer: [4096]u8 = undefined;
var reader = login_defs_file.reader(io, &buffer);
const login_defs_buffer = try reader.interface.allocRemaining(allocator, .unlimited);
defer allocator.free(login_defs_buffer);
var iterator = std.mem.splitScalar(u8, login_defs_buffer, '\n');
var uid_range = UidRange{};
var uid_min_Found = false;
var uid_max_Found = false;
while (iterator.next()) |line| {
const trimmed_line = std.mem.trim(u8, line, " \n\r\t");
if (std.mem.startsWith(u8, trimmed_line, "UID_MIN")) {
uid_range.uid_min = try parseValue(std.posix.uid_t, "UID_MIN", trimmed_line);
uid_min_Found = true;
} else if (std.mem.startsWith(u8, trimmed_line, "UID_MAX")) {
uid_range.uid_max = try parseValue(std.posix.uid_t, "UID_MAX", trimmed_line);
uid_max_Found = true;
}
}
if (!(uid_min_Found or uid_max_Found)) {
return error.UidNameNotFound;
}
if (!uid_min_Found) {
uid_range.uid_min = build_options.fallback_uid_min;
}
if (!uid_max_Found) {
uid_range.uid_max = build_options.fallback_uid_max;
}
return uid_range;
}
pub fn shutdownSystemImpl() !void {
std.posix.system.sync();
if (isError(std.os.linux.reboot(.MAGIC1, .MAGIC2, .POWER_OFF, null))) {
return error.CouldntShutdown;
}
}
pub fn rebootSystemImpl() !void {
std.posix.system.sync();
if (isError(std.os.linux.reboot(.MAGIC1, .MAGIC2, .RESTART, null))) {
return error.CouldntShutdown;
}
}
fn parseValue(comptime T: type, name: []const u8, buffer: []const u8) !T {
var iterator = std.mem.splitAny(u8, buffer, " \t");
var maybe_value: ?T = null;
while (iterator.next()) |slice| {
// Skip the slice if it's empty (whitespace) or is the name of the
// property (e.g. UID_MIN or UID_MAX)
if (slice.len == 0 or std.mem.eql(u8, slice, name)) continue;
maybe_value = std.fmt.parseInt(T, slice, 10) catch continue;
}
return maybe_value orelse error.ValueNotFound;
}
fn readBuffer(reader: *std.Io.Reader, buffer: []u8) !usize {
var bytes_read: usize = 0;
var byte: u8 = try reader.takeByte();
while (byte != 0 and bytes_read < buffer.len) {
buffer[bytes_read] = byte;
bytes_read += 1;
byte = reader.takeByte() catch break;
}
return bytes_read;
}
},
.freebsd => struct {
pub const kbio = @import("kbio");
pub const consio = @import("consio");
pub const reboot = @import("reboot");
pub const LedState = c_int;
pub const get_led_state = kbio.KDGETLED;
pub const set_led_state = kbio.KDSETLED;
pub const numlock_led = kbio.LED_NUM;
pub const capslock_led = kbio.LED_CAP;
pub const vt_activate = consio.VT_ACTIVATE;
pub const vt_waitactive = consio.VT_WAITACTIVE;
const FREEBSD_UID_MIN = 1000;
const FREEBSD_UID_MAX = 32000;
pub fn setUserContextImpl(username: [*:0]const u8, entry: UsernameEntry) !void {
// FreeBSD has initgroups() in unistd
const status = unistd.initgroups(username, @intCast(entry.gid));
if (status != 0) return error.GroupInitializationFailed;
// FreeBSD sets the GID and UID with setusercontext()
const result = pwd.setusercontext(null, entry.passwd_struct, @intCast(entry.uid), pwd.LOGIN_SETALL);
if (result != 0) return error.SetUserUidFailed;
}
pub fn getActiveTtyImpl(_: std.mem.Allocator, _: std.Io, _: bool) !u8 {
return error.FeatureUnimplemented;
}
pub fn getUserIdRange(_: std.mem.Allocator, _: std.Io, _: []const u8) !UidRange {
return .{
// Hardcoded default values chosen from
// /usr/src/usr.sbin/pw/pw_conf.c
.uid_min = FREEBSD_UID_MIN,
.uid_max = FREEBSD_UID_MAX,
};
}
pub fn shutdownSystemImpl() !void {
if (isError(unistd.reboot(reboot.RB_POWEROFF))) {
return error.CouldntShutdown;
}
}
pub fn rebootSystemImpl() !void {
if (isError(unistd.reboot(reboot.RB_AUTOBOOT))) {
return error.CouldntReboot;
}
}
},
else => @compileError("Unsupported target: " ++ builtin.os.tag),
};
}
const platform_struct = PlatformStruct();
pub fn isError(result: anytype) bool {
if (@typeInfo(@TypeOf(result)).int.signedness == .signed) {
return result < 0;
}
if (@typeInfo(@TypeOf(result)).int.signedness == .unsigned) {
return switch (builtin.os.tag) {
.linux => std.os.linux.errno(result) != .SUCCESS,
else => @compileError("interop.isError() not implemented for current target!"),
};
}
unreachable;
}
pub fn supportsUnicode() bool {
return builtin.os.tag == .linux or builtin.os.tag == .freebsd;
}
pub fn timeAsString(io: std.Io, buf: [:0]u8, format: [:0]const u8) []u8 {
const timer: isize = @intCast(std.Io.Timestamp.now(io, .real).toSeconds());
const tm_info = time.localtime(&timer);
const len = time.strftime(buf, buf.len, format, tm_info);
return buf[0..len];
}
pub fn getTimeOfDay() !TimeOfDay {
var tv: system_time.timeval = undefined;
const status = system_time.gettimeofday(&tv, null);
if (status != 0) return error.FailedToGetTimeOfDay;
return .{
.seconds = @intCast(tv.tv_sec),
.microseconds = @intCast(tv.tv_usec),
};
}
pub fn getActiveTty(allocator: std.mem.Allocator, io: std.Io, use_kmscon_vt: bool) !u8 {
return platform_struct.getActiveTtyImpl(allocator, io, use_kmscon_vt);
}
pub fn switchTty(tty: u8) !void {
var status = std.c.ioctl(std.posix.STDIN_FILENO, platform_struct.vt_activate, tty);
if (status != 0) return error.FailedToActivateTty;
status = std.c.ioctl(std.posix.STDIN_FILENO, platform_struct.vt_waitactive, tty);
if (status != 0) return error.FailedToWaitForActiveTty;
}
pub fn getLockState() !struct {
numlock: bool,
capslock: bool,
} {
var led: platform_struct.LedState = undefined;
const status = std.c.ioctl(std.posix.STDIN_FILENO, platform_struct.get_led_state, &led);
if (status != 0) return error.FailedToGetLockState;
return .{
.numlock = (led & platform_struct.numlock_led) != 0,
.capslock = (led & platform_struct.capslock_led) != 0,
};
}
pub fn setNumlock(val: bool) !void {
var led: platform_struct.LedState = undefined;
var status = std.c.ioctl(std.posix.STDIN_FILENO, platform_struct.get_led_state, &led);
if (status != 0) return error.FailedToGetNumlock;
const numlock = (led & platform_struct.numlock_led) != 0;
if (numlock != val) {
status = std.c.ioctl(std.posix.STDIN_FILENO, platform_struct.set_led_state, led ^ platform_struct.numlock_led);
if (status != 0) return error.FailedToSetNumlock;
}
}
pub fn setUserContext(allocator: std.mem.Allocator, entry: UsernameEntry) !void {
const username_z = try allocator.dupeZ(u8, entry.username.?);
defer allocator.free(username_z);
return platform_struct.setUserContextImpl(username_z.ptr, entry);
}
pub fn setUserShell(entry: *UsernameEntry) void {
unistd.setusershell();
const shell = unistd.getusershell();
entry.shell = std.mem.span(shell);
unistd.endusershell();
}
pub fn setEnvironmentVariable(allocator: std.mem.Allocator, name: []const u8, value: []const u8, replace: bool) !void {
const name_z = try allocator.dupeZ(u8, name);
defer allocator.free(name_z);
const value_z = try allocator.dupeZ(u8, value);
defer allocator.free(value_z);
const status = stdlib.setenv(name_z.ptr, value_z.ptr, @intFromBool(replace));
if (status != 0) return error.SetEnvironmentVariableFailed;
}
pub fn putEnvironmentVariable(name_and_value: []u8) !void {
const status = stdlib.putenv(name_and_value.ptr);
if (status != 0) return error.PutEnvironmentVariableFailed;
}
pub fn getNextUsernameEntry() ?UsernameEntry {
const entry = pwd.getpwent();
if (entry == null) return null;
return .{
.username = if (entry.*.pw_name) |name| std.mem.span(name) else null,
.uid = @intCast(entry.*.pw_uid),
.gid = @intCast(entry.*.pw_gid),
.home = if (entry.*.pw_dir) |dir| std.mem.span(dir) else null,
.shell = if (entry.*.pw_shell) |shell| std.mem.span(shell) else null,
.passwd_struct = entry,
};
}
pub fn getUsernameEntry(username: [:0]const u8) ?UsernameEntry {
const entry = pwd.getpwnam(username);
if (entry == null) return null;
return .{
.username = if (entry.*.pw_name) |name| std.mem.span(name) else null,
.uid = @intCast(entry.*.pw_uid),
.gid = @intCast(entry.*.pw_gid),
.home = if (entry.*.pw_dir) |dir| std.mem.span(dir) else null,
.shell = if (entry.*.pw_shell) |shell| std.mem.span(shell) else null,
.passwd_struct = entry,
};
}
pub fn closePasswordDatabase() void {
pwd.endpwent();
}
// This is very bad parsing, but we only need to get 2 values... and the format
// of the file doesn't seem to be standard? So this should be fine...
pub fn getUserIdRange(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8) !UidRange {
return platform_struct.getUserIdRange(allocator, io, file_path);
}
pub fn shutdownSystem() !void {
try platform_struct.shutdownSystemImpl();
}
pub fn rebootSystem() !void {
try platform_struct.rebootSystemImpl();
}

78
ly-core/src/root.zig Normal file
View file

@ -0,0 +1,78 @@
const std = @import("std");
pub const ini = @import("zigini");
pub const interop = @import("interop.zig");
pub const UidRange = @import("UidRange.zig");
pub const LogFile = @import("LogFile.zig");
pub const SharedError = @import("SharedError.zig");
pub fn IniParser(comptime Struct: type) type {
return struct {
const Self = @This();
const temporary_allocator = std.heap.page_allocator;
pub const Error = struct {
type_name: []const u8,
key: []const u8,
value: []const u8,
error_name: []const u8,
};
pub var global_errors: std.ArrayList(Error) = .empty;
ini_struct: ini.Ini(Struct),
structure: Struct,
maybe_load_error: ?anyerror,
errors: std.ArrayList(Error),
pub fn init(
allocator: std.mem.Allocator,
io: std.Io,
path: []const u8,
field_handler: ?fn (allocator: std.mem.Allocator, field: ini.IniField) ?ini.IniField,
) !Self {
var ini_struct = ini.Ini(Struct).init(allocator);
errdefer ini_struct.deinit();
var maybe_load_error: ?anyerror = null;
const structure = ini_struct.readFileToStruct(io, path, .{
.fieldHandler = field_handler,
.errorHandler = errorHandler,
.comment_characters = "#",
}) catch |err| load_error: {
maybe_load_error = err;
break :load_error Struct{};
};
return .{
.ini_struct = ini_struct,
.structure = structure,
.maybe_load_error = maybe_load_error,
.errors = global_errors,
};
}
pub fn deinit(self: *Self) void {
self.ini_struct.deinit();
for (0..global_errors.items.len) |i| {
const err = global_errors.items[i];
temporary_allocator.free(err.type_name);
temporary_allocator.free(err.key);
temporary_allocator.free(err.value);
}
global_errors.deinit(temporary_allocator);
}
fn errorHandler(type_name: []const u8, key: []const u8, value: []const u8, err: anyerror) void {
global_errors.append(temporary_allocator, .{
.type_name = temporary_allocator.dupe(u8, type_name) catch return,
.key = temporary_allocator.dupe(u8, key) catch return,
.value = temporary_allocator.dupe(u8, value) catch return,
.error_name = @errorName(err),
}) catch return;
}
};
}

62
ly-ui/build.zig Normal file
View file

@ -0,0 +1,62 @@
const std = @import("std");
const Translator = @import("translate_c").Translator;
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support") orelse true;
const mod = b.addModule("ly-ui", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const fallback_uid_min = b.option(std.posix.uid_t, "fallback_uid_min", "Set the fallback minimum UID (default is 1000). This value gets embedded into the binary");
const fallback_uid_max = b.option(std.posix.uid_t, "fallback_uid_max", "Set the fallback maximum UID (default is 60000). This value gets embedded into the binary");
const ly_core = b.dependency("ly_core", .{
.target = target,
.optimize = optimize,
.enable_x11_support = enable_x11_support,
.fallback_uid_min = fallback_uid_min,
.fallback_uid_max = fallback_uid_max,
});
mod.addImport("ly-core", ly_core.module("ly-core"));
const termbox_dep = b.dependency("termbox2", .{
.target = target,
.optimize = optimize,
});
const translate_c_dep = b.dependency("translate_c", .{
.target = target,
});
const termbox2: Translator = .init(translate_c_dep, .{
.c_source_file = termbox_dep.path("termbox2.h"),
.target = target,
.optimize = optimize,
});
termbox2.defineCMacro("TB_IMPL", null);
// TODO 0.16.0: Workaround until Aro gets better...
// https://codeberg.org/ziglang/translate-c/issues/319
termbox2.defineCMacro("_XOPEN_SOURCE", "700");
termbox2.defineCMacro("TB_OPT_ATTR_W", "32"); // Enable 24-bit color support + styling (32-bit)
// TODO 0.16.0: Including <fcntl.h> with -OReleaseSafe causes
// __attribute__(__error__()) to be called. Below
// is the workaround.
termbox2.defineCMacro("_FORTIFY_SOURCE", "0");
// TODO 0.16.0: Needed for now
if (target.result.os.tag == .freebsd) {
termbox2.defineCMacro("__BSD_VISIBLE", "1");
}
mod.addImport("termbox2", termbox2.mod);
const mod_tests = b.addTest(.{
.root_module = mod,
});
const run_mod_tests = b.addRunArtifact(mod_tests);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_mod_tests.step);
}

24
ly-ui/build.zig.zon Normal file
View file

@ -0,0 +1,24 @@
.{
.name = .ly_ui,
.version = "1.1.0",
.fingerprint = 0x8d11bf85a74ec803,
.minimum_zig_version = "0.16.0",
.dependencies = .{
.ly_core = .{
.path = "../ly-core",
},
.termbox2 = .{
.url = "git+https://github.com/AnErrupTion/termbox2?ref=master#c7f241e8888ce243e1748b05c26a42fcfaaad936",
.hash = "N-V-__8AAAUXBQD6Fwpi9m0MBqWXFFaqW5l1lVrJC2Ynj7a-",
},
.translate_c = .{
.url = "git+https://codeberg.org/ziglang/translate-c?ref=zig-0.16.x#6fe0ffc4549f15c5f2d9432c2b4460ba90ff85ac",
.hash = "translate_c-1.0.0-Q_BUWo_5BgD4flHdUhA31zOz0XvZk9k7lQv1ouzyNXj2",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

21
ly-ui/src/Cell.zig Normal file
View file

@ -0,0 +1,21 @@
const TerminalBuffer = @import("TerminalBuffer.zig");
const Cell = @This();
ch: u32,
fg: u32,
bg: u32,
pub fn init(ch: u32, fg: u32, bg: u32) Cell {
return .{
.ch = ch,
.fg = fg,
.bg = bg,
};
}
pub fn put(self: Cell, x: usize, y: usize) !void {
if (self.ch == 0) return;
try TerminalBuffer.setCell(x, y, self);
}

221
ly-ui/src/Position.zig Normal file
View file

@ -0,0 +1,221 @@
const Position = @This();
x: usize,
y: usize,
pub fn init(x: usize, y: usize) Position {
return .{
.x = x,
.y = y,
};
}
pub fn add(self: Position, other: Position) Position {
return .{
.x = self.x + other.x,
.y = self.y + other.y,
};
}
pub fn addIf(self: Position, other: Position, condition: bool) Position {
return .{
.x = self.x + if (condition) other.x else 0,
.y = self.y + if (condition) other.y else 0,
};
}
pub fn addX(self: Position, x: usize) Position {
return .{
.x = self.x + x,
.y = self.y,
};
}
pub fn addY(self: Position, y: usize) Position {
return .{
.x = self.x,
.y = self.y + y,
};
}
pub fn addXIf(self: Position, x: usize, condition: bool) Position {
return .{
.x = self.x + if (condition) x else 0,
.y = self.y,
};
}
pub fn addYIf(self: Position, y: usize, condition: bool) Position {
return .{
.x = self.x,
.y = self.y + if (condition) y else 0,
};
}
pub fn addXFrom(self: Position, other: Position) Position {
return .{
.x = self.x + other.x,
.y = self.y,
};
}
pub fn addYFrom(self: Position, other: Position) Position {
return .{
.x = self.x,
.y = self.y + other.y,
};
}
pub fn addXFromIf(self: Position, other: Position, condition: bool) Position {
return .{
.x = self.x + if (condition) other.x else 0,
.y = self.y,
};
}
pub fn addYFromIf(self: Position, other: Position, condition: bool) Position {
return .{
.x = self.x,
.y = self.y + if (condition) other.y else 0,
};
}
pub fn remove(self: Position, other: Position) Position {
return .{
.x = self.x - other.x,
.y = self.y - other.y,
};
}
pub fn removeIf(self: Position, other: Position, condition: bool) Position {
return .{
.x = self.x - if (condition) other.x else 0,
.y = self.y - if (condition) other.y else 0,
};
}
pub fn removeX(self: Position, x: usize) Position {
return .{
.x = self.x - x,
.y = self.y,
};
}
pub fn removeY(self: Position, y: usize) Position {
return .{
.x = self.x,
.y = self.y - y,
};
}
pub fn removeXIf(self: Position, x: usize, condition: bool) Position {
return .{
.x = self.x - if (condition) x else 0,
.y = self.y,
};
}
pub fn removeYIf(self: Position, y: usize, condition: bool) Position {
return .{
.x = self.x,
.y = self.y - if (condition) y else 0,
};
}
pub fn removeXFrom(self: Position, other: Position) Position {
return .{
.x = self.x - other.x,
.y = self.y,
};
}
pub fn removeYFrom(self: Position, other: Position) Position {
return .{
.x = self.x,
.y = self.y - other.y,
};
}
pub fn removeXFromIf(self: Position, other: Position, condition: bool) Position {
return .{
.x = self.x - if (condition) other.x else 0,
.y = self.y,
};
}
pub fn removeYFromIf(self: Position, other: Position, condition: bool) Position {
return .{
.x = self.x,
.y = self.y - if (condition) other.y else 0,
};
}
pub fn invert(self: Position, other: Position) Position {
return .{
.x = other.x - self.x,
.y = other.y - self.y,
};
}
pub fn invertIf(self: Position, other: Position, condition: bool) Position {
return .{
.x = if (condition) other.x - self.x else self.x,
.y = if (condition) other.y - self.y else self.y,
};
}
pub fn invertX(self: Position, width: usize) Position {
return .{
.x = width - self.x,
.y = self.y,
};
}
pub fn invertY(self: Position, height: usize) Position {
return .{
.x = self.x,
.y = height - self.y,
};
}
pub fn invertXIf(self: Position, width: usize, condition: bool) Position {
return .{
.x = if (condition) width - self.x else self.x,
.y = self.y,
};
}
pub fn invertYIf(self: Position, height: usize, condition: bool) Position {
return .{
.x = self.x,
.y = if (condition) height - self.y else self.y,
};
}
pub fn resetXFrom(self: Position, other: Position) Position {
return .{
.x = other.x,
.y = self.y,
};
}
pub fn resetYFrom(self: Position, other: Position) Position {
return .{
.x = self.x,
.y = other.y,
};
}
pub fn resetXFromIf(self: Position, other: Position, condition: bool) Position {
return .{
.x = if (condition) other.x else self.x,
.y = self.y,
};
}
pub fn resetYFromIf(self: Position, other: Position, condition: bool) Position {
return .{
.x = self.x,
.y = if (condition) other.y else self.y,
};
}

View file

@ -0,0 +1,680 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const Random = std.Random;
const ly_core = @import("ly-core");
const interop = ly_core.interop;
const LogFile = ly_core.LogFile;
const SharedError = ly_core.SharedError;
pub const termbox = @import("termbox2");
const Cell = @import("Cell.zig");
const keyboard = @import("keyboard.zig");
const Position = @import("Position.zig");
const Widget = @import("Widget.zig");
const TerminalBuffer = @This();
pub const KeybindCallbackFn = *const fn (*anyopaque) anyerror!bool;
pub const KeybindMap = std.AutoHashMap(keyboard.Key, struct {
callback: KeybindCallbackFn,
context: *anyopaque,
});
pub const InitOptions = struct {
fg: u32,
bg: u32,
border_fg: u32,
full_color: bool,
is_tty: bool,
};
pub const Styling = struct {
pub const BOLD = termbox.TB_BOLD;
pub const UNDERLINE = termbox.TB_UNDERLINE;
pub const REVERSE = termbox.TB_REVERSE;
pub const ITALIC = termbox.TB_ITALIC;
pub const BLINK = termbox.TB_BLINK;
pub const HI_BLACK = termbox.TB_HI_BLACK;
pub const BRIGHT = termbox.TB_BRIGHT;
pub const DIM = termbox.TB_DIM;
};
pub const Color = struct {
pub const DEFAULT = 0x00000000;
pub const TRUE_BLACK = Styling.HI_BLACK;
pub const TRUE_RED = 0x00FF0000;
pub const TRUE_GREEN = 0x0000FF00;
pub const TRUE_YELLOW = 0x00FFFF00;
pub const TRUE_BLUE = 0x000000FF;
pub const TRUE_MAGENTA = 0x00FF00FF;
pub const TRUE_CYAN = 0x0000FFFF;
pub const TRUE_WHITE = 0x00FFFFFF;
pub const TRUE_DIM_RED = 0x00800000;
pub const TRUE_DIM_GREEN = 0x00008000;
pub const TRUE_DIM_YELLOW = 0x00808000;
pub const TRUE_DIM_BLUE = 0x00000080;
pub const TRUE_DIM_MAGENTA = 0x00800080;
pub const TRUE_DIM_CYAN = 0x00008080;
pub const TRUE_DIM_WHITE = 0x00C0C0C0;
pub const ECOL_BLACK = 1;
pub const ECOL_RED = 2;
pub const ECOL_GREEN = 3;
pub const ECOL_YELLOW = 4;
pub const ECOL_BLUE = 5;
pub const ECOL_MAGENTA = 6;
pub const ECOL_CYAN = 7;
pub const ECOL_WHITE = 8;
};
pub const START_POSITION = Position.init(0, 0);
log_file: *LogFile,
random: Random,
width: usize,
height: usize,
fg: u32,
bg: u32,
border_fg: u32,
box_chars: struct {
left_up: u32,
left_down: u32,
right_up: u32,
right_down: u32,
top: u32,
bottom: u32,
left: u32,
right: u32,
},
blank_cell: Cell,
full_color: bool,
termios: ?std.posix.termios,
keybinds: KeybindMap,
handlable_widgets: std.ArrayList(*Widget),
run: bool,
update: bool,
active_widget_index: usize,
pub fn init(
allocator: Allocator,
io: std.Io,
options: InitOptions,
log_file: *LogFile,
random: Random,
) !TerminalBuffer {
// Initialize termbox
var err = termbox.tb_init();
if (err != 0) {
try log_file.err(
io,
"tui",
"failed to initialise termbox2: {s}, term: {s}",
.{ termbox.tb_strerror(err), std.c.getenv("TERM").? },
);
return error.TermboxInitFailed;
}
if (options.full_color) {
err = termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR);
if (err != 0) {
try log_file.err(
io,
"tui",
"failed to set termbox2 output mode to 24-bit color: {s}",
.{termbox.tb_strerror(err)},
);
return error.TermboxSetOutputModeFailed;
}
try log_file.info(
io,
"tui",
"termbox2 set to 24-bit color output mode",
.{},
);
} else {
try log_file.info(
io,
"tui",
"termbox2 set to eight-color output mode",
.{},
);
}
// Let's take some precautions here and clear the back buffer as well
try clearScreen(true);
const width = getWidth();
const height = getHeight();
try log_file.info(
io,
"tui",
"screen resolution is {d}x{d}",
.{ width, height },
);
return .{
.log_file = log_file,
.random = random,
.width = width,
.height = height,
.fg = options.fg,
.bg = options.bg,
.border_fg = options.border_fg,
.box_chars = if (interop.supportsUnicode()) .{
.left_up = 0x250C,
.left_down = 0x2514,
.right_up = 0x2510,
.right_down = 0x2518,
.top = 0x2500,
.bottom = 0x2500,
.left = 0x2502,
.right = 0x2502,
} else .{
.left_up = '+',
.left_down = '+',
.right_up = '+',
.right_down = '+',
.top = '-',
.bottom = '-',
.left = '|',
.right = '|',
},
.blank_cell = Cell.init(' ', options.fg, options.bg),
.full_color = options.full_color,
// Needed to reclaim the TTY after giving up its control
.termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO),
.keybinds = KeybindMap.init(allocator),
.handlable_widgets = .empty,
.run = true,
.update = true,
.active_widget_index = 0,
};
}
pub fn deinit(self: *TerminalBuffer) void {
self.keybinds.deinit();
TerminalBuffer.shutdown() catch {};
}
pub fn runEventLoop(
self: *TerminalBuffer,
allocator: Allocator,
io: std.Io,
shared_error: SharedError,
layers: [][]*Widget,
active_widget: *Widget,
inactivity_delay: u16,
position_widgets_fn: *const fn (*anyopaque) anyerror!void,
inactivity_event_fn: ?*const fn (*anyopaque) anyerror!void,
context: *anyopaque,
) !void {
try self.registerGlobalKeybind(io, "Ctrl+K", &moveCursorUp, self);
try self.registerGlobalKeybind(io, "Up", &moveCursorUp, self);
try self.registerGlobalKeybind(io, "Ctrl+J", &moveCursorDown, self);
try self.registerGlobalKeybind(io, "Down", &moveCursorDown, self);
try self.registerGlobalKeybind(io, "Tab", &wrapCursor, self);
try self.registerGlobalKeybind(io, "Shift+Tab", &wrapCursorReverse, self);
defer self.handlable_widgets.deinit(allocator);
var i: usize = 0;
for (layers) |layer| {
for (layer) |widget| {
try widget.update(context);
if (widget.vtable.handle_fn != null) {
try self.handlable_widgets.append(allocator, widget);
if (widget.id == active_widget.id) self.active_widget_index = i;
i += 1;
}
}
}
try @call(.auto, position_widgets_fn, .{context});
var event: termbox.tb_event = undefined;
var inactivity_cmd_ran = false;
var inactivity_time_start = try interop.getTimeOfDay();
while (self.run) {
var maybe_timeout: ?usize = null;
if (self.update) {
try TerminalBuffer.clearScreen(false);
// Reset cursor
const current_widget = self.getActiveWidget();
current_widget.handle(null) catch |err| {
shared_error.writeError(error.SetCursorFailed);
try self.log_file.err(
io,
"tui",
"failed to set cursor in active widget '{s}': {s}",
.{ current_widget.display_name, @errorName(err) },
);
};
for (layers) |layer| {
for (layer) |widget| {
try widget.update(context);
widget.draw();
if (try widget.calculateTimeout(context)) |widget_timeout| {
if (maybe_timeout == null or widget_timeout < maybe_timeout.?) maybe_timeout = widget_timeout;
}
}
}
// We don't care about present errors here
TerminalBuffer.presentBuffer() catch {};
}
if (inactivity_event_fn) |inactivity_fn| {
const time = try interop.getTimeOfDay();
if (!inactivity_cmd_ran and time.seconds - inactivity_time_start.seconds > inactivity_delay) {
try @call(.auto, inactivity_fn, .{context});
inactivity_cmd_ran = true;
}
}
const event_error = if (maybe_timeout) |timeout| termbox.tb_peek_event(&event, @intCast(timeout)) else termbox.tb_poll_event(&event);
self.update = maybe_timeout != null or event_error >= 0;
if (event_error < 0) continue;
// Input of some kind was detected, so reset the inactivity timer
inactivity_time_start = try interop.getTimeOfDay();
if (event.type == termbox.TB_EVENT_RESIZE) {
self.width = TerminalBuffer.getWidth();
self.height = TerminalBuffer.getHeight();
try self.log_file.info(
io,
"tui",
"screen resolution updated to {d}x{d}",
.{ self.width, self.height },
);
for (layers) |layer| {
for (layer) |widget| {
widget.realloc() catch |err| {
shared_error.writeError(error.WidgetReallocationFailed);
try self.log_file.err(
io,
"tui",
"failed to reallocate widget '{s}': {s}",
.{ widget.display_name, @errorName(err) },
);
};
}
}
try @call(.auto, position_widgets_fn, .{context});
self.update = true;
continue;
}
var maybe_keys = try self.handleKeybind(allocator, event);
if (maybe_keys) |*keys| {
defer keys.deinit(allocator);
const current_widget = self.getActiveWidget();
for (keys.items) |key| {
current_widget.handle(key) catch |err| {
shared_error.writeError(error.CurrentWidgetHandlingFailed);
try self.log_file.err(
io,
"tui",
"failed to handle active widget '{s}': {s}",
.{ current_widget.display_name, @errorName(err) },
);
};
}
self.update = true;
}
}
}
pub fn stopEventLoop(self: *TerminalBuffer) void {
self.run = false;
}
pub fn drawNextFrame(self: *TerminalBuffer, value: bool) void {
self.update = value;
}
pub fn getActiveWidget(self: *TerminalBuffer) *Widget {
return self.handlable_widgets.items[self.active_widget_index];
}
pub fn setActiveWidget(self: *TerminalBuffer, widget: *Widget) void {
for (self.handlable_widgets.items, 0..) |widg, i| {
if (widg.id == widget.id) self.active_widget_index = i;
}
}
pub fn getWidth() usize {
return @intCast(termbox.tb_width());
}
pub fn getHeight() usize {
return @intCast(termbox.tb_height());
}
pub fn setCursor(x: usize, y: usize) !void {
if (termbox.tb_set_cursor(@intCast(x), @intCast(y)) != 0) {
return error.TermboxSetCursorFailed;
}
}
pub fn clearScreen(clear_back_buffer: bool) !void {
if (termbox.tb_clear() != 0) return error.TermboxClearFailed;
if (clear_back_buffer) try clearBackBuffer();
}
pub fn shutdown() !void {
if (termbox.tb_shutdown() != 0) return error.TermboxShutdownFailed;
}
pub fn presentBuffer() !void {
if (termbox.tb_present() != 0) return error.TermboxPresentFailed;
}
pub fn getCell(x: usize, y: usize) ?Cell {
var maybe_cell: ?*termbox.tb_cell = undefined;
if (termbox.tb_get_cell(
@intCast(x),
@intCast(y),
1,
&maybe_cell,
) != 0) {
return null;
}
if (maybe_cell) |cell| {
return Cell.init(cell.ch, cell.fg, cell.bg);
}
return null;
}
pub fn setCell(x: usize, y: usize, cell: Cell) !void {
if (termbox.tb_set_cell(
@intCast(x),
@intCast(y),
cell.ch,
cell.fg,
cell.bg,
) != 0) {
return error.TermboxSetCellFailed;
}
}
pub fn setCellBoundsChecked(self: *TerminalBuffer, x: isize, y: isize, cell: Cell) !void {
if (0 <= x and x < self.width and 0 <= y and y < self.height) {
try cell.put(@intCast(x), @intCast(y));
}
}
pub fn reclaim(self: TerminalBuffer) !void {
if (self.termios) |termios| {
// Take back control of the TTY
const err = termbox.tb_init();
if (err != 0 and err != termbox.TB_ERR_INIT_ALREADY) return error.TermboxReinitFailed;
if (self.full_color and termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR) != 0) {
return error.TermboxSetOutputModeFailed;
}
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, termios);
}
}
pub fn registerKeybind(
self: *TerminalBuffer,
io: std.Io,
keybinds: *KeybindMap,
keybind: []const u8,
callback: KeybindCallbackFn,
context: *anyopaque,
) !void {
const key = try self.parseKeybind(io, keybind);
keybinds.put(key, .{
.callback = callback,
.context = context,
}) catch |err| {
try self.log_file.err(
io,
"tui",
"failed to register keybind {s}: {s}",
.{ keybind, @errorName(err) },
);
};
}
pub fn registerGlobalKeybind(
self: *TerminalBuffer,
io: std.Io,
keybind: []const u8,
callback: KeybindCallbackFn,
context: *anyopaque,
) !void {
try self.registerKeybind(io, &self.keybinds, keybind, callback, context);
}
pub fn simulateKeybind(self: *TerminalBuffer, io: std.Io, keybind: []const u8) !bool {
const key = try self.parseKeybind(io, keybind);
if (self.keybinds.get(key)) |binding| {
return try @call(
.auto,
binding.callback,
.{binding.context},
);
}
const current_widget = self.getActiveWidget();
if (current_widget.keybinds) |keybinds| {
if (keybinds.get(key)) |binding| {
return try @call(
.auto,
binding.callback,
.{binding.context},
);
}
}
return true;
}
pub fn drawText(
text: []const u8,
x: usize,
y: usize,
fg: u32,
bg: u32,
) !void {
const utf8view = std.unicode.Utf8View.init(text) catch return;
var utf8 = utf8view.iterator();
var i = x;
while (utf8.nextCodepoint()) |codepoint| : (i += @intCast(termbox.tb_wcwidth(codepoint))) {
const cell = Cell.init(codepoint, fg, bg);
try cell.put(i, y);
}
}
pub fn drawConfinedText(
text: []const u8,
x: usize,
y: usize,
max_length: usize,
fg: u32,
bg: u32,
) !void {
const utf8view = std.unicode.Utf8View.init(text) catch return;
var utf8 = utf8view.iterator();
var i = x;
while (utf8.nextCodepoint()) |codepoint| : (i += @intCast(termbox.tb_wcwidth(codepoint))) {
if (i - x >= max_length) break;
const cell = Cell.init(codepoint, fg, bg);
try cell.put(i, y);
}
}
pub fn drawCharMultiple(
char: u32,
x: usize,
y: usize,
length: usize,
fg: u32,
bg: u32,
) !void {
const cell = Cell.init(char, fg, bg);
for (0..length) |xx| try cell.put(x + xx, y);
}
// Every codepoint is assumed to have a width of 1.
// Since Ly is normally running in a TTY, this should be fine.
pub fn strWidth(str: []const u8) usize {
const utf8view = std.unicode.Utf8View.init(str) catch return str.len;
var utf8 = utf8view.iterator();
var length: c_int = 0;
while (utf8.nextCodepoint()) |codepoint| {
length += termbox.tb_wcwidth(codepoint);
}
return @intCast(length);
}
fn clearBackBuffer() !void {
if (termbox.global.initialized == 0) return;
// Clear the TTY because termbox2 doesn't seem to do it properly
const capability = termbox.global.caps[termbox.TB_CAP_CLEAR_SCREEN];
const capability_slice = std.mem.span(capability);
const result = std.posix.system.write(termbox.global.ttyfd, capability_slice.ptr, capability_slice.len);
if (result != capability_slice.len) return error.PartialClearBackBuffer;
if (result < 0) return error.ClearBackBufferFailed;
}
fn parseKeybind(self: *TerminalBuffer, io: std.Io, keybind: []const u8) !keyboard.Key {
var key = std.mem.zeroes(keyboard.Key);
var iterator = std.mem.splitScalar(u8, keybind, '+');
while (iterator.next()) |item| {
var found = false;
inline for (comptime std.meta.fieldNames(keyboard.Key)) |name| {
if (std.ascii.eqlIgnoreCase(name, item)) {
@field(key, name) = true;
found = true;
break;
}
}
if (!found) {
try self.log_file.err(
io,
"tui",
"failed to parse key {s} of keybind {s}",
.{ item, keybind },
);
}
}
return key;
}
fn handleKeybind(
self: *TerminalBuffer,
allocator: Allocator,
tb_event: termbox.tb_event,
) !?std.ArrayList(keyboard.Key) {
var keys = try keyboard.getKeyList(allocator, tb_event);
for (keys.items) |key| {
if (self.keybinds.get(key)) |binding| {
const passthrough_event = try @call(
.auto,
binding.callback,
.{binding.context},
);
if (!passthrough_event) {
keys.deinit(allocator);
return null;
}
return keys;
}
const current_widget = self.getActiveWidget();
if (current_widget.keybinds) |keybinds| {
if (keybinds.get(key)) |binding| {
const passthrough_event = try @call(
.auto,
binding.callback,
.{binding.context},
);
if (!passthrough_event) {
keys.deinit(allocator);
return null;
}
return keys;
}
}
}
return keys;
}
fn moveCursorUp(ptr: *anyopaque) !bool {
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
if (state.active_widget_index == 0) return false;
state.active_widget_index -= 1;
state.update = true;
return false;
}
fn moveCursorDown(ptr: *anyopaque) !bool {
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
if (state.active_widget_index == state.handlable_widgets.items.len - 1) return false;
state.active_widget_index += 1;
state.update = true;
return false;
}
fn wrapCursor(ptr: *anyopaque) !bool {
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
state.active_widget_index = (state.active_widget_index + 1) % state.handlable_widgets.items.len;
state.update = true;
return false;
}
fn wrapCursorReverse(ptr: *anyopaque) !bool {
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
state.active_widget_index = if (state.active_widget_index == 0) state.handlable_widgets.items.len - 1 else state.active_widget_index - 1;
state.update = true;
return false;
}

186
ly-ui/src/Widget.zig Normal file
View file

@ -0,0 +1,186 @@
const Widget = @This();
const keyboard = @import("keyboard.zig");
const TerminalBuffer = @import("TerminalBuffer.zig");
const VTable = struct {
deinit_fn: ?*const fn (ptr: *anyopaque) void,
realloc_fn: ?*const fn (ptr: *anyopaque) anyerror!void,
draw_fn: *const fn (ptr: *anyopaque) void,
update_fn: ?*const fn (ptr: *anyopaque, ctx: *anyopaque) anyerror!void,
handle_fn: ?*const fn (ptr: *anyopaque, maybe_key: ?keyboard.Key) anyerror!void,
calculate_timeout_fn: ?*const fn (ptr: *anyopaque, ctx: *anyopaque) anyerror!?usize,
};
pub var idCounter: u64 = 0;
id: u64,
display_name: []const u8,
keybinds: ?TerminalBuffer.KeybindMap,
pointer: *anyopaque,
vtable: VTable,
pub fn init(
display_name: []const u8,
keybinds: ?TerminalBuffer.KeybindMap,
pointer: anytype,
comptime deinit_fn: ?fn (ptr: @TypeOf(pointer)) void,
comptime realloc_fn: ?fn (ptr: @TypeOf(pointer)) anyerror!void,
comptime draw_fn: fn (ptr: @TypeOf(pointer)) void,
comptime update_fn: ?fn (ptr: @TypeOf(pointer), ctx: *anyopaque) anyerror!void,
comptime handle_fn: ?fn (ptr: @TypeOf(pointer), maybe_key: ?keyboard.Key) anyerror!void,
comptime calculate_timeout_fn: ?fn (ptr: @TypeOf(pointer), ctx: *anyopaque) anyerror!?usize,
) Widget {
const Pointer = @TypeOf(pointer);
const Impl = struct {
pub fn deinitImpl(ptr: *anyopaque) void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(
.always_inline,
deinit_fn.?,
.{impl},
);
}
pub fn reallocImpl(ptr: *anyopaque) !void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(
.always_inline,
realloc_fn.?,
.{impl},
);
}
pub fn drawImpl(ptr: *anyopaque) void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(
.always_inline,
draw_fn,
.{impl},
);
}
pub fn updateImpl(ptr: *anyopaque, ctx: *anyopaque) !void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(
.always_inline,
update_fn.?,
.{ impl, ctx },
);
}
pub fn handleImpl(ptr: *anyopaque, maybe_key: ?keyboard.Key) !void {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(
.always_inline,
handle_fn.?,
.{ impl, maybe_key },
);
}
pub fn calculateTimeoutImpl(ptr: *anyopaque, ctx: *anyopaque) !?usize {
const impl: Pointer = @ptrCast(@alignCast(ptr));
return @call(
.always_inline,
calculate_timeout_fn.?,
.{ impl, ctx },
);
}
const vtable = VTable{
.deinit_fn = if (deinit_fn != null) deinitImpl else null,
.realloc_fn = if (realloc_fn != null) reallocImpl else null,
.draw_fn = drawImpl,
.update_fn = if (update_fn != null) updateImpl else null,
.handle_fn = if (handle_fn != null) handleImpl else null,
.calculate_timeout_fn = if (calculate_timeout_fn != null) calculateTimeoutImpl else null,
};
};
idCounter += 1;
return .{
.id = idCounter,
.display_name = display_name,
.keybinds = keybinds,
.pointer = pointer,
.vtable = Impl.vtable,
};
}
pub fn deinit(self: *Widget) void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
if (self.vtable.deinit_fn) |deinit_fn| {
return @call(
.auto,
deinit_fn,
.{impl},
);
}
}
pub fn realloc(self: *Widget) !void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
if (self.vtable.realloc_fn) |realloc_fn| {
return @call(
.auto,
realloc_fn,
.{impl},
);
}
}
pub fn draw(self: *Widget) void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
@call(
.auto,
self.vtable.draw_fn,
.{impl},
);
}
pub fn update(self: *Widget, ctx: *anyopaque) !void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
if (self.vtable.update_fn) |update_fn| {
return @call(
.auto,
update_fn,
.{ impl, ctx },
);
}
}
pub fn handle(self: *Widget, maybe_key: ?keyboard.Key) !void {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
if (self.vtable.handle_fn) |handle_fn| {
return @call(
.auto,
handle_fn,
.{ impl, maybe_key },
);
}
}
pub fn calculateTimeout(self: *Widget, ctx: *anyopaque) !?usize {
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
if (self.vtable.calculate_timeout_fn) |calculate_timeout_fn| {
return @call(
.auto,
calculate_timeout_fn,
.{ impl, ctx },
);
}
return null;
}

View file

@ -0,0 +1,237 @@
const BigLabel = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
const ly_core = @import("ly-core");
const interop = ly_core.interop;
const en = @import("bigLabelLocales/en.zig");
const fa = @import("bigLabelLocales/fa.zig");
const Cell = @import("../Cell.zig");
const Position = @import("../Position.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const Widget = @import("../Widget.zig");
pub const CHAR_WIDTH = 5;
pub const CHAR_HEIGHT = 5;
pub const CHAR_SIZE = CHAR_WIDTH * CHAR_HEIGHT;
pub const X: u32 = if (ly_core.interop.supportsUnicode()) 0x2593 else '#';
pub const O: u32 = 0;
// zig fmt: off
pub const LocaleChars = struct {
ZERO: [CHAR_SIZE]u21,
ONE: [CHAR_SIZE]u21,
TWO: [CHAR_SIZE]u21,
THREE: [CHAR_SIZE]u21,
FOUR: [CHAR_SIZE]u21,
FIVE: [CHAR_SIZE]u21,
SIX: [CHAR_SIZE]u21,
SEVEN: [CHAR_SIZE]u21,
EIGHT: [CHAR_SIZE]u21,
NINE: [CHAR_SIZE]u21,
S: [CHAR_SIZE]u21,
E: [CHAR_SIZE]u21,
P: [CHAR_SIZE]u21,
A: [CHAR_SIZE]u21,
M: [CHAR_SIZE]u21,
};
// zig fmt: on
pub const BigLabelLocale = enum {
en,
fa,
};
instance: ?Widget = null,
allocator: ?Allocator = null,
buffer: *TerminalBuffer,
text: []const u8,
max_width: ?usize,
fg: u32,
bg: u32,
locale: BigLabelLocale,
update_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!void,
calculate_timeout_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!?usize,
component_pos: Position,
children_pos: Position,
pub fn init(
buffer: *TerminalBuffer,
text: []const u8,
max_width: ?usize,
fg: u32,
bg: u32,
locale: BigLabelLocale,
update_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!void,
calculate_timeout_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!?usize,
) BigLabel {
return .{
.instance = null,
.allocator = null,
.buffer = buffer,
.text = text,
.max_width = max_width,
.fg = fg,
.bg = bg,
.locale = locale,
.update_fn = update_fn,
.calculate_timeout_fn = calculate_timeout_fn,
.component_pos = TerminalBuffer.START_POSITION,
.children_pos = TerminalBuffer.START_POSITION,
};
}
pub fn deinit(self: *BigLabel) void {
if (self.allocator) |allocator| allocator.free(self.text);
}
pub fn widget(self: *BigLabel) *Widget {
if (self.instance) |*instance| return instance;
self.instance = Widget.init(
"BigLabel",
null,
self,
deinit,
null,
draw,
update,
null,
calculateTimeout,
);
return &self.instance.?;
}
pub fn setTextAlloc(
self: *BigLabel,
allocator: Allocator,
comptime fmt: []const u8,
args: anytype,
) !void {
self.text = try std.fmt.allocPrint(allocator, fmt, args);
self.allocator = allocator;
}
pub fn setTextBuf(
self: *BigLabel,
buffer: []u8,
comptime fmt: []const u8,
args: anytype,
) !void {
self.text = try std.fmt.bufPrint(buffer, fmt, args);
self.allocator = null;
}
pub fn setText(self: *BigLabel, text: []const u8) void {
self.text = text;
self.allocator = null;
}
pub fn positionX(self: *BigLabel, original_pos: Position) void {
self.component_pos = original_pos;
self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text) * CHAR_WIDTH);
}
pub fn positionY(self: *BigLabel, original_pos: Position) void {
self.component_pos = original_pos;
self.children_pos = original_pos.addY(CHAR_HEIGHT);
}
pub fn positionXY(self: *BigLabel, original_pos: Position) void {
self.component_pos = original_pos;
self.children_pos = Position.init(
TerminalBuffer.strWidth(self.text) * CHAR_WIDTH,
CHAR_HEIGHT,
).add(original_pos);
}
pub fn childrenPosition(self: BigLabel) Position {
return self.children_pos;
}
fn draw(self: *BigLabel) void {
for (self.text, 0..) |c, i| {
const clock_cell = clockCell(
c,
self.fg,
self.bg,
self.locale,
);
alphaBlit(
self.component_pos.x + i * (CHAR_WIDTH + 1),
self.component_pos.y,
self.buffer.width,
self.buffer.height,
clock_cell,
);
}
}
fn update(self: *BigLabel, context: *anyopaque) !void {
if (self.update_fn) |update_fn| {
return @call(
.auto,
update_fn,
.{ self, context },
);
}
}
fn calculateTimeout(self: *BigLabel, ctx: *anyopaque) !?usize {
if (self.calculate_timeout_fn) |calculate_timeout_fn| {
return @call(
.auto,
calculate_timeout_fn,
.{ self, ctx },
);
}
return null;
}
fn clockCell(char: u8, fg: u32, bg: u32, locale: BigLabelLocale) [CHAR_SIZE]Cell {
var cells: [CHAR_SIZE]Cell = undefined;
//@divTrunc(time.microseconds, 500000) != 0)
const clock_chars = toBigNumber(char, locale);
for (0..cells.len) |i| cells[i] = Cell.init(clock_chars[i], fg, bg);
return cells;
}
fn alphaBlit(x: usize, y: usize, tb_width: usize, tb_height: usize, cells: [CHAR_SIZE]Cell) void {
if (x + CHAR_WIDTH >= tb_width or y + CHAR_HEIGHT >= tb_height) return;
for (0..CHAR_HEIGHT) |yy| {
for (0..CHAR_WIDTH) |xx| {
const cell = cells[yy * CHAR_WIDTH + xx];
cell.put(x + xx, y + yy) catch {};
}
}
}
fn toBigNumber(char: u8, locale: BigLabelLocale) [CHAR_SIZE]u21 {
const locale_chars = switch (locale) {
.fa => fa.locale_chars,
.en => en.locale_chars,
};
return switch (char) {
'0' => locale_chars.ZERO,
'1' => locale_chars.ONE,
'2' => locale_chars.TWO,
'3' => locale_chars.THREE,
'4' => locale_chars.FOUR,
'5' => locale_chars.FIVE,
'6' => locale_chars.SIX,
'7' => locale_chars.SEVEN,
'8' => locale_chars.EIGHT,
'9' => locale_chars.NINE,
'p', 'P' => locale_chars.P,
'a', 'A' => locale_chars.A,
'm', 'M' => locale_chars.M,
':' => locale_chars.S,
else => locale_chars.E,
};
}

View file

@ -0,0 +1,190 @@
const std = @import("std");
const Cell = @import("../Cell.zig");
const Position = @import("../Position.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const Widget = @import("../Widget.zig");
const Box = @This();
instance: ?Widget = null,
buffer: *TerminalBuffer,
horizontal_margin: usize,
vertical_margin: usize,
width: usize,
height: usize,
show_borders: bool,
blank_box: bool,
top_title: ?[]const u8,
bottom_title: ?[]const u8,
border_fg: u32,
title_fg: u32,
bg: u32,
update_fn: ?*const fn (*Box, *anyopaque) anyerror!void,
left_pos: Position,
right_pos: Position,
children_pos: Position,
pub fn init(
buffer: *TerminalBuffer,
horizontal_margin: usize,
vertical_margin: usize,
width: usize,
height: usize,
show_borders: bool,
blank_box: bool,
top_title: ?[]const u8,
bottom_title: ?[]const u8,
border_fg: u32,
title_fg: u32,
bg: u32,
update_fn: ?*const fn (*Box, *anyopaque) anyerror!void,
) Box {
return .{
.instance = null,
.buffer = buffer,
.horizontal_margin = horizontal_margin,
.vertical_margin = vertical_margin,
.width = width,
.height = height,
.show_borders = show_borders,
.blank_box = blank_box,
.top_title = top_title,
.bottom_title = bottom_title,
.border_fg = border_fg,
.title_fg = title_fg,
.bg = bg,
.update_fn = update_fn,
.left_pos = TerminalBuffer.START_POSITION,
.right_pos = TerminalBuffer.START_POSITION,
.children_pos = TerminalBuffer.START_POSITION,
};
}
pub fn widget(self: *Box) *Widget {
if (self.instance) |*instance| return instance;
self.instance = Widget.init(
"Box",
null,
self,
null,
null,
draw,
update,
null,
null,
);
return &self.instance.?;
}
pub fn positionXY(self: *Box, original_pos: Position) void {
if (self.buffer.width < 2 or self.buffer.height < 2) return;
self.left_pos = original_pos;
self.right_pos = Position.init(
@min(self.buffer.width, self.width),
@min(self.buffer.height, self.height),
).add(self.left_pos);
self.children_pos = Position.init(
self.horizontal_margin,
self.vertical_margin,
).add(self.left_pos);
}
pub fn childrenPosition(self: Box) Position {
return self.children_pos;
}
fn draw(self: *Box) void {
if (self.show_borders) {
var left_up = Cell.init(
self.buffer.box_chars.left_up,
self.border_fg,
self.bg,
);
var right_up = Cell.init(
self.buffer.box_chars.right_up,
self.border_fg,
self.bg,
);
var left_down = Cell.init(
self.buffer.box_chars.left_down,
self.border_fg,
self.bg,
);
var right_down = Cell.init(
self.buffer.box_chars.right_down,
self.border_fg,
self.bg,
);
var top = Cell.init(
self.buffer.box_chars.top,
self.border_fg,
self.bg,
);
var bottom = Cell.init(
self.buffer.box_chars.bottom,
self.border_fg,
self.bg,
);
left_up.put(self.left_pos.x - 1, self.left_pos.y - 1) catch {};
right_up.put(self.right_pos.x, self.left_pos.y - 1) catch {};
left_down.put(self.left_pos.x - 1, self.right_pos.y) catch {};
right_down.put(self.right_pos.x, self.right_pos.y) catch {};
for (0..self.width) |i| {
top.put(self.left_pos.x + i, self.left_pos.y - 1) catch {};
bottom.put(self.left_pos.x + i, self.right_pos.y) catch {};
}
top.ch = self.buffer.box_chars.left;
bottom.ch = self.buffer.box_chars.right;
for (0..self.height) |i| {
top.put(self.left_pos.x - 1, self.left_pos.y + i) catch {};
bottom.put(self.right_pos.x, self.left_pos.y + i) catch {};
}
}
if (self.blank_box) {
for (0..self.height) |y| {
for (0..self.width) |x| {
self.buffer.blank_cell.put(self.left_pos.x + x, self.left_pos.y + y) catch {};
}
}
}
if (self.top_title) |title| {
TerminalBuffer.drawConfinedText(
title,
self.left_pos.x,
self.left_pos.y - 1,
self.width,
self.title_fg,
self.bg,
) catch {};
}
if (self.bottom_title) |title| {
TerminalBuffer.drawConfinedText(
title,
self.left_pos.x,
self.left_pos.y + self.height,
self.width,
self.title_fg,
self.bg,
) catch {};
}
}
fn update(self: *Box, ctx: *anyopaque) !void {
if (self.update_fn) |update_fn| {
return @call(
.auto,
update_fn,
.{ self, ctx },
);
}
}

View file

@ -0,0 +1,153 @@
const Label = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
const Cell = @import("../Cell.zig");
const Position = @import("../Position.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const Widget = @import("../Widget.zig");
instance: ?Widget,
allocator: ?Allocator,
text: []const u8,
max_width: ?usize,
fg: u32,
bg: u32,
update_fn: ?*const fn (*Label, *anyopaque) anyerror!void,
calculate_timeout_fn: ?*const fn (*Label, *anyopaque) anyerror!?usize,
component_pos: Position,
children_pos: Position,
pub fn init(
text: []const u8,
max_width: ?usize,
fg: u32,
bg: u32,
update_fn: ?*const fn (*Label, *anyopaque) anyerror!void,
calculate_timeout_fn: ?*const fn (*Label, *anyopaque) anyerror!?usize,
) Label {
return .{
.instance = null,
.allocator = null,
.text = text,
.max_width = max_width,
.fg = fg,
.bg = bg,
.update_fn = update_fn,
.calculate_timeout_fn = calculate_timeout_fn,
.component_pos = TerminalBuffer.START_POSITION,
.children_pos = TerminalBuffer.START_POSITION,
};
}
pub fn deinit(self: *Label) void {
if (self.allocator) |allocator| allocator.free(self.text);
}
pub fn widget(self: *Label) *Widget {
if (self.instance) |*instance| return instance;
self.instance = Widget.init(
"Label",
null,
self,
deinit,
null,
draw,
update,
null,
calculateTimeout,
);
return &self.instance.?;
}
pub fn setTextAlloc(
self: *Label,
allocator: Allocator,
comptime fmt: []const u8,
args: anytype,
) !void {
self.text = try std.fmt.allocPrint(allocator, fmt, args);
self.allocator = allocator;
}
pub fn setTextBuf(
self: *Label,
buffer: []u8,
comptime fmt: []const u8,
args: anytype,
) !void {
self.text = try std.fmt.bufPrint(buffer, fmt, args);
self.allocator = null;
}
pub fn setText(self: *Label, text: []const u8) void {
self.text = text;
self.allocator = null;
}
pub fn positionX(self: *Label, original_pos: Position) void {
self.component_pos = original_pos;
self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text));
}
pub fn positionY(self: *Label, original_pos: Position) void {
self.component_pos = original_pos;
self.children_pos = original_pos.addY(1);
}
pub fn positionXY(self: *Label, original_pos: Position) void {
self.component_pos = original_pos;
self.children_pos = Position.init(
TerminalBuffer.strWidth(self.text),
1,
).add(original_pos);
}
pub fn childrenPosition(self: Label) Position {
return self.children_pos;
}
fn draw(self: *Label) void {
if (self.max_width) |width| {
TerminalBuffer.drawConfinedText(
self.text,
self.component_pos.x,
self.component_pos.y,
width,
self.fg,
self.bg,
) catch {};
return;
}
TerminalBuffer.drawText(
self.text,
self.component_pos.x,
self.component_pos.y,
self.fg,
self.bg,
) catch {};
}
fn update(self: *Label, ctx: *anyopaque) !void {
if (self.update_fn) |update_fn| {
return @call(
.auto,
update_fn,
.{ self, ctx },
);
}
}
fn calculateTimeout(self: *Label, ctx: *anyopaque) !?usize {
if (self.calculate_timeout_fn) |calculate_timeout_fn| {
return @call(
.auto,
calculate_timeout_fn,
.{ self, ctx },
);
}
return null;
}

View file

@ -0,0 +1,250 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const keyboard = @import("../keyboard.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const Position = @import("../Position.zig");
const Widget = @import("../Widget.zig");
const DynamicString = std.ArrayList(u8);
const Text = @This();
instance: ?Widget,
allocator: Allocator,
buffer: *TerminalBuffer,
text: DynamicString,
end: usize,
cursor: usize,
visible_start: usize,
width: usize,
component_pos: Position,
children_pos: Position,
should_insert: bool,
masked: bool,
maybe_mask: ?u32,
fg: u32,
bg: u32,
keybinds: TerminalBuffer.KeybindMap,
pub fn init(
allocator: Allocator,
io: std.Io,
buffer: *TerminalBuffer,
should_insert: bool,
masked: bool,
maybe_mask: ?u32,
width: usize,
fg: u32,
bg: u32,
) !*Text {
var self = try allocator.create(Text);
self.* = Text{
.instance = null,
.allocator = allocator,
.buffer = buffer,
.text = .empty,
.end = 0,
.cursor = 0,
.visible_start = 0,
.width = width,
.component_pos = TerminalBuffer.START_POSITION,
.children_pos = TerminalBuffer.START_POSITION,
.should_insert = should_insert,
.masked = masked,
.maybe_mask = maybe_mask,
.fg = fg,
.bg = bg,
.keybinds = .init(allocator),
};
try buffer.registerKeybind(io, &self.keybinds, "Left", &goLeft, self);
try buffer.registerKeybind(io, &self.keybinds, "Right", &goRight, self);
try buffer.registerKeybind(io, &self.keybinds, "Delete", &delete, self);
try buffer.registerKeybind(io, &self.keybinds, "Backspace", &backspace, self);
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+U", &clearTextEntry, self);
return self;
}
pub fn deinit(self: *Text) void {
self.text.deinit(self.allocator);
self.keybinds.deinit();
self.allocator.destroy(self);
}
pub fn widget(self: *Text) *Widget {
if (self.instance) |*instance| return instance;
self.instance = Widget.init(
"Text",
self.keybinds,
self,
deinit,
null,
draw,
null,
handle,
null,
);
return &self.instance.?;
}
pub fn positionX(self: *Text, original_pos: Position) void {
self.component_pos = original_pos;
self.children_pos = original_pos.addX(self.width);
}
pub fn positionY(self: *Text, original_pos: Position) void {
self.component_pos = original_pos;
self.children_pos = original_pos.addY(1);
}
pub fn positionXY(self: *Text, original_pos: Position) void {
self.component_pos = original_pos;
self.children_pos = Position.init(
self.width,
1,
).add(original_pos);
}
pub fn childrenPosition(self: Text) Position {
return self.children_pos;
}
pub fn clear(self: *Text) void {
self.text.clearRetainingCapacity();
self.end = 0;
self.cursor = 0;
self.visible_start = 0;
}
pub fn toggleMask(self: *Text) void {
self.masked = !self.masked;
}
pub fn handle(self: *Text, maybe_key: ?keyboard.Key) !void {
if (maybe_key) |key| {
if (self.should_insert) {
const maybe_character = key.getEnabledPrintableAscii();
if (maybe_character) |character| try self.write(character);
}
}
if (self.masked and self.maybe_mask == null) {
try TerminalBuffer.setCursor(
self.component_pos.x,
self.component_pos.y,
);
return;
}
try TerminalBuffer.setCursor(
self.component_pos.x + (self.cursor - self.visible_start),
self.component_pos.y,
);
}
pub fn writeText(self: *Text, str: []const u8) !void {
for (str) |c| try self.write(c);
}
fn draw(self: *Text) void {
if (self.masked) {
if (self.maybe_mask) |mask| {
if (self.width < 1) return;
const length = @min(TerminalBuffer.strWidth(self.text.items), self.width - 1);
if (length == 0) return;
TerminalBuffer.drawCharMultiple(
mask,
self.component_pos.x,
self.component_pos.y,
length,
self.fg,
self.bg,
) catch {};
}
return;
}
const str_length = TerminalBuffer.strWidth(self.text.items);
const length = @min(str_length, self.width);
if (length == 0) return;
const visible_slice = vs: {
if (str_length > self.width and self.cursor < str_length) {
break :vs self.text.items[self.visible_start..(self.width + self.visible_start)];
} else {
break :vs self.text.items[self.visible_start..];
}
};
TerminalBuffer.drawText(
visible_slice,
self.component_pos.x,
self.component_pos.y,
self.fg,
self.bg,
) catch {};
}
fn goLeft(ptr: *anyopaque) !bool {
var self: *Text = @ptrCast(@alignCast(ptr));
if (self.cursor == 0) return false;
if (self.visible_start > 0) self.visible_start -= 1;
self.cursor -= 1;
return false;
}
fn goRight(ptr: *anyopaque) !bool {
var self: *Text = @ptrCast(@alignCast(ptr));
if (self.cursor >= self.end) return false;
if (self.cursor - self.visible_start == self.width - 1) self.visible_start += 1;
self.cursor += 1;
return false;
}
fn delete(ptr: *anyopaque) !bool {
var self: *Text = @ptrCast(@alignCast(ptr));
if (self.cursor >= self.end or !self.should_insert) return false;
_ = self.text.orderedRemove(self.cursor);
self.end -= 1;
return false;
}
fn backspace(ptr: *anyopaque) !bool {
const self: *Text = @ptrCast(@alignCast(ptr));
if (self.cursor == 0 or !self.should_insert) return false;
_ = try goLeft(ptr);
_ = try delete(ptr);
return false;
}
fn write(self: *Text, char: u8) !void {
if (char == 0) return;
try self.text.insert(self.allocator, self.cursor, char);
self.end += 1;
_ = try goRight(self);
}
fn clearTextEntry(ptr: *anyopaque) !bool {
var self: *Text = @ptrCast(@alignCast(ptr));
if (!self.should_insert) return false;
self.clear();
self.buffer.drawNextFrame(true);
return false;
}

View file

@ -0,0 +1,114 @@
const BigLabel = @import("../BigLabel.zig");
const LocaleChars = BigLabel.LocaleChars;
const X = BigLabel.X;
const O = BigLabel.O;
// zig fmt: off
pub const locale_chars = LocaleChars{
.ZERO = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,O,X,X,
X,X,O,X,X,
X,X,X,X,X,
},
.ONE = [_]u21{
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
},
.TWO = [_]u21{
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
X,X,O,O,O,
X,X,X,X,X,
},
.THREE = [_]u21{
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
},
.FOUR = [_]u21{
X,X,O,X,X,
X,X,O,X,X,
X,X,X,X,X,
O,O,O,X,X,
O,O,O,X,X,
},
.FIVE = [_]u21{
X,X,X,X,X,
X,X,O,O,O,
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
},
.SIX = [_]u21{
X,X,X,X,X,
X,X,O,O,O,
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
},
.SEVEN = [_]u21{
X,X,X,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
O,O,O,X,X,
},
.EIGHT = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
},
.NINE = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
O,O,O,X,X,
X,X,X,X,X,
},
.S = [_]u21{
O,O,O,O,O,
O,O,X,O,O,
O,O,O,O,O,
O,O,X,O,O,
O,O,O,O,O,
},
.E = [_]u21{
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
},
.P = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
X,X,O,O,O,
X,X,O,O,O,
},
.A = [_]u21{
X,X,X,X,X,
X,X,O,X,X,
X,X,X,X,X,
X,X,O,X,X,
X,X,O,X,X,
},
.M = [_]u21{
X,X,X,X,X,
X,O,X,O,X,
X,O,X,O,X,
X,O,O,O,X,
X,O,O,O,X,
},
};
// zig fmt: on

View file

@ -0,0 +1,114 @@
const BigLabel = @import("../BigLabel.zig");
const LocaleChars = BigLabel.LocaleChars;
const X = BigLabel.X;
const O = BigLabel.O;
// zig fmt: off
pub const locale_chars = LocaleChars{
.ZERO = [_]u21{
O,O,O,O,O,
O,O,X,O,O,
O,X,O,X,O,
O,O,X,O,O,
O,O,O,O,O,
},
.ONE = [_]u21{
O,O,X,O,O,
O,X,X,O,O,
O,O,X,O,O,
O,O,X,O,O,
O,O,X,O,O,
},
.TWO = [_]u21{
O,X,O,X,O,
O,X,X,X,O,
O,X,O,O,O,
O,X,O,O,O,
O,X,O,O,O,
},
.THREE = [_]u21{
X,O,X,O,X,
X,X,X,X,X,
X,O,O,O,O,
X,O,O,O,O,
X,O,O,O,O,
},
.FOUR = [_]u21{
O,X,O,X,X,
O,X,X,O,O,
O,X,X,X,X,
O,X,O,O,O,
O,X,O,O,O,
},
.FIVE = [_]u21{
O,O,X,X,O,
O,X,O,O,X,
X,O,O,O,X,
X,O,X,O,X,
O,X,O,X,O,
},
.SIX = [_]u21{
O,X,X,O,O,
O,X,O,O,X,
O,O,X,O,O,
O,X,O,O,O,
X,O,O,O,O,
},
.SEVEN = [_]u21{
X,O,O,O,X,
X,O,O,O,X,
O,X,O,X,O,
O,X,O,X,O,
O,O,X,O,O,
},
.EIGHT = [_]u21{
O,O,O,X,O,
O,O,X,O,X,
O,O,X,O,X,
O,X,O,O,X,
O,X,O,O,X,
},
.NINE = [_]u21{
O,X,X,X,O,
O,X,O,X,O,
O,X,X,X,O,
O,O,O,X,O,
O,O,O,X,O,
},
.P = [_]u21{
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
},
.A = [_]u21{
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
},
.M = [_]u21{
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
},
.S = [_]u21{
O,O,O,O,O,
O,O,X,O,O,
O,O,O,O,O,
O,O,X,O,O,
O,O,O,O,O,
},
.E = [_]u21{
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
O,O,O,O,O,
},
};
// zig fmt: on

View file

@ -0,0 +1,172 @@
const std = @import("std");
const Cell = @import("../Cell.zig");
const keyboard = @import("../keyboard.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig");
const Position = @import("../Position.zig");
pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) type {
return struct {
const Allocator = std.mem.Allocator;
const ItemList = std.ArrayList(ItemType);
const DrawItemFn = *const fn (*Self, ItemType, usize, usize, usize) void;
const ChangeItemFn = *const fn (ItemType, ?ChangeItemType) void;
const Self = @This();
allocator: Allocator,
buffer: *TerminalBuffer,
list: ItemList,
current: usize,
width: usize,
component_pos: Position,
children_pos: Position,
text_in_center: bool,
fg: u32,
bg: u32,
cursor: usize,
draw_item_fn: DrawItemFn,
change_item_fn: ?ChangeItemFn,
change_item_arg: ?ChangeItemType,
keybinds: TerminalBuffer.KeybindMap,
pub fn init(
allocator: Allocator,
io: std.Io,
buffer: *TerminalBuffer,
draw_item_fn: DrawItemFn,
change_item_fn: ?ChangeItemFn,
change_item_arg: ?ChangeItemType,
width: usize,
text_in_center: bool,
fg: u32,
bg: u32,
) !*Self {
var self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.buffer = buffer,
.list = .empty,
.current = 0,
.width = width,
.component_pos = TerminalBuffer.START_POSITION,
.children_pos = TerminalBuffer.START_POSITION,
.text_in_center = text_in_center,
.fg = fg,
.bg = bg,
.cursor = 0,
.draw_item_fn = draw_item_fn,
.change_item_fn = change_item_fn,
.change_item_arg = change_item_arg,
.keybinds = .init(allocator),
};
try buffer.registerKeybind(io, &self.keybinds, "Left", &goLeft, self);
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+H", &goLeft, self);
try buffer.registerKeybind(io, &self.keybinds, "Right", &goRight, self);
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+L", &goRight, self);
return self;
}
pub fn deinit(self: *Self) void {
self.list.deinit(self.allocator);
self.keybinds.deinit();
self.allocator.destroy(self);
}
pub fn positionX(self: *Self, original_pos: Position) void {
self.component_pos = original_pos;
self.cursor = self.component_pos.x + 2;
self.children_pos = original_pos.addX(self.width);
}
pub fn positionY(self: *Self, original_pos: Position) void {
self.component_pos = original_pos;
self.cursor = self.component_pos.x + 2;
self.children_pos = original_pos.addY(1);
}
pub fn positionXY(self: *Self, original_pos: Position) void {
self.component_pos = original_pos;
self.cursor = self.component_pos.x + 2;
self.children_pos = Position.init(
self.width,
1,
).add(original_pos);
}
pub fn childrenPosition(self: Self) Position {
return self.children_pos;
}
pub fn addItem(self: *Self, item: ItemType) !void {
try self.list.append(self.allocator, item);
self.current = self.list.items.len - 1;
}
pub fn handle(self: *Self, _: ?keyboard.Key) !void {
try TerminalBuffer.setCursor(
self.component_pos.x + self.cursor + 2,
self.component_pos.y,
);
}
pub fn draw(self: *Self) void {
if (self.list.items.len == 0) return;
if (self.width < 2) return;
var left_arrow = Cell.init('<', self.fg, self.bg);
var right_arrow = Cell.init('>', self.fg, self.bg);
left_arrow.put(self.component_pos.x, self.component_pos.y) catch {};
right_arrow.put(
self.component_pos.x + self.width - 1,
self.component_pos.y,
) catch {};
const current_item = self.list.items[self.current];
const x = self.component_pos.x + 2;
const y = self.component_pos.y;
const width = self.width - 2;
@call(
.auto,
self.draw_item_fn,
.{ self, current_item, x, y, width },
);
}
fn goLeft(ptr: *anyopaque) !bool {
var self: *Self = @ptrCast(@alignCast(ptr));
self.current = if (self.current == 0) self.list.items.len - 1 else self.current - 1;
if (self.change_item_fn) |change_item_fn| {
@call(
.auto,
change_item_fn,
.{ self.list.items[self.current], self.change_item_arg },
);
}
return false;
}
fn goRight(ptr: *anyopaque) !bool {
var self: *Self = @ptrCast(@alignCast(ptr));
self.current = if (self.current == self.list.items.len - 1) 0 else self.current + 1;
if (self.change_item_fn) |change_item_fn| {
@call(
.auto,
change_item_fn,
.{ self.list.items[self.current], self.change_item_arg },
);
}
return false;
}
};
}

717
ly-ui/src/keyboard.zig Normal file
View file

@ -0,0 +1,717 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const KeyList = std.ArrayList(Key);
const TerminalBuffer = @import("TerminalBuffer.zig");
const termbox = TerminalBuffer.termbox;
pub const Key = packed struct {
ctrl: bool,
shift: bool,
alt: bool,
f1: bool,
f2: bool,
f3: bool,
f4: bool,
f5: bool,
f6: bool,
f7: bool,
f8: bool,
f9: bool,
f10: bool,
f11: bool,
f12: bool,
insert: bool,
delete: bool,
home: bool,
end: bool,
pageup: bool,
pagedown: bool,
up: bool,
down: bool,
left: bool,
right: bool,
tab: bool,
backspace: bool,
enter: bool,
@" ": bool,
@"!": bool,
@"`": bool,
esc: bool,
@"[": bool,
@"\\": bool,
@"]": bool,
@"/": bool,
_: bool,
@"'": bool,
@"\"": bool,
@",": bool,
@"-": bool,
@".": bool,
@"#": bool,
@"$": bool,
@"%": bool,
@"&": bool,
@"*": bool,
@"(": bool,
@")": bool,
@"+": bool,
@"=": bool,
@":": bool,
@";": bool,
@"<": bool,
@">": bool,
@"?": bool,
@"@": bool,
@"^": bool,
@"~": bool,
@"{": bool,
@"}": bool,
@"|": bool,
@"0": bool,
@"1": bool,
@"2": bool,
@"3": bool,
@"4": bool,
@"5": bool,
@"6": bool,
@"7": bool,
@"8": bool,
@"9": bool,
a: bool,
b: bool,
c: bool,
d: bool,
e: bool,
f: bool,
g: bool,
h: bool,
i: bool,
j: bool,
k: bool,
l: bool,
m: bool,
n: bool,
o: bool,
p: bool,
q: bool,
r: bool,
s: bool,
t: bool,
u: bool,
v: bool,
w: bool,
x: bool,
y: bool,
z: bool,
pub fn getEnabledPrintableAscii(self: Key) ?u8 {
if (self.ctrl or self.alt) return null;
inline for (comptime std.meta.fieldNames(Key)) |name| {
if (name.len == 1 and std.ascii.isPrint(name[0]) and @field(self, name)) {
if (self.shift) {
if (!std.ascii.isAlphanumeric(name[0])) return null;
return std.ascii.toUpper(name[0]);
}
return name[0];
}
}
return null;
}
};
pub fn getKeyList(allocator: Allocator, tb_event: termbox.tb_event) !KeyList {
var keys: KeyList = .empty;
var key = std.mem.zeroes(Key);
if (tb_event.mod & termbox.TB_MOD_CTRL != 0) key.ctrl = true;
if (tb_event.mod & termbox.TB_MOD_SHIFT != 0) key.shift = true;
if (tb_event.mod & termbox.TB_MOD_ALT != 0) key.alt = true;
if (tb_event.key == termbox.TB_KEY_BACK_TAB) {
key.shift = true;
key.tab = true;
} else if (tb_event.key > termbox.TB_KEY_BACK_TAB) {
const code = 0xFFFF - tb_event.key;
switch (code) {
0 => key.f1 = true,
1 => key.f2 = true,
2 => key.f3 = true,
3 => key.f4 = true,
4 => key.f5 = true,
5 => key.f6 = true,
6 => key.f7 = true,
7 => key.f8 = true,
8 => key.f9 = true,
9 => key.f10 = true,
10 => key.f11 = true,
11 => key.f12 = true,
12 => key.insert = true,
13 => key.delete = true,
14 => key.home = true,
15 => key.end = true,
16 => key.pageup = true,
17 => key.pagedown = true,
18 => key.up = true,
19 => key.down = true,
20 => key.left = true,
21 => key.right = true,
else => {},
}
if (code >= 18 and code <= 21) {
// https://github.com/termbox/termbox2/blob/605398fa79108412976191e062ea14bd4bd30213/termbox2.h#L446
key.ctrl = false;
key.shift = false;
}
} else if (tb_event.ch < 128) {
const code = if (tb_event.ch == 0 and tb_event.key < 128) tb_event.key else tb_event.ch;
switch (code) {
// Non-standard control codes
0 => {
key.ctrl = true;
key.@"2" = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key.@"`" = true;
},
1 => {
key.ctrl = true;
key.a = true;
},
2 => {
key.ctrl = true;
key.b = true;
},
3 => {
key.ctrl = true;
key.c = true;
},
4 => {
key.ctrl = true;
key.d = true;
},
5 => {
key.ctrl = true;
key.e = true;
},
6 => {
key.ctrl = true;
key.f = true;
},
7 => {
key.ctrl = true;
key.g = true;
},
8 => {
key.ctrl = true;
key.h = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key.backspace = true;
},
9 => {
key.ctrl = true;
key.i = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key.tab = true;
},
10 => {
key.ctrl = true;
key.j = true;
},
11 => {
key.ctrl = true;
key.k = true;
},
12 => {
key.ctrl = true;
key.l = true;
},
13 => {
key.ctrl = true;
key.m = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key.enter = true;
},
14 => {
key.ctrl = true;
key.n = true;
},
15 => {
key.ctrl = true;
key.o = true;
},
16 => {
key.ctrl = true;
key.p = true;
},
17 => {
key.ctrl = true;
key.q = true;
},
18 => {
key.ctrl = true;
key.r = true;
},
19 => {
key.ctrl = true;
key.s = true;
},
20 => {
key.ctrl = true;
key.t = true;
},
21 => {
key.ctrl = true;
key.u = true;
},
22 => {
key.ctrl = true;
key.v = true;
},
23 => {
key.ctrl = true;
key.w = true;
},
24 => {
key.ctrl = true;
key.x = true;
},
25 => {
key.ctrl = true;
key.y = true;
},
26 => {
key.ctrl = true;
key.z = true;
},
27 => {
key.ctrl = true;
key.@"3" = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key.esc = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key.@"[" = true;
},
28 => {
key.ctrl = true;
key.@"4" = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key.@"\\" = true;
},
29 => {
key.ctrl = true;
key.@"5" = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key.@"]" = true;
},
30 => {
key.ctrl = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key.@"6" = true;
},
31 => {
key.ctrl = true;
key.@"7" = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key.@"/" = true;
try keys.append(allocator, key);
key = std.mem.zeroes(Key);
key._ = true;
},
// Standard ASCII characters
32 => {
key = std.mem.zeroes(Key);
key.@" " = true;
},
33 => {
key = std.mem.zeroes(Key);
key.@"!" = true;
},
34 => {
key = std.mem.zeroes(Key);
key.@"\"" = true;
},
35 => {
key = std.mem.zeroes(Key);
key.@"#" = true;
},
36 => {
key = std.mem.zeroes(Key);
key.@"$" = true;
},
37 => {
key = std.mem.zeroes(Key);
key.@"%" = true;
},
38 => {
key = std.mem.zeroes(Key);
key.@"&" = true;
},
39 => {
key = std.mem.zeroes(Key);
key.@"'" = true;
},
40 => {
key = std.mem.zeroes(Key);
key.@"(" = true;
},
41 => {
key = std.mem.zeroes(Key);
key.@")" = true;
},
42 => {
key = std.mem.zeroes(Key);
key.@"*" = true;
},
43 => {
key = std.mem.zeroes(Key);
key.@"+" = true;
},
44 => {
key = std.mem.zeroes(Key);
key.@"," = true;
},
45 => {
key = std.mem.zeroes(Key);
key.@"-" = true;
},
46 => {
key = std.mem.zeroes(Key);
key.@"." = true;
},
47 => {
key = std.mem.zeroes(Key);
key.@"/" = true;
},
48 => {
key = std.mem.zeroes(Key);
key.@"0" = true;
},
49 => {
key = std.mem.zeroes(Key);
key.@"1" = true;
},
50 => {
key = std.mem.zeroes(Key);
key.@"2" = true;
},
51 => {
key = std.mem.zeroes(Key);
key.@"3" = true;
},
52 => {
key = std.mem.zeroes(Key);
key.@"4" = true;
},
53 => {
key = std.mem.zeroes(Key);
key.@"5" = true;
},
54 => {
key = std.mem.zeroes(Key);
key.@"6" = true;
},
55 => {
key = std.mem.zeroes(Key);
key.@"7" = true;
},
56 => {
key = std.mem.zeroes(Key);
key.@"8" = true;
},
57 => {
key = std.mem.zeroes(Key);
key.@"9" = true;
},
58 => {
key = std.mem.zeroes(Key);
key.@":" = true;
},
59 => {
key = std.mem.zeroes(Key);
key.@";" = true;
},
60 => {
key = std.mem.zeroes(Key);
key.@"<" = true;
},
61 => {
key = std.mem.zeroes(Key);
key.@"=" = true;
},
62 => {
key = std.mem.zeroes(Key);
key.@">" = true;
},
63 => {
key = std.mem.zeroes(Key);
key.@"?" = true;
},
64 => {
key = std.mem.zeroes(Key);
key.@"@" = true;
},
65 => {
key.shift = true;
key.a = true;
},
66 => {
key.shift = true;
key.b = true;
},
67 => {
key.shift = true;
key.c = true;
},
68 => {
key.shift = true;
key.d = true;
},
69 => {
key.shift = true;
key.e = true;
},
70 => {
key.shift = true;
key.f = true;
},
71 => {
key.shift = true;
key.g = true;
},
72 => {
key.shift = true;
key.h = true;
},
73 => {
key.shift = true;
key.i = true;
},
74 => {
key.shift = true;
key.j = true;
},
75 => {
key.shift = true;
key.k = true;
},
76 => {
key.shift = true;
key.l = true;
},
77 => {
key.shift = true;
key.m = true;
},
78 => {
key.shift = true;
key.n = true;
},
79 => {
key.shift = true;
key.o = true;
},
80 => {
key.shift = true;
key.p = true;
},
81 => {
key.shift = true;
key.q = true;
},
82 => {
key.shift = true;
key.r = true;
},
83 => {
key.shift = true;
key.s = true;
},
84 => {
key.shift = true;
key.t = true;
},
85 => {
key.shift = true;
key.u = true;
},
86 => {
key.shift = true;
key.v = true;
},
87 => {
key.shift = true;
key.w = true;
},
88 => {
key.shift = true;
key.x = true;
},
89 => {
key.shift = true;
key.y = true;
},
90 => {
key.shift = true;
key.z = true;
},
91 => {
key = std.mem.zeroes(Key);
key.@"[" = true;
},
92 => {
key = std.mem.zeroes(Key);
key.@"\\" = true;
},
93 => {
key = std.mem.zeroes(Key);
key.@"]" = true;
},
94 => {
key = std.mem.zeroes(Key);
key.@"^" = true;
},
95 => {
key = std.mem.zeroes(Key);
key._ = true;
},
96 => {
key = std.mem.zeroes(Key);
key.@"`" = true;
},
97 => {
key.a = true;
},
98 => {
key.b = true;
},
99 => {
key.c = true;
},
100 => {
key.d = true;
},
101 => {
key.e = true;
},
102 => {
key.f = true;
},
103 => {
key.g = true;
},
104 => {
key.h = true;
},
105 => {
key.i = true;
},
106 => {
key.j = true;
},
107 => {
key.k = true;
},
108 => {
key.l = true;
},
109 => {
key.m = true;
},
110 => {
key.n = true;
},
111 => {
key.o = true;
},
112 => {
key.p = true;
},
113 => {
key.q = true;
},
114 => {
key.r = true;
},
115 => {
key.s = true;
},
116 => {
key.t = true;
},
117 => {
key.u = true;
},
118 => {
key.v = true;
},
119 => {
key.w = true;
},
120 => {
key.x = true;
},
121 => {
key.y = true;
},
122 => {
key.z = true;
},
123 => {
key.@"{" = true;
},
124 => {
key = std.mem.zeroes(Key);
key.@"|" = true;
},
125 => {
key = std.mem.zeroes(Key);
key.@"}" = true;
},
126 => {
key = std.mem.zeroes(Key);
key.@"~" = true;
},
127 => {
key = std.mem.zeroes(Key);
key.backspace = true;
},
else => {},
}
}
try keys.append(allocator, key);
return keys;
}

13
ly-ui/src/root.zig Normal file
View file

@ -0,0 +1,13 @@
pub const ly_core = @import("ly-core");
pub const Cell = @import("Cell.zig");
pub const keyboard = @import("keyboard.zig");
pub const Position = @import("Position.zig");
pub const TerminalBuffer = @import("TerminalBuffer.zig");
pub const Widget = @import("Widget.zig");
pub const BigLabel = @import("components/BigLabel.zig");
pub const Box = @import("components/Box.zig");
pub const CyclableLabel = @import("components/generic.zig").CyclableLabel;
pub const Label = @import("components/Label.zig");
pub const Text = @import("components/Text.zig");

347
readme.md
View file

@ -1,207 +1,294 @@
# The Ly display manager
# Ly - a TUI display manager
![Ly screenshot](https://user-images.githubusercontent.com/5473047/88958888-65efbf80-d2a1-11ea-8ae5-3f263bce9cce.png "Ly screenshot")
![Ly screenshot](.github/screenshot.png "Ly screenshot")
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
_Note: the above animation can be found [here](https://codeberg.org/fairyglade/ly-community/src/branch/main/animations/dur/blackhole-smooth-240x67.dur)!_
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD, designed with portability in mind and doesn't require systemd to run.
Join us on Matrix over at [#ly-dm:matrix.org](https://matrix.to/#/#ly-dm:matrix.org)!
> [!NOTE]
> Development happens on [Codeberg](https://codeberg.org/fairyglade/ly) with a mirror on [GitHub](https://github.com/fairyglade/ly).
## Dependencies
- Compile-time:
- zig 0.12.0
- a C standard library
- zig 0.16.x (you must use a **release version** of zig; check that `zig version` does not have a `-dev*` suffix)
- libc
- pam
- xcb
- xcb (optional, required by default; needed for X11 support)
- Runtime (with default config):
- xorg
- xorg-xauth
- mcookie
- tput
- shutdown
- brightnessctl
### Debian
```
# apt install build-essential libpam0g-dev libxcb-xkb-dev
# apt install build-essential libpam0g-dev libxcb-xkb-dev xauth xserver-xorg brightnessctl
```
### Fedora
**Warning**: You may encounter issues with SELinux on Fedora.
It is recommended to add a rule for Ly as it currently does not ship one.
```
# dnf install kernel-devel pam-devel libxcb-devel
# dnf install kernel-devel pam-devel libxcb-devel zig xorg-x11-xauth xorg-x11-server brightnessctl
```
> [!WARNING]
> Distributions using SELinux such as Fedora and openSUSE Tumbleweed may encounter issues. If you encounter such issues, such as session launch failures, proceed with the following steps:
>
> ```
> # ausearch -m avc -ts recent
> ```
>
> If SELinux is denying process context transition, you will see this output:
>
> ```
> denied { transition } for pid=XXXX comm="ly" path="/usr/bin/bash"
> scontext=system_u:system_r:unconfined_service_t:s0
> tcontext=unconfined_u:unconfined_r:unconfined_t:s0
> tclass=process permissive=0
> ```
>
> Pipe this output into `audit2allow` to generate a security module package. This will persist regardless of changes to filesystem permissions:
>
> ```
> # ausearch -m avc -ts recent | audit2allow -M ly-local
> ```
>
> ```
> # semodule -i ly-local.pp
> ```
>
> _This fix has been confirmed on Fedora 39 and 44 and openSUSE Tumbleweed. (#494)_
### FreeBSD
```
# pkg install ca_root_nss libxcb git xorg xauth
```
## Availability
[![Packaging status](https://repology.org/badge/vertical-allrepos/ly-display-manager.svg?exclude_unsupported=1)](https://repology.org/project/ly-display-manager/versions)
## Custom animations & user scripts
If you want to pimp your Ly installation even further than what the traditional configuration file allows you to, a [community repository](https://codeberg.org/fairyglade/ly-community) exists containing custom animations & scripts, so go check it out!
## Support
The following desktop environments were tested with success:
- awesome
- bspwm
- budgie
- cinnamon
- deepin
- dwl
- dwm
- enlightenment
- gnome
- i3
- kde
- labwc
- lxde
- lxqt
- mate
- maxx
- pantheon
- qtile
- spectrwm
- sway
- windowmaker
- xfce
- xmonad
Ly should work with any X desktop environment, and provides
basic wayland support (sway works very well, for example).
Every environment that works on other login managers also should work on Ly.
## systemd?
Unlike what you may have heard, Ly does not require `systemd`,
and was even specifically designed not to depend on `logind`.
You should be able to make it work easily with a better init,
changing the source code won't be necessary :)
- Unlike most login managers Ly has an xinitrc and shell entry.
- If you installed your favorite environment and you don't see it, that's because Ly doesn't automatically refresh itself. To fix this you should restart Ly service (depends on your init system) or the easy way is to reboot your system.
- If your environment is still missing then check at `/usr/share/xsessions` or `/usr/share/wayland-sessions` to see if a .desktop file is present.
- If there isn't a .desktop file then create a new one at `/etc/ly/custom-sessions` that launches your favorite environment. These .desktop files can be only seen by Ly and if you want them system-wide you also can create at those directories instead.
- If Xorg sessions don't work then check if your distro compiles Ly with Xorg.
Logs are defined by `/etc/ly/config.ini`:
- The session log is located at `~/.local/state/ly-session.log` by default.
- The system log is located at `/var/log/ly.log` by default.
## Manually building
The procedure for manually building Ly is pretty standard:
## Cloning and Compiling
Clone the repository
```
$ git clone https://github.com/fairyglade/ly
```
Change the directory to ly
```
$ git clone https://codeberg.org/fairyglade/ly.git
$ cd ly
```
Compile
```
$ zig build
```
Test in the configured tty (tty2 by default)
or a terminal emulator (but desktop environments won't start)
After building, you can (optionally) test Ly in a terminal emulator, although authentication will **not** work:
```
# zig build run
$ zig build run
```
Install Ly and the provided systemd service file
> [!IMPORTANT]
> While you can run Ly in a terminal emulator as root, it is **not** recommended. If you want to test Ly, please enable its service (as described below) and reboot your machine.
> [!NOTE]
> You can, however, test your configuration file changes like that. Note that you must do Ctrl+C in order to exit Ly.
The next sections will explain how to use Ly with a variety of init systems. Detailed explanation is only given for systemd, but should be applicable for all.
> [!NOTE]
> All following sections will assume you are using LightDM for convenience sake.
### systemd
Now, you can install Ly on your system:
```
# zig build installsystemd
# zig build installexe -Dinit_system=systemd
```
Enable the service
> [!NOTE]
> The `init_system` parameter is optional and defaults to `systemd`.
Note that you also need to disable your current display manager. For example, if you are using LightDM, you can execute the following command:
```
# systemctl enable ly.service
# systemctl disable lightdm.service
```
If you need to switch between ttys after Ly's start you also have to
disable getty on Ly's tty to prevent "login" from spawning on top of it
Then, similarly to the previous command, you need to enable the Ly service:
```
# systemctl enable ly@tty2.service
```
> [!IMPORTANT]
> Because Ly runs in a TTY, you **must** disable the TTY service that Ly will run on, otherwise bad things will happen. For example, to disable `getty` spawning on TTY 2, you need to execute the following command:
```
# systemctl disable getty@tty2.service
```
On non-systemd systems, you can change the TTY Ly will run on by editing the corresponding service file for your platform.
### OpenRC
**NOTE**: On Gentoo, Ly will disable the `display-manager-init` service in order to run.
Clone, compile and test.
Install Ly and the provided OpenRC service
```
# zig build installopenrc
```
Enable the service
```
# zig build installexe -Dinit_system=openrc
# rc-update del lightdm
# rc-update add ly
```
You can edit which tty Ly will start on by editing the `tty` option in the configuration file.
If you choose a tty that already has a login/getty running (has a basic login prompt),
then you have to disable getty, so it doesn't respawn on top of ly
```
# rc-update del agetty.tty2
```
> [!NOTE]
> On Gentoo, Alpine and potentially others, you also **must** comment out the appropriate line for the TTY in /etc/inittab.
### runit
```
# zig build installrunit
# zig build installexe -Dinit_system=runit
# rm /var/service/lightdm
# ln -s /etc/sv/ly /var/service/
```
By default, ly will run on tty2. To change the tty it must be set in `/etc/ly/config.ini`
You should as well disable your existing display manager service if needed, e.g.:
```
# rm /var/service/lxdm
```
The agetty service for the tty console where you are running ly should be disabled.
For instance, if you are running ly on tty2 (that's the default, check your `/etc/ly/config.ini`)
you should disable the agetty-tty2 service like this:
```
# rm /var/service/agetty-tty2
```
### s6
```
# zig build installexe -Dinit_system=s6
# s6-rc -d change lightdm
# s6-service add default ly-srv
# s6-db-reload
# s6-rc -u change ly-srv
```
To disable TTY 2, edit `/etc/s6/config/tty2.conf` and set `SPAWN="no"`.
### dinit
```
# zig build installexe -Dinit_system=dinit
# dinitctl disable lightdm
# dinitctl enable ly
```
To disable TTY 2, go to `/etc/dinit.d/config/console.conf` and modify `ACTIVE_CONSOLES`.
### sysvinit
```
# zig build installexe -Dinit_system=sysvinit
# update-rc.d lightdm disable
# update-rc.d ly defaults
```
To disable TTY 2, go to `/etc/inittab` and comment out the line containing `tty2`.
### FreeBSD
```
# zig build installexe -Dprefix_directory=/usr/local -Dconfig_directory=/usr/local/etc -Dinit_system=freebsd
# sysrc lightdm_enable="NO"
```
To enable Ly, add the following entry to `/etc/gettytab`:
```
Ly:\
:lo=/usr/local/bin/ly_wrapper:\
:al=root:
```
Then, modify the command field of the `ttyv1` terminal entry in `/etc/ttys` (TTYs in FreeBSD start at 0):
```
ttyv1 "/usr/libexec/getty Ly" xterm on secure
```
### Updating
You can also install Ly without copying the system service and the configuration file. That's
called *updating*. To update, simply run:
You can also install Ly without overriding the current configuration file. This is called **updating**. To update, simply run:
```
# zig build installnoconf
```
If you want to also copy the default config file (but still not the system service), run:
```
# zig build installexe
```
## Arch Linux Installation
You can install ly from the [`[extra]` repos](https://archlinux.org/packages/extra/x86_64/ly/):
```
$ sudo pacman -S ly
```
You can, of course, still select the init system of your choice when using this command.
## Configuration
You can find all the configuration in `/etc/ly/config.ini`.
The file is commented, and includes the default values.
You can find all the configuration in `/etc/ly/config.ini`. The file is fully commented, and includes the default values.
You may also check the validity of your configuration file (i.e. if there are any errors in it) with the following command:
```
$ ly --validate-config /etc/ly/config.ini
```
## Controls
Use the up and down arrow keys to change the current field, and the
left and right arrow keys to change the target desktop environment
while on the desktop field (above the login field).
## .xinitrc
If your .xinitrc doesn't work make sure it is executable and includes a shebang.
This file is supposed to be a shell script! Quoting from xinit's man page:
Use the Up/Down arrow keys to change the current field, and the Left/Right arrow keys to scroll through the different fields (whether it be the info line, the desktop environment, or the username). The info line is where messages and errors are displayed.
## A note on .xinitrc
If your `.xinitrc` file doesn't work, make sure it is executable and includes a shebang. This file is supposed to be a shell script! Quoting from `xinit`'s man page:
> If no specific client program is given on the command line, xinit will look for a file in the user's home directory called .xinitrc to run as a shell script to start up client programs.
On Arch Linux, the example .xinitrc (/etc/X11/xinit/xinitrc) starts like this:
A typical shebang for a shell script looks like this:
```
#!/bin/sh
```
## Tips
The numlock and capslock state is printed in the top-right corner.
Use the F1 and F2 keys to respectively shutdown and reboot.
Take a look at your .xsession if X doesn't start, as it can interfere
(this file is launched with X to configure the display properly).
## PSX DOOM fire animation
To enable the famous PSX DOOM fire described by [Fabien Sanglard](http://fabiensanglard.net/doom_fire_psx/index.html),
just uncomment `animate = true` in `/etc/ly/config.ini`. You may also
disable the main box borders with `hide_borders = true`.
- The numlock and capslock states are printed in the top-right corner.
## Additional Information
The name "Ly" is a tribute to the fairy from the game Rayman.
Ly was tested by oxodao, who is some seriously awesome dude.
- Use the F1 and F2 keys to respectively shutdown and reboot.
## Gentoo (OpenRC) installation tip
To avoid a console spawning on top on Ly, comment out the appropriate line from /etc/inittab (default is 2).
- Take a look at your `.xsession` file if X doesn't start, as it can interfere (this file is launched with X to configure the display properly).
## A final note
The name "Ly" is a tribute to the fairy from the game Rayman. Ly was tested by oxodao, who is some seriously awesome dude.
Also, Ly wouldn't be there today without [ashametrine](https://github.com/ashametrine), who has done significant contributions to the project for the Zig rewrite, which lead to the release of Ly v1.0.0. Massive thanks, and sorry for not crediting you enough beforehand!
### Donate
If you like Ly and wish to support my work further, feel free to donate via my
[Liberapay link](https://liberapay.com/ShiningLea)!

View file

@ -1,153 +1,451 @@
# Ly supports 24-bit true color with styling, which means each color is a 32-bit value.
# The format is 0xSSRRGGBB, where SS is the styling, RR is red, GG is green, and BB is blue.
# Here are the possible styling options:
# TB_BOLD 0x01000000
# TB_UNDERLINE 0x02000000
# TB_REVERSE 0x04000000
# TB_ITALIC 0x08000000
# TB_BLINK 0x10000000
# TB_HI_BLACK 0x20000000
# TB_BRIGHT 0x40000000
# TB_DIM 0x80000000
# Programmatically, you'd apply them using the bitwise OR operator (|), but because Ly's
# configuration doesn't support using it, you have to manually compute the color value.
# Note that, if you want to use the default color value of the terminal, you can use the
# special value 0x00000000. This means that, if you want to use black, you *must* use
# the styling option TB_HI_BLACK (the RGB values are ignored when using this option).
# Allow empty password or not when authenticating
allow_empty_password = true
# The active animation
# none -> Nothing (default)
# doom -> PSX DOOM fire
# matrix -> CMatrix
# none -> Nothing
# doom -> PSX DOOM fire
# matrix -> CMatrix
# colormix -> Color mixing shader
# gameoflife -> John Conway's Game of Life
# dur_file -> .dur file format (https://github.com/cmang/durdraw/tree/master)
# lua -> user-made animation written in LuaJIT
animation = none
# Format string for clock in top right corner (see strftime specification). Example: %c
clock = null
# Delay between each animation frame in milliseconds
animation_frame_delay = 5
# Enable/disable big clock
bigclock = false
# Stop the animation after some time
# 0 -> Run forever
# 1..2e12 -> Stop the animation after this many seconds
animation_timeout_sec = 0
# The character used to mask the password
# You can either type it directly as a UTF-8 character (like *), or use a UTF-32
# codepoint (for example 0x2022 for a bullet point)
# If null, the password will be hidden
# Note: you can use a # by escaping it like so: \#
asterisk = *
# Erase password input on failure
clear_password = false
# The number of failed authentications before a special animation is played... ;)
# If set to 0, the animation will never be played
auth_fails = 10
# Enable vi keybindings
vi_mode = false
# Automatic login configuration
# This feature allows Ly to automatically log in a user without password prompt.
# IMPORTANT: Both auto_login_user and auto_login_session must be set for this to work.
# Autologin only happens once at startup - it won't re-trigger after logout.
# The `fg` and `bg` color settings take a digit 0-8 corresponding to:
#define TB_DEFAULT 0x00
#define TB_BLACK 0x01
#define TB_RED 0x02
#define TB_GREEN 0x03
#define TB_YELLOW 0x04
#define TB_BLUE 0x05
#define TB_MAGENTA 0x06
#define TB_CYAN 0x07
#define TB_WHITE 0x08
#
# Setting both to zero makes `bg` black and `fg` white. To set the actual color palette you are encouraged to use another tool
# such as [mkinitcpio-colors](https://github.com/evanpurkhiser/mkinitcpio-colors). Note that the color palette defined with
# `mkinitcpio-colors` takes 16 colors (0-15), only values 0-8 are valid for `ly` config and these values do not correspond
# exactly. For instance, in defining palettes with `mkinitcpio-colors` the order is black, dark red, dark green, brown, dark
# blue, dark purple, dark cyan, light gray, dark gray, bright red, bright green, yellow, bright blue, bright purple, bright
# cyan, and white, indexed in that order 0 through 15. For example, the color defined for white (indexed at 15 in the mkinitcpio
# config) will be used by `ly` for `fg = 8`.
# PAM service name to use for automatic login
# The default service (ly-autologin) uses pam_permit to allow login without password
# The appropriate platform-specific PAM configuration (ly-autologin) will be used automatically
auto_login_service = ly-autologin
# Session name to launch automatically
# To find available session names, check the .desktop files in:
# - /usr/share/xsessions/ (for X11 sessions)
# - /usr/share/wayland-sessions/ (for Wayland sessions)
# Use the filename without .desktop extension, the Name field inside the file or the value of the DesktopNames field
# Examples: "i3", "sway", "gnome", "plasma", "xfce"
# If null, automatic login is disabled
auto_login_session = null
# Username to automatically log in
# Must be a valid user on the system
# If null, automatic login is disabled
auto_login_user = null
# Identifier for battery whose charge to display at top left
# Primary battery is usually BAT0 or BAT1
# If set to null, battery status won't be shown
# Unused on FreeBSD (a sysctl is used there)
battery_id = null
# Background color id
bg = 0
bg = 0x00000000
# Foreground color id
fg = 8
# Change the state and language of the big clock
# none -> Disabled (default)
# en -> English
# fa -> Farsi
bigclock = none
# Border color
border_fg = 8
# Set bigclock to 12-hour notation.
bigclock_12hr = false
# Set bigclock to show the seconds.
bigclock_seconds = false
# Blank main box background
# Setting to false will make it transparent
blank_box = true
# Border foreground color id
border_fg = 0x00FFFFFF
# Relative horizontal position from the end of the screen
# default: 0.5
box_position_h = 0.5
# Relative vertical position from the bottom of the screen
# default: 0.5
box_position_v = 0.5
# Title to show at the top of the main box
# If set to null, none will be shown
box_title = null
# Brightness decrease command
brightness_down_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q -n s 10%-
# Brightness decrease key combination
# If null, the keybind is disabled and isn't shown
brightness_down_key = F5
# Brightness increase command
brightness_up_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q -n s +10%
# Brightness increase key combination
# If null, the keybind is disabled and isn't shown
brightness_up_key = F6
# Erase password input on failure
clear_password = false
# Format string for clock in top right corner (see strftime specification). Example: %c
# If null, the clock won't be shown
clock = null
# CMatrix animation foreground color id
cmatrix_fg = 0x0000FF00
# CMatrix animation character string head color id
cmatrix_head_col = 0x01FFFFFF
# CMatrix animation minimum codepoint. It uses a 16-bit integer
# For Japanese characters for example, you can use 0x3000 here
cmatrix_min_codepoint = 0x21
# CMatrix animation maximum codepoint. It uses a 16-bit integer
# For Japanese characters for example, you can use 0x30FF here
cmatrix_max_codepoint = 0x7B
# Color mixing animation first color id
colormix_col1 = 0x00FF0000
# Color mixing animation second color id
colormix_col2 = 0x000000FF
# Color mixing animation third color id
colormix_col3 = 0x20000000
# Screen corners customization
# Keywords:
# shutdown -> Shutdown key
# restart -> Restart key
# britup -> Brightness up key
# britdown -> Brightness down key
# password -> Toggle password key
# clock -> Clock (format defined by 'clock' option)
# tty -> Active TTY number
# battery -> Battery percentage
# version -> Ly version string
# numlock -> Numlock state
# capslock -> Capslock state
# labels -> All custom info labels (lbl:)
# binds -> All custom keybind hints (cmd:)
# lbl:name -> Specific custom info label
# cmd:key -> Specific custom keybind hint
#
# If using a keyword that groups multiple labels into one (e.g. labels, binds),
# they'll be placed horizontally
#
# Also, the order defines the vertical stack (first item is at the edge)
# If items are separted by commas, they'll be placed horizontally
# It is possible to have both horizontal and vertical items on the same corner
# Bottom left
corner_bottom_left = version
# Bottom right
corner_bottom_right = labels
# Top left
corner_top_left = shutdown,restart,britup,britdown,password battery
# Top right
corner_top_right = clock numlock,capslock
# For custom binds: the horizontal limit in characters for each
# line of custom binds before moving on to the next.
# If null, defaults to the width of the terminal instead.
custom_bind_width = null
# Custom sessions directory
# You can specify multiple directories,
# e.g. $CONFIG_DIRECTORY/ly/custom-sessions:$PREFIX_DIRECTORY/share/custom-sessions
custom_sessions = $CONFIG_DIRECTORY/ly/custom-sessions
# Input box active by default on startup
# Available inputs: info_line, session, login, password
default_input = login
# DOOM animation fire height (1 thru 9)
doom_fire_height = 6
# DOOM animation fire spread (0 thru 4)
doom_fire_spread = 2
# DOOM animation custom top color (low intensity flames)
doom_top_color = 0x009F2707
# DOOM animation custom middle color (medium intensity flames)
doom_middle_color = 0x00C78F17
# DOOM animation custom bottom color (high intensity flames)
doom_bottom_color = 0x00FFFFFF
# Dur file path
dur_file_path = $CONFIG_DIRECTORY/ly/example.dur
# Dur file alignment
# The dur file can be aligned with a direction and centered easily with the flags below
# Available inputs: topleft, topcenter, topright, centerleft, center, centerright, bottomleft, bottomcenter, bottomright
dur_offset_alignment = center
# Dur offset x direction (value is added to the current position determined by alignment, negatives are supported)
dur_x_offset = 0
# Dur offset y direction (value is added to the current position determined by alignment, negatives are supported)
dur_y_offset = 0
# Set margin to the edges of the DM (useful for curved monitors)
edge_margin = 0
# Error background color id
error_bg = 0x00000000
# Error foreground color id
# Default is red and bold
error_fg = 0x01FF0000
# Foreground color id
fg = 0x00FFFFFF
# Render true colors (if supported)
# If false, output will be in eight-color mode
# All eight-color mode color codes:
# TB_DEFAULT 0x0000
# TB_BLACK 0x0001
# TB_RED 0x0002
# TB_GREEN 0x0003
# TB_YELLOW 0x0004
# TB_BLUE 0x0005
# TB_MAGENTA 0x0006
# TB_CYAN 0x0007
# TB_WHITE 0x0008
# If full color is off, the styling options still work. The colors are
# always 32-bit values with the styling in the most significant byte.
# Note: If using the dur_file animation option and the dur file's color range
# is saved as 256 with this option disabled, the file will not be drawn.
full_color = true
# Game of Life entropy interval (0 = disabled, >0 = add entropy every N generations)
# 0 -> Pure Conway's Game of Life (will eventually stabilize)
# 10 -> Add entropy every 10 generations (recommended for continuous activity)
# 50+ -> Less frequent entropy for more natural evolution
gameoflife_entropy_interval = 10
# Game of Life animation foreground color id
gameoflife_fg = 0x0000FF00
# Game of Life frame delay (lower = faster animation, higher = slower)
# 1-3 -> Very fast animation
# 6 -> Default smooth animation speed
# 10+ -> Slower, more contemplative speed
gameoflife_frame_delay = 6
# Game of Life initial cell density (0.0 to 1.0)
# 0.1 -> Sparse, minimal activity
# 0.4 -> Balanced activity (recommended)
# 0.7+ -> Dense, chaotic patterns
gameoflife_initial_density = 0.4
# Remove main box borders
hide_borders = false
# Main box margins
margin_box_h = 2
margin_box_v = 1
# Command executed when no input is detected for a certain time
# If null, no command will be executed
inactivity_cmd = null
# Executes a command after a certain amount of seconds
inactivity_delay = 0
# Initial text to show on the info line
# If set to null, the info line defaults to the hostname
initial_info_text = null
# Input boxes length
input_len = 34
# Max input sizes
max_desktop_len = 100
max_login_len = 255
max_password_len = 255
# Input box active by default on startup
# Available inputs: session, login, password
default_input = login
# Load the saved desktop and username
load = true
# Save the current desktop and login as defaults
save = true
# Deprecated - Will be removed in a future version
# New save files are now loaded from the same directory as the config
# Currently used to migrate old save files to the new version
# File in which to save and load the default desktop and login
save_file = /etc/ly/save
# Remove power management command hints
hide_key_hints = false
# Specifies the key used for shutdown (F1-F12)
shutdown_key = F1
# Specifies the key used for restart (F1-F12)
restart_key = F2
# Specifies the key used for sleep (F1-F12)
sleep_key = F3
# Command executed when pressing shutdown_key
shutdown_cmd = /sbin/shutdown -a now
# Command executed when pressing restart_key
restart_cmd = /sbin/shutdown -r now
# Command executed when pressing sleep key (can be null)
sleep_cmd = null
# Active language
# Available languages are found in /etc/ly/lang/
# Available languages are found in $CONFIG_DIRECTORY/ly/lang/
lang = en
# TTY in use
tty = 2
# Command executed when logging in
# If null, no command will be executed
# Important: the code itself must end with `exec "$@"` in order to launch the session!
# You can also set environment variables in there, they'll persist until logout
login_cmd = null
# Console path
console_dev = /dev/console
# Path for login.defs file (used for listing all local users on the system on
# Linux)
login_defs_path = /etc/login.defs
# Default path. If null, ly doesn't set a path.
path = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin
# Command executed when logging out
# If null, no command will be executed
# Important: the session will already be terminated when this command is executed, so
# no need to add `exec "$@"` at the end
logout_cmd = null
# Event timeout in milliseconds
min_refresh_delta = 5
# The file pointing to the Lua file to be used when using the Lua animation option
lua_animation_file = $CONFIG_DIRECTORY/ly/example.lua
# General log file path
# If null, syslog will be used instead
ly_log = /var/log/ly.log
# Main box horizontal margin
margin_box_h = 2
# Main box vertical margin
margin_box_v = 1
# Set numlock on/off at startup
numlock = false
# Default path
# If null, ly doesn't set a path
path = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Specifies the key combination used for restart
# If null, the keybind is disabled and isn't shown
restart_key = F2
# Absolute directory for the save file
# If null, current desktop & login won't be saved nor loaded
save_file_dir = $CONFIG_DIRECTORY/ly
# Service name (set to ly to use the provided pam config file)
service_name = ly
# Terminal reset command (tput is faster)
term_reset_cmd = /usr/bin/tput reset
# Session log file path
# This will contain stdout and stderr of Wayland sessions
# By default it's saved in the user's home directory
# Important: due to technical limitations, X11, shell sessions as well as
# launching session via KMSCON aren't supported, which means you won't get any
# logs from those sessions.
# If null, no session log will be created
session_log = .local/state/ly-session.log
# Terminal restore cursor command
term_restore_cursor_cmd = /usr/bin/tput cnorm
# Setup command
setup_cmd = $CONFIG_DIRECTORY/ly/setup.sh
# Cookie generator
mcookie_cmd = /usr/bin/mcookie
# Show the shell session in the session list
# If false, the shell session will be hidden
shell = true
# Wayland setup command
wayland_cmd = /etc/ly/wsetup.sh
# Specifies the key combination used for showing the password
# If null, the keybind is disabled and isn't shown
show_password_key = F7
# Specifies the key combination used for shutdown
# If null, the keybind is disabled and isn't shown
shutdown_key = F1
# Command executed when starting Ly (before the TTY is taken control of)
# See file at path below for an example of changing the default TTY colors
start_cmd = $CONFIG_DIRECTORY/ly/startup.sh
# Center the session name.
text_in_center = false
# If true, user will need to manually type username instead of selecting from the list
# of discovered users
type_username = false
# Default vi mode
# normal -> normal mode
# insert -> insert mode
vi_default_mode = normal
# Enable vi keybindings
vi_mode = false
# Wayland desktop environments
waylandsessions = /usr/share/wayland-sessions
# xinitrc (hidden if null)
xinitrc = ~/.xinitrc
# You can specify multiple directories,
# e.g. $PREFIX_DIRECTORY/share/wayland-sessions:$PREFIX_DIRECTORY/local/share/wayland-sessions
# If null, Wayland sessions will not be shown
waylandsessions = $PREFIX_DIRECTORY/share/wayland-sessions
# Xorg server command
x_cmd = /usr/bin/X
# Add the -quiet argument to hide startup logs from the server
x_cmd = $PREFIX_DIRECTORY/bin/X
# Xorg setup command
x_cmd_setup = /etc/ly/xsetup.sh
# Xorg virtual terminal number
# Mostly useful for FreeBSD where choosing the current TTY causes issues
# If null, the current TTY will be chosen
x_vt = null
# Xorg xauthority edition tool
xauth_cmd = /usr/bin/xauth
xauth_cmd = $PREFIX_DIRECTORY/bin/xauth
# xinitrc
# If null, the xinitrc session will be hidden
xinitrc = ~/.xinitrc
# Xorg desktop environments
xsessions = /usr/share/xsessions
# You can specify multiple directories,
# e.g. $PREFIX_DIRECTORY/share/xsessions:$PREFIX_DIRECTORY/local/share/xsessions
# If null, X11 sessions will not be shown
xsessions = $PREFIX_DIRECTORY/share/xsessions
# Custom Commands and Labels:
# The following examples below give an outline for setting up custom commands and labels.
# Unless specified as optional, an option is mandatory.
# Comments preceding with '##' are for documentation.
# Comments preceding with '#' comment out the example INI.
## Declare a command with the F8 binding.
#[cmd:F8]
## The name of the command to show up in Ly.
## Note: "$" in "$brightness_up" fetches the appropriate string from the specified locale file
## and is replaced with the value representing "brightness_up".
## You can see the list of keys in any locale file in $CONFIG_DIRECTORY/ly/lang.
#cmd = touch /tmp/ly.gaming
#name = custom command $brightness_up
## Declare a label with an ID. This ID should be unique across all labels.
#[lbl:kernel]
#cmd = uname -srn
## Optional, defaulting to 0.
## In frames, the time to re-run the command and update the label.
## If 0, only run once and do not refresh afterwards
#refresh = 0

View file

@ -0,0 +1,23 @@
A custom session is just a desktop entry file, like for X11 and Wayland
sessions. For example:
[Desktop Entry]
Name=Fish shell
Exec=$PREFIX_DIRECTORY/bin/fish
DesktopNames=null
Terminal=true
The DesktopNames value is optional and sets the XDG_SESSION_DESKTOP and
XDG_CURRENT_DESKTOP environment variables. If equal to null or if not present,
XDG_SESSION_DESKTOP and XDG_CURRENT_DESKTOP will not be set. Otherwise, the
syntax is the same as described in the Freedesktop Desktop Entry Specification.
The Terminal value specifies if standard output and standard error should be
redirected to the session log file found in Ly's configuration file. If set to
true, Ly will consider the program is going to run in a TTY, and thus will not
redirect standard output & error. It is optional and defaults to false.
Finally, do note that if the Terminal value is set to true, the
XDG_SESSION_TYPE environment variable will be set to "tty". Otherwise, it will
be set to "unspecified" (without quotes), which is behavior that at least
systemd recognizes (see pam_systemd's man page).

BIN
res/example.dur Normal file

Binary file not shown.

119
res/example.lua Normal file
View file

@ -0,0 +1,119 @@
-- [[
-- This is an example of using LuaJIT to create a custom animation in Ly, in this case
-- bouncing squares that change colors.
--
-- You are given the following `ly` table:
-- {
-- height: number -- The height of the terminal
-- width: number -- The width of the terminal
-- putCell(byte, fg, bg, x, y) -- Draw a cell.
-- All arguments to this function are integers, and
-- must be in the unsigned 32-bit integer range: 0 to 2^32-1.
-- If an argument cannot be converted to this range, it will throw
-- an error.
--
-- For reference, the XY coordinates (0,0) draw a cell on the top-left
-- of the terminal, where the positive-X axis moves right and the
-- positive-Y axis moves down.
--
-- For arguments fg and bg: they are colors in the format
-- 0xSSRRGGBB, where SS is for styling. See your
-- config.ini for more details.
--
-- For the byte argument, you may use string.byte to fill this argument.
--
-- putRect(byte, fg, bg, x, y, w, h) -- Draw a rectangle.
-- Arguments are the same as putCell except for w and h, which are also
-- unsigned integers. The rectangle will be drawn from the top-left, with
-- argument w extending it to the right and argument h extending downwards.
--
--
-- putLabel(str, fg, bg, x, y) -- Draw text in argument str. See putCell()
-- for info on the rest of the arguments.
--
-- clock() -- The time, in microseconds.
-- }
--
-- A function named `draw()` must be declared in the script. This is ran every
-- frame.
--
-- In addition to the base library, you are also given the following standard
-- libraries:
-- bit (A library exclusive to LuaJIT, see https://bitop.luajit.org/api.html)
-- math
-- string
-- table
--
-- The std libraries io and debug are NOT included.
--
-- ]]
-- You should probably copy FPS and FPS_COUNT into any future LuaJIT animations
-- you create.
local FPS_COUNT = 40
local function FPS()
return (1 / FPS_COUNT) * 1000000
end
local SQUARE_WIDTH = 10
local SQUARE_HEIGHT = 5
local SQUARE_COUNT = 25
local squares = {}
for i = 1, SQUARE_COUNT do
local vx = 1
local vy = 1
if math.random(1, 2) == 2 then vx = -vx end
if math.random(1, 2) == 2 then vy = -vy end
squares[#squares + 1] = {
x = math.random(1, ly.width - SQUARE_WIDTH),
y = math.random(1, ly.height - SQUARE_HEIGHT),
vx = vx,
vy = vy,
color = math.random(0xFFFFFF)
}
end
local timer = ly.clock()
local perf = ly.clock()
function draw()
-- Rather than progressing the animation by frame, do it based on
-- seconds, via ly.clock(). In this timeframe, you can update the animation
-- state.
-- DO NOT DRAW CELLS IN THIS TIMEFRAME. You will get flickering.
-- if this check passes, we can update the animation
if timer + FPS() < ly.clock() then
for i, v in ipairs(squares) do
v.x = v.x + v.vx
v.y = v.y + v.vy
if v.x == 0 then
v.vx = 1; v.color = math.random(0xFFFFFF)
end
if v.x + SQUARE_WIDTH >= ly.width - 1 then
v.vx = -1; v.color = math.random(0xFFFFFF)
end
if v.y == 0 then
v.vy = 1; v.color = math.random(0xFFFFFF)
end
if v.y + SQUARE_HEIGHT >= ly.height - 1 then
v.vy = -1; v.color = math.random(0xFFFFFF)
end
end
timer = ly.clock()
end
for i, v in ipairs(squares) do
ly.putRect(string.byte(' '), 0, v.color, v.x, v.y, SQUARE_WIDTH, SQUARE_HEIGHT)
end
local new_perf = ly.clock()
local str = "FT: " .. ((new_perf - perf) / 1000) .. "ms"
ly.putLabel(str, 0x00FFFFFF, 0, (ly.width / 2) - (string.len(str) / 2), ly.height - 1)
perf = new_perf
end

82
res/lang/ar.ini Normal file
View file

@ -0,0 +1,82 @@
authenticating = جاري المصادقة...
brightness_down = خفض السطوع
brightness_up = رفع السطوع
capslock = capslock
custom = مخصص
custom_info_err_output_long = الإخراج طويل جداً
custom_info_err_no_output = لا يوجد إخراج
custom_info_err_no_output_error = ، خطأ محتمل
err_alloc = فشل في تخصيص الذاكرة
err_args = تعذر تحليل وسيطات سطر الأوامر
err_autologin_session = لم يتم العثور على جلسة تسجيل الدخول التلقائي
err_bounds = out-of-bounds index
err_brightness_change = فشل في تغيير سطوع الشاشة
err_chdir = فشل في فتح مجلد المنزل
err_clock_too_long = نص الساعة طويل جداً
err_config = فشل في تفسير ملف الإعدادات
err_crawl = فشل الزحف في أدلة الجلسة
err_dgn_oob = رسالة سجل (Log)
err_domain = اسم نطاق غير صالح
err_empty_password = لا يُسمح بكلمة مرور فارغة
err_envlist = فشل في جلب قائمة المتغيرات البيئية
err_get_active_tty = فشل الحصول على tty النشط
err_hibernate = فشل تنفيذ أمر الإسبات
err_hostname = فشل في جلب اسم المضيف (Hostname)
err_inactivity = فشل تنفيذ أمر عدم النشاط
err_lock_state = فشل الحصول على حالة القفل
err_log = فشل فتح ملف السجل
err_mlock = فشل في تأمين ذاكرة كلمة المرور (mlock)
err_null = مؤشر فارغ (Null pointer)
err_numlock = فشل في ضبط Num Lock
err_pam = فشل في معاملة PAM
err_pam_abort = تم إلغاء معاملة PAM
err_pam_acct_expired = الحساب منتهي الصلاحية
err_pam_auth = خطأ في المصادقة (Authentication error)
err_pam_authinfo_unavail = فشل في الحصول على معلومات المستخدم
err_pam_authok_reqd = انتهت صلاحية رمز المصادقة (Token)
err_pam_buf = خطأ في ذاكرة التخزين المؤقت (Buffer)
err_pam_cred_err = فشل في تعيين بيانات الاعتماد (Credentials)
err_pam_cred_expired = بيانات الاعتماد منتهية الصلاحية
err_pam_cred_insufficient = بيانات الاعتماد غير كافية
err_pam_cred_unavail = فشل في الحصول على بيانات الاعتماد
err_pam_maxtries = تم بلوغ الحد الأقصى لمحاولات المصادقة
err_pam_perm_denied = تم رفض الوصول (Permission denied)
err_pam_session = خطأ في جلسة المستخدم (Session error)
err_pam_sys = خطأ في النظام (System error)
err_pam_user_unknown = المستخدم غير موجود
err_path = فشل في تعيين متغير PATH
err_perm_dir = فشل في تغيير المجلد الحالي
err_perm_group = فشل في تخفيض صلاحيات المجموعة (Group permissions)
err_perm_user = فشل في تخفيض صلاحيات المستخدم (User permissions)
err_pwnam = فشل في جلب معلومات المستخدم
err_sleep = فشل في تنفيذ أمر sleep
err_start = فشل تنفيذ أمر البدء
err_battery = فشل تحميل حالة البطارية
err_switch_tty = فشل تبديل tty
err_tty_ctrl = فشل في نقل تحكم الطرفية (TTY)
err_no_users = لم يتم العثور على مستخدمين
err_uid_range = فشل الحصول الديناميكي على نطاق uid
err_user_gid = فشل في تعيين معرّف المجموعة (GID) للمستخدم
err_user_init = فشل في تهيئة بيانات المستخدم
err_user_uid = فشل في تعيين معرّف المستخدم (UID)
err_xauth = فشل في تنفيذ أمر xauth
err_xcb_conn = فشل في الاتصال بمكتبة XCB
err_xsessions_dir = فشل في العثور على مجلد Xsessions
err_xsessions_open = فشل في فتح مجلد Xsessions
hibernate = إسبات
insert = ادخال
login = تسجيل الدخول
logout = تم تسجيل خروجك
no_x11_support = دعم x11 معطّل في وقت الترجمة
normal = عادي
numlock = numlock
other = اخر
password = كلمة السر
restart = اعادة التشغيل
shell = shell
shutdown = ايقاف التشغيل
sleep = وضع السكون
toggle_password = إظهار/إخفاء كلمة المرور
wayland = wayland
x11 = x11
xinitrc = xinitrc

82
res/lang/bg.ini Normal file
View file

@ -0,0 +1,82 @@
authenticating = удостоверяване...
brightness_down = намаляване на яркостта
brightness_up = увеличаване на яркостта
capslock = caps lock
custom = персонализирано
custom_info_err_output_long = резултатът е твърде дълъг
custom_info_err_no_output = няма резултат
custom_info_err_no_output_error = , възможна грешка
err_alloc = неуспешно заделяне на памет
err_args = неуспешен анализ на аргументите от командния ред
err_autologin_session = сесията за автоматично влизане не е намерена
err_bounds = индексът е извън границите
err_brightness_change = неуспешна промяна на яркостта
err_chdir = неуспешно отваряне на домашната папка
err_clock_too_long = низът на часовника е твърде дълъг
err_config = неуспешен анализ на конфигурационния файл
err_crawl = неуспешно обхождане на папките със сесии
err_dgn_oob = съобщение в дневника
err_domain = невалиден домейн
err_empty_password = не е позволена празна парола
err_envlist = неуспешно получаване на списъка с променливи на средата
err_get_active_tty = неуспешно откриване на активния TTY
err_hibernate = неуспешно изпълнение на командата за хибернация
err_hostname = неуспешно получаване на името на хоста
err_inactivity = неуспешно изпълнение на командата за неактивност
err_lock_state = неуспешно получаване на състоянието на заключване
err_log = неуспешно отваряне на файла с дневника
err_mlock = неуспешно заключване на паметта за паролата
err_null = нулев указател
err_numlock = неуспешно задаване на num lock
err_pam = неуспешна транзакция
err_pam_abort = прекратена транзакция
err_pam_acct_expired = изтекъл профил
err_pam_auth = грешка при удостоверяването
err_pam_authinfo_unavail = неуспешно получаване на информация за потребителя
err_pam_authok_reqd = изтекъл жетон
err_pam_buf = грешка в буфера на паметта
err_pam_cred_err = неуспешно задаване на удостоверения
err_pam_cred_expired = изтекли удостоверения
err_pam_cred_insufficient = недостатъчни удостоверения
err_pam_cred_unavail = неуспешно получаване на удостоверения
err_pam_maxtries = достигнат е максималният лимит на опитите
err_pam_perm_denied = достъпът е отказан
err_pam_session = грешка в сесията
err_pam_sys = системна грешка
err_pam_user_unknown = непознат потребител
err_path = неуспешно задаване на пътя
err_perm_dir = неуспешна смяна на текущата папка
err_perm_group = неуспешно понижаване на правата на групата
err_perm_user = неуспешно понижаване на правата на потребителя
err_pwnam = неуспешно получаване на информация за потребителя
err_sleep = неуспешно изпълнение на командата за заспиване
err_start = неуспешно изпълнение на командата за стартиране
err_battery = неуспешно зареждане на състоянието на батерията
err_switch_tty = неуспешна смяна на TTY
err_tty_ctrl = неуспешно прехвърляне на контрола над TTY
err_no_users = не са намерени потребители
err_uid_range = неуспешно динамично получаване на UID обхват
err_user_gid = неуспешно задаване на потребителския GID
err_user_init = неуспешна стартиране на потребителя
err_user_uid = неуспешно задаване на потребителския UID
err_xauth = неуспешна команда xauth
err_xcb_conn = неуспешна xcb връзка
err_xsessions_dir = папката със сесии не е намерена
err_xsessions_open = неуспешно отваряне на папката със сесии
hibernate = хибернация
insert = вмъкване
login = вход
logout = излизане
no_x11_support = поддръжката на x11 е изключена при компилирането
normal = нормално
numlock = num lock
other = друго
password = парола
restart = рестартиране
shell = обвивка
shutdown = изключване
sleep = заспиване
toggle_password = превключване на паролата
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,45 +1,82 @@
authenticating = autenticant...
brightness_down = abaixar brillantor
brightness_up = apujar brillantor
capslock = Bloq Majús
err_alloc = falla d'assignació de memòria
err_bounds = índex fora de límit
err_chdir = error al obrir carpeta home
err_console_dev = error al accedir a la consola
custom = personalitzat
custom_info_err_output_long = sortida massa llarga
custom_info_err_no_output = sense sortida
custom_info_err_no_output_error = , possible error
err_alloc = assignació de memòria fallida
err_args = no s'han pogut analitzar els arguments de la línia d'ordres
err_autologin_session = no s'ha trobat la sessió d'inici de sessió automàtic
err_bounds = índex fora de límits
err_brightness_change = error en canviar la brillantor
err_chdir = error en obrir la carpeta home
err_clock_too_long = la cadena del rellotge és massa llarga
err_config = no s'ha pogut analitzar el fitxer de configuració
err_crawl = no s'han pogut explorar els directoris de sessió
err_dgn_oob = missatge de registre
err_domain = domini invàlid
err_hostname = error al obtenir el nom del host
err_mlock = error al bloquejar la clau de memòria
err_empty_password = no es permet la contrasenya buida
err_envlist = error en obtenir l'envlist
err_get_active_tty = no s'ha pogut obtenir el tty actiu
err_hibernate = no s'ha pogut executar l'ordre d'hibernació
err_hostname = error en obtenir el nom de l'amfitrió
err_inactivity = no s'ha pogut executar l'ordre d'inactivitat
err_lock_state = no s'ha pogut obtenir l'estat de bloqueig
err_log = no s'ha pogut obrir el fitxer de registre
err_mlock = error en bloquejar la memòria de clau
err_null = punter nul
err_numlock = error en establir el Bloq num
err_pam = error en la transacció pam
err_pam_abort = transacció pam avortada
err_pam_acct_expired = compte expirat
err_pam_auth = error d'autenticació
err_pam_authinfo_unavail = error al obtenir informació de l'usuari
err_pam_authinfo_unavail = error en obtenir la informació de l'usuari
err_pam_authok_reqd = token expirat
err_pam_buf = error de la memòria intermitja
err_pam_cred_err = error al establir les credencials
err_pam_buf = error en la memòria intermèdia
err_pam_cred_err = error en establir les credencials
err_pam_cred_expired = credencials expirades
err_pam_cred_insufficient = credencials insuficients
err_pam_cred_unavail = error al obtenir credencials
err_pam_maxtries = s'ha assolit al màxim nombre d'intents
err_pam_cred_unavail = error en obtenir credencials
err_pam_maxtries = s'ha assolit al nombre màxim d'intents
err_pam_perm_denied = permís denegat
err_pam_session = error de sessió
err_pam_sys = error de sistema
err_pam_user_unknown = usuari desconegut
err_path = error al establir la ruta
err_perm_dir = error al canviar de directori actual
err_perm_group = error al degradar els permisos de grup
err_perm_user = error al degradar els permisos de l'usuari
err_pwnam = error al obtenir la informació de l'usuari
err_user_gid = error al establir el GID de l'usuari
err_user_init = error al inicialitzar usuari
err_user_uid = error al establir el UID de l'usuari
err_xsessions_dir = error al cercar la carpeta de sessions
err_xsessions_open = error al obrir la carpeta de sessions
err_path = error en establir la ruta
err_perm_dir = error en canviar el directori actual
err_perm_group = error en degradar els permisos de grup
err_perm_user = error en degradar els permisos de l'usuari
err_pwnam = error en obtenir la informació de l'usuari
err_sleep = no s'ha pogut executar l'ordre de suspensió
err_start = no s'ha pogut executar l'ordre d'inici
err_battery = no s'ha pogut carregar l'estat de la bateria
err_switch_tty = no s'ha pogut canviar de tty
err_tty_ctrl = ha fallat la transferència del control tty
err_no_users = no s'han trobat usuaris
err_uid_range = no s'ha pogut obtenir dinàmicament el rang d'uid
err_user_gid = error en establir el GID de l'usuari
err_user_init = error en inicialitzar usuari
err_user_uid = error en establir l'UID de l'usuari
err_xauth = error en la comanda xauth
err_xcb_conn = error en la connexió xcb
err_xsessions_dir = error en trobar la carpeta de sessions
err_xsessions_open = error en obrir la carpeta de sessions
hibernate = hibernar
insert = inserir
login = iniciar sessió
logout = tancar sessió
logout = sessió tancada
no_x11_support = suport x11 desactivat en temps de compilació
normal = normal
numlock = Bloq Num
other = altres
password = Clau
restart = reiniciar
shell = shell
shutdown = aturar
sleep = suspendre
toggle_password = mostrar/amagar contrasenya
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,13 +1,33 @@
authenticating = ověřování...
brightness_down = snížit jas
brightness_up = zvýšit jas
capslock = capslock
custom = vlastní
custom_info_err_output_long = výstup je příliš dlouhý
custom_info_err_no_output = žádný výstup
custom_info_err_no_output_error = , možná chyba
err_alloc = alokace paměti selhala
err_args = nelze analyzovat argumenty příkazového řádku
err_autologin_session = relace automatického přihlášení nebyla nalezena
err_bounds = index je mimo hranice pole
err_brightness_change = nepodařilo se změnit jas
err_chdir = nelze otevřít domovský adresář
err_console_dev = chyba při přístupu do konzole
err_clock_too_long = řetězec hodin je příliš dlouhý
err_config = nelze analyzovat konfigurační soubor
err_crawl = nepodařilo se prohledat adresáře relací
err_dgn_oob = zpráva protokolu
err_domain = neplatná doména
err_empty_password = prázdné heslo není povoleno
err_envlist = nepodařilo se získat seznam proměnných prostředí
err_get_active_tty = nepodařilo se získat aktivní tty
err_hibernate = nepodařilo se spustit příkaz hibernace
err_hostname = nelze získat název hostitele
err_inactivity = nepodařilo se spustit příkaz nečinnosti
err_lock_state = nepodařilo se získat stav zámku
err_log = nepodařilo se otevřít soubor protokolu
err_mlock = uzamčení paměti hesel selhalo
err_null = nulový ukazatel
err_numlock = nepodařilo se nastavit numlock
err_pam = pam transakce selhala
err_pam_abort = pam transakce přerušena
err_pam_acct_expired = platnost účtu vypršela
@ -29,17 +49,34 @@ err_perm_dir = nepodařilo se změnit adresář
err_perm_group = nepodařilo se snížit skupinová oprávnění
err_perm_user = nepodařilo se snížit uživatelská oprávnění
err_pwnam = nelze získat informace o uživateli
err_sleep = nepodařilo se spustit příkaz spánku
err_start = nepodařilo se spustit příkaz spuštění
err_battery = nepodařilo se načíst stav baterie
err_switch_tty = nepodařilo se přepnout tty
err_tty_ctrl = přenos řízení tty selhal
err_no_users = nebyli nalezeni žádní uživatelé
err_uid_range = nepodařilo se dynamicky získat rozsah uid
err_user_gid = nastavení GID uživatele selhalo
err_user_init = inicializace uživatele selhala
err_user_uid = nastavení UID uživateli selhalo
err_xauth = příkaz xauth selhal
err_xcb_conn = připojení xcb selhalo
err_xsessions_dir = nepodařilo se najít složku relací
err_xsessions_open = nepodařilo se otevřít složku relací
hibernate = hibernace
insert = vložit
login = uživatel
logout = odhlášen
no_x11_support = podpora x11 zakázána při kompilaci
normal = normální
numlock = numlock
other = jiné
password = heslo
restart = restartovat
shell = příkazový řádek
shutdown = vypnout
sleep = uspat
toggle_password = zobrazit/skrýt heslo
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,45 +1,82 @@
authenticating = authentifizieren...
brightness_down = Helligkeit-
brightness_up = Helligkeit+
capslock = Feststelltaste
custom = benutzerdefiniert
custom_info_err_output_long = Ausgabe zu lang
custom_info_err_no_output = keine Ausgabe
custom_info_err_no_output_error = , möglicher Fehler
err_alloc = Speicherzuweisung fehlgeschlagen
err_bounds = Listenindex ist außerhalb des Bereichs
err_chdir = Fehler beim oeffnen des home-ordners
err_console_dev = Zugriff auf die Konsole fehlgeschlagen
err_dgn_oob = Protokoll Nachricht
err_domain = Unzulaessige domain
err_hostname = Holen des Hostnames fehlgeschlagen
err_mlock = Abschließen des Passwortspeichers fehlgeschlagen
err_null = Null Zeiger
err_pam = pam Transaktion fehlgeschlagen
err_pam_abort = pam Transaktion abgebrochen
err_args = Kommandozeilenargumente konnten nicht verarbeitet werden
err_autologin_session = Autologin-Sitzung nicht gefunden
err_bounds = Index ausserhalb des Bereichs
err_brightness_change = Helligkeitsänderung fehlgeschlagen
err_chdir = Fehler beim Oeffnen des Home-Ordners
err_clock_too_long = Uhrzeitzeichenkette zu lang
err_config = Fehler beim Verarbeiten der Konfigurationsdatei
err_crawl = Sitzungsverzeichnisse konnten nicht durchsucht werden
err_dgn_oob = Diagnose-Nachricht
err_domain = Ungueltige Domain
err_empty_password = Leeres Passwort nicht zugelassen
err_envlist = Fehler beim Abrufen der Umgebungs-Variablen
err_get_active_tty = Aktives tty konnte nicht ermittelt werden
err_hibernate = Ruhezustand-Befehl konnte nicht ausgeführt werden
err_hostname = Abrufen des Hostnames fehlgeschlagen
err_inactivity = Inaktivitätsbefehl konnte nicht ausgeführt werden
err_lock_state = Sperrstatus konnte nicht ermittelt werden
err_log = Protokolldatei konnte nicht geöffnet werden
err_mlock = Sperren des Passwortspeichers fehlgeschlagen
err_null = Null Pointer
err_numlock = Numlock konnte nicht aktiviert werden
err_pam = PAM-Transaktion fehlgeschlagen
err_pam_abort = PAM-Transaktion abgebrochen
err_pam_acct_expired = Benutzerkonto abgelaufen
err_pam_auth = Authentifizierungs Fehler
err_pam_authinfo_unavail = holen der Benutzerinformationen fehlgeschlagen
err_pam_authok_reqd = Schluessel abgelaufen
err_pam_auth = Authentifizierungsfehler
err_pam_authinfo_unavail = Abrufen der Benutzerinformationen fehlgeschlagen
err_pam_authok_reqd = Passwort abgelaufen
err_pam_buf = Speicherpufferfehler
err_pam_cred_err = Fehler beim setzen der Anmeldedaten
err_pam_cred_err = Fehler beim Setzen der Anmeldedaten
err_pam_cred_expired = Anmeldedaten abgelaufen
err_pam_cred_insufficient = Anmeldedaten unzureichend
err_pam_cred_unavail = Fehler beim holen der Anmeldedaten
err_pam_maxtries = Maximale Versuche erreicht
err_pam_perm_denied = Zugriff Verweigert
err_pam_cred_unavail = Fehler beim Abrufen der Anmeldedaten
err_pam_maxtries = Maximale Versuchsanzahl erreicht
err_pam_perm_denied = Zugriff verweigert
err_pam_session = Sitzungsfehler
err_pam_sys = Systemfehler
err_pam_user_unknown = Unbekannter Nutzer
err_path = Fehler beim setzen des Pfades
err_perm_dir = Fehler beim wechseln des Ordners
err_perm_group = Fehler beim heruntersetzen der Gruppen Berechtigungen
err_perm_user = Fehler beim heruntersetzen der Nutzer Berechtigungen
err_pwnam = Holen der Benutzerinformationen fehlgeschlagen
err_user_gid = Fehler beim setzen der Gruppen Id des Nutzers
err_user_init = Initialisierung des Nutzers fehlgeschlagen
err_user_uid = Setzen der Benutzer Id fehlgeschlagen
err_xsessions_dir = Fehler beim finden des Sitzungsordners
err_xsessions_open = Fehler beim öffnen des Sitzungsordners
login = Anmelden
logout = Abgemeldet
numlock = Numtaste
err_path = Fehler beim Setzen des Pfades
err_perm_dir = Ordnerwechsel fehlgeschlagen
err_perm_group = Fehler beim Heruntersetzen der Gruppenberechtigungen
err_perm_user = Fehler beim Heruntersetzen der Nutzerberechtigungen
err_pwnam = Abrufen der Benutzerinformationen fehlgeschlagen
err_sleep = Sleep-Befehl fehlgeschlagen
err_start = Startbefehl konnte nicht ausgeführt werden
err_battery = Akkustand konnte nicht geladen werden
err_switch_tty = tty konnte nicht gewechselt werden
err_tty_ctrl = Fehler bei der TTY-Uebergabe
err_no_users = Keine Benutzer gefunden
err_uid_range = uid-Bereich konnte nicht dynamisch ermittelt werden
err_user_gid = Fehler beim Setzen der Gruppen-ID
err_user_init = Nutzer-Initialisierung fehlgeschlagen
err_user_uid = Setzen der Benutzer-ID fehlgeschlagen
err_xauth = Xauth-Befehl fehlgeschlagen
err_xcb_conn = xcb-Verbindung fehlgeschlagen
err_xsessions_dir = Fehler beim Finden des Sitzungsordners
err_xsessions_open = Fehler beim Oeffnen des Sitzungsordners
hibernate = Ruhezustand
insert = Einfügen
login = Nutzer
logout = Abmelden
no_x11_support = x11-Unterstützung zur Kompilierzeit deaktiviert
normal = Normal
numlock = Numlock
other = Andere
password = Passwort
restart = Neustarten
shell = shell
shell = Shell
shutdown = Herunterfahren
sleep = Sleep
toggle_password = Passwort anzeigen/verbergen
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,13 +1,33 @@
authenticating = authenticating...
brightness_down = decrease brightness
brightness_up = increase brightness
capslock = capslock
custom = custom
custom_info_err_output_long = output too long
custom_info_err_no_output = no output
custom_info_err_no_output_error = , possible error
err_alloc = failed memory allocation
err_args = unable to parse command line arguments
err_autologin_session = autologin session not found
err_bounds = out-of-bounds index
err_brightness_change = failed to change brightness
err_chdir = failed to open home folder
err_console_dev = failed to access console
err_clock_too_long = clock string too long
err_config = unable to parse config file
err_crawl = failed to crawl session directories
err_dgn_oob = log message
err_domain = invalid domain
err_empty_password = empty password not allowed
err_envlist = failed to get envlist
err_get_active_tty = failed to get active tty
err_hibernate = failed to execute hibernate command
err_hostname = failed to get hostname
err_inactivity = failed to execute inactivity command
err_lock_state = failed to get lock state
err_log = failed to open log file
err_mlock = failed to lock password memory
err_null = null pointer
err_numlock = failed to set numlock
err_pam = pam transaction failed
err_pam_abort = pam transaction aborted
err_pam_acct_expired = account expired
@ -29,22 +49,34 @@ err_perm_dir = failed to change current directory
err_perm_group = failed to downgrade group permissions
err_perm_user = failed to downgrade user permissions
err_pwnam = failed to get user info
err_unknown = an unknown error occurred
err_sleep = failed to execute sleep command
err_start = failed to execute start command
err_battery = failed to load battery status
err_switch_tty = failed to switch tty
err_tty_ctrl = tty control transfer failed
err_no_users = no users found
err_uid_range = failed to dynamically get uid range
err_user_gid = failed to set user GID
err_user_init = failed to initialize user
err_user_uid = failed to set user UID
err_xauth = xauth command failed
err_xcb_conn = xcb connection failed
err_xsessions_dir = failed to find sessions folder
err_xsessions_open = failed to open sessions folder
hibernate = hibernate
insert = insert
login = login
logout = logged out
no_x11_support = x11 support disabled at compile-time
normal = normal
numlock = numlock
other = other
password = password
restart = reboot
shell = shell
shutdown = shutdown
sleep = sleep
toggle_password = toggle password
wayland = wayland
xinitrc = xinitrc
x11 = x11
xinitrc = xinitrc

82
res/lang/eo.ini Normal file
View file

@ -0,0 +1,82 @@
authenticating = aŭtentigado...
brightness_down = malpliigi helecon
brightness_up = pliigi helecon
capslock = majuskla baskulo
custom = propra
custom_info_err_output_long = eligo tro longa
custom_info_err_no_output = neniu eligo
custom_info_err_no_output_error = , ebla eraro
err_alloc = malsukcesis memorasignon
err_args = ne povas analizi argumentojn de komanda linio
err_autologin_session = aŭtomatan ensalutan seancon ne trovis
err_bounds = indico estas ekster-intervala
err_brightness_change = malsukcesis ŝanĝi la helecon
err_chdir = malsukcesis malfermi hejman dosierujon
err_clock_too_long = horloĝa ĉeno estas tro longa
err_config = ne povas analizi agordan dosieron
err_crawl = malsukcesis dum serĉado de seancaj dosierujoj
err_dgn_oob = protokola mesaĝo
err_domain = malvalida domajno
err_empty_password = ne akceptas malplenan pasvorton
err_envlist = malsukcesis preni la medivariablojn
err_get_active_tty = malsukcesis preni la aktivan TTY-on
err_hibernate = malsukcesis ruli la komandon por diskodormo
err_hostname = malsukcesis preni la sistemnomon
err_inactivity = malsukcesis ruli la agorditan komandon por malaktiveco
err_lock_state = malsukcesis preni la ŝlosan staton
err_log = malsukcesis malfermi la protokolan dosieron
err_mlock = malsukcesis ŝlosi pasvortan memoron
err_null = nula memorloko
err_numlock = malsukcesis agordi numeran baskulon
err_pam = PAM-a transakcio malsukcesis
err_pam_abort = PAM-a transakcio malsukcesis
err_pam_acct_expired = konto eksvalidiĝis
err_pam_auth = aŭtentiga eraro
err_pam_authinfo_unavail = malsukcesis preni uzantajn informojn
err_pam_authok_reqd = memorsigno eksvalidiĝis
err_pam_buf = bufra eraro
err_pam_cred_err = malsukcesis agordi akreditaĵon
err_pam_cred_expired = akreditaĵo eksvalidiĝis
err_pam_cred_insufficient = nesufiĉa akreditaĵo
err_pam_cred_unavail = malsukcesis preni akreditaĵon
err_pam_maxtries = atingis maksimuman kvanton da provoj
err_pam_perm_denied = permeso negis
err_pam_session = seancan eraron
err_pam_sys = sisteman eraron
err_pam_user_unknown = ne konas uzanton
err_path = malsukcesis agordi la median dosierindikon
err_perm_dir = malsukcesis ŝanĝi la nunan dosierujon
err_perm_group = malsukcesis redukti grupajn permesojn
err_perm_user = malsukcesis redukti uzantajn permesojn
err_pwnam = malsukcesis preni uzantajn informojn
err_sleep = malsukcesis ruli memordorman komandon
err_start = malsukcesis ruli startan komandon
err_battery = malsukcesis ŝargi baterian staton
err_switch_tty = malsukcesis ŝanĝi TTY-on
err_tty_ctrl = TTY-an stiran transigon malsukcesis
err_no_users = nul uzantojn trovas
err_uid_range = malsukcesis dinamike preni UID-an intervalon
err_user_gid = malsukcesis agordi uzantan GID-on
err_user_init = malsukcesis iniciĝi uzanto
err_user_uid = malsukcesis agordi uzantan UID-on
err_xauth = malsukcesis plenumi je xauth
err_xcb_conn = malsukcesis dum konectado al xcb
err_xsessions_dir = malsukcesis trovi seancan dosierujon
err_xsessions_open = malsukcesis malfermi seancan dosierujon
hibernate = diskodormi
insert = enmeti
login = uzanto
logout = elsalutis
no_x11_support = x11 estas foriĝita de kompil-tempo
normal = normala
numlock = numera baskulo
other = alia
password = pasvorto
restart = restartigi
shell = ŝelo
shutdown = malŝalti
sleep = memordormi
toggle_password = montri/kaŝi pasvorton
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,13 +1,33 @@
authenticating = autenticando...
brightness_down = bajar brillo
brightness_up = subir brillo
capslock = Bloq Mayús
custom = personalizado
custom_info_err_output_long = salida demasiado larga
custom_info_err_no_output = sin salida
custom_info_err_no_output_error = , posible error
err_alloc = asignación de memoria fallida
err_args = no se pudieron analizar los argumentos de la línea de comandos
err_autologin_session = no se encontró la sesión de inicio de sesión automático
err_bounds = índice fuera de límites
err_brightness_change = no se pudo cambiar el brillo
err_chdir = error al abrir la carpeta home
err_console_dev = error al acceder a la consola
err_clock_too_long = la cadena del reloj es demasiado larga
err_config = no se pudo analizar el archivo de configuración
err_crawl = no se pudieron explorar los directorios de sesión
err_dgn_oob = mensaje de registro
err_domain = dominio inválido
err_empty_password = no se permite contraseña vacía
err_envlist = no se pudo obtener la lista de variables de entorno
err_get_active_tty = no se pudo obtener el tty activo
err_hibernate = no se pudo ejecutar el comando de hibernación
err_hostname = error al obtener el nombre de host
err_inactivity = no se pudo ejecutar el comando de inactividad
err_lock_state = no se pudo obtener el estado de bloqueo
err_log = no se pudo abrir el archivo de registro
err_mlock = error al bloquear la contraseña de memoria
err_null = puntero nulo
err_numlock = no se pudo configurar numlock
err_pam = error en la transacción pam
err_pam_abort = transacción pam abortada
err_pam_acct_expired = cuenta expirada
@ -29,17 +49,34 @@ err_perm_dir = error al cambiar el directorio actual
err_perm_group = error al degradar los permisos del grupo
err_perm_user = error al degradar los permisos del usuario
err_pwnam = error al obtener la información del usuario
err_sleep = no se pudo ejecutar el comando de suspensión
err_start = no se pudo ejecutar el comando de inicio
err_battery = no se pudo cargar el estado de la batería
err_switch_tty = no se pudo cambiar de tty
err_tty_ctrl = falló la transferencia de control tty
err_no_users = no se encontraron usuarios
err_uid_range = no se pudo obtener dinámicamente el rango de uid
err_user_gid = error al establecer el GID del usuario
err_user_init = error al inicializar usuario
err_user_uid = error al establecer el UID del usuario
err_xauth = falló el comando xauth
err_xcb_conn = falló la conexión xcb
err_xsessions_dir = error al buscar la carpeta de sesiones
err_xsessions_open = error al abrir la carpeta de sesiones
login = iniciar sesión
hibernate = hibernar
insert = insertar
login = usuario
logout = cerrar sesión
no_x11_support = soporte x11 desactivado en tiempo de compilación
normal = normal
numlock = Bloq Num
other = otro
password = contraseña
restart = reiniciar
shell = shell
shutdown = apagar
sleep = suspender
toggle_password = mostrar/ocultar contraseña
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,19 +1,39 @@
capslock = verr.maj
authenticating = authentification...
brightness_down = diminuer la luminosité
brightness_up = augmenter la luminosité
capslock = verr.maj
custom = customisé
custom_info_err_output_long = sortie trop longue
custom_info_err_no_output = pas de sortie
custom_info_err_no_output_error = , erreur possible
err_alloc = échec d'allocation mémoire
err_args = échec de l'analyse des arguments en lignes de commande
err_autologin_session = session de connexion automatique introuvable
err_bounds = indice hors-limite
err_brightness_change = échec du changement de luminosité
err_chdir = échec de l'ouverture du répertoire home
err_console_dev = échec d'accès à la console
err_clock_too_long = chaîne de formattage de l'horloge trop longue
err_config = échec de lecture du fichier de configuration
err_crawl = échec de la navigation des répertoires de session
err_dgn_oob = message
err_domain = domaine invalide
err_empty_password = mot de passe vide non autorisé
err_envlist = échec de lecture de la liste d'environnement
err_get_active_tty = échec de lecture du terminal actif
err_hibernate = échec de l'exécution de la commande de veille prolongée
err_hostname = échec de lecture du nom d'hôte
err_inactivity = échec de l'exécution de la commande d'inactivité
err_lock_state = échec de lecture de l'état de verrouillage
err_log = échec de l'ouverture du fichier de journal
err_mlock = échec du verrouillage mémoire
err_null = pointeur null
err_numlock = échec de modification du verr.num
err_pam = échec de la transaction pam
err_pam_abort = transaction pam avortée
err_pam_acct_expired = compte expiré
err_pam_auth = erreur d'authentification
err_pam_authok_reqd = tiquet expiré
err_pam_authinfo_unavail = échec de l'obtention des infos utilisateur
err_pam_authok_reqd = tiquet expiré
err_pam_buf = erreur de mémoire tampon
err_pam_cred_err = échec de la modification des identifiants
err_pam_cred_expired = identifiants expirés
@ -29,17 +49,34 @@ err_perm_dir = échec de changement de répertoire
err_perm_group = échec du déclassement des permissions de groupe
err_perm_user = échec du déclassement des permissions utilisateur
err_pwnam = échec de lecture des infos utilisateur
err_sleep = échec de l'exécution de la commande de veille
err_start = échec de l'exécution de la commande de démarrage
err_battery = échec de lecture de l'état de la batterie
err_switch_tty = échec du changement de terminal
err_tty_ctrl = échec du transfert de contrôle du terminal
err_no_users = aucun utilisateur trouvé
err_uid_range = échec de récupération dynamique de la plage d'UID
err_user_gid = échec de modification du GID
err_user_init = échec d'initialisation de l'utilisateur
err_user_uid = échec de modification du UID
err_xauth = échec de la commande xauth
err_xcb_conn = échec de la connexion xcb
err_xsessions_dir = échec de la recherche du dossier de sessions
err_xsessions_open = échec de l'ouverture du dossier de sessions
hibernate = veille prolongée
insert = insertion
login = identifiant
logout = déconnection
logout = déconnecté
no_x11_support = support pour x11 désactivé lors de la compilation
normal = normal
numlock = verr.num
other = autre
password = mot de passe
restart = redémarrer
shell = shell
shutdown = éteindre
sleep = veille
toggle_password = afficher le mot de passe
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,13 +1,33 @@
authenticating = autenticazione in corso...
brightness_down = diminuisci luminosità
brightness_up = aumenta luminosità
capslock = capslock
custom = personalizzato
custom_info_err_output_long = output troppo lungo
custom_info_err_no_output = nessun output
custom_info_err_no_output_error = , possibile errore
err_alloc = impossibile allocare memoria
err_args = impossibile analizzare gli argomenti della riga di comando
err_autologin_session = sessione di accesso automatico non trovata
err_bounds = indice fuori limite
err_brightness_change = impossibile modificare la luminosità
err_chdir = impossibile aprire home directory
err_console_dev = impossibile aprire console
err_clock_too_long = stringa dell'orologio troppo lunga
err_config = impossibile analizzare il file di configurazione
err_crawl = impossibile esplorare le directory delle sessioni
err_dgn_oob = messaggio log
err_domain = dominio non valido
err_empty_password = password vuota non consentita
err_envlist = impossibile ottenere la lista delle variabili d'ambiente
err_get_active_tty = impossibile ottenere il tty attivo
err_hibernate = impossibile eseguire il comando di ibernazione
err_hostname = impossibile ottenere hostname
err_inactivity = impossibile eseguire il comando di inattività
err_lock_state = impossibile ottenere lo stato di blocco
err_log = impossibile aprire il file di log
err_mlock = impossibile ottenere lock per la password in memoria
err_null = puntatore nullo
err_numlock = impossibile impostare il numlock
err_pam = transazione PAM fallita
err_pam_abort = transazione PAM interrotta
err_pam_acct_expired = account scaduto
@ -29,17 +49,34 @@ err_perm_dir = impossibile cambiare directory corrente
err_perm_group = impossibile ridurre permessi gruppo
err_perm_user = impossibile ridurre permessi utente
err_pwnam = impossibile ottenere dati utente
err_sleep = impossibile eseguire il comando di sospensione
err_start = impossibile eseguire il comando di avvio
err_battery = impossibile caricare lo stato della batteria
err_switch_tty = impossibile cambiare tty
err_tty_ctrl = trasferimento del controllo tty fallito
err_no_users = nessun utente trovato
err_uid_range = impossibile ottenere dinamicamente l'intervallo uid
err_user_gid = impossibile impostare GID utente
err_user_init = impossibile inizializzare utente
err_user_uid = impossible impostare UID utente
err_xauth = comando xauth fallito
err_xcb_conn = connessione xcb fallita
err_xsessions_dir = impossibile localizzare cartella sessioni
err_xsessions_open = impossibile aprire cartella sessioni
hibernate = ibernazione
insert = inserisci
login = username
logout = scollegato
no_x11_support = supporto x11 disabilitato in fase di compilazione
normal = normale
numlock = numlock
other = altro
password = password
restart = riavvio
shell = shell
shutdown = arresto
sleep = sospendi
toggle_password = mostra/nascondi password
wayland = wayland
x11 = x11
xinitrc = xinitrc

82
res/lang/ja_JP.ini Normal file
View file

@ -0,0 +1,82 @@
authenticating = 認証中...
brightness_down = 明るさを下げる
brightness_up = 明るさを上げる
capslock = CapsLock
custom = カスタム
custom_info_err_output_long = 出力が長すぎます
custom_info_err_no_output = 出力なし
custom_info_err_no_output_error = 、エラーの可能性あり
err_alloc = メモリ割り当て失敗
err_args = コマンドライン引数を解析できません
err_autologin_session = 自動ログインセッションが見つかりません
err_bounds = 境界外インデックス
err_brightness_change = 明るさの変更に失敗しました
err_chdir = ホームフォルダを開けませんでした
err_clock_too_long = 時計の文字列が長すぎます
err_config = 設定ファイルを解析できません
err_crawl = セッションディレクトリのクロールに失敗しました
err_dgn_oob = ログメッセージ
err_domain = 無効なドメイン
err_empty_password = 空のパスワードは許可されていません
err_envlist = 環境変数リストの取得に失敗しました
err_get_active_tty = アクティブなttyの取得に失敗しました
err_hibernate = 休止状態コマンドの実行に失敗しました
err_hostname = ホスト名の取得に失敗しました
err_inactivity = 無操作コマンドの実行に失敗しました
err_lock_state = ロック状態の取得に失敗しました
err_log = ログファイルを開けませんでした
err_mlock = パスワードメモリのロックに失敗しました
err_null = ヌルポインタ
err_numlock = NumLockの設定に失敗しました
err_pam = PAMトランザクション失敗
err_pam_abort = PAMトランザクションが中断されました
err_pam_acct_expired = アカウントの有効期限が切れています
err_pam_auth = 認証エラー
err_pam_authinfo_unavail = ユーザー情報の取得に失敗しました
err_pam_authok_reqd = トークンの有効期限が切れています
err_pam_buf = メモリバッファエラー
err_pam_cred_err = 認証情報の設定に失敗しました
err_pam_cred_expired = 認証情報の有効期限が切れています
err_pam_cred_insufficient = 認証情報が不十分です
err_pam_cred_unavail = 認証情報の取得に失敗しました
err_pam_maxtries = 最大試行回数に到達しました
err_pam_perm_denied = アクセスが拒否されました
err_pam_session = セッションエラー
err_pam_sys = システムエラー
err_pam_user_unknown = 不明なユーザー
err_path = パスの設定に失敗しました
err_perm_dir = カレントディレクトリの変更に失敗しました
err_perm_group = グループ権限のダウングレードに失敗しました
err_perm_user = ユーザー権限のダウングレードに失敗しました
err_pwnam = ユーザー情報の取得に失敗しました
err_sleep = スリープコマンドの実行に失敗しました
err_start = 起動コマンドの実行に失敗しました
err_battery = バッテリー状態の読み込みに失敗しました
err_switch_tty = ttyの切り替えに失敗しました
err_tty_ctrl = TTY制御の転送に失敗しました
err_no_users = ユーザーが見つかりません
err_uid_range = uidの範囲を動的に取得できませんでした
err_user_gid = ユーザーGIDの設定に失敗しました
err_user_init = ユーザーの初期化に失敗しました
err_user_uid = ユーザーUIDの設定に失敗しました
err_xauth = xauthコマンドの実行に失敗しました
err_xcb_conn = XCB接続に失敗しました
err_xsessions_dir = セッションフォルダが見つかりませんでした
err_xsessions_open = セッションフォルダを開けませんでした
hibernate = 休止状態
insert = 挿入
login = ログイン
logout = ログアウト済み
no_x11_support = x11サポートはコンパイル時に無効化されています
normal = 通常
numlock = NumLock
other = その他
password = パスワード
restart = 再起動
shell = シェル
shutdown = シャットダウン
sleep = スリープ
toggle_password = パスワードの表示/非表示
wayland = Wayland
x11 = x11
xinitrc = xinitrc

82
res/lang/ku.ini Normal file
View file

@ -0,0 +1,82 @@
authenticating = tê piştrastkirin...
brightness_down = ronahiyê kêm bike
brightness_up = ronahiyê bilind bike
capslock = tîpên girdek (capslock)
custom = kesane
custom_info_err_output_long = encam pir dirêj e
custom_info_err_no_output = encam tune
custom_info_err_no_output_error = , xeletiya mimkun
err_alloc = veqetandina bîrê têk çû
err_args = argumanên rêzika fermanê nehatin analîzkirin
err_autologin_session = danişîna têketina xweber nehate dîtin
err_bounds = îndeksa derveyî sînor
err_brightness_change = guherandina ronahiyê têk çû
err_chdir = vekirina peldanka malê têk çû
err_clock_too_long = rêzika demjimêrê pir dirêj e
err_config = pela rêkxistinê nehat analîzkirin
err_crawl = gerandina pelrêçên danişînê têk çû
err_dgn_oob = peyama têketinê
err_domain = navpara nederbasdar
err_empty_password = borînpeyv nabe ku vala be
err_envlist = girtina lîsteya jîngehê (envlist) têk çû
err_get_active_tty = girtina tty ya çalak têk çû
err_hibernate = fermana cemidaninê nehat xebitandin
err_hostname = girtina navê mêvandar têk çû
err_inactivity = fermana neçalaktiyê nehat xebitandin
err_lock_state = girtina rewşa kilîtkirinê têk çû
err_log = vekirina pelê têkeinê têk çû
err_mlock = kilîtkirina bîra borînpeyvê têk çû
err_null = nîşandera null
err_numlock = sazkirina numlock têk çû
err_pam = danûstendina pam têk çû
err_pam_abort = danûstendina pam hate têkbirin
err_pam_acct_expired = dema jimarê derbas bûye
err_pam_auth = şaşetiya piştrastkirinê
err_pam_authinfo_unavail = zanyariyên bikarhêner nehatin girtin
err_pam_authok_reqd = dema nîşandanê derbas bûye
err_pam_buf = şaşetiya bîra demkî
err_pam_cred_err = sazkirina rastkitinê têk çû
err_pam_cred_expired = dema rastkitinê derbas bûye
err_pam_cred_insufficient = rastkitinê kêm
err_pam_cred_unavail = girtina rastkitinê têk çû
err_pam_maxtries = sînorê hewldanên herî bilind hat gihîştin
err_pam_perm_denied = mafdayîn hat paşguhkirin
err_pam_session = şaşetiya danişînê
err_pam_sys = şaşetiya pergalê
err_pam_user_unknown = bikarhênerê nenas
err_path = sazkirina rêgehê têk çû
err_perm_dir = guhertina pelrêçê heyî têk çû
err_perm_group = kêmkirina mafdayînên komê têk çû
err_perm_user = kêmkirina mafdayînên bikarhêner têk çû
err_pwnam = girtina zanyariyên bikarhêner têk çû
err_sleep = fermana cemidaninê nehat xebitandin
err_start = fermana destpêkirinê nehat xebitandin
err_battery = barkirina rewşa betariyê têk çû
err_switch_tty = guhertina tty têk çû
err_tty_ctrl = guhertina kontrola tty têk çû
err_no_users = tu bikarhêner nehatin dîtin
err_uid_range = girtina rêjeya dînamîk a sînorê uid têk çû
err_user_gid = sazkirina GID a bikarhêner têk çû
err_user_init = destpêkirina bikarhêner têk çû
err_user_uid = sazkirina UID a bikarhêner têk çû
err_xauth = fermana xauth têk çû
err_xcb_conn = girêdana xcb têk çû
err_xsessions_dir = dîtina peldanka danişînan têk çû
err_xsessions_open = vekirina peldanka danişînan têk çû
hibernate = bicemidîne
insert = têxîne
login = têketin
logout = derkeve
no_x11_support = piştgiriya x11 di dema berhevkirinê de hatiye girtin
normal = normal
numlock = numlock
other = ên din
password = borînpeyv
restart = ji nû ve bide destpêkirin
shell = shell
shutdown = vemirîne
sleep = têxîne xewê
toggle_password = şîfre nîşan bide/veşêre
wayland = wayland
x11 = x11
xinitrc = xinitrc

82
res/lang/lv.ini Normal file
View file

@ -0,0 +1,82 @@
authenticating = autentificējas...
brightness_down = samazināt spilgtumu
brightness_up = palielināt spilgtumu
capslock = caps lock
custom = pielāgots
custom_info_err_output_long = izvade pārāk gara
custom_info_err_no_output = nav izvades
custom_info_err_no_output_error = , iespējama kļūda
err_alloc = neizdevās atmiņas piešķiršana
err_args = nevar parsēt komandrindas argumentus
err_autologin_session = automātiskās pieteikšanās sesija nav atrasta
err_bounds = indekss ārpus robežām
err_brightness_change = neizdevās mainīt spilgtumu
err_chdir = neizdevās atvērt mājas mapi
err_clock_too_long = pulksteņa virkne pārāk gara
err_config = neizdevās parsēt konfigurācijas failu
err_crawl = neizdevās pārlūkot sesiju direktorijus
err_dgn_oob = žurnāla ziņojums
err_domain = nederīgs domēns
err_empty_password = tukša parole nav atļauta
err_envlist = neizdevās iegūt vides mainīgo sarakstu
err_get_active_tty = neizdevās iegūt aktīvo tty
err_hibernate = neizdevās izpildīt hibernācijas komandu
err_hostname = neizdevās iegūt hostname
err_inactivity = neizdevās izpildīt neaktivitātes komandu
err_lock_state = neizdevās iegūt bloķēšanas stāvokli
err_log = neizdevās atvērt žurnāla failu
err_mlock = neizdevās bloķēt paroles atmiņu
err_null = null rādītājs
err_numlock = neizdevās iestatīt numlock
err_pam = pam transakcija neizdevās
err_pam_abort = pam transakcija pārtraukta
err_pam_acct_expired = konts novecojis
err_pam_auth = autentifikācijas kļūda
err_pam_authinfo_unavail = neizdevās iegūt lietotāja informāciju
err_pam_authok_reqd = žetons beidzies
err_pam_buf = atmiņas bufera kļūda
err_pam_cred_err = neizdevās iestatīt akreditācijas datus
err_pam_cred_expired = akreditācijas dati novecojuši
err_pam_cred_insufficient = nepietiekami akreditācijas dati
err_pam_cred_unavail = neizdevās iegūt akreditācijas datus
err_pam_maxtries = sasniegts maksimālais mēģinājumu skaits
err_pam_perm_denied = piekļuve liegta
err_pam_session = sesijas kļūda
err_pam_sys = sistēmas kļūda
err_pam_user_unknown = nezināms lietotājs
err_path = neizdevās iestatīt ceļu
err_perm_dir = neizdevās mainīt pašreizējo mapi
err_perm_group = neizdevās pazemināt grupas atļaujas
err_perm_user = neizdevās pazemināt lietotāja atļaujas
err_pwnam = neizdevās iegūt lietotāja informāciju
err_sleep = neizdevās izpildīt miega komandu
err_start = neizdevās izpildīt startēšanas komandu
err_battery = neizdevās ielādēt akumulatora stāvokli
err_switch_tty = neizdevās pārslēgt tty
err_tty_ctrl = tty vadības nodošana neizdevās
err_no_users = lietotāji nav atrasti
err_uid_range = neizdevās dinamiski iegūt uid diapazonu
err_user_gid = neizdevās iestatīt lietotāja GID
err_user_init = neizdevās inicializēt lietotāju
err_user_uid = neizdevās iestatīt lietotāja UID
err_xauth = xauth komanda neizdevās
err_xcb_conn = xcb savienojums neizdevās
err_xsessions_dir = neizdevās atrast sesiju mapi
err_xsessions_open = neizdevās atvērt sesiju mapi
hibernate = hibernācija
insert = ievietot
login = lietotājs
logout = iziet
no_x11_support = x11 atbalsts atspējots kompilācijas laikā
normal = parastais
numlock = numlock
other = cits
password = parole
restart = restartēt
shell = terminālis
shutdown = izslēgt
sleep = snauda
toggle_password = rādīt/slēpt paroli
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -0,0 +1,49 @@
#!/usr/bin/env python3
from pathlib import Path
from sys import stderr
def process_lang_file(path: Path, lang_keys: list[str]) -> None:
# read key-value-pairs from lang file into dict
existing_entries = {}
with open(path, "r", encoding="UTF-8") as fh:
while line := fh.readline():
try:
key, value = line.split("=", 1)
existing_entries[key.strip()] = value.strip()
except ValueError: # line does not contain '='
continue
# re-write current lang file with entries in order of occurence in `lang_keys`
# and with empty lines for missing translations
with open(path, "w", encoding="UTF-8") as fh:
for item in lang_keys:
try:
fh.write(f"{item} = {existing_entries[item]}\n")
except KeyError: # no translation for `item` yet
fh.write("\n")
def main() -> None:
zig_lang_file = Path(__file__).parent.joinpath("../../src/config/Lang.zig").resolve()
if not zig_lang_file.exists():
print(f"ERROR: File '{zig_lang_file.as_posix()}' does not exist. Exiting.", file=stderr)
exit(1)
# read "language keys" from `zig_lang_file` into list
lang_keys = []
with open(zig_lang_file, "r", encoding="UTF-8") as fh:
while line := fh.readline():
# only process lines that are not empty or no comments
if not (line.strip() == "" or line.startswith("//")):
lang_keys.append(line.split(":")[0].strip())
lang_files = [f for f in Path.iterdir(Path(__file__).parent) if f.name.endswith(".ini") and f.is_file()]
for file in lang_files:
process_lang_file(file, lang_keys)
if __name__ == "__main__":
main()

View file

@ -1,45 +1,82 @@
authenticating = uwierzytelnianie...
brightness_down = zmniejsz jasność
brightness_up = zwiększ jasność
capslock = capslock
custom = własny
custom_info_err_output_long = wyjście zbyt długie
custom_info_err_no_output = brak wyjścia
custom_info_err_no_output_error = , możliwy błąd
err_alloc = nieudana alokacja pamięci
err_bounds = indeks poza granicami
err_args = nie można przetworzyć argumentów wiersza poleceń
err_autologin_session = nie znaleziono sesji autologowania
err_bounds = indeks poza zakresem
err_brightness_change = nie udało się zmienić jasności
err_chdir = nie udało się otworzyć folderu domowego
err_console_dev = nie udało się uzyskać dostępu do konsoli
err_clock_too_long = ciąg znaków zegara jest za długi
err_config = nie można przetworzyć pliku konfiguracyjnego
err_crawl = nie udało się przeszukać katalogów sesji
err_dgn_oob = wiadomość loga
err_domain = niepoprawna domena
err_empty_password = puste hasło jest niedozwolone
err_envlist = nie udało się pobrać listy zmiennych środowiskowych
err_get_active_tty = nie udało się uzyskać aktywnego tty
err_hibernate = nie udało się wykonać polecenia hibernacji
err_hostname = nie udało się uzyskać nazwy hosta
err_inactivity = nie udało się wykonać polecenia nieaktywności
err_lock_state = nie udało się uzyskać stanu blokady
err_log = nie udało się otworzyć pliku logu
err_mlock = nie udało się zablokować pamięci haseł
err_null = wskaźnik zerowy
err_null = pusty wskaźnik
err_numlock = nie udało się ustawić numlock
err_pam = transakcja pam nieudana
err_pam_abort = transakcja pam przerwana
err_pam_acct_expired = konto wygasło
err_pam_auth = błąd autentyfikacji
err_pam_auth = błąd uwierzytelniania
err_pam_authinfo_unavail = nie udało się zdobyć informacji o użytkowniku
err_pam_authok_reqd = token wygasł
err_pam_buf = błąd bufora pamięci
err_pam_cred_err = nie udało się ustawić uwierzytelnienia
err_pam_cred_err = nie udało się ustawić uwierzytelnienia
err_pam_cred_expired = uwierzytelnienie wygasło
err_pam_cred_insufficient = niewystarczające uwierzytelnienie
err_pam_cred_unavail = nie udało się uzyskać uwierzytelnienia
err_pam_cred_unavail = nie udało się uzyskać uwierzytelnienia
err_pam_maxtries = osiągnięto limit prób
err_pam_perm_denied = brak uprawnień
err_pam_perm_denied = odmowa dostępu
err_pam_session = błąd sesji
err_pam_sys = błąd systemu
err_pam_user_unknown = nieznany użytkownik
err_path = nie udało się ustawić ścieżki
err_perm_dir = nie udało się zmienić obecnego katalogu
err_perm_group = nie udało się obniżyć uprawnień grupy
err_perm_user = nie udało się obniżyć uprawnień użytkownika
err_pwnam = nie udało się uzyskać informacji o użytkowniku
err_path = nie udało się ustawić ścieżki
err_perm_dir = nie udało się zmienić obecnego katalogu
err_perm_group = nie udało się obniżyć uprawnień grupy
err_perm_user = nie udało się obniżyć uprawnień użytkownika
err_pwnam = nie udało się uzyskać informacji o użytkowniku
err_sleep = nie udało się wykonać polecenia sleep
err_start = nie udało się wykonać polecenia startowego
err_battery = nie udało się sprawdzić statusu baterii
err_switch_tty = nie można przełączyć tty
err_tty_ctrl = nie udało się przekazać kontroli tty
err_no_users = nie znaleziono żadnego użytkownika
err_uid_range = nie udało się dynamicznie pobrać zakresu uid
err_user_gid = nie udało się ustawić GID użytkownika
err_user_init = nie udało się zainicjalizować użytkownika
err_user_init = nie udało się zainicjalizować użytkownika
err_user_uid = nie udało się ustawić UID użytkownika
err_xsessions_dir = nie udało się znaleźć folderu sesji
err_xsessions_open = nie udało się otworzyć folderu sesji
err_xauth = polecenie xauth nie powiodło się
err_xcb_conn = połączenie xcb nie powiodło się
err_xsessions_dir = nie udało się znaleźć folderu sesji
err_xsessions_open = nie udało się otworzyć folderu sesji
hibernate = hibernuj
insert = wstaw
login = login
logout = wylogowano
no_x11_support = obsługa x11 wyłączona podczas kompilacji
normal = normalny
numlock = numlock
other = inny
password = hasło
restart = uruchom ponownie
shell = powłoka
shutdown = wyłącz
sleep = uśpij
toggle_password = Pokaż/ukryj hasło
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,13 +1,33 @@
authenticating = a autenticar...
brightness_down = diminuir brilho
brightness_up = aumentar brilho
capslock = capslock
custom = personalizado
custom_info_err_output_long = saída demasiado longa
custom_info_err_no_output = sem saída
custom_info_err_no_output_error = , possível erro
err_alloc = erro na atribuição de memória
err_args = não foi possível analisar os argumentos da linha de comandos
err_autologin_session = sessão de início de sessão automático não encontrada
err_bounds = índice fora de limites
err_brightness_change = não foi possível alterar o brilho
err_chdir = erro ao abrir a pasta home
err_console_dev = erro ao aceder à consola
err_clock_too_long = a cadeia de caracteres do relógio é demasiado longa
err_config = não foi possível analisar o ficheiro de configuração
err_crawl = não foi possível explorar os diretórios de sessão
err_dgn_oob = mensagem de registo
err_domain = domínio inválido
err_empty_password = palavra-passe vazia não é permitida
err_envlist = não foi possível obter a lista de variáveis de ambiente
err_get_active_tty = não foi possível obter o tty ativo
err_hibernate = não foi possível executar o comando de hibernação
err_hostname = erro ao obter o nome do host
err_inactivity = não foi possível executar o comando de inatividade
err_lock_state = não foi possível obter o estado de bloqueio
err_log = não foi possível abrir o ficheiro de registo
err_mlock = erro de bloqueio de memória
err_null = ponteiro nulo
err_numlock = não foi possível definir o numlock
err_pam = erro na transação pam
err_pam_abort = transação pam abortada
err_pam_acct_expired = conta expirada
@ -29,17 +49,34 @@ err_perm_dir = erro ao alterar o diretório atual
err_perm_group = erro ao reduzir as permissões do grupo
err_perm_user = erro ao reduzir as permissões do utilizador
err_pwnam = erro ao obter informação do utilizador
err_sleep = não foi possível executar o comando de suspensão
err_start = não foi possível executar o comando de início
err_battery = não foi possível carregar o estado da bateria
err_switch_tty = não foi possível mudar de tty
err_tty_ctrl = falhou a transferência de controlo do tty
err_no_users = nenhum utilizador encontrado
err_uid_range = não foi possível obter dinamicamente o intervalo de uid
err_user_gid = erro ao definir o GID do utilizador
err_user_init = erro ao iniciar o utilizador
err_user_uid = erro ao definir o UID do utilizador
err_xauth = o comando xauth falhou
err_xcb_conn = a ligação xcb falhou
err_xsessions_dir = erro ao localizar a pasta das sessões
err_xsessions_open = erro ao abrir a pasta das sessões
hibernate = hibernar
insert = inserir
login = iniciar sessão
logout = terminar sessão
no_x11_support = suporte a x11 desativado em tempo de compilação
normal = normal
numlock = numlock
other = outro
password = palavra-passe
restart = reiniciar
shell = shell
shutdown = encerrar
sleep = suspender
toggle_password = mostrar/ocultar palavra-passe
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,19 +1,39 @@
authenticating = autenticando...
brightness_down = diminuir brilho
brightness_up = aumentar brilho
capslock = caixa alta
custom = personalizado
custom_info_err_output_long = saída muito longa
custom_info_err_no_output = sem saída
custom_info_err_no_output_error = , possível erro
err_alloc = alocação de memória malsucedida
err_args = não foi possível analisar os argumentos da linha de comando
err_autologin_session = sessão de login automático não encontrada
err_bounds = índice fora de limites
err_brightness_change = não foi possível alterar o brilho
err_chdir = não foi possível abrir o diretório home
err_console_dev = não foi possível acessar o console
err_clock_too_long = a string do relógio é muito longa
err_config = não foi possível analisar o arquivo de configuração
err_crawl = não foi possível explorar os diretórios de sessão
err_dgn_oob = mensagem de log
err_domain = domínio inválido
err_empty_password = senha vazia não é permitida
err_envlist = não foi possível obter a lista de variáveis de ambiente
err_get_active_tty = não foi possível obter o tty ativo
err_hibernate = não foi possível executar o comando de hibernação
err_hostname = não foi possível obter o nome do host
err_inactivity = não foi possível executar o comando de inatividade
err_lock_state = não foi possível obter o estado de bloqueio
err_log = não foi possível abrir o arquivo de log
err_mlock = bloqueio da memória de senha malsucedido
err_null = ponteiro nulo
err_numlock = não foi possível definir o numlock
err_pam = transação pam malsucedida
err_pam_abort = transação pam abortada
err_pam_acct_expired = conta expirada
err_pam_auth = erro de autenticação
err_pam_authinfo_unavail = não foi possível obter informações do usuário
err_pam_authok_reqd = token expirada
err_pam_authok_reqd = token expirado
err_pam_buf = erro de buffer de memória
err_pam_cred_err = erro para definir credenciais
err_pam_cred_expired = credenciais expiradas
@ -29,17 +49,34 @@ err_perm_dir = não foi possível alterar o diretório atual
err_perm_group = não foi possível reduzir as permissões de grupo
err_perm_user = não foi possível reduzir as permissões de usuário
err_pwnam = não foi possível obter informações do usuário
err_sleep = não foi possível executar o comando de suspensão
err_start = não foi possível executar o comando de início
err_battery = não foi possível carregar o status da bateria
err_switch_tty = não foi possível mudar de tty
err_tty_ctrl = a transferência de controle do tty falhou
err_no_users = nenhum usuário encontrado
err_uid_range = não foi possível obter dinamicamente o intervalo de uid
err_user_gid = não foi possível definir o GID do usuário
err_user_init = não foi possível iniciar o usuário
err_user_uid = não foi possível definir o UID do usuário
err_xauth = o comando xauth falhou
err_xcb_conn = a conexão xcb falhou
err_xsessions_dir = não foi possível encontrar a pasta das sessões
err_xsessions_open = não foi possível abrir a pasta das sessões
hibernate = hibernar
insert = inserir
login = conectar
logout = desconectado
no_x11_support = suporte a x11 desativado em tempo de compilação
normal = normal
numlock = numlock
other = outro
password = senha
restart = reiniciar
shell = shell
shutdown = desligar
sleep = suspender
toggle_password = mostrar/ocultar senha
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,14 +1,34 @@
authenticating = autentificare...
brightness_down = scade luminozitatea
brightness_up = crește luminozitatea
capslock = capslock
err_console_dev = nu s-a putut accesa consola
custom = personalizat
custom_info_err_output_long = ieșire prea lungă
custom_info_err_no_output = fără ieșire
custom_info_err_no_output_error = , posibil eroare
err_alloc = alocare de memorie eșuată
err_args = imposibil de analizat argumentele liniei de comandă
err_autologin_session = sesiunea de autentificare automată nu a fost găsită
err_bounds = index în afara limitelor
err_brightness_change = imposibil de schimbat luminozitatea
err_chdir = imposibil de deschis folderul de acasă
err_clock_too_long = șirul de ceas este prea lung
err_config = imposibil de analizat fișierul de configurare
err_crawl = imposibil de explorat directoarele de sesiune
err_dgn_oob = mesaj jurnal
err_domain = domeniu invalid
err_empty_password = parola goală nu este permisă
err_envlist = imposibil de obținut lista de variabile de mediu
err_get_active_tty = imposibil de obținut tty-ul activ
err_hibernate = imposibil de executat comanda de hibernare
err_hostname = imposibil de obținut numele gazdei
err_inactivity = imposibil de executat comanda de inactivitate
err_lock_state = imposibil de obținut starea de blocare
err_log = imposibil de deschis fișierul jurnal
err_mlock = imposibil de blocat memoria parolei
err_null = pointer nul
err_numlock = imposibil de setat numlock
err_pam = tranzacție pam eșuată
err_pam_abort = tranzacţie pam anulată
err_pam_acct_expired = cont expirat
err_pam_auth = eroare de autentificare
@ -24,22 +44,39 @@ err_pam_perm_denied = acces interzis
err_pam_session = eroare de sesiune
err_pam_sys = eroare de sistem
err_pam_user_unknown = utilizator necunoscut
err_path = imposibil de setat calea
err_perm_dir = nu s-a putut schimba dosarul (folder-ul) curent
err_perm_group = nu s-a putut face downgrade permisiunilor de grup
err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator
err_pwnam = imposibil de obținut informații despre utilizator
err_sleep = imposibil de executat comanda de repaus
err_start = imposibil de executat comanda de pornire
err_battery = imposibil de încărcat starea bateriei
err_switch_tty = imposibil de comutat tty
err_tty_ctrl = transferul controlului tty a eșuat
err_no_users = niciun utilizator găsit
err_uid_range = imposibil de obținut dinamic intervalul uid
err_user_gid = imposibil de setat GID-ul utilizatorului
err_user_init = imposibil de inițializa utilizatorul
err_user_uid = imposibil de setat UID-ul utilizatorului
err_xauth = comanda xauth a eșuat
err_xcb_conn = conexiunea xcb a eșuat
err_xsessions_dir = imposibil de găsit folderul de sesiuni
err_xsessions_open = imposibil de deschis folderul de sesiuni
hibernate = hibernare
insert = inserare
login = utilizator
logout = opreşte sesiunea
no_x11_support = suportul x11 dezactivat la compilare
normal = normal
numlock = numlock
other = altul
password = parolă
restart = resetează
shell = shell
shutdown = opreşte sistemul
sleep = repaus
toggle_password = afișare/ascundere parolă
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,13 +1,33 @@
authenticating = аутентификация...
brightness_down = уменьшить яркость
brightness_up = увеличить яркость
capslock = capslock
custom = пользовательский
custom_info_err_output_long = вывод слишком длинный
custom_info_err_no_output = нет вывода
custom_info_err_no_output_error = , возможная ошибка
err_alloc = не удалось выделить память
err_args = не удалось разобрать аргументы командной строки
err_autologin_session = не найдена сессия с автологином
err_bounds = за пределами индекса
err_brightness_change = не удалось изменить яркость
err_chdir = не удалось открыть домашнюю папку
err_console_dev = не удалось получить доступ к консоли
err_clock_too_long = строка часов слишком длинная
err_config = не удалось разобрать файл конфигурации
err_crawl = не удалось просканировать каталоги сессии
err_dgn_oob = отладочное сообщение (log)
err_domain = неверный домен
err_empty_password = пустой пароль не допустим
err_envlist = не удалось получить список переменных среды
err_get_active_tty = не удалось получить активный tty
err_hibernate = не удалось выполнить команду гибернации
err_hostname = не удалось получить имя хоста
err_inactivity = не удалось выполнить команду бездействия
err_lock_state = не удалось получить состояние lock
err_log = не удалось открыть файл log
err_mlock = сбой блокировки памяти
err_null = нулевой указатель
err_numlock = не удалось установить numlock
err_pam = pam транзакция не удалась
err_pam_abort = pam транзакция прервана
err_pam_acct_expired = срок действия аккаунта истёк
@ -29,17 +49,34 @@ err_perm_dir = не удалось изменить текущий катало
err_perm_group = не удалось понизить права доступа группы
err_perm_user = не удалось понизить права доступа пользователя
err_pwnam = не удалось получить информацию о пользователе
err_sleep = не удалось выполнить команду sleep
err_start = не удалось выполнить команду запуска
err_battery = не удалось получить статус батареи
err_switch_tty = не удалось переключить tty
err_tty_ctrl = передача управления tty не удалась
err_no_users = пользователи не найдены
err_uid_range = не удалось динамически получить диапазон uid
err_user_gid = не удалось установить GID пользователя
err_user_init = не удалось инициализировать пользователя
err_user_uid = не удалось установить UID пользователя
err_xauth = команда xauth не выполнена
err_xcb_conn = ошибка подключения xcb
err_xsessions_dir = не удалось найти сессионную папку
err_xsessions_open = не удалось открыть сессионную папку
hibernate = гибернация
insert = вставка
login = логин
logout = logged out
logout = вышел из системы
no_x11_support = поддержка x11 отключена при компиляции
normal = обычный
numlock = numlock
other = прочие
password = пароль
restart = перезагрузить
shell = shell
shell = оболочка
shutdown = выключить
sleep = сон
toggle_password = показать/скрыть пароль
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,45 +1,82 @@
authenticating = autentifikacija...
brightness_down = smanji osvetljenost
brightness_up = povećaj osvetljenost
capslock = capslock
err_alloc = neuspijesna alokacija memorije
custom = prilagođeno
custom_info_err_output_long = izlaz predugačak
custom_info_err_no_output = nema izlaza
custom_info_err_no_output_error = , moguća greška
err_alloc = neuspješna alokacija memorije
err_args = nije moguće raščlaniti argumente komandne linije
err_autologin_session = sesija automatske prijave nije pronađena
err_bounds = izvan granica indeksa
err_chdir = neuspijesno otvaranje home foldera
err_console_dev = neuspijesno pristupanje konzoli
err_brightness_change = nije uspela promena osvetljenosti
err_chdir = neuspješno otvaranje home foldera
err_clock_too_long = niska sata je preduga
err_config = nije moguće raščlaniti konfiguracioni fajl
err_crawl = nije uspelo skeniranje direktorijuma sesija
err_dgn_oob = log poruka
err_domain = nevazeci domen
err_hostname = neuspijesno trazenje hostname-a
err_mlock = neuspijesno zakljucavanje memorije lozinke
err_null = null pokazivac
err_pam = pam transakcija neuspijesna
err_domain = nevažeći domen
err_empty_password = prazna lozinka nije dozvoljena
err_envlist = nije uspelo dobijanje liste promenljivih okruženja
err_get_active_tty = nije uspelo dobijanje aktivnog tty
err_hibernate = nije uspelo izvršavanje komande hibernacije
err_hostname = neuspješno traženje hostname-a
err_inactivity = nije uspelo izvršavanje komande neaktivnosti
err_lock_state = nije uspelo dobijanje stanja zaključavanja
err_log = nije uspelo otvaranje fajla dnevnika
err_mlock = neuspješno zaključavanje memorije lozinke
err_null = null pokazivač
err_numlock = nije uspelo postavljanje numlock
err_pam = pam transakcija neuspješna
err_pam_abort = pam transakcija prekinuta
err_pam_acct_expired = nalog istekao
err_pam_auth = greska pri autentikaciji
err_pam_authinfo_unavail = neuspjelo uzimanje informacija o korisniku
err_pam_auth = greška pri autentikaciji
err_pam_authinfo_unavail = neuspješno uzimanje informacija o korisniku
err_pam_authok_reqd = token istekao
err_pam_buf = greska bafera memorije
err_pam_cred_err = neuspjelo postavljanje kredencijala
err_pam_buf = greška bafera memorije
err_pam_cred_err = neuspješno postavljanje kredencijala
err_pam_cred_expired = kredencijali istekli
err_pam_cred_insufficient = nedovoljni kredencijali
err_pam_cred_unavail = neuspjelo uzimanje kredencijala
err_pam_maxtries = dostignut maksimalan broj pokusaja
err_pam_perm_denied = nedozovoljeno
err_pam_session = greska sesije
err_pam_sys = greska sistema
err_pam_cred_unavail = neuspješno uzimanje kredencijala
err_pam_maxtries = dostignut maksimalan broj pokušaja
err_pam_perm_denied = nedozvoljeno
err_pam_session = greška sesije
err_pam_sys = greška sistema
err_pam_user_unknown = nepoznat korisnik
err_path = neuspjelo postavljanje path-a
err_perm_dir = neuspjelo mijenjanje foldera
err_perm_group = neuspjesno snizavanje dozvola grupe
err_perm_user = neuspijesno snizavanje dozvola korisnika
err_pwnam = neuspijesno skupljanje informacija o korisniku
err_user_gid = neuspijesno postavljanje korisničkog GID-a
err_user_init = neuspijensa inicijalizacija korisnika
err_user_uid = neuspijesno postavljanje UID-a korisnika
err_xsessions_dir = neuspijesno pronalazenje foldera sesija
err_xsessions_open = neuspijesno otvaranje foldera sesija
err_path = neuspješno postavljanje path-a
err_perm_dir = neuspješno mijenjanje foldera
err_perm_group = neuspješno snižavanje dozvola grupe
err_perm_user = neuspješno snižavanje dozvola korisnika
err_pwnam = neuspješno skupljanje informacija o korisniku
err_sleep = nije uspelo izvršavanje komande spavanja
err_start = nije uspelo izvršavanje komande pokretanja
err_battery = nije uspelo učitavanje statusa baterije
err_switch_tty = nije uspelo prebacivanje tty
err_tty_ctrl = prenos kontrole tty nije uspeo
err_no_users = nisu pronađeni korisnici
err_uid_range = nije uspelo dinamičko dobijanje opsega uid
err_user_gid = neuspješno postavljanje korisničkog GID-a
err_user_init = neuspješna inicijalizacija korisnika
err_user_uid = neuspješno postavljanje UID-a korisnika
err_xauth = komanda xauth nije uspela
err_xcb_conn = xcb veza nije uspela
err_xsessions_dir = neuspješno pronalaženje foldera sesija
err_xsessions_open = neuspješno otvaranje foldera sesija
hibernate = hibernacija
insert = umetni
login = korisnik
logout = izlogovan
no_x11_support = x11 podrška onemogućena tokom prevođenja
normal = normalno
numlock = numlock
other = ostalo
password = lozinka
restart = ponovo pokreni
shell = shell
shutdown = ugasi
sleep = uspavaj
toggle_password = prikaži/sakrij lozinku
wayland = wayland
x11 = x11
xinitrc = xinitrc

82
res/lang/sr_Cyrl.ini Normal file
View file

@ -0,0 +1,82 @@
authenticating = аутентификација...
brightness_down = смањи осветљеност
brightness_up = повећај осветљеност
capslock = capslock
custom = прилагођено
custom_info_err_output_long = излаз предугачак
custom_info_err_no_output = нема излаза
custom_info_err_no_output_error = , могућа грешка
err_alloc = неуспешна алокација меморије
err_args = није могуће рашчланити аргументе командне линије
err_autologin_session = сесија аутоматске пријаве није пронађена
err_bounds = изван граница индекса
err_brightness_change = није успела промена осветљености
err_chdir = неуспешно отварање home фолдера
err_clock_too_long = ниска сата је предуга
err_config = није могуће рашчланити конфигурациони фајл
err_crawl = није успело скенирање директоријума сесија
err_dgn_oob = лог порука
err_domain = неважећи домен
err_empty_password = празна лозинка није дозвољена
err_envlist = није успело добијање листе променљивих окружења
err_get_active_tty = није успело добијање активног tty
err_hibernate = није успело извршавање команде хибернације
err_hostname = неуспешно тражење hostname-а
err_inactivity = није успело извршавање команде неактивности
err_lock_state = није успело добијање стања закључавања
err_log = није успело отварање фајла дневника
err_mlock = неуспешно закључавање меморије лозинке
err_null = null показивач
err_numlock = није успело постављање numlock
err_pam = pam трансакција неуспешна
err_pam_abort = pam трансакција прекинута
err_pam_acct_expired = налог истекао
err_pam_auth = грешка при аутентикацији
err_pam_authinfo_unavail = неуспешно узимање информација о кориснику
err_pam_authok_reqd = токен истекао
err_pam_buf = грешка бафера меморије
err_pam_cred_err = неуспешно постављање акредитива
err_pam_cred_expired = акредитиви истекли
err_pam_cred_insufficient = недовољни акредитиви
err_pam_cred_unavail = неуспешно узимање акредитива
err_pam_maxtries = достигнут максималан број покушаја
err_pam_perm_denied = недозвољено
err_pam_session = грешка сесије
err_pam_sys = грешка система
err_pam_user_unknown = непознат корисник
err_path = неуспешно постављање путање
err_perm_dir = неуспешно мењање фолдера
err_perm_group = неуспешно снижавање дозвола групе
err_perm_user = неуспешно снижавање дозвола корисника
err_pwnam = неуспешно прикупљање информација о кориснику
err_sleep = није успело извршавање команде спавања
err_start = није успело извршавање команде покретања
err_battery = није успело учитавање статуса батерије
err_switch_tty = није успело пребацивање tty
err_tty_ctrl = пренос контроле tty није успео
err_no_users = нису пронађени корисници
err_uid_range = није успело динамичко добијање опсега uid
err_user_gid = неуспешно постављање корисничког GID-а
err_user_init = неуспешна иницијализација корисника
err_user_uid = неуспешно постављање UID-а корисника
err_xauth = команда xauth није успела
err_xcb_conn = xcb веза није успела
err_xsessions_dir = неуспешно проналажење фолдера сесија
err_xsessions_open = неуспешно отварање фолдера сесија
hibernate = хибернација
insert = уметни
login = корисник
logout = одјављен
no_x11_support = x11 подршка онемогућена током превођења
normal = нормално
numlock = numlock
other = остало
password = лозинка
restart = поново покрени
shell = shell
shutdown = угаси
sleep = успавај
toggle_password = прикажи/сакриј лозинку
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,45 +1,82 @@
authenticating = autentiserar...
brightness_down = minska ljusstyrka
brightness_up = öka ljusstyrka
capslock = capslock
err_alloc = misslyckad minnesallokering
err_bounds = utanför banan index
custom = anpassad
custom_info_err_output_long = utdata för lång
custom_info_err_no_output = ingen utdata
custom_info_err_no_output_error = , möjligt fel
err_alloc = minnesallokering misslyckades
err_args = tolkning av kommandoargument misslyckades
err_autologin_session = autologin-session hittades inte
err_bounds = index-värde utanför intervallet
err_brightness_change = ändring av ljusstyrka misslyckades
err_chdir = misslyckades att öppna hemkatalog
err_console_dev = misslyckades att komma åt konsol
err_clock_too_long = klocksträng för lång
err_config = tolkning av konfigfil misslyckades
err_crawl = genomsökning av sessionskataloger misslyckades
err_dgn_oob = loggmeddelande
err_domain = okänd domän
err_hostname = misslyckades att hämta värdnamn
err_mlock = misslyckades att låsa lösenordsminne
err_domain = ogitlig domän
err_empty_password = tomt lösenord godtas ej
err_envlist = hämtning av env-lista misslyckades
err_get_active_tty = hämtning av aktiv tty misslyckades
err_hibernate = vilolägets kommando misslyckades
err_hostname = hämtning av hostname misslyckades
err_inactivity = inaktivitetslägets kommando misslyckades
err_lock_state = hämtning av låsningsstatus misslyckades
err_log = öppning av loggfil misslyckades
err_mlock = låsning av lösenordsminne misslyckades
err_null = nullpekare
err_numlock = inställning av numlock misslyckades
err_pam = pam-transaktion misslyckades
err_pam_abort = pam-transaktion avbröts
err_pam_acct_expired = konto upphört
err_pam_auth = autentiseringsfel
err_pam_authinfo_unavail = misslyckades att hämta användarinfo
err_pam_authok_reqd = token utgången
err_pam_buf = minnesbuffer fel
err_pam_cred_err = misslyckades att ställa in inloggningsuppgifter
err_pam_cred_expired = inloggningsuppgifter upphörda
err_pam_acct_expired = kontot har löpt ut
err_pam_auth = autentisering misslyckades
err_pam_authinfo_unavail = hämtning av användarinformation misslyckades
err_pam_authok_reqd = token har löpt ut
err_pam_buf = minnesbufferfel
err_pam_cred_err = inställning av inloggningsuppgifter misslyckades
err_pam_cred_expired = inloggningsuppgifterna har löpt ut
err_pam_cred_insufficient = otillräckliga inloggningsuppgifter
err_pam_cred_unavail = misslyckades att hämta inloggningsuppgifter
err_pam_maxtries = nådde maximal försöksgräns
err_pam_perm_denied = åtkomst nekad
err_pam_cred_unavail = hämtning av inloggningsuppgifter misslyckades
err_pam_maxtries = gränsen för antal försök nådd
err_pam_perm_denied = tillstånd nekas
err_pam_session = sessionsfel
err_pam_sys = systemfel
err_pam_user_unknown = okänd användare
err_path = misslyckades att ställa in sökväg
err_perm_dir = misslyckades att ändra aktuell katalog
err_perm_group = misslyckades att nergradera gruppbehörigheter
err_perm_user = misslyckades att nergradera användarbehörigheter
err_pwnam = misslyckades att hämta användarinfo
err_user_gid = misslyckades att ställa in användar-GID
err_user_init = misslyckades att initialisera användaren
err_user_uid = misslyckades att ställa in användar-UID
err_xsessions_dir = misslyckades att hitta sessionskatalog
err_xsessions_open = misslyckades att öppna sessionskatalog
err_path = inställning av sökväg misslyckades
err_perm_dir = byte av nuvarande katalog misslyckades
err_perm_group = nedgradering av grupptillstånd misslyckades
err_perm_user = nedgradering av användartillstånd misslyckades
err_pwnam = hämtning av användarinformation misslyckades
err_sleep = strömsparlägets kommando misslyckades
err_start = startkommando misslyckades
err_battery = hämtning av batteristatus misslyckades
err_switch_tty = byte av tty misslyckades
err_tty_ctrl = överföring av tty-kontroll misslyckades
err_no_users = inga användare hittades
err_uid_range = dynamisk hämtning av uid-intervall misslyckades
err_user_gid = inställning av användarens GID misslyckades
err_user_init = initiering av användare misslyckades
err_user_uid = inställning av användarens UID misslyckades
err_xauth = xauth-kommando misslyckades
err_xcb_conn = xcb-anslutning misslyckades
err_xsessions_dir = sessionskatalog hittades inte
err_xsessions_open = öppning av sessionskatalog misslyckades
hibernate = viloläge
insert = infoga
login = inloggning
logout = utloggad
no_x11_support = x11-stöd inaktiverat vid kompilering
normal = normal
numlock = numlock
other = övrig
password = lösenord
restart = starta om
shell = skal
shell = shell
shutdown = stäng av
sleep = viloläge
toggle_password = visa/dölj lösenord
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,13 +1,33 @@
authenticating = kimlik doğrulanıyor...
brightness_down = parlakligi azalt
brightness_up = parlakligi arttir
capslock = capslock
custom = özel
custom_info_err_output_long = çıktı çok uzun
custom_info_err_no_output = çıktı yok
custom_info_err_no_output_error = , olası hata
err_alloc = basarisiz bellek ayirma
err_args = komut satırı argümanları ayrıştırılamıyor
err_autologin_session = otomatik oturum açma oturumu bulunamadı
err_bounds = sinirlarin disinda dizin
err_brightness_change = parlaklık değiştirilemedi
err_chdir = ev klasoru acilamadi
err_console_dev = konsola erisilemedi
err_clock_too_long = saat dizesi çok uzun
err_config = yapılandırma dosyası ayrıştırılamıyor
err_crawl = oturum dizinleri taranamadı
err_dgn_oob = log mesaji
err_domain = gecersiz etki alani
err_empty_password = boş parola kullanılamaz
err_envlist = ortam değişkenleri listesi alınamadı
err_get_active_tty = aktif tty alınamadı
err_hibernate = hazırda bekletme komutu çalıştırılamadı
err_hostname = ana bilgisayar adi alinamadi
err_inactivity = hareketsizlik komutu çalıştırılamadı
err_lock_state = kilit durumu alınamadı
err_log = günlük dosyasıılamadı
err_mlock = parola bellegi kilitlenemedi
err_null = bos isaretci hatasi
err_numlock = numlock ayarlanamadı
err_pam = pam islemi basarisiz oldu
err_pam_abort = pam islemi durduruldu
err_pam_acct_expired = hesabin suresi dolmus
@ -29,17 +49,34 @@ err_perm_dir = gecerli dizin degistirilemedi
err_perm_group = grup izinleri dusurulemedi
err_perm_user = kullanici izinleri dusurulemedi
err_pwnam = kullanici bilgileri alinamadi
err_sleep = uyku komutu çalıştırılamadı
err_start = başlatma komutu çalıştırılamadı
err_battery = pil durumu yüklenemedi
err_switch_tty = tty değiştirilemedi
err_tty_ctrl = tty kontrol aktarımı başarısız oldu
err_no_users = kullanıcı bulunamadı
err_uid_range = uid aralığı dinamik olarak alınamadı
err_user_gid = kullanici icin GID ayarlanamadi
err_user_init = kullanici oturumu baslatilamadi
err_user_uid = kullanici icin UID ayarlanamadi
err_xauth = xauth komutu başarısız oldu
err_xcb_conn = xcb bağlantısı başarısız oldu
err_xsessions_dir = oturumlar klasoru bulunamadi
err_xsessions_open = oturumlar klasoru acilamadi
hibernate = askiya al
insert = ekle
login = kullanici
logout = oturumdan cikis yapildi
no_x11_support = x11 desteği derleme zamanında devre dışı bırakıldı
normal = normal
numlock = numlock
other = baska
password = sifre
restart = yeniden baslat
shell = shell
shutdown = makineyi kapat
sleep = uykuya al
toggle_password = parolayı göster/gizle
wayland = wayland
x11 = x11
xinitrc = xinitrc

View file

@ -1,13 +1,33 @@
authenticating = автентифікація...
brightness_down = зменшити яскравість
brightness_up = збільшити яскравість
capslock = capslock
custom = власний
custom_info_err_output_long = вивід занадто довгий
custom_info_err_no_output = немає виводу
custom_info_err_no_output_error = , можлива помилка
err_alloc = невдале виділення пам'яті
err_args = не вдалося розібрати аргументи командного рядка
err_autologin_session = сеанс автоматичного входу не знайдено
err_bounds = поза межами індексу
err_brightness_change = не вдалося змінити яскравість
err_chdir = не вдалося відкрити домашній каталог
err_console_dev = невдалий доступ до консолі
err_clock_too_long = рядок годинника занадто довгий
err_config = не вдалося розібрати файл конфігурації
err_crawl = не вдалося сканувати каталоги сесій
err_dgn_oob = повідомлення журналу (log)
err_domain = недійсний домен
err_empty_password = порожній пароль не дозволено
err_envlist = не вдалося отримати список змінних середовища
err_get_active_tty = не вдалося отримати активний tty
err_hibernate = не вдалося виконати команду гібернації
err_hostname = не вдалося отримати ім'я хосту
err_inactivity = не вдалося виконати команду неактивності
err_lock_state = не вдалося отримати стан блокування
err_log = не вдалося відкрити файл журналу
err_mlock = збій блокування пам'яті
err_null = нульовий вказівник
err_numlock = не вдалося встановити numlock
err_pam = невдала pam транзакція
err_pam_abort = pam транзакція перервана
err_pam_acct_expired = термін дії акаунту вичерпано
@ -29,17 +49,34 @@ err_perm_dir = не вдалося змінити поточний катало
err_perm_group = не вдалося понизити права доступу групи
err_perm_user = не вдалося понизити права доступу користувача
err_pwnam = не вдалося отримати дані користувача
err_sleep = не вдалося виконати команду сну
err_start = не вдалося виконати команду запуску
err_battery = не вдалося завантажити стан акумулятора
err_switch_tty = не вдалося переключити tty
err_tty_ctrl = передача керування tty не вдалася
err_no_users = користувачів не знайдено
err_uid_range = не вдалося динамічно отримати діапазон uid
err_user_gid = не вдалося змінити GID користувача
err_user_init = не вдалося ініціалізувати користувача
err_user_uid = не вдалося змінити UID користувача
err_xauth = команда xauth не вдалася
err_xcb_conn = з'єднання xcb не вдалося
err_xsessions_dir = не вдалося знайти каталог сесій
err_xsessions_open = не вдалося відкрити каталог сесій
hibernate = гібернація
insert = вставити
login = логін
logout = вийти
no_x11_support = підтримку x11 вимкнено під час компіляції
normal = нормальний
numlock = numlock
other = інший
password = пароль
restart = перезавантажити
shell = оболонка
shutdown = вимкнути
sleep = сплячий режим
toggle_password = показати/приховати пароль
wayland = wayland
x11 = x11
xinitrc = xinitrc

82
res/lang/zh_CN.ini Normal file
View file

@ -0,0 +1,82 @@
authenticating = 正在认证...
brightness_down = 降低亮度
brightness_up = 提高亮度
capslock = 大写锁定
custom = 自定义
custom_info_err_output_long = 输出过长
custom_info_err_no_output = 无输出
custom_info_err_no_output_error = ,可能有错误
err_alloc = 内存分配失败
err_args = 无法解析命令行参数
err_autologin_session = 未找到自动登录会话
err_bounds = 索引越界
err_brightness_change = 无法更改亮度
err_chdir = 无法打开home文件夹
err_clock_too_long = 时钟字符串过长
err_config = 无法解析配置文件
err_crawl = 无法扫描会话目录
err_dgn_oob = 日志消息
err_domain = 无效的域
err_empty_password = 不允许空密码
err_envlist = 无法获取环境变量列表
err_get_active_tty = 无法获取当前活动的tty
err_hibernate = 无法执行休眠命令
err_hostname = 获取主机名失败
err_inactivity = 无法执行非活动命令
err_lock_state = 无法获取锁定状态
err_log = 无法打开日志文件
err_mlock = 锁定密码存储器失败
err_null = 空指针
err_numlock = 无法设置numlock
err_pam = PAM事件失败
err_pam_abort = PAM事务已中止
err_pam_acct_expired = 帐户已过期
err_pam_auth = 身份验证错误
err_pam_authinfo_unavail = 获取用户信息失败
err_pam_authok_reqd = 口令已过期
err_pam_buf = 内存缓冲区错误
err_pam_cred_err = 设置凭据失败
err_pam_cred_expired = 凭据已过期
err_pam_cred_insufficient = 凭据不足
err_pam_cred_unavail = 无法获取凭据
err_pam_maxtries = 已达到最大尝试次数限制
err_pam_perm_denied = 拒绝访问
err_pam_session = 会话错误
err_pam_sys = 系统错误
err_pam_user_unknown = 未知用户
err_path = 无法设置路径
err_perm_dir = 更改当前目录失败
err_perm_group = 组权限降级失败
err_perm_user = 用户权限降级失败
err_pwnam = 获取用户信息失败
err_sleep = 无法执行睡眠命令
err_start = 无法执行启动命令
err_battery = 无法加载电池状态
err_switch_tty = 无法切换tty
err_tty_ctrl = tty控制转移失败
err_no_users = 未找到用户
err_uid_range = 无法动态获取uid范围
err_user_gid = 设置用户GID失败
err_user_init = 初始化用户失败
err_user_uid = 设置用户UID失败
err_xauth = xauth命令失败
err_xcb_conn = xcb连接失败
err_xsessions_dir = 找不到会话文件夹
err_xsessions_open = 无法打开会话文件夹
hibernate = 休眠
insert = 插入
login = 登录
logout = 注销
no_x11_support = x11支持在编译时被禁用
normal = 正常
numlock = 数字锁定
other = 其他
password = 密码
restart = 重启
shell = shell
shutdown = 关机
sleep = 睡眠
toggle_password = 显示/隐藏密码
wayland = wayland
x11 = x11
xinitrc = xinitrc

82
res/lang/zh_TW.ini Normal file
View file

@ -0,0 +1,82 @@
authenticating = 正在驗證...
brightness_down = 降低亮度
brightness_up = 提高亮度
capslock = 大寫鎖定
custom = 自訂
custom_info_err_output_long = 輸出過長
custom_info_err_no_output = 無輸出
custom_info_err_no_output_error = ,可能有錯誤
err_alloc = 記憶體配置失敗
err_args = 無法解析命令列參數
err_autologin_session = 找不到自動登入工作階段
err_bounds = 索引超出範圍
err_brightness_change = 無法變更亮度
err_chdir = 無法開啟家目錄
err_clock_too_long = 時鐘字串過長
err_config = 無法解析設定檔
err_crawl = 無法掃描工作階段目錄
err_dgn_oob = 日誌訊息
err_domain = 無效的網域
err_empty_password = 不允許空密碼
err_envlist = 無法取得環境變數清單
err_get_active_tty = 無法取得目前使用中的 tty
err_hibernate = 無法執行休眠命令
err_hostname = 取得主機名稱失敗
err_inactivity = 無法執行閒置命令
err_lock_state = 無法取得鎖定狀態
err_log = 無法開啟日誌檔案
err_mlock = 鎖定密碼記憶體失敗
err_null = 空指標
err_numlock = 無法設定 numlock
err_pam = PAM 交易失敗
err_pam_abort = PAM 交易已中止
err_pam_acct_expired = 帳號已過期
err_pam_auth = 驗證錯誤
err_pam_authinfo_unavail = 取得使用者資訊失敗
err_pam_authok_reqd = 金鑰已過期
err_pam_buf = 記憶體緩衝區錯誤
err_pam_cred_err = 設定憑證失敗
err_pam_cred_expired = 憑證已過期
err_pam_cred_insufficient = 憑證不足
err_pam_cred_unavail = 無法取得憑證
err_pam_maxtries = 已達到最大嘗試次數限制
err_pam_perm_denied = 拒絕存取
err_pam_session = 工作階段錯誤
err_pam_sys = 系統錯誤
err_pam_user_unknown = 未知的使用者
err_path = 無法設定路徑
err_perm_dir = 變更目前目錄失敗
err_perm_group = 群組權限降級失敗
err_perm_user = 使用者權限降級失敗
err_pwnam = 取得使用者資訊失敗
err_sleep = 無法執行睡眠命令
err_start = 無法執行啟動命令
err_battery = 無法載入電池狀態
err_switch_tty = 無法切換 tty
err_tty_ctrl = tty 控制權移轉失敗
err_no_users = 找不到使用者
err_uid_range = 無法動態取得 uid 範圍
err_user_gid = 設定使用者 GID 失敗
err_user_init = 初始化使用者失敗
err_user_uid = 設定使用者 UID 失敗
err_xauth = xauth 命令失敗
err_xcb_conn = xcb 連線失敗
err_xsessions_dir = 找不到工作階段資料夾
err_xsessions_open = 無法開啟工作階段資料夾
hibernate = 休眠
insert = 插入
login = 登入
logout = 登出
no_x11_support = x11 支援已在編譯時停用
normal = 正常
numlock = 數字鎖定
other = 其他
password = 密碼
restart = 重新啟動
shell = shell
shutdown = 關機
sleep = 睡眠
toggle_password = 顯示/隱藏密碼
wayland = wayland
x11 = x11
xinitrc = xinitrc

8
res/ly-dinit Normal file
View file

@ -0,0 +1,8 @@
type = process
restart = true
smooth-recovery = true
command = $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME
depends-on = login.target
termsignal = HUP
# ly needs access to the console while login.target already occupies it
options = shares-console

7
res/ly-freebsd-wrapper Normal file
View file

@ -0,0 +1,7 @@
#!/bin/sh
# On FreeBSD, even if we override the default login program, getty will still
# try to append "login -fp root" as arguments to Ly, which is not supported.
# To avoid this, we use a wrapper script that ignores these arguments before
# actually executing Ly.
exec $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME

28
res/ly-kmsconvt@.service Normal file
View file

@ -0,0 +1,28 @@
# Default kmsconvt@.service
# Modified for Ly by AnErrupTion <anerruption@disroot.org>
[Unit]
Description=TUI display manager on %I using KMSCON
After=systemd-user-sessions.service plymouth-quit-wait.service rc-local.service kmsconvt@%i.service
Conflicts=getty@%i.service kmsconvt@%i.service ly@%i.service
OnFailure=ly@%i.service
[Service]
ExecStart=$PREFIX_DIRECTORY/bin/kmscon --term=linux --font-engine unifont --vt=%I --login -- $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME --use-kmscon-vt
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
StandardInput=tty
StandardOutput=tty
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
IgnoreSIGPIPE=no
SendSIGHUP=yes
[Install]
Alias=autovt@.service
WantedBy=multi-user.target
DefaultInstance=tty2

View file

@ -19,17 +19,13 @@ then
commandUL="/sbin/agetty"
fi
## Get the tty from the conf file
CONFTTY=$(cat /etc/ly/config.ini | sed -n 's/^tty.*=[^1-9]*// p')
## The execution vars
# If CONFTTY is empty then default to 2
TTY="tty${CONFTTY:-2}"
TTY="tty$DEFAULT_TTY"
TERM=linux
BAUD=38400
# If we don't have getty then we should have agetty
command=${commandB:-$commandUL}
command_args_foreground="-nl /usr/bin/ly $TTY $BAUD $TERM"
command_args_foreground="-nl $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME $TTY $BAUD $TERM"
depend() {
after agetty

View file

@ -7,6 +7,4 @@ fi
BAUD_RATE=38400
TERM_NAME=linux
auxtty=$(/bin/cat /etc/ly/config.ini 2>/dev/null 1| /bin/sed -n 's/\(^[[:space:]]*tty[[:space:]]*=[[:space:]]*\)\([[:digit:]][[:digit:]]*\)\(.*\)/\2/p')
TTY=tty${auxtty:-2}
TTY=tty$DEFAULT_TTY

View file

@ -10,4 +10,4 @@ elif [ -x /sbin/agetty -o -x /bin/agetty ]; then
GETTY=agetty
fi
exec setsid ${GETTY} ${GETTY_ARGS} -nl /usr/bin/ly "${TTY}" "${BAUD_RATE}" "${TERM_NAME}"
exec setsid ${GETTY} ${GETTY_ARGS} -nl $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME "${TTY}" "${BAUD_RATE}" "${TERM_NAME}"

2
res/ly-s6/run Normal file
View file

@ -0,0 +1,2 @@
#!/bin/execlineb -P
exec agetty -L -8 -n -l $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME tty$DEFAULT_TTY 115200

1
res/ly-s6/type Normal file
View file

@ -0,0 +1 @@
longrun

65
res/ly-sysvinit Executable file
View file

@ -0,0 +1,65 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: ly
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Ly display manager
# Description: Starts and stops the Ly display manager
### END INIT INFO
#
# Author: AnErrupTion <anerruption@disroot.org>
#
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DAEMON=/usr/bin/ly
TTY=/dev/tty$DEFAULT_TTY
PIDFILE=/var/run/ly.pid
NAME=ly
DESC="Ly display manager"
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting $DESC on $TTY..."
if [ -f "$PIDFILE" ]; then
log_progress_msg "$DESC is already running"
log_end_msg 0
return 0
fi
# Ensure TTY exists
[ -c "$TTY" ] || {
log_failure_msg "$TTY does not exist"
return 1
}
start-stop-daemon --start --background --make-pidfile --pidfile $PIDFILE \
--chdir / --exec /bin/sh -- -c "exec setsid sh -c 'exec <$TTY >$TTY 2>&1 $DAEMON'"
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $DESC..."
start-stop-daemon --stop --pidfile $PIDFILE --retry 5
RETVAL=$?
[ $RETVAL -eq 0 ] && rm -f "$PIDFILE"
log_end_msg $RETVAL
;;
restart)
echo "Restarting $DESC..."
$0 stop
sleep 1
$0 start
;;
status)
status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $?
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart|status}"
exit 1
;;
esac
exit 0

View file

@ -1,17 +0,0 @@
[Unit]
Description=TUI display manager
After=systemd-user-sessions.service plymouth-quit-wait.service
After=getty@tty2.service
Conflicts=getty@tty2.service
[Service]
Type=idle
ExecStart=/usr/bin/ly
StandardError=journal
StandardInput=tty
TTYPath=/dev/tty2
TTYReset=yes
TTYVHangup=yes
[Install]
Alias=display-manager.service

46
res/ly@.service Normal file
View file

@ -0,0 +1,46 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Modified for Ly by AnErrupTion <anerruption@disroot.org>
[Unit]
Description=TUI display manager on %I
After=systemd-user-sessions.service plymouth-quit-wait.service getty@%i.service
Conflicts=getty@%i.service kmsconvt@%i.service ly-kmsconvt@%i.service
[Service]
ExecStart=$PREFIX_DIRECTORY/bin/agetty -nl $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME %I ${TERM}
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
StandardInput=tty
StandardOutput=tty
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
IgnoreSIGPIPE=no
SendSIGHUP=yes
ImportCredential=tty.virtual.%I.agetty.*:agetty.
ImportCredential=tty.virtual.%I.login.*:login.
ImportCredential=agetty.*
ImportCredential=login.*
ImportCredential=shell.*
# Unset locale for the console getty since the console has problems
# displaying some internationalized messages.
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
[Install]
Alias=autovt@.service
WantedBy=multi-user.target
DefaultInstance=tty2

View file

@ -1,5 +1,7 @@
#%PAM-1.0
# OpenPAM (used in FreeBSD) doesn't support prepending "-" for ignoring missing
# modules.
auth include login
account include login
password include login

View file

@ -0,0 +1,8 @@
#%PAM-1.0
# OpenPAM (used in FreeBSD) doesn't support prepending "-" for ignoring missing
# modules.
auth required pam_permit.so
account include login
password include login
session include login

16
res/pam.d/ly-linux Normal file
View file

@ -0,0 +1,16 @@
#%PAM-1.0
auth include login
-auth optional pam_gnome_keyring.so
-auth optional pam_kwallet5.so
account include login
password include login
-password optional pam_gnome_keyring.so use_authtok
-session optional pam_systemd.so class=greeter
-session optional pam_elogind.so
session include login
-session optional pam_gnome_keyring.so auto_start
-session optional pam_kwallet5.so auto_start

View file

@ -0,0 +1,16 @@
#%PAM-1.0
auth required pam_permit.so
-auth optional pam_gnome_keyring.so
-auth optional pam_kwallet5.so
account include login
password include login
-password optional pam_gnome_keyring.so use_authtok
-session optional pam_systemd.so class=greeter
-session optional pam_elogind.so
session include login
-session optional pam_gnome_keyring.so auto_start
-session optional pam_kwallet5.so auto_start

Some files were not shown because too many files have changed in this diff Show more