mirror of https://github.com/20kdc/OC-KittenOS.git
Compare commits
153 Commits
Author | SHA1 | Date |
---|---|---|
20kdc | 9d152bb994 | |
20kdc | d2ad9ce4eb | |
20kdc | d623375c04 | |
20kdc | 0f333b9c2f | |
20kdc | 85869aa80d | |
20kdc | 6fc56b4d64 | |
20kdc | f083efd9a3 | |
20kdc | 2ea52fc362 | |
20kdc | 2dbc3cdb0e | |
20kdc | 2fe4884558 | |
20kdc | dcd7154ec2 | |
20kdc | 881193aa9a | |
20kdc | e308f54ad7 | |
20kdc | cbe40da3bb | |
20kdc | 8bd316338b | |
20kdc | 3dbea9d3f7 | |
20kdc | 2ebc0840ac | |
20kdc | da58c88d0d | |
20kdc | fe17b0fb93 | |
20kdc | c9157a1b7c | |
20kdc | ca4b6e5df2 | |
20kdc | 7d1f6d2cae | |
20kdc | a585ce4a75 | |
20kdc | 3ed1cebe25 | |
20kdc | 7c70a1128c | |
20kdc | 5ac8f9ff11 | |
20kdc | f761ad5824 | |
20kdc | aef0043d4a | |
20kdc | 151097cdce | |
20kdc | bfcb4f9028 | |
20kdc | cc55e8a66f | |
20kdc | 3c4a3147c4 | |
20kdc | bc4d626b4e | |
20kdc | dd21abe8fa | |
20kdc | 22c1c211ef | |
20kdc | 7840f0a231 | |
20kdc | 339571ee9b | |
20kdc | 479412a5bb | |
20kdc | d2ee505316 | |
20kdc | f5ba0489b2 | |
20kdc | 20c016f068 | |
20kdc | fab88f137c | |
20kdc | 7680aa7579 | |
20kdc | 429de87a61 | |
20kdc | 7307eb30a4 | |
20kdc | 0d9583fcff | |
20kdc | 375995c2d3 | |
20kdc | efae7716da | |
20kdc | 27bd71f9e4 | |
20kdc | 3d3517bc53 | |
20kdc | 916d127337 | |
20kdc | 630c5f57f5 | |
20kdc | 3502cdedc2 | |
20kdc | 21294872f3 | |
20kdc | 71a0aa0b08 | |
20kdc | 99cb58d9fc | |
20kdc | 324ed86335 | |
20kdc | 13d3abfd26 | |
20kdc | 27f7fe35da | |
20kdc | 6255587090 | |
20kdc | f903a16166 | |
20kdc | 347bebdb03 | |
20kdc | 8f86dfd730 | |
20kdc | 76448b8187 | |
20kdc | b320dfe083 | |
20kdc | 3bc323e268 | |
20kdc | 1e3bf096d5 | |
20kdc | 6237871d36 | |
20kdc | d371044146 | |
20kdc | 050941a513 | |
20kdc | 3ba0792dfe | |
20kdc | 7ca2c26979 | |
20kdc | ccb9c3b279 | |
20kdc | 8ab47c96b3 | |
20kdc | e1530057a6 | |
20kdc | 9254745a33 | |
20kdc | 4c12bb548a | |
20kdc | 46d60df1ec | |
20kdc | e75cd20aa4 | |
20kdc | ae89024112 | |
20kdc | c3b6cdc898 | |
20kdc | d1e83616e7 | |
20kdc | 154d9676ab | |
20kdc | e6afe02457 | |
20kdc | 3df5b6d3c9 | |
20kdc | f011da9810 | |
20kdc | 28d639e1d1 | |
20kdc | 7fa7441794 | |
20kdc | d5405685dd | |
20kdc | 7092b41f78 | |
20kdc | 75e043f14f | |
20kdc | e57eb00a4e | |
20kdc | a438333f92 | |
Izaya | 42e4212dc6 | |
20kdc | 5ae3a6019d | |
20kdc | 5fda45b663 | |
Izaya | 7996915103 | |
Izaya | 704d54bb8f | |
Izaya | 08a34619e0 | |
Izaya | 233eb9be7e | |
Izaya | c76bf62a53 | |
Izaya | afb7277310 | |
20kdc | 1338185eea | |
20kdc | 51c417b55c | |
20kdc | f6a13c4038 | |
20kdc | 5004f15c2d | |
20kdc | 921425ada4 | |
20kdc | a22ac86b2d | |
20kdc | cda9a8e939 | |
20kdc | beb76fcf6f | |
20kdc | 5ddfb128cd | |
20kdc | bed9f55d70 | |
20kdc | 0b918437d0 | |
20kdc | 6be34ec7e6 | |
20kdc | 26be65e1bb | |
20kdc | 5c4d9ac17f | |
20kdc | 2081cd8e49 | |
20kdc | 6f2c1e2f2f | |
20kdc | 0c2fd021fe | |
20kdc | 6e44b1a834 | |
20kdc | 0b7e6557a5 | |
20kdc | b75dc370dc | |
20kdc | f49a4bf433 | |
20kdc | de822181bc | |
20kdc | 380c325691 | |
20kdc | 584108d6af | |
20kdc | 981ea559c6 | |
20kdc | 3d399dc047 | |
20kdc | 1bb8d16298 | |
20kdc | 2b15193aa9 | |
20kdc | be85af84c7 | |
20kdc | 093c50a3a7 | |
20kdc | a9670637d1 | |
20kdc | d808885c59 | |
20kdc | 491f5ad3cc | |
20kdc | 0fc497c13f | |
20kdc | 3ee7c42d02 | |
20kdc | 783bf9361f | |
20kdc | e487439b3b | |
20kdc | 2e283d2767 | |
20kdc | 2597c9e5aa | |
20kdc | a5372eafe1 | |
20kdc | 6c0659de60 | |
20kdc | d482611b9d | |
20kdc | ef28bfafd5 | |
20kdc | 9caf0115d2 | |
20kdc | a837148c3b | |
20kdc | 899a3b2521 | |
20kdc | c5b58e79ff | |
20kdc | 3e43cd3aaa | |
20kdc | e984f97ea9 | |
20kdc | d16a9602f3 | |
20kdc | c1d2856755 |
|
@ -1,9 +1,8 @@
|
|||
# This is released into the public domain.
|
||||
# No warranty is provided, implied or otherwise.
|
||||
|
||||
# leaving in preSH.tar.gz for anyone who's interested
|
||||
# in how NOT to do compression
|
||||
code.tar
|
||||
code/data/app-claw/*
|
||||
work.tar
|
||||
work
|
||||
work/
|
||||
|
@ -20,7 +19,24 @@ repobuild/*/*
|
|||
repobuild/*/*/
|
||||
repobuild/*/*/*
|
||||
|
||||
laboratory/ocemu.cfg
|
||||
laboratory/bios.lua
|
||||
laboratory/data
|
||||
laboratory/log
|
||||
laboratory/*/
|
||||
laboratory/*/*
|
||||
laboratory/*/*/
|
||||
laboratory/*/*/*
|
||||
laboratory/*/*/*/
|
||||
laboratory/*/*/*/*
|
||||
|
||||
inst.lua
|
||||
com2/code.tar.bd
|
||||
# Available as the respective release
|
||||
inst-gold.lua
|
||||
# Compression stuff
|
||||
inst/iSymTab
|
||||
# internal
|
||||
upldr.sh
|
||||
upldr-dev.sh
|
||||
upldr-gold.sh
|
||||
repository/inst.lua
|
||||
|
|
219
README.md
219
README.md
|
@ -1,40 +1,62 @@
|
|||
# KittenOS NEO (pre-release)
|
||||
# ATTENTION! WARNING! ETCETERA!!!
|
||||
|
||||
AS OF 11TH JULY 2022, ALL SUPPORT AND MAINTENANCE ON THIS REPOSITORY HAS BEEN DROPPED.
|
||||
|
||||
Microsoft finally turned off whatever mechanism PolyMC was using to let people play multiplayer on Mojang accounts.
|
||||
|
||||
Use UltimMC. Use something like https://modrinth.com/mod/easyauth . Host your servers this way.
|
||||
|
||||
Microsoft have proven they are willing to remove a bought product off your hands if you don't agree to their new rules about it.
|
||||
|
||||
They have proven that "buying" something off of them means nothing and anything you've "bought" from Mojang means nothing.
|
||||
|
||||
*They have stolen what they have been paid money for. Therefore they don't deserve to have anything bought off them at all.*
|
||||
|
||||
And before any of you go "just migrate", look at the warning signs as to the ban systems.
|
||||
|
||||
Maybe watch FitMC's video: https://www.youtube.com/watch?v=rdoFUhd0EkI
|
||||
|
||||
Or maybe read the migration horror stories:
|
||||
|
||||
+ https://www.reddit.com/r/xbox/comments/sscoa7/does_anyone_have_an_idea_why_was_i_locked_out_of/
|
||||
+ https://news.ycombinator.com/item?id=31551846
|
||||
+ https://answers.microsoft.com/en-us/xbox/forum/all/im-locked-out-of-minecraft-after-migrating-my/9f1f64cb-6a2d-4cd2-abb8-0010a6bc6f31
|
||||
+ https://www.reddit.com/r/Minecraft/comments/we8asn/why_microsoft_ever_since_i_was_forced_to_migrate/
|
||||
+ even rekrap1 got bitten by this: https://www.youtube.com/watch?v=rzPYH98TTMM
|
||||
|
||||
.
|
||||
|
||||
.
|
||||
|
||||
.
|
||||
|
||||
.
|
||||
|
||||
.
|
||||
|
||||
.
|
||||
|
||||
.
|
||||
|
||||
.
|
||||
|
||||
.
|
||||
|
||||
.
|
||||
|
||||
. OLD README BELOW
|
||||
|
||||
# KittenOS NEO
|
||||
|
||||
As per usual, no warranty, not my responsibility if this breaks, or if you somehow try to run it on an actual (non-OpenComputers) computer.
|
||||
|
||||
The first commit is after I got the installer working again after the new compression system (BDIVIDE).
|
||||
|
||||
That's what the "SYSTEM HEROES" thing is about.
|
||||
|
||||
## Known Issues (That Aren't KittenOS NEO's Fault)
|
||||
|
||||
Touch calibration is off because OC's setPrecise seems to offset coordinates down and right by a character. OCEmu's does not. I consider this an OC bug. This is known to occur on at least `OpenComputers-MC1.12.2-1.7.2.67.jar`. If you want to check my routines, see sys-everest, search for the lowest instance of `"touch"`. Or just use OCEmu, which doesn't change anything when precise is set, and thus can't be doing anything different than the setPrecise(false) behavior.
|
||||
|
||||
Wide character support *may* encounter issues due to performance-saving tricks in some old OC versions. The 1.12.2 version being used at LimboCon doesn't have the issue, so it's been dealt with. Point is, not a KittenOS NEO bug if it happens.
|
||||
|
||||
## Known Issues (That Are KittenOS NEO's Fault But Aren't Really Fixable)
|
||||
|
||||
If you move a window over another window, that window has to rerender. The alternative is buffering the window. Since memory is a concern, that is not going to happen. Some windows are more expensive to render than others (`klogo` tries to use less RAM if the system is 192K, at the expense of disk access) - move the most expensive window out of the way, since once a window is top-most, moving it around is usually "free".
|
||||
|
||||
If the system runs out of memory, the kernel could crash, or alternatively the system goes into a limbo state. You're more or less doomed. Given that almost everything in Lua causes a memory allocation, I'm not exactly sure how I'd be supposed to fix this properly.
|
||||
|
||||
Any situation where the system fails to boot *may* be fixable with Safe Mode.
|
||||
This includes if you copied a sufficiently large bit of text into the persistent clipboard, and now Icecap or Everest won't start.
|
||||
The catch is, it wipes your settings. As the settings are always in RAM, and contain just about every *fixable* thing that can break your boot,
|
||||
nuking them should bring you to defaults.
|
||||
|
||||
And finally, just because a system can multitask somewhat on 192K doesn't mean it can do the impossible regarding memory usage.
|
||||
Lesson learned: Cleaner design -> Higher memory usage.
|
||||
So anyone who wants the design to be made even cleaner should probably reread this paragraph.
|
||||
(In R0, editing the kernel causes 192K systems to fail to open filedialogs. I've fixed this in R1.)
|
||||
|
||||
## Description
|
||||
|
||||
At least in theory: "efficient. multi-tasking. clean. security-oriented".
|
||||
|
||||
KittenOS NEO is an OpenComputers operating system designed for Tier 1 hardware.
|
||||
|
||||
This means, among other things, it has an operating overhead limit of 192KiB real-world (on 32-bit or 64-bit).
|
||||
This means, among other things, it has an operating overhead limit of 192KiB in os.totalMemory() units, on 32-bit or 64-bit runtimes (given the default scale value).
|
||||
|
||||
Unlike the original KittenOS (now in the "legacy" branch), it is also designed with some attempt at cleanliness.
|
||||
|
||||
|
@ -50,6 +72,100 @@ KittenOS NEO will install itself.
|
|||
|
||||
(This does not account for custom EEPROMs.)
|
||||
|
||||
NOTE: Attempting to run the KittenOS NEO installer as a program in an OS will fail,
|
||||
giving you instructions as shown here.
|
||||
|
||||
## Authors & Licensing
|
||||
|
||||
Disclaimer: I Am Not A Lawyer. I probably screwed something up in this.
|
||||
|
||||
It would be really nice if, if I have screwed up, that you tell me how.
|
||||
|
||||
Preferably with a solution that fits the technological constraints.
|
||||
|
||||
Licensing in this project is rather fluid,
|
||||
but everything that is not in `repository/` is unconditionally under the following license:
|
||||
|
||||
Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
|
||||
This will be referred to as "BSD0".
|
||||
|
||||
It should be considered equivalent to CC0, and this is the intent,
|
||||
but it is smaller, which is somewhat important when optimizing for size.
|
||||
|
||||
At this time, the majority of the code/ folder is by 20kdc, but exceptions may occur.
|
||||
|
||||
These exceptions will be documented below, and must be for a PR affecting code/ to be accepted:
|
||||
|
||||
```
|
||||
No exceptions exist at this time.
|
||||
```
|
||||
|
||||
The repository folder is much more complex, as the structure represents places in a running system,
|
||||
so licensing information cannot be directly bundled with the files that require it.
|
||||
|
||||
The contents of the repository/docs/licensing files represent a "full text" for a given license,
|
||||
used in order to ensure legal compliance with a given license's "distribute with the program" clauses.
|
||||
|
||||
It is assumed that this is sufficient.
|
||||
|
||||
A separate package is used for each license such that the user must go out of their way to not download the license.
|
||||
|
||||
The limitations of OpenComputers affect the available choices here, and having separate license copies for each package is not an available choice.
|
||||
|
||||
Nor is having a separate license package for each individual license, unless you would prefer an unbrowsable repository.
|
||||
|
||||
The contents of the repository/docs/repoauthors folder
|
||||
is a human-readable per-package manifest of all files and their
|
||||
licenses.
|
||||
|
||||
If you find this uncompliant with the license of a package,
|
||||
please request the removal of the affected packages.
|
||||
|
||||
## About NOTE-TO-MS.asc
|
||||
|
||||
It exists because it needs to exist.
|
||||
It does not represent the opinions of those who have contributed to the repository,
|
||||
only those of the person who digitally signed it (20kdc).
|
||||
|
||||
## Known Issues (That Aren't KittenOS NEO's Fault)
|
||||
|
||||
Touch calibration could be off if the setPrecise support mess didn't work properly.
|
||||
|
||||
Wide character support *may* encounter issues due to performance-saving tricks (?) in some old OC versions.
|
||||
|
||||
The 1.12.2 version being used at LimboCon doesn't have the issue, so it's been dealt with. Point is, not a KittenOS NEO bug if it happens.
|
||||
|
||||
## Known Issues (That Are KittenOS NEO's Fault But Aren't Really Fixable)
|
||||
|
||||
Having a window around that uses the palette-setting interface can cause funky graphical issues on
|
||||
a window that does not receive or lose focus when the palette changes.
|
||||
The alternative is rerendering all windows on palette change, or attempting to detect this particular case.
|
||||
This isn't very fast, so the graphics corruption is considered worth it.
|
||||
Critical UI gets protected from this by having a set of 4 reserved colours,
|
||||
but this can't be expanded without hurting Tier 2 systems.
|
||||
|
||||
If you move a window over another window, that window has to rerender. The alternative is buffering the window. Since memory is a concern, that is not going to happen. Some windows are more expensive to render than others (`klogo` tries to use less RAM if the system is 192K, at the expense of disk access) - move the most expensive window out of the way, since once a window is top-most, moving it around is usually "free".
|
||||
|
||||
If the system runs out of memory, the kernel could crash, or alternatively the system goes into a limbo state. You're more or less doomed.
|
||||
Given that almost everything in Lua causes a memory allocation, I'm not exactly sure how I'd be supposed to fix this properly.
|
||||
|
||||
Any situation where the system fails to boot *may* be fixable with Safe Mode.
|
||||
This includes if you copied a sufficiently large bit of text into the persistent clipboard, and now Icecap or Everest won't start.
|
||||
The catch is, it wipes your settings. As the settings are always in RAM, and contain just about every *fixable* thing that can break your boot,
|
||||
nuking them should bring you to defaults.
|
||||
|
||||
And finally, just because a system can multitask somewhat on 192K doesn't mean it can do the impossible regarding memory usage.
|
||||
Lesson learned: Cleaner design -> Higher memory usage.
|
||||
So anyone who wants the design to be made even cleaner should probably reread this paragraph.
|
||||
(In R0, editing the kernel causes 192K systems to fail to open filedialogs. I've fixed this in R1.
|
||||
I don't know if I've screwed this up in R2, because all this focus on usability improvements has probably gone back a step regarding memory use.)
|
||||
|
||||
## Policy regarding updates
|
||||
|
||||
KittenOS NEO's installer, including the full KittenOS NEO base operating system, is 65536 bytes or below.
|
||||
|
@ -58,21 +174,7 @@ As the installer must be loaded in full into RAM, this is not negotiable.
|
|||
|
||||
If it can't be kept this way with the current compressor, then a better compressor will have to be made.
|
||||
|
||||
Everything following is completely a draft. This is more like a guideline rather than actual policy.
|
||||
|
||||
All kernel or security-critical `sys-` process bugs will cause an installer update.
|
||||
|
||||
Other bugs will merely result in an updated copy in the repository.
|
||||
|
||||
This copy will be copied to installer code if and only if another condition requires the installer code be updated.
|
||||
|
||||
The code in the `code/` folder is the code meant for the installer.
|
||||
|
||||
Non-installer code is in the `repository/`, and thus accessible via CLAW.
|
||||
|
||||
As HTTPS is not used for this due to various weirdness that occurs when I try, I'm hosting the repository and `inst.lua` at `http://20kdc.duckdns.org/neo`.
|
||||
|
||||
Requests for additional features in system APIs will NOT cause an installer update.
|
||||
Frankly I don't even know what policy after that ought to be.
|
||||
|
||||
## Building
|
||||
|
||||
|
@ -82,33 +184,6 @@ The tools that I haven't gotten rid of are the ones that still work properly.
|
|||
|
||||
Firstly, for an uncompressed installer (just to test installer basecode), you use `mkucinst.lua`.
|
||||
|
||||
This kind of has some overlap with `package.sh` so that needs to be dealt with at some point.
|
||||
|
||||
Secondly, for a compressed installer, you use `package.sh` to rebuild `code.tar`, then use something along the lines of:
|
||||
|
||||
lua heroes.lua `wc -c code.tar` > inst.lua
|
||||
|
||||
This will build the compressed installer.
|
||||
|
||||
## Kernel Architecture
|
||||
|
||||
KittenOS NEO is an idea of what a Lua-based efficient microkernel might look like.
|
||||
|
||||
Scheduling is based entirely around uptime and timers,
|
||||
which cause something to be executed at a given uptime.
|
||||
|
||||
That said, for a microkernel it's still a bit larger than I'd have hoped.
|
||||
|
||||
If anyone has any ideas, put them in an issue? If they're not too damaging, I'll use the saved space to add a thank-you-note to them in the kernel.
|
||||
|
||||
## Installer Architecture
|
||||
|
||||
The installer is split into a generic TAR extractor frontend `insthead.lua` and a replacable compression backend (written in by relevant tools - in current versions, `heroes.lua` is where it starts).
|
||||
|
||||
There was more details on this but the details changed.
|
||||
|
||||
## License
|
||||
|
||||
This is released into the public domain.
|
||||
No warranty is provided, implied or otherwise.
|
||||
Secondly, for a compressed installer, you use `package.sh`.
|
||||
|
||||
That rebuilds `code.tar` and `inst.lua`, and also prepares the final structure of the repository to upload.
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
# Claw2 Formats
|
||||
|
||||
## .c2l format
|
||||
|
||||
The .c2l format is the server package list for Claw2.
|
||||
|
||||
In an exception to the rule, this file only exists on the server.
|
||||
|
||||
It is used solely in the main package list panel.
|
||||
|
||||
It is a file made up of lines.
|
||||
|
||||
Each line contains a package name, followed by a dot, followed by the package version.
|
||||
|
||||
## .V.c2p format
|
||||
|
||||
The .V.c2p (where V is the version) format is the entire contents of the package view panel,
|
||||
as text, with newlines, in UTF-8.
|
||||
|
||||
This is used when a package is selected in Claw2.
|
||||
|
||||
## .c2x format
|
||||
|
||||
The .c2x format is the actual installation script for the package.
|
||||
|
||||
It is executed by svc-claw-worker.
|
||||
|
||||
It's loaded in all-at-once, then it's gmatched
|
||||
with the pattern [^\n]+.
|
||||
|
||||
A line starting with "?" represents a dependency.
|
||||
|
||||
A line starting & ending with "/" represents a directory creation.
|
||||
|
||||
And a line starting with "+" represents a file.
|
||||
|
||||
Package metadata is not implied.
|
||||
|
||||
Thus, a valid .c2x is:
|
||||
|
||||
```
|
||||
?neo
|
||||
/apps/
|
||||
+apps/app-carrot.0.c2p
|
||||
+apps/app-carrot.c2x
|
||||
```
|
||||
|
||||
## Claw2 Architecture
|
||||
|
||||
app-claw is a very dumb client, but the only thing that'll bother
|
||||
to parse a .c2l (because it has package list/search),
|
||||
and the only thing that cares about version numbers.
|
||||
|
||||
The purpose of it is to provide an older-CLAW-style GUI.
|
||||
|
||||
It *may* take an argument, in which case a package panel is opened,
|
||||
otherwise the main search panel is opened.
|
||||
|
||||
When it wants to do anything, it shuts itself down, running svc-claw-worker.
|
||||
|
||||
svc-app-claw-worker does all package consistency & such work.
|
||||
|
||||
It can only be run from app-claw, and runs app-claw after it's done.
|
||||
|
||||
It takes 5 arguments:
|
||||
|
||||
1. The target filesystem proxy.
|
||||
2. The target package name. This package is viewed in app-claw after completion.
|
||||
3. The source to download files from.
|
||||
If nil, the package is being deleted.
|
||||
Otherwise, can either be a proxy or a string.
|
||||
Proxy means it's a filesystem,
|
||||
string means it's an internet base.
|
||||
4. Checked flag
|
||||
5. primary inet card proxy, if any
|
|
@ -0,0 +1,38 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- CLAW local.lua converter. Expects to be run from outermost folder.
|
||||
|
||||
local target = ...
|
||||
|
||||
local serial = loadfile("code/libs/serial.lua")()
|
||||
|
||||
for k, v in pairs(serial.deserialize(io.read("*a"))) do
|
||||
print(k .. "." .. v.v .. ".c2p")
|
||||
print(k .. ".c2x")
|
||||
local f2 = io.open(target .. k .. "." .. v.v .. ".c2p", "wb")
|
||||
f2:write(k .. "\n")
|
||||
f2:write(v.desc .. "\n")
|
||||
f2:write("v" .. v.v .. " deps " .. table.concat(v.deps, ", "))
|
||||
f2:close()
|
||||
f2 = io.open(target .. k .. ".c2x", "wb")
|
||||
for _, vx in ipairs(v.deps) do
|
||||
f2:write("?" .. vx .. "\n")
|
||||
end
|
||||
for _, vx in ipairs(v.dirs) do
|
||||
f2:write("/" .. vx .. "\n")
|
||||
end
|
||||
for _, vx in ipairs(v.files) do
|
||||
f2:write("+" .. vx .. "\n")
|
||||
end
|
||||
f2:write("/data\n")
|
||||
f2:write("/data/app-claw\n")
|
||||
f2:write("+data/app-claw/" .. k .. ".c2x\n")
|
||||
f2:write("+data/app-claw/" .. k .. "." .. v.v .. ".c2p\n")
|
||||
f2:close()
|
||||
end
|
||||
|
|
@ -1,7 +1,14 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
return {
|
||||
["neo"] = {
|
||||
desc = "KittenOS NEO Kernel & Base Libs",
|
||||
v = 1,
|
||||
v = 10,
|
||||
deps = {
|
||||
},
|
||||
dirs = {
|
||||
|
@ -16,13 +23,16 @@ return {
|
|||
"libs/serial.lua",
|
||||
"libs/fmttext.lua",
|
||||
"libs/neoux.lua",
|
||||
"libs/lineedit.lua",
|
||||
"libs/braille.lua",
|
||||
"libs/sys-filewrap.lua"
|
||||
"libs/bmp.lua",
|
||||
"libs/sys-filewrap.lua",
|
||||
"libs/sys-gpualloc.lua"
|
||||
},
|
||||
},
|
||||
["neo-init"] = {
|
||||
desc = "KittenOS NEO / sys-init (startup)",
|
||||
v = 0,
|
||||
v = 11,
|
||||
deps = {
|
||||
"neo",
|
||||
"neo-icecap",
|
||||
|
@ -37,7 +47,7 @@ return {
|
|||
},
|
||||
["neo-launcher"] = {
|
||||
desc = "KittenOS NEO / Default app-launcher",
|
||||
v = 0,
|
||||
v = 10,
|
||||
deps = {
|
||||
"neo"
|
||||
},
|
||||
|
@ -50,7 +60,7 @@ return {
|
|||
},
|
||||
["neo-everest"] = {
|
||||
desc = "KittenOS NEO / Everest (windowing)",
|
||||
v = 0,
|
||||
v = 10,
|
||||
deps = {
|
||||
"neo"
|
||||
},
|
||||
|
@ -63,7 +73,7 @@ return {
|
|||
},
|
||||
["neo-icecap"] = {
|
||||
desc = "KittenOS NEO / Icecap",
|
||||
v = 1,
|
||||
v = 10,
|
||||
deps = {
|
||||
"neo"
|
||||
},
|
||||
|
@ -80,7 +90,7 @@ return {
|
|||
},
|
||||
["neo-secpolicy"] = {
|
||||
desc = "KittenOS NEO / Secpolicy",
|
||||
v = 1,
|
||||
v = 10,
|
||||
deps = {
|
||||
},
|
||||
dirs = {
|
||||
|
@ -92,7 +102,7 @@ return {
|
|||
},
|
||||
["neo-coreapps"] = {
|
||||
desc = "KittenOS NEO Core Apps",
|
||||
v = 1,
|
||||
v = 10,
|
||||
deps = {
|
||||
"neo"
|
||||
},
|
||||
|
@ -101,29 +111,39 @@ return {
|
|||
},
|
||||
files = {
|
||||
"apps/app-textedit.lua",
|
||||
"apps/app-batmon.lua",
|
||||
"apps/app-control.lua",
|
||||
"apps/app-taskmgr.lua"
|
||||
}
|
||||
},
|
||||
["app-klogo"] = {
|
||||
desc = "KittenOS NEO Logo",
|
||||
v = 0,
|
||||
["app-bmpview"] = {
|
||||
desc = "KittenOS NEO .bmp viewer",
|
||||
v = 10,
|
||||
deps = {
|
||||
"neo"
|
||||
"neo",
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"data",
|
||||
"data/app-klogo"
|
||||
"apps"
|
||||
},
|
||||
files = {
|
||||
"apps/app-klogo.lua",
|
||||
"data/app-klogo/logo.data"
|
||||
"apps/app-bmpview.lua",
|
||||
},
|
||||
},
|
||||
["neo-logo"] = {
|
||||
desc = "KittenOS NEO Logo (data)",
|
||||
v = 10,
|
||||
deps = {
|
||||
},
|
||||
dirs = {
|
||||
"docs"
|
||||
},
|
||||
files = {
|
||||
"docs/logo.bmp"
|
||||
},
|
||||
},
|
||||
["app-flash"] = {
|
||||
desc = "KittenOS NEO EEPROM Flasher",
|
||||
v = 0,
|
||||
v = 10,
|
||||
deps = {
|
||||
"neo"
|
||||
},
|
||||
|
@ -134,24 +154,50 @@ return {
|
|||
"apps/app-flash.lua"
|
||||
},
|
||||
},
|
||||
["app-claw"] = {
|
||||
desc = "KittenOS NEO Package Manager",
|
||||
v = 1,
|
||||
["app-wget"] = {
|
||||
desc = "KittenOS Web Retriever",
|
||||
v = 10,
|
||||
deps = {
|
||||
"neo"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"libs"
|
||||
"apps"
|
||||
},
|
||||
files = {
|
||||
"apps/app-wget.lua"
|
||||
},
|
||||
},
|
||||
["app-claw"] = {
|
||||
desc = "KittenOS NEO Package Manager",
|
||||
v = 10,
|
||||
deps = {
|
||||
"neo"
|
||||
},
|
||||
dirs = {
|
||||
"apps"
|
||||
},
|
||||
files = {
|
||||
"apps/app-claw.lua",
|
||||
"libs/claw.lua"
|
||||
"apps/svc-app-claw-worker.lua"
|
||||
},
|
||||
},
|
||||
["svc-t"] = {
|
||||
desc = "KittenOS NEO Terminal System",
|
||||
v = 10,
|
||||
deps = {
|
||||
"neo"
|
||||
},
|
||||
dirs = {
|
||||
"apps"
|
||||
},
|
||||
files = {
|
||||
"apps/svc-t.lua",
|
||||
"apps/app-luashell.lua"
|
||||
},
|
||||
},
|
||||
["neo-meta"] = {
|
||||
desc = "KittenOS NEO: Use 'All' to install to other disks",
|
||||
v = 0,
|
||||
v = 10,
|
||||
deps = {
|
||||
"neo",
|
||||
"neo-init",
|
||||
|
@ -160,9 +206,12 @@ return {
|
|||
"neo-icecap",
|
||||
"neo-secpolicy",
|
||||
"neo-coreapps",
|
||||
"app-klogo",
|
||||
"neo-logo",
|
||||
"app-bmpview",
|
||||
"app-flash",
|
||||
"app-claw"
|
||||
"app-claw",
|
||||
"svc-t",
|
||||
"app-wget"
|
||||
},
|
||||
dirs = {
|
||||
},
|
|
@ -0,0 +1,328 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- local.lua : CLAW Repository Metadata
|
||||
-- Authors: 20kdc
|
||||
|
||||
return {
|
||||
["app-eeprog"] = {
|
||||
desc = "Example program: EEPROM programmer / copier",
|
||||
v = 1,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/app-eeprog.lua",
|
||||
"docs/repoauthors/app-eeprog"
|
||||
},
|
||||
},
|
||||
["neo-docs"] = {
|
||||
desc = "KittenOS NEO system documentation",
|
||||
v = 10,
|
||||
deps = {
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"docs/an-intro",
|
||||
"docs/kn-intro",
|
||||
"docs/kn-refer",
|
||||
"docs/kn-sched",
|
||||
"docs/kn-perms",
|
||||
"docs/us-perms",
|
||||
"docs/us-nxapp",
|
||||
"docs/us-setti",
|
||||
"docs/us-evrst",
|
||||
"docs/us-clawf",
|
||||
"docs/us-termi",
|
||||
"docs/ul-seria",
|
||||
"docs/ul-fwrap",
|
||||
"docs/ul-event",
|
||||
"docs/ul-fmttx",
|
||||
"docs/ul-neoux",
|
||||
"docs/ul-brail",
|
||||
"docs/ul-bmp__",
|
||||
"docs/ul-linee",
|
||||
"docs/gp-pedan",
|
||||
"docs/repoauthors/neo-docs"
|
||||
},
|
||||
},
|
||||
["app-nbox2018"] = {
|
||||
desc = "NBOX2018 and NPRT2018, a 3D-printing toolbox",
|
||||
v = 1,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/app-nbox2018.lua",
|
||||
"apps/app-nprt2018.lua",
|
||||
"docs/repoauthors/app-nbox2018"
|
||||
},
|
||||
},
|
||||
["app-allmem"] = {
|
||||
desc = "Near-reproducible memory usage figures",
|
||||
v = 1,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/app-allmem.lua",
|
||||
"docs/repoauthors/app-allmem"
|
||||
},
|
||||
},
|
||||
["app-telnet"] = {
|
||||
desc = "TELNET client",
|
||||
v = 1,
|
||||
deps = {
|
||||
"neo",
|
||||
"svc-t",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/app-telnet.lua",
|
||||
"docs/repoauthors/app-telnet"
|
||||
},
|
||||
},
|
||||
["svc-ghostie"] = {
|
||||
desc = "Application that schedules a scare after a random time to test svc autostart",
|
||||
v = 1,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/svc-ghostie.lua",
|
||||
"apps/app-ghostcall.lua",
|
||||
"docs/repoauthors/svc-ghostie"
|
||||
},
|
||||
},
|
||||
["app-metamachine"] = {
|
||||
desc = "Virtual machine",
|
||||
v = 5,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"libs",
|
||||
"docs",
|
||||
"docs/repoauthors",
|
||||
"data",
|
||||
"data/app-metamachine"
|
||||
},
|
||||
files = {
|
||||
"apps/app-metamachine.lua",
|
||||
"libs/metamachine-vgpu.lua",
|
||||
"libs/metamachine-vfs.lua",
|
||||
"docs/repoauthors/app-metamachine",
|
||||
"data/app-metamachine/confboot.lua",
|
||||
"data/app-metamachine/lucaboot.lua"
|
||||
},
|
||||
},
|
||||
["app-pclogix-upload"] = {
|
||||
desc = "paste.pc-logix.com text uploader",
|
||||
v = 1,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/app-pclogix-upload.lua",
|
||||
"docs/repoauthors/app-pclogix-upload"
|
||||
},
|
||||
},
|
||||
["app-rsctrl"] = {
|
||||
desc = "Redstone control",
|
||||
v = 1,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/app-rsctrl.lua",
|
||||
"docs/repoauthors/app-rsctrl"
|
||||
},
|
||||
},
|
||||
["app-nbcompose"] = {
|
||||
desc = "Music player/composer using the NBS format",
|
||||
v = 2,
|
||||
deps = {
|
||||
"neo",
|
||||
"lib-knbs",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/app-nbcompose.lua",
|
||||
"docs/repoauthors/app-nbcompose"
|
||||
},
|
||||
},
|
||||
["app-tapedeck"] = {
|
||||
desc = "Computronics Tape Drive interface",
|
||||
v = 3,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/app-tapedeck.lua",
|
||||
"docs/repoauthors/app-tapedeck"
|
||||
},
|
||||
},
|
||||
["app-launchbar"] = {
|
||||
desc = "Application launcher bar",
|
||||
v = 0,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-pd"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/app-launchbar.lua",
|
||||
"docs/repoauthors/app-launchbar"
|
||||
},
|
||||
},
|
||||
["app-slaunch"] = {
|
||||
desc = "Searching launcher",
|
||||
v = 0,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-pd"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/app-slaunch.lua",
|
||||
"docs/repoauthors/app-slaunch"
|
||||
},
|
||||
},
|
||||
-- libraries
|
||||
["lib-knbs"] = {
|
||||
desc = "NBS reader/writer library",
|
||||
v = 2,
|
||||
deps = {
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"libs",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"libs/knbs.lua",
|
||||
"docs/repoauthors/lib-knbs"
|
||||
},
|
||||
},
|
||||
["svc-virtudev"] = {
|
||||
desc = "a clone of vcomponent",
|
||||
v = 2,
|
||||
deps = {
|
||||
"neo",
|
||||
"zzz-license-kosneo-bsd0"
|
||||
},
|
||||
dirs = {
|
||||
"apps",
|
||||
"docs",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"apps/svc-virtudev.lua",
|
||||
"apps/app-vdrslamp.lua",
|
||||
"docs/us-virtu",
|
||||
"docs/repoauthors/svc-virtudev"
|
||||
},
|
||||
},
|
||||
-- licenses (MUST BE IMMUTABLE)
|
||||
["zzz-license-pd"] = {
|
||||
desc = "license file 'Public Domain'",
|
||||
v = 0,
|
||||
deps = {
|
||||
},
|
||||
dirs = {
|
||||
"docs",
|
||||
"docs/licensing",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"docs/licensing/Public Domain",
|
||||
"docs/repoauthors/zzz-license-pd"
|
||||
},
|
||||
},
|
||||
["zzz-license-kosneo-bsd0"] = {
|
||||
desc = "license file 'KittenOS NEO BSD0'",
|
||||
v = 0,
|
||||
deps = {
|
||||
},
|
||||
dirs = {
|
||||
"docs",
|
||||
"docs/licensing",
|
||||
"docs/repoauthors"
|
||||
},
|
||||
files = {
|
||||
"docs/licensing/KittenOS NEO BSD0",
|
||||
"docs/repoauthors/zzz-license-kosneo-bsd0"
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
local merges = {...}
|
||||
neo = {
|
||||
wrapMeta = function (x)
|
||||
return x
|
||||
end
|
||||
}
|
||||
local serial = loadfile("code/libs/serial.lua")()
|
||||
local repo = {}
|
||||
for _, v in ipairs(merges) do
|
||||
local f = io.open(v, "rb")
|
||||
local fd = f:read("*a")
|
||||
f:close()
|
||||
for k, v in pairs(serial.deserialize(fd)) do
|
||||
repo[k] = v
|
||||
end
|
||||
end
|
||||
io.write(serial.serialize(repo))
|
|
@ -0,0 +1,78 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- app-batmon: Still not batman.
|
||||
-- Port of the original 'batmon.lua' from KittenOS Legacy.
|
||||
local window = neo.requireAccess("x.neo.pub.window", "window")(10, 2)
|
||||
|
||||
-- OCE/s, OCE at last check, uptime of last timer set, uptime of last check
|
||||
local lastChange, lastValue, lastTimer, lpTimer = 0
|
||||
local usage = {
|
||||
"[####]:",
|
||||
"[###:]:",
|
||||
"[### ]:",
|
||||
"[##: ]:",
|
||||
"[## ]:",
|
||||
"[#: ]:",
|
||||
"[# ]:",
|
||||
"[: ]:",
|
||||
"[ ]:",
|
||||
"WARNING"
|
||||
}
|
||||
local function getText(y)
|
||||
if y == 2 then
|
||||
if not lastChange then
|
||||
return "Wait..."
|
||||
end
|
||||
local ind = "Dc. "
|
||||
local wc = lastChange
|
||||
local wv = os.energy()
|
||||
if wc > 0 then
|
||||
wc = -wc
|
||||
wv = os.maxEnergy() - wv
|
||||
ind = "Ch. "
|
||||
end
|
||||
local m = math.floor((wv / -wc) / 60)
|
||||
return ind .. m .. "m"
|
||||
end
|
||||
local dec = os.energy() / os.maxEnergy()
|
||||
-- dec is from 0 to 1.
|
||||
local potential = math.floor(dec * #usage)
|
||||
if potential < 0 then potential = 1 end
|
||||
if potential >= #usage then potential = #usage - 1 end
|
||||
return usage[#usage - potential]
|
||||
end
|
||||
local function update()
|
||||
local nv = os.energy()
|
||||
if lastValue then
|
||||
lastChange = (nv - lastValue) / (os.uptime() - lpTimer)
|
||||
end
|
||||
lpTimer = os.uptime()
|
||||
lastValue = nv
|
||||
lastTimer = os.uptime() + 10
|
||||
if lastChange then
|
||||
if lastChange > 10 then
|
||||
lastTimer = lastTimer - 9
|
||||
end
|
||||
end
|
||||
neo.scheduleTimer(lastTimer)
|
||||
window.setSize(10, 2)
|
||||
end
|
||||
update()
|
||||
while true do
|
||||
local ev, a, b, c = coroutine.yield()
|
||||
if ev == "x.neo.pub.window" then
|
||||
if b == "close" then
|
||||
return
|
||||
elseif b == "line" then
|
||||
local tx = getText(c):sub(1, 10)
|
||||
window.span(1, c, tx .. (" "):rep(10 - #tx), 0xFFFFFF, 0)
|
||||
end
|
||||
elseif ev == "k.timer" then
|
||||
update()
|
||||
end
|
||||
end
|
|
@ -0,0 +1,113 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
local event = require("event")(neo)
|
||||
local neoux = require("neoux")(event, neo)
|
||||
local braille = require("braille")
|
||||
local bmp = require("bmp")
|
||||
local file = neoux.fileDialog(false)
|
||||
|
||||
local header = file.read(bmp.headerMinSzBMP)
|
||||
local palette = ""
|
||||
|
||||
local lcBase = bmp.headerMinSzBMP
|
||||
local palBase = bmp.headerMinSzBMP
|
||||
local lcWidth = 1
|
||||
|
||||
local lc = {}
|
||||
local lcdq = {}
|
||||
local queueSize = 4
|
||||
if os.totalMemory() > (256 * 1024) then
|
||||
queueSize = 40
|
||||
end
|
||||
for i = 1, queueSize do
|
||||
lcdq[i] = 0
|
||||
end
|
||||
local function getLine(y)
|
||||
if not lc[y] then
|
||||
local idx = y * lcWidth
|
||||
file.seek("set", lcBase + idx - 1)
|
||||
if lcdq[1] then
|
||||
lc[table.remove(lcdq, 1)] = nil
|
||||
end
|
||||
table.insert(lcdq, y)
|
||||
lc[y] = file.read(lcWidth)
|
||||
end
|
||||
return lc[y]
|
||||
end
|
||||
|
||||
local bitmap = bmp.connect(function (i)
|
||||
if i >= palBase then
|
||||
local v = palette:byte(i + 1 - palBase)
|
||||
if v then
|
||||
return v
|
||||
end
|
||||
end
|
||||
if i >= lcBase then
|
||||
local ld = getLine(math.floor((i - lcBase) / lcWidth))
|
||||
i = ((i - lcBase) % lcWidth) + 1
|
||||
return ld:byte(i) or 0
|
||||
end
|
||||
return header:byte(i) or 0
|
||||
end)
|
||||
|
||||
file.seek("set", bitmap.paletteAddress - 1)
|
||||
palette = file.read(bitmap.paletteCol * 4)
|
||||
palBase = bitmap.paletteAddress
|
||||
lcBase = bitmap.dataAddress
|
||||
lcWidth = bitmap.dsSpan
|
||||
|
||||
local running = true
|
||||
|
||||
local function decodeRGB(rgb, igp, col)
|
||||
local r, g, b = math.floor(rgb / 65536) % 256, math.floor(rgb / 256) % 256, rgb % 256
|
||||
-- the new KittenOS NEO logo is 'sensitive' to dithering, so disable it
|
||||
if not col then
|
||||
-- ...and the palette is a bit odd, oh well
|
||||
if math.max(r, g, b) < 0xC0 then
|
||||
return 0, 0, 0
|
||||
end
|
||||
return 255, 255, 255
|
||||
end
|
||||
return r, g, b
|
||||
end
|
||||
|
||||
local bW, bH = math.ceil(bitmap.width / 2), math.ceil(bitmap.height / 4)
|
||||
|
||||
local fp = neoux.tcwindow(bW, bH, {
|
||||
braille.new(1, 1, bW, bH, {
|
||||
selectable = true,
|
||||
get = function (window, x, y, bg, fg, selected, colour)
|
||||
if x > bitmap.width then return 0, 0, 0 end
|
||||
if y > bitmap.height then return 0, 0, 0 end
|
||||
if bitmap.ignoresPalette then
|
||||
return decodeRGB(bitmap.getPixel(x - 1, y - 1, 0), true, colour)
|
||||
end
|
||||
return decodeRGB(bitmap.getPalette(bitmap.getPixel(x - 1, y - 1, 0)), false, colour)
|
||||
end
|
||||
}, 1)
|
||||
}, function (w)
|
||||
w.close()
|
||||
running = false
|
||||
end, 0xFFFFFF, 0)
|
||||
|
||||
neoux.create(bW, bH, nil, function (w, t, r, ...)
|
||||
if t == "focus" then
|
||||
if r and not bitmap.ignoresPalette then
|
||||
local pal = {}
|
||||
for i = 0, math.min(15, bitmap.paletteCol - 1) do
|
||||
pal[i + 1] = bitmap.getPalette(i)
|
||||
end
|
||||
w.recommendPalette(pal)
|
||||
end
|
||||
end
|
||||
return fp(w, t, r, ...)
|
||||
end)
|
||||
|
||||
while running do
|
||||
event.pull()
|
||||
end
|
|
@ -1,147 +1,84 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- app-claw: Package manager.
|
||||
|
||||
local ldrPkg, _, tgtPkg = ...
|
||||
|
||||
-- libs & such
|
||||
local event = require("event")(neo)
|
||||
local neoux, err = require("neoux")
|
||||
if not neoux then error(err) end
|
||||
neoux = neoux(event, neo)
|
||||
local claw = require("claw")()
|
||||
local neoux = require("neoux")(event, neo)
|
||||
|
||||
local source = "http://20kdc.duckdns.org/neo/"
|
||||
local disks = neo.requireAccess("c.filesystem", "searching disks for packages")
|
||||
local primaryDisk = disks.primary
|
||||
local primaryINet = neo.requestAccess("c.internet")
|
||||
if primaryINet then primaryINet = primaryINet.list()() end
|
||||
|
||||
--
|
||||
|
||||
local function yielder()
|
||||
-- slightly dangerous, but what can we do?
|
||||
pcall(event.sleepTo, os.uptime() + 0.05)
|
||||
end
|
||||
|
||||
local function download(url, cb)
|
||||
if not primaryINet then return nil, "no internet" end
|
||||
local req, err = primaryINet.request(source .. url)
|
||||
if not req then
|
||||
cb(nil)
|
||||
return nil, "dlR/" .. tostring(err)
|
||||
end
|
||||
-- OpenComputers#535
|
||||
req.finishConnect()
|
||||
while true do
|
||||
local n, n2 = req.read(neo.readBufSize)
|
||||
local o, r = cb(n)
|
||||
if not o then
|
||||
req.close()
|
||||
return nil, r
|
||||
end
|
||||
if not n then
|
||||
req.close()
|
||||
if n2 then
|
||||
return nil, n2
|
||||
else
|
||||
break
|
||||
end
|
||||
local function readFile(src, url, ocb)
|
||||
local buf = ""
|
||||
local function cb(data)
|
||||
if not data then
|
||||
ocb(buf)
|
||||
else
|
||||
if n == "" then
|
||||
yielder()
|
||||
end
|
||||
buf = buf .. data
|
||||
buf = buf:gsub("[^\n]*\n", function (t)
|
||||
ocb(t:sub(1, -2))
|
||||
return ""
|
||||
end)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function fsSrc(disk)
|
||||
return function (url, cb)
|
||||
local h, e = disk.open(url, "rb")
|
||||
if not h then cb(nil) return nil, tostring(e) end
|
||||
local c = ""
|
||||
while c do
|
||||
c = disk.read(h, neo.readBufSize)
|
||||
local o, r = cb(c)
|
||||
if not o then return nil, r end
|
||||
end
|
||||
disk.close(h)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function fsDst(disk)
|
||||
return {function (url)
|
||||
local h, e = disk.open(url, "wb")
|
||||
if not h then return nil, tostring(e) end
|
||||
return function (d)
|
||||
local ok, r = true
|
||||
if d then
|
||||
ok, r = disk.write(h, d)
|
||||
if type(src) == "string" then
|
||||
assert(primaryINet, "no internet")
|
||||
local req, err = primaryINet.request(src .. url)
|
||||
assert(req, err)
|
||||
-- OpenComputers#535
|
||||
req.finishConnect()
|
||||
while true do
|
||||
local n, n2 = req.read(neo.readBufSize)
|
||||
cb(n)
|
||||
if not n then
|
||||
req.close()
|
||||
if n2 then
|
||||
error(n2)
|
||||
else
|
||||
cb(nil)
|
||||
break
|
||||
end
|
||||
else
|
||||
disk.close(h)
|
||||
if n == "" then
|
||||
-- slightly dangerous, but what can we do?
|
||||
pcall(event.sleepTo, os.uptime() + 0.05)
|
||||
end
|
||||
end
|
||||
if not ok then return nil, tostring(r) end
|
||||
return true
|
||||
end
|
||||
end, disk.makeDirectory, disk.exists, disk.isDirectory, disk.remove, disk.rename}
|
||||
end
|
||||
|
||||
local function checked(...)
|
||||
local res, res2, err = pcall(...)
|
||||
if not res then
|
||||
neoux.startDialog(tostring(res2), "error!", true)
|
||||
elseif not res2 then
|
||||
neoux.startDialog(tostring(err), "failed!", true)
|
||||
else
|
||||
return res2
|
||||
if url == "data/app-claw/local.c2l" then
|
||||
for _, v in ipairs(src.list("data/app-claw/")) do
|
||||
ocb(v)
|
||||
end
|
||||
return
|
||||
end
|
||||
local h, e = src.open(url, "rb")
|
||||
assert(h, e)
|
||||
repeat
|
||||
local c = src.read(h, neo.readBufSize)
|
||||
cb(c)
|
||||
until not c
|
||||
src.close(h)
|
||||
end
|
||||
end
|
||||
|
||||
-- Beginning Of The App (well, the actual one)
|
||||
|
||||
local genCurrent, genPrimary, genPackage, primaryWindow
|
||||
local windows = 1
|
||||
|
||||
-- primary
|
||||
local primarySearchTx = ""
|
||||
local primaryPage = 1
|
||||
local primaryList = {}
|
||||
local primaryNextMinus = false
|
||||
|
||||
-- package
|
||||
local packageLock = nil
|
||||
local packageId = "FIXME"
|
||||
|
||||
|
||||
local function describe(pkg)
|
||||
local weHave = claw.getInfo(pkg, "local")
|
||||
local theyHave = claw.getInfo(pkg, "local")
|
||||
local someoneHas = claw.getInfo(pkg, nil, true)
|
||||
if weHave then
|
||||
if theyHave.v > weHave.v then
|
||||
return pkg .. " [v" .. weHave.v .. "!]"
|
||||
end
|
||||
if someoneHas.v < weHave.v then
|
||||
return pkg .. " (v" .. weHave.v .. ") R<"
|
||||
end
|
||||
return pkg .. " (v" .. weHave.v .. ")"
|
||||
end
|
||||
return pkg
|
||||
end
|
||||
|
||||
local function primaryWindowRegenCore()
|
||||
local gen, gens = genCurrent()
|
||||
return 25, 12, "claw", neoux.tcwindow(25, 12, gen, function (w)
|
||||
w.close()
|
||||
windows = windows - 1
|
||||
end, 0xFF8F00, 0, gens)
|
||||
end
|
||||
local function primaryWindowRegen()
|
||||
primaryWindow.reset(primaryWindowRegenCore())
|
||||
end
|
||||
-- Sources
|
||||
|
||||
local sources = {}
|
||||
local sourceList = {}
|
||||
-- Use all non-primary filesystems
|
||||
local disks = neo.requireAccess("c.filesystem", "searching disks for packages")
|
||||
local primaryDisk = disks.primary
|
||||
for pass = 1, 3 do
|
||||
for v in disks.list() do
|
||||
local nam = nil
|
||||
|
@ -153,27 +90,115 @@ for pass = 1, 3 do
|
|||
nam = v.address
|
||||
end
|
||||
if nam then
|
||||
local ok, r = claw.addSource(nam, fsSrc(v), (not v.isReadOnly()) and fsDst(v))
|
||||
if not ok and nam == "local" then
|
||||
claw.unlock()
|
||||
error(r)
|
||||
sources[nam] = v
|
||||
table.insert(sourceList, nam)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- No longer needed
|
||||
disks = nil
|
||||
|
||||
if primaryINet then
|
||||
sources["inet"] = "http://20kdc.duckdns.org/neo/"
|
||||
table.insert(sourceList, "inet")
|
||||
end
|
||||
|
||||
-- List scanning for package window
|
||||
|
||||
local function scanList(content)
|
||||
local lst = {}
|
||||
local lst2 = {}
|
||||
for k, v in pairs(sources) do
|
||||
local ok, err = pcall(readFile, v, "data/app-claw/local.c2l", function (l)
|
||||
if l:sub(-4) == ".c2p" then
|
||||
local lt, ltv = l:sub(1, -5)
|
||||
ltv = lt:match("%.[0-9]+$")
|
||||
if ltv and l:find(content, 1, true) then
|
||||
lt = lt:sub(1, -(#ltv + 1))
|
||||
lst2[lt] = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
if (not ok) and ((k == "inet") or (k == "local")) then
|
||||
neoux.startDialog(tostring(err), k)
|
||||
end
|
||||
end
|
||||
for k, v in pairs(lst2) do
|
||||
table.insert(lst, k)
|
||||
end
|
||||
table.sort(lst)
|
||||
return lst
|
||||
end
|
||||
|
||||
-- Beginning Of The App (well, the actual one)
|
||||
|
||||
local genCurrent, genPrimary, genPackage, primaryWindow
|
||||
local running = true
|
||||
|
||||
-- primary
|
||||
local primarySearchTx = ""
|
||||
local primaryPage = 1
|
||||
local primaryList = scanList("")
|
||||
local primaryNextMinus = false
|
||||
|
||||
-- package
|
||||
local packageLock = nil
|
||||
local packageId = "FIXME"
|
||||
|
||||
local function describe(pkgs)
|
||||
local lowestV, highestV, myV = {}, {}, {}
|
||||
for pk, pv in ipairs(pkgs) do
|
||||
lowestV[pk] = math.huge
|
||||
highestV[pk] = -math.huge
|
||||
end
|
||||
for k, v in pairs(sources) do
|
||||
pcall(readFile, v, "data/app-claw/local.c2l", function (l)
|
||||
local lp = l:match("%.[0-9]+%.c2p$")
|
||||
for pk, pkg in ipairs(pkgs) do
|
||||
if lp and l:sub(1, -(#lp + 1)) == pkg then
|
||||
local v = tonumber(lp:sub(2, -5))
|
||||
if k == "local" then
|
||||
myV[pk] = v
|
||||
end
|
||||
lowestV[pk] = math.min(lowestV[pk], v)
|
||||
highestV[pk] = math.max(highestV[pk], v)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
for pk, pkg in ipairs(pkgs) do
|
||||
if lowestV[pk] == math.huge then
|
||||
pkgs[pk] = pkg .. " (ERR)"
|
||||
elseif myV[pk] then
|
||||
if highestV[pk] > myV[pk] then
|
||||
pkgs[pk] = pkg .. " [v" .. myV[pk] .. "!]"
|
||||
elseif lowestV[pk] < myV[pk] then
|
||||
pkgs[pk] = pkg .. " (v" .. myV[pk] .. ") R<"
|
||||
else
|
||||
pkgs[pk] = pkg .. " (v" .. myV[pk] .. ")"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if primaryINet then
|
||||
checked(claw.addSource, "inet", download)
|
||||
local function primaryWindowRegenCore()
|
||||
local gen, gens = genCurrent()
|
||||
return 25, 14, "claw", neoux.tcwindow(25, 14, gen, function (w)
|
||||
w.close()
|
||||
running = false
|
||||
end, 0xFF8F00, 0, gens)
|
||||
end
|
||||
local function primaryWindowRegen()
|
||||
primaryWindow.reset(primaryWindowRegenCore())
|
||||
end
|
||||
|
||||
primaryList = claw.getList()
|
||||
|
||||
-- Sections
|
||||
|
||||
function genPrimary()
|
||||
local minus = (primaryNextMinus and 3) or nil
|
||||
primaryNextMinus = false
|
||||
local pgs = 10
|
||||
local pgs = 12
|
||||
local pages = math.ceil(#primaryList / pgs)
|
||||
local elems = {
|
||||
neoux.tcbutton(23, 1, "+", function (w)
|
||||
|
@ -192,33 +217,40 @@ function genPrimary()
|
|||
end)
|
||||
}
|
||||
local base = (primaryPage - 1) * pgs
|
||||
local pkgs = {}
|
||||
for i = 1, pgs do
|
||||
local ent = primaryList[base + i]
|
||||
if ent then
|
||||
local enttx = describe(ent)
|
||||
pkgs[i] = ent
|
||||
end
|
||||
end
|
||||
describe(pkgs)
|
||||
for i = 1, pgs do
|
||||
local ent = primaryList[base + i]
|
||||
if ent then
|
||||
local enttx = pkgs[i]
|
||||
table.insert(elems, neoux.tcbutton(1, i + 1, unicode.safeTextFormat(enttx), function (w)
|
||||
-- FREE UP MEMORY NOW
|
||||
elems = {}
|
||||
w.reset(25, 14, "claw", function (ev)
|
||||
if ev == "close" then
|
||||
w.close()
|
||||
running = false
|
||||
end
|
||||
end)
|
||||
packageId = ent
|
||||
genCurrent = genPackage
|
||||
primaryWindowRegen()
|
||||
end))
|
||||
end
|
||||
end
|
||||
table.insert(elems, neoux.tcfield(1, 12, 16, function (s)
|
||||
table.insert(elems, neoux.tcfield(1, 14, 16, function (s)
|
||||
if s then primarySearchTx = s end
|
||||
return primarySearchTx
|
||||
end))
|
||||
table.insert(elems, neoux.tcbutton(17, 12, "Search!", function (w)
|
||||
local n = {}
|
||||
for _, v in ipairs(claw.getList()) do
|
||||
for i = 1, #v do
|
||||
if v:sub(i, i + #primarySearchTx - 1) == primarySearchTx then
|
||||
table.insert(n, v)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(elems, neoux.tcbutton(17, 14, "Search!", function (w)
|
||||
primaryPage = 1
|
||||
primaryList = n
|
||||
primaryList = scanList(primarySearchTx)
|
||||
primaryWindowRegen()
|
||||
end))
|
||||
return elems, minus
|
||||
|
@ -231,49 +263,38 @@ local function packageGetBB(src, lclI, srcI, srcW)
|
|||
if srcI and srcW then
|
||||
table.insert(buttons, {
|
||||
"Del",
|
||||
function ()
|
||||
if packageLock then return end
|
||||
packageLock = ""
|
||||
checked(claw.remove, src, packageId, true)
|
||||
packageLock = nil
|
||||
primaryWindowRegen()
|
||||
function (w)
|
||||
w.close()
|
||||
running = false
|
||||
neo.executeAsync("svc-app-claw-worker", sources[src], packageId, nil, src == "local", primaryINet)
|
||||
end
|
||||
})
|
||||
end
|
||||
if srcI and ((not lclI) or (lclI.v < srcI.v)) then
|
||||
if srcI and ((not lclI) or (lclI < srcI)) then
|
||||
table.insert(buttons, {
|
||||
"Get",
|
||||
function ()
|
||||
if packageLock then return end
|
||||
packageLock = "installing from " .. src
|
||||
primaryWindowRegen()
|
||||
checked(claw.installTo, "local", packageId, src, true, yielder)
|
||||
packageLock = nil
|
||||
primaryWindowRegen()
|
||||
function (w)
|
||||
w.close()
|
||||
running = false
|
||||
neo.executeAsync("svc-app-claw-worker", sources["local"], packageId, sources[src], true, primaryINet)
|
||||
end
|
||||
})
|
||||
end
|
||||
if srcW and lclI and not srcI then
|
||||
table.insert(buttons, {
|
||||
"All",
|
||||
function ()
|
||||
if packageLock then return end
|
||||
packageLock = "storing w/ dependencies at " .. src
|
||||
primaryWindowRegen()
|
||||
checked(claw.installTo, src, packageId, "local", true, yielder)
|
||||
packageLock = nil
|
||||
primaryWindowRegen()
|
||||
function (w)
|
||||
w.close()
|
||||
running = false
|
||||
neo.executeAsync("svc-app-claw-worker", sources[src], packageId, sources["local"], true, primaryINet)
|
||||
end
|
||||
})
|
||||
table.insert(buttons, {
|
||||
"Put",
|
||||
function ()
|
||||
if packageLock then return end
|
||||
packageLock = "storing at " .. src
|
||||
primaryWindowRegen()
|
||||
checked(claw.installTo, src, packageId, "local", false, yielder)
|
||||
packageLock = nil
|
||||
primaryWindowRegen()
|
||||
function (w)
|
||||
w.close()
|
||||
running = false
|
||||
neo.executeAsync("svc-app-claw-worker", sources[src], packageId, sources["local"], false, primaryINet)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
@ -292,27 +313,50 @@ function genPackage()
|
|||
-- inet v21 <pull>
|
||||
-- dir v22 <pull> <push>
|
||||
-- crockett <push>
|
||||
local info = claw.getInfo(packageId)
|
||||
local infoL = claw.getInfo(packageId, "local")
|
||||
local sourceVers = {}
|
||||
local c2pSrc = "local"
|
||||
local c2pVer = -1
|
||||
for k, v in pairs(sources) do
|
||||
local ok, err = pcall(readFile, v, "data/app-claw/local.c2l", function (l)
|
||||
local lp = l:match("%.[0-9]+%.c2p$")
|
||||
if lp and l:sub(1, -(#lp + 1)) == packageId then
|
||||
sourceVers[k] = tonumber(lp:sub(2, -5))
|
||||
if c2pVer < sourceVers[k] then
|
||||
c2pSrc = k
|
||||
c2pVer = sourceVers[k]
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
if sourceVers["local"] then
|
||||
c2pSrc = "local"
|
||||
c2pVer = sourceVers["local"]
|
||||
end
|
||||
local text = ""
|
||||
local ok = pcall(readFile, sources[c2pSrc], "data/app-claw/" .. packageId .. "." .. c2pVer .. ".c2p", function (l)
|
||||
text = text .. l .. "\n"
|
||||
end)
|
||||
if not ok then
|
||||
text = packageId .. "\nUnable to read v" .. c2pVer .. " c2p from: " .. c2pSrc
|
||||
end
|
||||
local elems = {
|
||||
neoux.tcrawview(1, 1, neoux.fmtText(unicode.safeTextFormat(packageId .. "\n" .. info.desc .. "\nv" .. info.v .. " deps " .. table.concat(info.deps, ", ")), 25)),
|
||||
neoux.tcrawview(1, 1, neoux.fmtText(unicode.safeTextFormat(text), 25)),
|
||||
neoux.tcbutton(20, 1, "Back", function ()
|
||||
if packageLock then return end
|
||||
genCurrent = genPrimary
|
||||
primaryWindowRegen()
|
||||
end)
|
||||
}
|
||||
local srcs = claw.getSources()
|
||||
for k, v in ipairs(srcs) do
|
||||
local lI = claw.getInfo(packageId, v[1])
|
||||
local row = 12 + k - #srcs
|
||||
for k, v in ipairs(sourceList) do
|
||||
local row = 14 + k - #sourceList
|
||||
local pfx = " "
|
||||
if lI then
|
||||
pfx = "v" .. string.format("%04i", lI.v) .. " "
|
||||
if sourceVers[v] then
|
||||
pfx = "v" .. string.format("%04i", sourceVers[v]) .. " "
|
||||
end
|
||||
table.insert(elems, neoux.tcrawview(1, row, {neoux.pad(pfx .. v[1], 14, false, true)}))
|
||||
table.insert(elems, neoux.tcrawview(1, row, {neoux.pad(pfx .. v, 14, false, true)}))
|
||||
local col = 26
|
||||
for _, bv in ipairs(packageGetBB(v[1], infoL, lI, v[2])) do
|
||||
local srcW = type(sources[v]) ~= "string"
|
||||
for _, bv in ipairs(packageGetBB(v, sourceVers["local"], sourceVers[v], srcW)) do
|
||||
local b = neoux.tcbutton(col, row, bv[1], bv[2])
|
||||
col = col - b.w
|
||||
b.x = col
|
||||
|
@ -324,10 +368,14 @@ end
|
|||
|
||||
--
|
||||
|
||||
genCurrent = genPrimary
|
||||
if ldrPkg == "svc-app-claw-worker" and tgtPkg then
|
||||
packageId = tgtPkg
|
||||
genCurrent = genPackage
|
||||
else
|
||||
genCurrent = genPrimary
|
||||
end
|
||||
primaryWindow = neoux.create(primaryWindowRegenCore())
|
||||
|
||||
while windows > 0 do
|
||||
while running do
|
||||
event.pull()
|
||||
end
|
||||
claw.unlock()
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- app-control: Settings changer
|
||||
local settings = neo.requireAccess("x.neo.sys.manage", "management")
|
||||
|
@ -113,12 +117,12 @@ local advPage = 1
|
|||
local advPlusH = false
|
||||
|
||||
local function advAsker(info, def, r, parent)
|
||||
info = unicode.safeTextFormat(info)
|
||||
local ww = math.max(25, unicode.len(info))
|
||||
return function ()
|
||||
return 25, 2, nil, neoux.tcwindow(25, 2, {
|
||||
neoux.tcrawview(1, 1, {
|
||||
unicode.safeTextFormat(info)
|
||||
}),
|
||||
neoux.tcfield(1, 2, 25, function (tx)
|
||||
return ww, 2, nil, neoux.tcwindow(ww, 2, {
|
||||
neoux.tcrawview(1, 1, {info}),
|
||||
neoux.tcfield(1, 2, ww, function (tx)
|
||||
def = tx or def
|
||||
return def
|
||||
end)
|
||||
|
@ -152,7 +156,7 @@ local function advGen()
|
|||
currentGen = advAsker("setting ID", "my.setting", function (r)
|
||||
settings.setSetting(r, "")
|
||||
end, currentGen)
|
||||
w.reset(advGen())
|
||||
w.reset(currentGen())
|
||||
end),
|
||||
}
|
||||
local ofs = (advPage - 1) * 7
|
||||
|
@ -226,7 +230,7 @@ window = neoux.create(currentGen())
|
|||
while running do
|
||||
local src, id, k, v = event.pull()
|
||||
if src == "x.neo.sys.manage" then
|
||||
if id == "set_setting" then
|
||||
if id == "set_setting" and currentGen ~= logGen then
|
||||
window.reset(currentGen())
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
local event = require("event")(neo)
|
||||
local neoux = require("neoux")(event, neo)
|
||||
|
@ -20,12 +24,14 @@ local busy = false
|
|||
local regenCore
|
||||
|
||||
local function regenLabeller(set, get, wd)
|
||||
local tx = get()
|
||||
return wd, 2, nil, neoux.tcwindow(wd, 1, {
|
||||
neoux.tcfield(1, 1, wd, function (nt)
|
||||
if nt then
|
||||
tx = nt
|
||||
set(nt)
|
||||
end
|
||||
return get()
|
||||
return tx
|
||||
end)
|
||||
}, function (w)
|
||||
busy = false
|
||||
|
@ -39,7 +45,7 @@ function regenCore()
|
|||
for v in eeprom.list() do
|
||||
local lbl = unicode.safeTextFormat(v.getLabel())
|
||||
table.insert(elems, neoux.tcrawview(1, l, {
|
||||
v.address:sub(1, 8) .. " " .. lbl
|
||||
require("fmttext").pad(v.address:sub(1, 8) .. " " .. lbl, 25, false, true)
|
||||
}))
|
||||
table.insert(elems, neoux.tcbutton(1, l + 1, "get", function (window)
|
||||
if busy then return end
|
||||
|
@ -50,6 +56,7 @@ function regenCore()
|
|||
fd.close()
|
||||
busy = false
|
||||
neoux.startDialog("Got the data!", nil, true)
|
||||
window.reset(regenCore())
|
||||
end))
|
||||
table.insert(elems, neoux.tcbutton(6, l + 1, "set", function (window)
|
||||
if busy then return end
|
||||
|
@ -62,6 +69,7 @@ function regenCore()
|
|||
report = (wasOk and tostring(report)) or "Flash successful.\nI recommend relabelling the EEPROM."
|
||||
busy = false
|
||||
neoux.startDialog(report, nil, true)
|
||||
window.reset(regenCore())
|
||||
end))
|
||||
local function dHandler(set, get, wd)
|
||||
local setter = v[set]
|
||||
|
@ -85,5 +93,11 @@ end
|
|||
local window = neoux.create(regenCore())
|
||||
|
||||
while running do
|
||||
event.pull()
|
||||
local s = {event.pull()}
|
||||
if (s[1] == "h.component_added" or s[1] == "h.component_removed") and busy then
|
||||
-- Anything important?
|
||||
if s[3] == "gpu" or s[3] == "screen" then
|
||||
window.reset(regenCore())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- app-fm: dummy app to start FM
|
||||
neo.requestAccess("x.neo.pub.base").showFileDialogAsync(nil)
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
local event = require("event")(neo)
|
||||
local neoux = require("neoux")(event, neo)
|
||||
local braille = require("braille")
|
||||
local icecap = neo.requireAccess("x.neo.pub.base", "loadimg")
|
||||
local qt = icecap.open("/logo.data", false)
|
||||
|
||||
local lc = {}
|
||||
local lcdq = {}
|
||||
local queueSize = 4
|
||||
if os.totalMemory() > (256 * 1024) then
|
||||
queueSize = 40
|
||||
end
|
||||
for i = 1, queueSize do
|
||||
lcdq[i] = 0
|
||||
end
|
||||
local function getLine(y)
|
||||
if not lc[y] then
|
||||
local idx = (y - 1) * 120
|
||||
qt.seek("set", idx)
|
||||
if lcdq[1] then
|
||||
lc[table.remove(lcdq, 1)] = nil
|
||||
end
|
||||
table.insert(lcdq, y)
|
||||
lc[y] = qt.read(120) or ""
|
||||
end
|
||||
return lc[y]
|
||||
end
|
||||
|
||||
local running = true
|
||||
|
||||
neoux.create(20, 10, nil, neoux.tcwindow(20, 10, {
|
||||
braille.new(1, 1, 20, 10, {
|
||||
selectable = true,
|
||||
get = function (window, x, y, bg, fg, selected, colour)
|
||||
local data = getLine(y)
|
||||
local idx = ((x - 1) * 3) + 1
|
||||
return data:byte(idx) or 255, data:byte(idx + 1) or 0, data:byte(idx + 2) or 255
|
||||
end
|
||||
}, 1)
|
||||
}, function (w)
|
||||
w.close()
|
||||
running = false
|
||||
end, 0xFFFFFF, 0))
|
||||
|
||||
while running do
|
||||
event.pull()
|
||||
end
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- app-launcher: The launcher
|
||||
local event = require("event")(neo)
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
local _, _, termId = ...
|
||||
local ok = pcall(function ()
|
||||
assert(string.sub(termId, 1, 12) == "x.neo.pub.t/")
|
||||
end)
|
||||
|
||||
local termClose
|
||||
|
||||
if not ok then
|
||||
termId = nil
|
||||
assert(neo.executeAsync("svc-t", function (res)
|
||||
termId = res.access
|
||||
termClose = res.close
|
||||
neo.scheduleTimer(0)
|
||||
end, "luashell"))
|
||||
while not termId do
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
TERM = neo.requireAccess(termId, "terminal")
|
||||
|
||||
-- Using event makes it easier for stuff
|
||||
-- within the shell to not spectacularly explode.
|
||||
event = require("event")(neo)
|
||||
|
||||
local alive = true
|
||||
event.listen("k.procdie", function (_, _, pid)
|
||||
if pid == TERM.pid then
|
||||
alive = false
|
||||
end
|
||||
end)
|
||||
|
||||
TERM.write([[
|
||||
KittenOS NEO Shell Usage Notes
|
||||
|
||||
Prefixing = is an alias for 'return '.
|
||||
io.read(): Reads a line.
|
||||
print: 'print with table dumping' impl.
|
||||
TERM: Your terminal. (see us-termi doc.)
|
||||
os.execute(): launch terminal apps!
|
||||
tries '*', 'sys-t-*', 'svc-t-*', 'app-*'
|
||||
example: os.execute("luashell")
|
||||
os.exit(): quit the shell
|
||||
=listCmdApps(): -t- (terminal) apps
|
||||
event: useful for setting up listeners
|
||||
without breaking shell functionality
|
||||
]])
|
||||
|
||||
function listCmdApps()
|
||||
local apps = {}
|
||||
for _, v in ipairs(neo.listApps()) do
|
||||
if v:sub(4, 6) == "-t-" then
|
||||
table.insert(apps, v)
|
||||
end
|
||||
end
|
||||
return apps
|
||||
end
|
||||
|
||||
local function vPrint(slike, ...)
|
||||
local s = {...}
|
||||
if #s > 1 then
|
||||
for i = 1, #s do
|
||||
if i ~= 1 then TERM.write("\t") end
|
||||
vPrint(slike, s[i])
|
||||
end
|
||||
elseif slike and type(s[1]) == "string" then
|
||||
TERM.write("\"" .. s[1] .. "\"")
|
||||
elseif type(s[1]) ~= "table" then
|
||||
TERM.write(tostring(s[1]))
|
||||
else
|
||||
TERM.write("{")
|
||||
for k, v in pairs(s[1]) do
|
||||
TERM.write("[")
|
||||
vPrint(true, k)
|
||||
TERM.write("] = ")
|
||||
vPrint(true, v)
|
||||
TERM.write(", ")
|
||||
end
|
||||
TERM.write("}")
|
||||
end
|
||||
end
|
||||
|
||||
print = function (...)
|
||||
vPrint(false, ...)
|
||||
TERM.write("\r\n")
|
||||
end
|
||||
|
||||
local ioBuffer = ""
|
||||
|
||||
io = {
|
||||
read = function ()
|
||||
while alive do
|
||||
local pos = ioBuffer:find("\n")
|
||||
if pos then
|
||||
local line = ioBuffer:sub(1, pos):gsub("\r", "")
|
||||
ioBuffer = ioBuffer:sub(pos + 1)
|
||||
return line
|
||||
end
|
||||
local e = {event.pull()}
|
||||
if e[1] == TERM.id then
|
||||
if e[2] == "data" then
|
||||
ioBuffer = ioBuffer .. e[3]
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
write = function (s) TERM.write(s) end
|
||||
}
|
||||
|
||||
local originalOS = os
|
||||
os = setmetatable({}, {
|
||||
__index = originalOS
|
||||
})
|
||||
|
||||
function os.exit()
|
||||
alive = false
|
||||
end
|
||||
|
||||
function os.execute(x, ...)
|
||||
local subPid = neo.executeAsync(x, TERM.id, ...)
|
||||
if not subPid then
|
||||
subPid = neo.executeAsync("sys-t-" .. x, TERM.id, ...)
|
||||
end
|
||||
if not subPid then
|
||||
subPid = neo.executeAsync("svc-t-" .. x, TERM.id, ...)
|
||||
end
|
||||
if not subPid then
|
||||
subPid = neo.executeAsync("app-" .. x, TERM.id, ...)
|
||||
end
|
||||
if not subPid then
|
||||
error("cannot find " .. x)
|
||||
end
|
||||
while true do
|
||||
local e = {event.pull()}
|
||||
if e[1] == "k.procdie" then
|
||||
if e[3] == subPid then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while alive do
|
||||
TERM.write("> ")
|
||||
local code = io.read()
|
||||
if code then
|
||||
local ok, err = pcall(function ()
|
||||
if code:sub(1, 1) == "=" then
|
||||
code = "return " .. code:sub(2)
|
||||
end
|
||||
print(assert(load(code))())
|
||||
end)
|
||||
if not ok then
|
||||
TERM.write(tostring(err) .. "\r\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if termClose then
|
||||
termClose()
|
||||
end
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- app-taskmgr: Task manager
|
||||
-- a-hello : simple test program for Everest.
|
||||
|
@ -67,6 +71,7 @@ local function updateConsistentProcList(pt, lp)
|
|||
end
|
||||
local p = os.uptime()
|
||||
neo.scheduleTimer(p)
|
||||
local ctrl = false
|
||||
while true do
|
||||
local n = {coroutine.yield()}
|
||||
if n[1] == "x.neo.pub.window" then
|
||||
|
@ -77,20 +82,38 @@ while true do
|
|||
return
|
||||
end
|
||||
if n[3] == "key" then
|
||||
if n[6] then
|
||||
if n[4] == 8 then
|
||||
if consistentProcList[camY] then
|
||||
kill(consistentProcList[camY][1])
|
||||
if n[5] == 29 then
|
||||
ctrl = n[6]
|
||||
elseif n[6] then
|
||||
if ctrl then
|
||||
if n[5] == 200 then
|
||||
sH = math.max(headlines + 1, sH - 1)
|
||||
window.setSize(sW, sH)
|
||||
elseif n[5] == 208 then
|
||||
sH = sH + 1
|
||||
window.setSize(sW, sH)
|
||||
elseif n[5] == 203 then
|
||||
sW = math.max(20, sW - 1)
|
||||
window.setSize(sW, sH)
|
||||
elseif n[5] == 205 then
|
||||
sW = sW + 1
|
||||
window.setSize(sW, sH)
|
||||
end
|
||||
else
|
||||
if n[4] == 8 or n[5] == 211 then
|
||||
if consistentProcList[camY] then
|
||||
kill(consistentProcList[camY][1])
|
||||
end
|
||||
end
|
||||
if n[5] == 200 then
|
||||
camY = camY - 1
|
||||
if camY < 1 then camY = 1 end
|
||||
for i = (headlines + 1), sH do drawLine(i) end
|
||||
end
|
||||
if n[5] == 208 then
|
||||
camY = camY + 1
|
||||
for i = (headlines + 1), sH do drawLine(i) end
|
||||
end
|
||||
end
|
||||
if n[5] == 200 then
|
||||
camY = camY - 1
|
||||
if camY < 1 then camY = 1 end
|
||||
for i = (headlines + 1), sH do drawLine(i) end
|
||||
end
|
||||
if n[5] == 208 then
|
||||
camY = camY + 1
|
||||
for i = (headlines + 1), sH do drawLine(i) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- 'neolithic': Text Editor
|
||||
-- This was textedit (femto) from KittenOS 'ported' to NEO.
|
||||
|
@ -11,7 +15,7 @@ local lines = {
|
|||
"F5, F6, ^←: Copy, Paste, Delete Line",
|
||||
-- These two are meant to replace similar functionality in GNU Nano
|
||||
-- (which I consider the best console text editor out there - Neolithic is an *imitation* and a poor one at that),
|
||||
-- except fixing a UI flaw by instead making J responsible for resetting the append flag,
|
||||
-- except fixing a UI flaw by instead adding a visible way to reset the append flag,
|
||||
-- so the user can more or less arbitrarily mash together lines
|
||||
"F7: Reset 'append' flag for Cut Lines",
|
||||
"F8: Cut Line(s)",
|
||||
|
@ -26,59 +30,37 @@ local lines = {
|
|||
-- and then after an action occurs switches to "append" until *any cursor action is performed*.
|
||||
-- The way I have things setup is that you perform J then K(repeat) *instead*, which means you have to explicitly say "destroy current clipboard".
|
||||
|
||||
local event = require("event")(neo)
|
||||
local clipsrc = neo.requireAccess("x.neo.pub.globals", "clipboard")
|
||||
local windows = neo.requireAccess("x.neo.pub.window", "windows")
|
||||
local files = neo.requireAccess("x.neo.pub.base", "files").showFileDialogAsync
|
||||
|
||||
local lineEdit = require("lineedit")
|
||||
|
||||
local cursorX = 1
|
||||
local cursorY = math.ceil(#lines / 2)
|
||||
local cFlash = true
|
||||
local ctrlFlag = false
|
||||
local ctrlFlag, appendFlag
|
||||
local dialogLock = false
|
||||
local appendFlag = false
|
||||
local sW, sH = 37, #lines + 2
|
||||
local windows = neo.requestAccess("x.neo.pub.window")
|
||||
local window = windows(sW, sH)
|
||||
local filedialog = nil
|
||||
local flush
|
||||
|
||||
local function splitCur()
|
||||
local s = lines[cursorY]
|
||||
local st = unicode.sub(s, 1, cursorX - 1)
|
||||
local en = unicode.sub(s, cursorX)
|
||||
return st, en
|
||||
end
|
||||
|
||||
local function clampCursorX()
|
||||
local s = lines[cursorY]
|
||||
if unicode.len(s) < (cursorX - 1) then
|
||||
cursorX = unicode.len(s) + 1
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local cbs = {}
|
||||
|
||||
local function fileDialog(writing, callback)
|
||||
local tag = neo.requestAccess("x.neo.pub.base").showFileDialogAsync(writing)
|
||||
local f
|
||||
function f(_, evt, tag2, res)
|
||||
if evt == "filedialog" then
|
||||
if tag == tag2 then
|
||||
local ok, e = pcall(callback, res)
|
||||
if not ok then
|
||||
e = unicode.safeTextFormat(tostring(e))
|
||||
local wnd = windows(unicode.len(e), 1, "ERROR")
|
||||
cbs[wnd.id] = {
|
||||
wnd.close,
|
||||
wnd.span,
|
||||
e
|
||||
}
|
||||
end
|
||||
event.ignore(f)
|
||||
end
|
||||
filedialog = function (res)
|
||||
local ok, e = pcall(callback, res)
|
||||
if not ok then
|
||||
e = unicode.safeTextFormat(tostring(e))
|
||||
local wnd = windows(unicode.len(e), 1, "ERROR")
|
||||
cbs[wnd.id] = {
|
||||
wnd.close,
|
||||
wnd.span,
|
||||
e
|
||||
}
|
||||
end
|
||||
end
|
||||
event.listen("x.neo.pub.base", f)
|
||||
files(writing)
|
||||
end
|
||||
|
||||
-- Save/Load
|
||||
|
@ -86,14 +68,19 @@ local function startSave()
|
|||
dialogLock = true
|
||||
fileDialog(true, function (res)
|
||||
dialogLock = false
|
||||
local x = ""
|
||||
if res then
|
||||
for k, v in ipairs(lines) do
|
||||
if k ~= 1 then
|
||||
res.write("\n" .. v)
|
||||
else
|
||||
res.write(v)
|
||||
x = x .. "\n"
|
||||
end
|
||||
x = x .. v
|
||||
while #x >= neo.readBufSize do
|
||||
res.write(x:sub(1, neo.readBufSize))
|
||||
x = x:sub(neo.readBufSize + 1)
|
||||
end
|
||||
end
|
||||
res.write(x)
|
||||
res.close()
|
||||
end
|
||||
end)
|
||||
|
@ -107,7 +94,7 @@ local function startLoad()
|
|||
lines = {}
|
||||
local lb = ""
|
||||
while true do
|
||||
local l = res.read(64)
|
||||
local l = res.read(neo.readBufSize)
|
||||
if not l then
|
||||
table.insert(lines, lb)
|
||||
cursorX = 1
|
||||
|
@ -141,31 +128,14 @@ local function getline(y)
|
|||
-- rX is difficult!
|
||||
local rX = 1
|
||||
local Xthold = math.max(1, math.floor(sW / 2) - 1)
|
||||
local _, cursorXP = unicode.safeTextFormat(lines[cursorY], cursorX)
|
||||
local cLine, cursorXP = unicode.safeTextFormat(lines[cursorY], cursorX)
|
||||
rX = (math.max(0, math.floor(cursorXP / Xthold) - 1) * Xthold) + 1
|
||||
local line = lines[rY]
|
||||
if not line then
|
||||
return ("¬"):rep(sW)
|
||||
end
|
||||
line = unicode.safeTextFormat(line)
|
||||
-- <alter RX here by 1 if needed>
|
||||
local tl = unicode.sub(line, rX, rX + sW - 1)
|
||||
cursorXP = (cursorXP - rX) + 1
|
||||
if cFlash then
|
||||
if rY == cursorY then
|
||||
if cursorXP >= 1 then
|
||||
if cursorXP <= sW then
|
||||
local start = unicode.sub(tl, 1, cursorXP - 1)
|
||||
local endx = unicode.sub(tl, cursorXP + 1)
|
||||
tl = start .. "_" .. endx
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
while unicode.len(tl) < sW do
|
||||
tl = tl .. " "
|
||||
end
|
||||
return tl
|
||||
return lineEdit.draw(sW, line, rY == cursorY and cursorXP, rX)
|
||||
end
|
||||
local function delLine()
|
||||
local contents = lines[cursorY]
|
||||
|
@ -181,22 +151,7 @@ local function delLine()
|
|||
end
|
||||
return contents
|
||||
end
|
||||
-- add a single character
|
||||
local function putLetter(ch)
|
||||
if ch == "\r" then
|
||||
local a, b = splitCur()
|
||||
lines[cursorY] = a
|
||||
table.insert(lines, cursorY + 1, b)
|
||||
cursorY = cursorY + 1
|
||||
cursorX = 1
|
||||
return
|
||||
end
|
||||
local a, b = splitCur()
|
||||
a = a .. ch
|
||||
lines[cursorY] = a .. b
|
||||
cursorX = unicode.len(a) + 1
|
||||
end
|
||||
local function ev_key(ka, kc, down)
|
||||
local function key(ks, kc, down)
|
||||
if dialogLock then
|
||||
return false
|
||||
end
|
||||
|
@ -214,23 +169,23 @@ local function ev_key(ka, kc, down)
|
|||
sH = 1
|
||||
end
|
||||
sW, sH = window.setSize(sW, sH)
|
||||
end
|
||||
if kc == 208 then -- Down
|
||||
return false
|
||||
elseif kc == 208 then -- Down
|
||||
sH = sH + 1
|
||||
sW, sH = window.setSize(sW, sH)
|
||||
end
|
||||
if kc == 203 then -- Left
|
||||
return false
|
||||
elseif kc == 203 then -- Left
|
||||
sW = sW - 1
|
||||
if sW == 0 then
|
||||
sW = 1
|
||||
end
|
||||
sW, sH = window.setSize(sW, sH)
|
||||
end
|
||||
if kc == 205 then -- Right
|
||||
return false
|
||||
elseif kc == 205 then -- Right
|
||||
sW = sW + 1
|
||||
sW, sH = window.setSize(sW, sH)
|
||||
end
|
||||
if kc == 14 then -- ^Backspace
|
||||
return false
|
||||
elseif kc == 14 then -- ^Backspace
|
||||
delLine()
|
||||
return true
|
||||
end
|
||||
|
@ -246,10 +201,9 @@ local function ev_key(ka, kc, down)
|
|||
if cursorY < 1 then
|
||||
cursorY = 1
|
||||
end
|
||||
clampCursorX()
|
||||
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
|
||||
return true
|
||||
end
|
||||
if kc == 208 or kc == 209 then -- Go down one - go down page
|
||||
elseif kc == 208 or kc == 209 then -- Go down one - go down page
|
||||
local moveAmount = 1
|
||||
if kc == 209 then
|
||||
moveAmount = math.floor(sH / 2)
|
||||
|
@ -258,39 +212,7 @@ local function ev_key(ka, kc, down)
|
|||
if cursorY > #lines then
|
||||
cursorY = #lines
|
||||
end
|
||||
clampCursorX()
|
||||
return true
|
||||
end
|
||||
if kc == 203 then
|
||||
if cursorX > 1 then
|
||||
cursorX = cursorX - 1
|
||||
else
|
||||
if cursorY > 1 then
|
||||
cursorY = cursorY - 1
|
||||
cursorX = unicode.len(lines[cursorY]) + 1
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
if kc == 205 then
|
||||
cursorX = cursorX + 1
|
||||
if clampCursorX() then
|
||||
if cursorY < #lines then
|
||||
cursorY = cursorY + 1
|
||||
cursorX = 1
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
-- Extra Non-Control Keys
|
||||
if kc == 199 then
|
||||
cursorX = 1
|
||||
return true
|
||||
end
|
||||
if kc == 207 then
|
||||
cursorX = unicode.len(lines[cursorY]) + 1
|
||||
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
|
||||
return true
|
||||
end
|
||||
-- Major Actions
|
||||
|
@ -299,17 +221,16 @@ local function ev_key(ka, kc, down)
|
|||
cursorX = 1
|
||||
cursorY = 1
|
||||
return true
|
||||
end
|
||||
if kc == 61 then -- F3
|
||||
elseif kc == 61 then -- F3
|
||||
startLoad()
|
||||
end
|
||||
if kc == 62 then -- F4
|
||||
return false
|
||||
elseif kc == 62 then -- F4
|
||||
startSave()
|
||||
end
|
||||
if kc == 63 then -- F5
|
||||
return false
|
||||
elseif kc == 63 then -- F5
|
||||
clipsrc.setSetting("clipboard", lines[cursorY])
|
||||
end
|
||||
if kc == 64 then -- F6
|
||||
return false
|
||||
elseif kc == 64 then -- F6
|
||||
local tx = clipsrc.getSetting("clipboard") or ""
|
||||
local txi = tx:find("\n")
|
||||
local nt = {}
|
||||
|
@ -323,11 +244,10 @@ local function ev_key(ka, kc, down)
|
|||
table.insert(lines, cursorY, v)
|
||||
end
|
||||
return true
|
||||
end
|
||||
if kc == 65 then -- F7
|
||||
elseif kc == 65 then -- F7
|
||||
appendFlag = false
|
||||
end
|
||||
if kc == 66 then -- F8
|
||||
return false
|
||||
elseif kc == 66 then -- F8
|
||||
if appendFlag then
|
||||
local base = clipsrc.getSetting("clipboard")
|
||||
clipsrc.setSetting("clipboard", base .. "\n" .. delLine())
|
||||
|
@ -338,41 +258,37 @@ local function ev_key(ka, kc, down)
|
|||
return true
|
||||
end
|
||||
end
|
||||
-- Letters
|
||||
if ka ~= 0 then
|
||||
if ka == 8 then
|
||||
if cursorX == 1 then
|
||||
if cursorY == 1 then
|
||||
return false
|
||||
end
|
||||
local l = table.remove(lines, cursorY)
|
||||
cursorY = cursorY - 1
|
||||
cursorX = unicode.len(lines[cursorY]) + 1
|
||||
lines[cursorY] = lines[cursorY] .. l
|
||||
else
|
||||
local a, b = splitCur()
|
||||
a = unicode.sub(a, 1, unicode.len(a) - 1)
|
||||
lines[cursorY] = a.. b
|
||||
cursorX = cursorX - 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
putLetter(unicode.char(ka))
|
||||
return true
|
||||
-- LEL Keys
|
||||
local lT, lC, lX = lineEdit.key(ks, kc, lines[cursorY], cursorX)
|
||||
if lT then
|
||||
lines[cursorY] = lT
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function ev_clipboard(t)
|
||||
for i = 1, unicode.len(t) do
|
||||
local c = unicode.sub(t, i, i)
|
||||
if c ~= "\r" then
|
||||
if c == "\n" then
|
||||
c = "\r"
|
||||
end
|
||||
putLetter(c)
|
||||
end
|
||||
if lC then
|
||||
cursorX = lC
|
||||
end
|
||||
if lX == "l<" and cursorY > 1 then
|
||||
cursorY = cursorY - 1
|
||||
cursorX = unicode.len(lines[cursorY]) + 1
|
||||
elseif lX == "l>" and cursorY < #lines then
|
||||
cursorY = cursorY + 1
|
||||
cursorX = 1
|
||||
elseif lX == "w<" and cursorY ~= 1 then
|
||||
local l = table.remove(lines, cursorY)
|
||||
cursorY = cursorY - 1
|
||||
cursorX = unicode.len(lines[cursorY]) + 1
|
||||
lines[cursorY] = lines[cursorY] .. l
|
||||
elseif lX == "w>" and cursorY ~= #lines then
|
||||
local l = table.remove(lines, cursorY)
|
||||
cursorX = unicode.len(l) + 1
|
||||
lines[cursorY] = l .. lines[cursorY]
|
||||
elseif lX == "nl" then
|
||||
local line = lines[cursorY]
|
||||
lines[cursorY] = unicode.sub(line, 1, cursorX - 1)
|
||||
table.insert(lines, cursorY + 1, unicode.sub(line, cursorX))
|
||||
cursorX = 1
|
||||
cursorY = cursorY + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
flush = function ()
|
||||
|
@ -380,56 +296,53 @@ flush = function ()
|
|||
window.span(1, i, getline(i), 0xFFFFFF, 0)
|
||||
end
|
||||
end
|
||||
local flash
|
||||
flash = function ()
|
||||
cFlash = not cFlash
|
||||
-- reverse:
|
||||
--local rY = (y + cursorY) - math.ceil(sH / 2)
|
||||
local csY = math.ceil(sH / 2)
|
||||
window.span(1, csY, getline(csY), 0xFFFFFF, 0)
|
||||
event.runAt(os.uptime() + 0.5, flash)
|
||||
end
|
||||
event.runAt(os.uptime() + 0.5, flash)
|
||||
|
||||
while true do
|
||||
local e = {event.pull()}
|
||||
local e = {coroutine.yield()}
|
||||
if e[1] == "x.neo.pub.window" then
|
||||
if e[2] == window.id then
|
||||
if e[3] == "touch" then
|
||||
if e[3] == "line" then
|
||||
window.span(1, e[4], getline(e[4]), 0xFFFFFF, 0)
|
||||
elseif filedialog then
|
||||
elseif e[3] == "touch" then
|
||||
-- reverse:
|
||||
--local rY = (y + cursorY) - math.ceil(sH / 2)
|
||||
local csY = math.ceil(sH / 2)
|
||||
local nY = math.max(1, math.min(#lines, (math.floor(e[5]) - csY) + cursorY))
|
||||
cursorY = nY
|
||||
clampCursorX()
|
||||
cursorX = lineEdit.clamp(lines[cursorY], cursorX)
|
||||
flush()
|
||||
end
|
||||
if e[3] == "key" then
|
||||
if ev_key(e[4], e[5], e[6]) then
|
||||
elseif e[3] == "key" then
|
||||
if key(e[4] ~= 0 and unicode.char(e[4]), e[5], e[6]) then
|
||||
flush()
|
||||
end
|
||||
end
|
||||
if e[3] == "line" then
|
||||
window.span(1, e[4], getline(e[4]), 0xFFFFFF, 0)
|
||||
end
|
||||
if e[3] == "focus" then
|
||||
elseif e[3] == "focus" then
|
||||
ctrlFlag = false
|
||||
end
|
||||
if e[3] == "close" then
|
||||
elseif e[3] == "close" then
|
||||
return
|
||||
end
|
||||
if e[3] == "clipboard" then
|
||||
ev_clipboard(e[4])
|
||||
elseif e[3] == "clipboard" then
|
||||
local t = e[4]
|
||||
for i = 1, unicode.len(t) do
|
||||
local c = unicode.sub(t, i, i)
|
||||
if c ~= "\r" then
|
||||
if c == "\n" then
|
||||
c = "\r"
|
||||
end
|
||||
key(c, 0, true)
|
||||
end
|
||||
end
|
||||
flush()
|
||||
end
|
||||
elseif cbs[e[2]] then
|
||||
if e[3] == "line" then
|
||||
cbs[e[2]][2](1, 1, cbs[e[2]][3], 0, 0xFFFFFF)
|
||||
end
|
||||
if e[3] == "close" then
|
||||
elseif e[3] == "close" then
|
||||
cbs[e[2]][1]()
|
||||
cbs[e[2]] = nil
|
||||
end
|
||||
end
|
||||
elseif e[1] == "x.neo.pub.base" and e[2] == "filedialog" and filedialog then
|
||||
filedialog(e[4])
|
||||
filedialog = nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
local event = require("event")(neo)
|
||||
local neoux = require("neoux")(event, neo)
|
||||
|
||||
local primaryINet = neo.requireAccess("c.internet", "internet access").list()()
|
||||
|
||||
-- Enter URL dialog
|
||||
local running = true
|
||||
local sRunning = true
|
||||
-- useful to perform a system update
|
||||
local url = "http://20kdc.duckdns.org/neo/inst.lua"
|
||||
local function doWorking()
|
||||
return 50, 1, nil, neoux.tcwindow(50, 1, {
|
||||
neoux.tcrawview(1, 1, {"Downloading now..."}),
|
||||
}, function (w)
|
||||
sRunning = false
|
||||
end, 0xFFFFFF, 0)
|
||||
end
|
||||
local function doMainWin()
|
||||
return 50, 3, nil, neoux.tcwindow(50, 3, {
|
||||
neoux.tcrawview(1, 1, {"URL to download?"}),
|
||||
neoux.tcfield(1, 2, 50, function (t)
|
||||
url = t or url
|
||||
return url
|
||||
end),
|
||||
neoux.tcbutton(41, 3, "Download", function (w)
|
||||
sRunning = true
|
||||
w.reset(doWorking())
|
||||
local nurl = url
|
||||
local fd = neoux.fileDialog(true)
|
||||
if not fd then
|
||||
w.reset(doMainWin())
|
||||
return
|
||||
end
|
||||
-- download!
|
||||
local req, err = primaryINet.request(nurl)
|
||||
if not req then
|
||||
fd.close()
|
||||
neoux.startDialog("failed request:\n" .. tostring(err))
|
||||
w.reset(doMainWin())
|
||||
return
|
||||
end
|
||||
-- OpenComputers#535
|
||||
req.finishConnect()
|
||||
while sRunning do
|
||||
local n, n2 = req.read(neo.readBufSize)
|
||||
if not n then
|
||||
if n2 then
|
||||
neoux.startDialog("failed download:\n" .. tostring(n2))
|
||||
break
|
||||
else
|
||||
break
|
||||
end
|
||||
else
|
||||
if n == "" then
|
||||
event.sleepTo(os.uptime() + 0.05)
|
||||
else
|
||||
local o, r = fd.write(n)
|
||||
if not o then
|
||||
neoux.startDialog("failed write:\n" .. tostring(r))
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
pcall(req.close)
|
||||
pcall(fd.close)
|
||||
w.reset(doMainWin())
|
||||
end)
|
||||
}, function (w)
|
||||
w.close()
|
||||
running = false
|
||||
end, 0xFFFFFF, 0)
|
||||
end
|
||||
local w = neoux.create(doMainWin())
|
||||
|
||||
while running do
|
||||
event.pull()
|
||||
end
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- svc-app-claw-worker: Who stays stays. Who goes goes.
|
||||
|
||||
local callerPkg, _, destProx, packageId, downloadSrc, checked, primaryINet = ...
|
||||
if callerPkg ~= "app-claw" then error("Internal process for app-claw's bidding.") end
|
||||
-- This 'mutex' remains active as long as the process does.
|
||||
neo.requireAccess("r.svc.app-claw-worker", "CLAW mutex")
|
||||
|
||||
local function wrapLines(ocb)
|
||||
local buf = ""
|
||||
return function (data)
|
||||
if not data then
|
||||
ocb(buf)
|
||||
else
|
||||
buf = buf .. data
|
||||
buf = buf:gsub("[^\n]*\n", function (t)
|
||||
ocb(t:sub(1, -2))
|
||||
return ""
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function download(url, cb, src)
|
||||
if type(src) == "string" then
|
||||
assert(primaryINet, "no internet")
|
||||
local req, err = primaryINet.request(src .. url)
|
||||
assert(req, err)
|
||||
-- OpenComputers#535
|
||||
req.finishConnect()
|
||||
while true do
|
||||
local n, n2 = req.read(neo.readBufSize)
|
||||
cb(n)
|
||||
if not n then
|
||||
req.close()
|
||||
if n2 then
|
||||
error(n2)
|
||||
else
|
||||
cb(nil)
|
||||
break
|
||||
end
|
||||
else
|
||||
if n == "" then
|
||||
neo.scheduleTimer(os.uptime() + 0.05)
|
||||
while true do
|
||||
local res = coroutine.yield()
|
||||
if res == "k.timer" then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local h, e = src.open(url, "rb")
|
||||
assert(h, e)
|
||||
repeat
|
||||
local c = src.read(h, neo.readBufSize)
|
||||
cb(c)
|
||||
until not c
|
||||
src.close(h)
|
||||
end
|
||||
end
|
||||
|
||||
local opInstall, opRemove
|
||||
|
||||
function opInstall(packageId, checked)
|
||||
local gback = {} -- the ultimate strategy
|
||||
local gdir = {}
|
||||
local preinstall = {}
|
||||
download("data/app-claw/" .. packageId .. ".c2x", wrapLines(function (l)
|
||||
if l:sub(1, 1) == "?" and checked then
|
||||
preinstall[l:sub(2)] = true
|
||||
elseif l:sub(1, 1) == "+" then
|
||||
table.insert(gback, l:sub(2))
|
||||
elseif l:sub(1, 1) == "/" then
|
||||
table.insert(gdir, l:sub(2))
|
||||
end
|
||||
end), downloadSrc)
|
||||
for _, v in ipairs(gdir) do
|
||||
destProx.makeDirectory(v)
|
||||
assert(destProx.isDirectory(v), "unable to create dir " .. v)
|
||||
end
|
||||
gdir = nil
|
||||
for k, _ in pairs(preinstall) do
|
||||
if not destProx.exists("data/app-claw/" .. k .. ".c2x") then
|
||||
opInstall(k, true)
|
||||
end
|
||||
end
|
||||
preinstall = nil
|
||||
for _, v in ipairs(gback) do
|
||||
local f = destProx.open(v .. ".C2T", "wb")
|
||||
assert(f, "unable to create download file")
|
||||
local ok, e = pcall(download, v, function (b)
|
||||
assert(destProx.write(f, b or ""), "unable to save data")
|
||||
end, downloadSrc)
|
||||
destProx.close(f)
|
||||
if not ok then error(e) end
|
||||
end
|
||||
-- CRITICAL SECTION --
|
||||
if destProx.exists("data/app-claw/" .. packageId .. ".c2x") then
|
||||
opRemove(packageId, false)
|
||||
end
|
||||
for _, v in ipairs(gback) do
|
||||
if destProx.exists(v) then
|
||||
for _, v in ipairs(gback) do
|
||||
destProx.remove(v .. ".C2T")
|
||||
end
|
||||
error("file conflict: " .. v)
|
||||
end
|
||||
end
|
||||
for _, v in ipairs(gback) do
|
||||
destProx.rename(v .. ".C2T", v)
|
||||
end
|
||||
end
|
||||
|
||||
function opRemove(packageId, checked)
|
||||
if checked then
|
||||
local dependents = {}
|
||||
for _, pidf in ipairs(destProx.list("data/app-claw/")) do
|
||||
if pidf:sub(-4) == ".c2x" then
|
||||
local pid = pidf:sub(1, -5)
|
||||
download("data/app-claw/" .. pidf, wrapLines(function (l)
|
||||
if l == "?" .. packageId then
|
||||
table.insert(dependents, pid)
|
||||
end
|
||||
end), destProx)
|
||||
end
|
||||
end
|
||||
assert(not dependents[1], "Cannot remove " .. packageId .. ", required by:\n" .. table.concat(dependents, ", "))
|
||||
end
|
||||
local rmSchedule = {}
|
||||
download("data/app-claw/" .. packageId .. ".c2x", wrapLines(function (l)
|
||||
if l:sub(1, 1) == "+" then
|
||||
table.insert(rmSchedule, l:sub(2))
|
||||
end
|
||||
end), destProx)
|
||||
for _, v in ipairs(rmSchedule) do
|
||||
destProx.remove(v)
|
||||
end
|
||||
end
|
||||
|
||||
local ok, e
|
||||
if downloadSrc then
|
||||
ok, e = pcall(opInstall, packageId, checked)
|
||||
else
|
||||
ok, e = pcall(opRemove, packageId, checked)
|
||||
end
|
||||
destProx = nil
|
||||
downloadSrc = nil
|
||||
primaryINet = nil
|
||||
neo.executeAsync("app-claw", packageId)
|
||||
if not ok then
|
||||
error(e)
|
||||
end
|
|
@ -0,0 +1,495 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- svc-t.lua : terminal
|
||||
|
||||
local _, _, retTbl, title = ...
|
||||
|
||||
assert(retTbl, "need to alert creator")
|
||||
|
||||
if title ~= nil then
|
||||
assert(type(title) == "string", "title must be string")
|
||||
end
|
||||
|
||||
local function rW()
|
||||
return string.format("%04x", math.random(0, 65535))
|
||||
end
|
||||
|
||||
local id = "neo.pub.t/" .. rW() .. rW() .. rW() .. rW()
|
||||
local closeNow = false
|
||||
|
||||
-- Terminus Registration State --
|
||||
|
||||
local tReg = neo.requireAccess("r." .. id, "registration")
|
||||
local sendSigs = {}
|
||||
|
||||
-- Display State --
|
||||
-- unicode.safeTextFormat'd lines.
|
||||
-- The size of this must not go below 1.
|
||||
local console = {}
|
||||
-- This must not go below 3.
|
||||
local conW = 40
|
||||
local conCX, conCY = 1, 1
|
||||
local conSCX, conSCY = 1, 1
|
||||
-- Performance
|
||||
local consoleShown = {}
|
||||
local conCYShown
|
||||
for i = 1, 14 do
|
||||
console[i] = (" "):rep(conW)
|
||||
end
|
||||
|
||||
-- Line Editing State --
|
||||
-- Nil if line editing is off.
|
||||
-- In this case, the console height
|
||||
-- must be adjusted accordingly.
|
||||
local leText = ""
|
||||
-- These are NOT nil'd out,
|
||||
-- particularly not the history buffer.
|
||||
local leCX = 1
|
||||
local leHistory = {
|
||||
-- Size = history buffer size
|
||||
"", "", "", ""
|
||||
}
|
||||
local function cycleHistoryUp()
|
||||
local backupFirst = leHistory[1]
|
||||
for i = 1, #leHistory - 1 do
|
||||
leHistory[i] = leHistory[i + 1]
|
||||
end
|
||||
leHistory[#leHistory] = backupFirst
|
||||
end
|
||||
local function cycleHistoryDown()
|
||||
local backup = leHistory[1]
|
||||
for i = 2, #leHistory do
|
||||
backup, leHistory[i] = leHistory[i], backup
|
||||
end
|
||||
leHistory[1] = backup
|
||||
end
|
||||
|
||||
-- Window --
|
||||
|
||||
local window = neo.requireAccess("x.neo.pub.window", "window")(conW, #console + 1, title)
|
||||
|
||||
-- Core Terminal Functions --
|
||||
|
||||
local function setSize(w, h)
|
||||
conW = w
|
||||
for i = 1, #console do
|
||||
consoleShown[i] = nil
|
||||
end
|
||||
while #console < h do
|
||||
table.insert(console, "")
|
||||
end
|
||||
while #console > h do
|
||||
table.remove(console, 1)
|
||||
end
|
||||
for i = 1, #console do
|
||||
console[i] = unicode.sub(console[i], 1, w) .. (" "):rep(w - unicode.len(console[i]))
|
||||
end
|
||||
if leText then
|
||||
window.setSize(w, h + 1)
|
||||
else
|
||||
window.setSize(w, h)
|
||||
end
|
||||
conCX, conCY = 1, h
|
||||
end
|
||||
|
||||
local function setLineEditing(state)
|
||||
if state and not leText then
|
||||
leText = ""
|
||||
leCX = 1
|
||||
setSize(conW, #console)
|
||||
elseif leText and not state then
|
||||
leText = nil
|
||||
setSize(conW, #console)
|
||||
end
|
||||
end
|
||||
|
||||
local function draw(i)
|
||||
if console[i] then
|
||||
window.span(1, i, console[i], 0, 0xFFFFFF)
|
||||
if i == conCY and not leText then
|
||||
window.span(conCX, i, unicode.sub(console[i], conCX, conCX), 0xFFFFFF, 0)
|
||||
end
|
||||
elseif leText then
|
||||
window.span(1, i, require("lineedit").draw(conW, unicode.safeTextFormat(leText, leCX)), 0xFFFFFF, 0)
|
||||
end
|
||||
end
|
||||
local function drawDisplay()
|
||||
for i = 1, #console do
|
||||
if consoleShown[i] ~= console[i] or i == conCY or i == conCYShown then
|
||||
draw(i)
|
||||
consoleShown[i] = console[i]
|
||||
end
|
||||
end
|
||||
conCYShown = conCY
|
||||
end
|
||||
|
||||
-- Terminal Visual --
|
||||
|
||||
local function consoleSD()
|
||||
for i = 1, #console - 1 do
|
||||
console[i] = console[i + 1]
|
||||
end
|
||||
console[#console] = (" "):rep(conW)
|
||||
end
|
||||
local function consoleSU()
|
||||
local backup = (" "):rep(conW)
|
||||
for i = 1, #console do
|
||||
backup, console[i] = console[i], backup
|
||||
end
|
||||
end
|
||||
|
||||
local function consoleCLS()
|
||||
for i = 1, #console do
|
||||
console[i] = (" "):rep(conW)
|
||||
end
|
||||
conCX, conCY = 1, 1
|
||||
end
|
||||
|
||||
local function writeFF()
|
||||
if conCY ~= #console then
|
||||
conCY = conCY + 1
|
||||
else
|
||||
consoleSD()
|
||||
end
|
||||
end
|
||||
|
||||
local function writeData(data)
|
||||
-- handle data until completion
|
||||
while #data > 0 do
|
||||
local char = unicode.sub(data, 1, 1)
|
||||
--neo.emergency("svc-t.data: " .. char:byte())
|
||||
data = unicode.sub(data, 2)
|
||||
-- handle character
|
||||
if char == "\t" then
|
||||
-- not ideal, but allowed
|
||||
char = " "
|
||||
end
|
||||
if char == "\r" then
|
||||
conCX = 1
|
||||
elseif char == "\x00" then
|
||||
-- caused by TELNET \r rules
|
||||
elseif char == "\n" then
|
||||
conCX = 1
|
||||
writeFF()
|
||||
elseif char == "\a" then
|
||||
-- Bell (er...)
|
||||
elseif char == "\b" then
|
||||
conCX = math.max(1, conCX - 1)
|
||||
elseif char == "\v" or char == "\f" then
|
||||
writeFF()
|
||||
else
|
||||
local charL = unicode.wlen(char)
|
||||
if (conCX + charL - 1) > conW then
|
||||
conCX = 1
|
||||
writeFF()
|
||||
end
|
||||
local spaces = (" "):rep(charL - 1)
|
||||
console[conCY] = unicode.sub(console[conCY], 1, conCX - 1) .. char .. spaces .. unicode.sub(console[conCY], conCX + charL)
|
||||
conCX = conCX + charL
|
||||
-- Cursor can be (intentionally!) off-screen here
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function writeANSI(s)
|
||||
--neo.emergency("svc-t.ansi: " .. s)
|
||||
-- This supports just about enough to get by.
|
||||
if s == "c" then
|
||||
consoleCLS()
|
||||
return
|
||||
end
|
||||
local pfx = s:sub(1, 1)
|
||||
local cmd = s:sub(#s)
|
||||
if pfx == "[" then
|
||||
local np = tonumber(s:sub(2, -2)) or 1
|
||||
if cmd == "A" then
|
||||
conCY = conCY - np
|
||||
elseif cmd == "B" then
|
||||
conCY = conCY + np
|
||||
elseif cmd == "C" then
|
||||
conCX = conCX + np
|
||||
elseif cmd == "D" then
|
||||
conCX = conCX - np
|
||||
elseif cmd == "f" or cmd == "H" then
|
||||
local p = s:find(";")
|
||||
if not p then
|
||||
conCY = np
|
||||
conCX = 1
|
||||
else
|
||||
conCY = tonumber(s:sub(2, p - 1)) or 1
|
||||
conCX = tonumber(s:sub(p + 1, -2)) or 1
|
||||
end
|
||||
elseif cmd == "J" then
|
||||
consoleCLS()
|
||||
elseif cmd == "K" then
|
||||
if s == "[K" or s == "[0K" then
|
||||
-- bash needs this
|
||||
console[conCY] = unicode.sub(console[conCY], 1, conCX - 1) .. (" "):rep(1 + conW - conCX)
|
||||
else
|
||||
console[conCY] = (" "):rep(conW)
|
||||
end
|
||||
elseif cmd == "n" then
|
||||
if s == "[6n" then
|
||||
for _, v in pairs(sendSigs) do
|
||||
v("data", "\x1b[" .. conY .. ";" .. conX .. "R")
|
||||
end
|
||||
end
|
||||
elseif cmd == "s" then
|
||||
conSCX, conSCY = conCX, conCY
|
||||
elseif cmd == "u" then
|
||||
conCX, conCY = conSCX, conSCY
|
||||
end
|
||||
end
|
||||
conCX = math.min(math.max(math.floor(conCX), 1), conW)
|
||||
conCY = math.min(math.max(math.floor(conCY), 1), #console)
|
||||
end
|
||||
|
||||
-- The Terminus --
|
||||
|
||||
local tvBuildingCmd = ""
|
||||
local tvBuildingUTF = ""
|
||||
local tvSubnegotiation = false
|
||||
local function incoming(s)
|
||||
tvBuildingCmd = tvBuildingCmd .. s
|
||||
-- Flush Cmd
|
||||
while #tvBuildingCmd > 0 do
|
||||
if tvBuildingCmd:byte() == 255 then
|
||||
-- It's a command. Uhoh.
|
||||
if #tvBuildingCmd < 2 then break end
|
||||
local cmd = tvBuildingCmd:byte(2)
|
||||
local param = tvBuildingCmd:byte(3)
|
||||
local cmdLen = 2
|
||||
-- Command Lengths
|
||||
if cmd >= 251 and cmd <= 254 then cmdLen = 3 end
|
||||
if #tvBuildingCmd < cmdLen then break end
|
||||
if cmd == 240 then
|
||||
-- SE
|
||||
tvSubnegotiation = false
|
||||
elseif cmd == 250 then
|
||||
-- SB
|
||||
tvSubnegotiation = true
|
||||
elseif cmd == 251 and param == 1 then
|
||||
-- WILL ECHO (respond with DO ECHO, disable line editing)
|
||||
-- test using io.write("\xFF\xFB\x01")
|
||||
for _, v in pairs(sendSigs) do
|
||||
v("telnet", "\xFF\xFD\x01")
|
||||
end
|
||||
setLineEditing(false)
|
||||
elseif cmd == 252 and param == 1 then
|
||||
-- WON'T ECHO (respond with DON'T ECHO, enable line editing)
|
||||
for _, v in pairs(sendSigs) do
|
||||
v("telnet", "\xFF\xFE\x01")
|
||||
end
|
||||
setLineEditing(true)
|
||||
elseif cmd == 251 or cmd == 252 then
|
||||
-- WILL/WON'T (x) (respond with DON'T (X))
|
||||
local res = "\xFF\xFE" .. string.char(param)
|
||||
for _, v in pairs(sendSigs) do
|
||||
v("telnet", res)
|
||||
end
|
||||
elseif cmd == 253 or cmd == 254 then
|
||||
-- DO/DON'T (x) (respond with WON'T (X))
|
||||
local res = "\xFF\xFC" .. string.char(param)
|
||||
for _, v in pairs(sendSigs) do
|
||||
v("telnet", res)
|
||||
end
|
||||
elseif cmd == 255 then
|
||||
if not tvSubnegotiation then
|
||||
tvBuildingUTF = tvBuildingUTF .. "\xFF"
|
||||
end
|
||||
end
|
||||
tvBuildingCmd = tvBuildingCmd:sub(cmdLen + 1)
|
||||
else
|
||||
if not tvSubnegotiation then
|
||||
tvBuildingUTF = tvBuildingUTF .. tvBuildingCmd:sub(1, 1)
|
||||
end
|
||||
tvBuildingCmd = tvBuildingCmd:sub(2)
|
||||
end
|
||||
end
|
||||
-- Flush UTF/Display
|
||||
while #tvBuildingUTF > 0 do
|
||||
local head = tvBuildingUTF:byte()
|
||||
local len = 1
|
||||
local handled = false
|
||||
if head == 27 then
|
||||
local h2 = tvBuildingUTF:byte(2)
|
||||
if h2 == 91 then
|
||||
for i = 3, #tvBuildingUTF do
|
||||
local cmd = tvBuildingUTF:byte(i)
|
||||
if cmd >= 0x40 and cmd <= 0x7E then
|
||||
writeANSI(tvBuildingUTF:sub(2, i))
|
||||
len = i
|
||||
handled = true
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif h2 then
|
||||
len = 2
|
||||
writeANSI(tvBuildingUTF:sub(2, 2))
|
||||
handled = true
|
||||
end
|
||||
if not handled then break end
|
||||
end
|
||||
if not handled then
|
||||
if head < 192 then
|
||||
len = 1
|
||||
elseif head < 224 then
|
||||
len = 2
|
||||
elseif head < 240 then
|
||||
len = 3
|
||||
elseif head < 248 then
|
||||
len = 4
|
||||
elseif head < 252 then
|
||||
len = 5
|
||||
elseif head < 254 then
|
||||
len = 6
|
||||
end
|
||||
if #tvBuildingUTF < len then
|
||||
break
|
||||
end
|
||||
-- verified one full character...
|
||||
writeData(tvBuildingUTF:sub(1, len))
|
||||
end
|
||||
tvBuildingUTF = tvBuildingUTF:sub(len + 1)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
tReg(function (_, pid, sendSig)
|
||||
sendSigs[pid] = sendSig
|
||||
return {
|
||||
id = "x." .. id,
|
||||
pid = neo.pid,
|
||||
write = function (text)
|
||||
incoming(tostring(text))
|
||||
drawDisplay()
|
||||
end
|
||||
}
|
||||
end, true)
|
||||
|
||||
if retTbl then
|
||||
coroutine.resume(coroutine.create(retTbl), {
|
||||
access = "x." .. id,
|
||||
close = function ()
|
||||
closeNow = true
|
||||
neo.scheduleTimer(0)
|
||||
end
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local control = false
|
||||
|
||||
local function key(a, c)
|
||||
if control then
|
||||
if c == 203 and conW > 8 then
|
||||
setSize(conW - 1, #console)
|
||||
return
|
||||
elseif c == 200 and #console > 1 then
|
||||
setSize(conW, #console - 1)
|
||||
return
|
||||
elseif c == 205 then
|
||||
setSize(conW + 1, #console)
|
||||
return
|
||||
elseif c == 208 then
|
||||
setSize(conW, #console + 1)
|
||||
return
|
||||
end
|
||||
end
|
||||
-- so with the reserved ones dealt with...
|
||||
if not leText then
|
||||
-- Line Editing not active.
|
||||
-- For now support a bare minimum.
|
||||
for _, v in pairs(sendSigs) do
|
||||
if a == "\x03" then
|
||||
v("telnet", "\xFF\xF4")
|
||||
elseif c == 199 then
|
||||
v("data", "\x1b[H")
|
||||
elseif c == 201 then
|
||||
v("data", "\x1b[5~")
|
||||
elseif c == 207 then
|
||||
v("data", "\x1b[F")
|
||||
elseif c == 209 then
|
||||
v("data", "\x1b[6~")
|
||||
elseif c == 203 then
|
||||
v("data", "\x1b[D")
|
||||
elseif c == 205 then
|
||||
v("data", "\x1b[C")
|
||||
elseif c == 200 then
|
||||
v("data", "\x1b[A")
|
||||
elseif c == 208 then
|
||||
v("data", "\x1b[B")
|
||||
elseif a == "\r" then
|
||||
v("data", "\r\n")
|
||||
elseif a then
|
||||
v("data", a)
|
||||
end
|
||||
end
|
||||
elseif not control then
|
||||
-- Line Editing active and control isn't involved
|
||||
if c == 200 or c == 208 then
|
||||
-- History cursor up (history down)
|
||||
leText = leHistory[#leHistory]
|
||||
leCX = unicode.len(leText) + 1
|
||||
if c == 208 then
|
||||
cycleHistoryUp()
|
||||
else
|
||||
cycleHistoryDown()
|
||||
end
|
||||
return
|
||||
end
|
||||
local lT, lC, lX = require("lineedit").key(a, c, leText, leCX)
|
||||
leText = lT or leText
|
||||
leCX = lC or leCX
|
||||
if lX == "nl" then
|
||||
cycleHistoryUp()
|
||||
leHistory[#leHistory] = leText
|
||||
-- the whole thing {
|
||||
local fullText = leText .. "\r\n"
|
||||
writeData(fullText)
|
||||
drawDisplay()
|
||||
for _, v in pairs(sendSigs) do
|
||||
v("data", fullText)
|
||||
end
|
||||
-- }
|
||||
leText = ""
|
||||
leCX = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while not closeNow do
|
||||
local e = {coroutine.yield()}
|
||||
if e[1] == "k.procdie" then
|
||||
sendSigs[e[3]] = nil
|
||||
elseif e[1] == "x.neo.pub.window" then
|
||||
if e[3] == "close" then
|
||||
break
|
||||
elseif e[3] == "clipboard" then
|
||||
for i = 1, unicode.len(e[4]) do
|
||||
local c = unicode.sub(e[4], i, i)
|
||||
if c ~= "\r" then
|
||||
if c == "\n" then
|
||||
c = "\r"
|
||||
end
|
||||
key(c, 0)
|
||||
end
|
||||
end
|
||||
draw(#console + 1)
|
||||
elseif e[3] == "key" then
|
||||
if e[5] == 29 or e[5] == 157 then
|
||||
control = e[6]
|
||||
elseif e[6] then
|
||||
key(e[4] ~= 0 and unicode.char(e[4]), e[5])
|
||||
draw(#console + 1)
|
||||
end
|
||||
elseif e[3] == "line" then
|
||||
draw(e[4])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- s-everest
|
||||
|
||||
|
@ -15,16 +19,13 @@
|
|||
-- though with automatically closing windows on process death.
|
||||
|
||||
-- How Bristol talks to this is:
|
||||
-- 1. Bristol starts up Everest. Everest does not claim new monitors by default.
|
||||
-- 2. Bristol claims all available monitors to blank out the display
|
||||
-- 3. The user logs in
|
||||
-- 4. Bristol runs "startSession", enabling claiming of free monitors, and then promptly dies.
|
||||
-- 1. The user logs in
|
||||
-- 2. Bristol starts up Everest, and frees the primary monitor
|
||||
-- 3. The primary monitor is claimed by Everest and becomes monitor 1
|
||||
-- 4. After a small time, Bristol dies, unclaiming all monitors
|
||||
-- 5. Everest claims the new monitors, and the desktop session begins
|
||||
-- 6. Everest dies/respawns, or endSession is called - in both cases,
|
||||
-- Everest is now essentially back at the state in 1.
|
||||
-- 7. Either this is Bristol, so go to 2,
|
||||
-- or this is a screensaver host, and has a saving-throw to start Bristol if it dies unexpectedly.
|
||||
-- In any case, this eventually returns to 2 or 4.
|
||||
-- 6. Everest shuts down for some reason,
|
||||
-- sys-init gets started UNLESS endSession(false) was used
|
||||
|
||||
local everestProvider = neo.requireAccess("r.neo.pub.window", "registering npw")
|
||||
local everestSessionProvider = neo.requireAccess("r.neo.sys.session", "registering nsse")
|
||||
|
@ -46,10 +47,10 @@ neo.requestAccess("s.h.key_down")
|
|||
local monitors = {}
|
||||
|
||||
-- NULL VIRTUAL MONITOR!
|
||||
-- This is where we stuff processes while Bristol isn't online
|
||||
-- This is where we stuff processes until monitors show up
|
||||
monitors[0] = {nil, nil, 160, 50}
|
||||
|
||||
-- {monitor, x, y, w, h, callback}
|
||||
-- {monitor, x, y, w, h, callback, title}
|
||||
-- callback events are:
|
||||
-- key ka kc down
|
||||
-- line y
|
||||
|
@ -61,12 +62,15 @@ local lIM = 1
|
|||
-- Stops the main loop
|
||||
local shuttingDown = false
|
||||
|
||||
-- Also used for settings.
|
||||
local savingThrow = neo.requestAccess("x.neo.sys.manage")
|
||||
|
||||
local draggingWindowX, draggingWindowY
|
||||
|
||||
local function suggestAppsStop()
|
||||
for k, v in ipairs(surfaces) do
|
||||
for i = 1, 4 do
|
||||
v[6]("close")
|
||||
v[6](v[8], "close")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -83,8 +87,8 @@ local function dying()
|
|||
-- If a process evades the deathtrap then it clearly has reason to stay alive regardless of Everest status.
|
||||
-- Also note, should savingThrow fail, neo.dead is now a thing.
|
||||
for _, v in ipairs(surfaces) do
|
||||
pcall(v[6], "line", 1)
|
||||
pcall(v[6], "line", 2)
|
||||
pcall(v[6], v[8], "line", 1)
|
||||
pcall(v[6], v[8], "line", 2)
|
||||
end
|
||||
surfaces = {}
|
||||
end
|
||||
|
@ -123,11 +127,11 @@ local function monitorGPUColours(m, cb, bg, fg)
|
|||
local nbg = m[5]
|
||||
local nfg = m[6]
|
||||
if nbg ~= bg then
|
||||
cb.setBackground(bg)
|
||||
pcall(cb.setBackground, bg)
|
||||
m[5] = bg
|
||||
end
|
||||
if nfg ~= fg then
|
||||
cb.setForeground(fg)
|
||||
pcall(cb.setForeground, fg)
|
||||
m[6] = fg
|
||||
end
|
||||
end
|
||||
|
@ -141,14 +145,71 @@ local function doBackgroundLine(m, mg, bdx, bdy, bdl)
|
|||
monitorGPUColours(m, mg, 0x000000, 0xFFFFFF)
|
||||
local str = unicode.sub(statusLine, bdx, bdx + bdl - 1)
|
||||
local strl = unicode.len(str)
|
||||
mg.set(bdx, bdy, unicode.undoSafeTextFormat(str))
|
||||
mg.fill(bdx + strl, bdy, bdl - strl, 1, " ")
|
||||
pcall(mg.set, bdx, bdy, unicode.undoSafeTextFormat(str))
|
||||
pcall(mg.set, bdx + strl, bdy, (" "):rep(bdl - strl))
|
||||
else
|
||||
monitorGPUColours(m, mg, 0x000020, 0)
|
||||
mg.fill(bdx, bdy, bdl, 1, " ")
|
||||
monitorGPUColours(m, mg, 0x000040, 0)
|
||||
pcall(mg.set, bdx, bdy, (" "):rep(bdl))
|
||||
end
|
||||
end
|
||||
|
||||
local function handleSpan(target, x, y, text, bg, fg)
|
||||
if not renderingAllowed() then return end
|
||||
local m = monitors[target[1]]
|
||||
local cb, rb = m[1]()
|
||||
if not cb then return end
|
||||
if rb then
|
||||
monitorResetBF(m)
|
||||
end
|
||||
-- It is assumed basic type checks were handled earlier.
|
||||
if y < 1 then return end
|
||||
if y > target[5] then return end
|
||||
if x < 1 then return end
|
||||
-- Note the use of unicode.len here.
|
||||
-- It's assumed that if the app is using Unicode text, then it used safeTextFormat earlier.
|
||||
-- This works for a consistent safety check.
|
||||
local w = unicode.len(text)
|
||||
if ((x + w) - 1) > target[4] then return end
|
||||
-- Checks complete, now commence screen cropping...
|
||||
local worldY = ((y + target[3]) - 1)
|
||||
if worldY < 1 then return end
|
||||
if worldY > monitors[target[1]][4] then return end
|
||||
-- The actual draw loop
|
||||
local buildingSegmentWX = nil
|
||||
local buildingSegmentWY = nil
|
||||
local buildingSegment = nil
|
||||
local buildingSegmentE = nil
|
||||
local function submitSegment()
|
||||
if buildingSegment then
|
||||
base = unicode.sub(text, buildingSegment, buildingSegmentE)
|
||||
-- rely on undoSafeTextFormat for this transform now
|
||||
monitorGPUColours(m, cb, bg, fg)
|
||||
pcall(cb.set, buildingSegmentWX, buildingSegmentWY, unicode.undoSafeTextFormat(base))
|
||||
buildingSegment = nil
|
||||
end
|
||||
end
|
||||
for i = 1, w do
|
||||
local rWX = (i - 1) + (x - 1) + target[2]
|
||||
local rWY = (y - 1) + target[3]
|
||||
local s = surfaceAt(target[1], rWX, rWY)
|
||||
local ok = false
|
||||
if s then
|
||||
ok = surfaces[s] == target
|
||||
end
|
||||
if ok then
|
||||
if not buildingSegment then
|
||||
buildingSegmentWX = rWX
|
||||
buildingSegmentWY = rWY
|
||||
buildingSegment = i
|
||||
end
|
||||
buildingSegmentE = i
|
||||
else
|
||||
submitSegment()
|
||||
end
|
||||
end
|
||||
submitSegment()
|
||||
end
|
||||
|
||||
local function updateRegion(monitorId, x, y, w, h, surfaceSpanCache)
|
||||
if not renderingAllowed() then return end
|
||||
local m = monitors[monitorId]
|
||||
|
@ -182,7 +243,27 @@ local function updateRegion(monitorId, x, y, w, h, surfaceSpanCache)
|
|||
end
|
||||
if not surfaceSpanCache[monitorId .. "_" .. t .. "_" .. ty] then
|
||||
surfaceSpanCache[monitorId .. "_" .. t .. "_" .. ty] = true
|
||||
surfaces[t][6]("line", ty)
|
||||
if ty == 1 then
|
||||
local lw = surfaces[t][4]
|
||||
local bg = 0x0080FF
|
||||
local fg = 0x000000
|
||||
local tx = "-"
|
||||
if t == 1 then
|
||||
bg = 0x000000
|
||||
fg = 0x0080FF
|
||||
tx = "+"
|
||||
end
|
||||
local vtitle = surfaces[t][7]
|
||||
local vto = unicode.len(vtitle)
|
||||
if vto < lw then
|
||||
vtitle = vtitle .. (tx):rep(lw - vto)
|
||||
else
|
||||
vtitle = unicode.sub(vtitle, 1, lw)
|
||||
end
|
||||
handleSpan(surfaces[t], 1, 1, vtitle, bg, fg)
|
||||
else
|
||||
surfaces[t][6](surfaces[t][8], "line", ty - 1)
|
||||
end
|
||||
end
|
||||
elseif not backgroundMarkStart then
|
||||
backgroundMarkStart = sx
|
||||
|
@ -215,8 +296,8 @@ local function ensureOnscreen(monitor, x, y, w, h)
|
|||
if monitor >= (#monitors + 1) then monitor = 1 end
|
||||
-- Failing anything else, revert to monitor 0
|
||||
if #monitors == 0 then monitor = 0 end
|
||||
x = math.min(math.max(1, x), monitors[monitor][3] - (w - 1))
|
||||
y = math.max(1, math.min(monitors[monitor][4] - (h - 1), y))
|
||||
x = math.min(math.max(x, 1), monitors[monitor][3] - (w - 1))
|
||||
y = math.max(2 - h, math.min(monitors[monitor][4], y))
|
||||
return monitor, x, y
|
||||
end
|
||||
|
||||
|
@ -268,13 +349,16 @@ local function moveSurface(surface, m, x, y, w, h, force)
|
|||
if renderingAllowed() and not force then
|
||||
local cb, b = monitors[m][1]()
|
||||
if b then
|
||||
monitorResetBF(b)
|
||||
monitorResetBF(monitors[m])
|
||||
end
|
||||
if cb then
|
||||
cb.copy(ox, oy, w, h, x - ox, y - oy)
|
||||
local monH = monitors[m][4]
|
||||
local cutTop = math.max(0, 1 - math.min(oy, y))
|
||||
local cutBottom = math.max(0, (math.max(oy + oh, y + h) - 1) - monH)
|
||||
pcall(cb.copy, ox, oy + cutTop, w, h - (cutTop + cutBottom), x - ox, y - oy)
|
||||
if surface == surfaces[1] then
|
||||
local cacheControl = {}
|
||||
for i = 1, h do
|
||||
for i = 1 + cutTop, h - cutBottom do
|
||||
cacheControl[om .. "_1_" .. i] = true
|
||||
end
|
||||
updateRegion(om, ox, oy, ow, oh, cacheControl)
|
||||
|
@ -298,61 +382,61 @@ local function ofsMSurface(focus, dm)
|
|||
moveSurface(focus, m, x, y)
|
||||
end
|
||||
|
||||
local function handleSpan(target, x, y, text, bg, fg)
|
||||
if not renderingAllowed() then return end
|
||||
local m = monitors[target[1]]
|
||||
local basePalT2 = {
|
||||
-- on T2 we provide 'system colours' by default
|
||||
0x000000, 0x0080FF, 0x000040, 0xFFFFFF,
|
||||
-- stuff above cannot be altered by user applications, to prevent graphical glitches.
|
||||
-- Provide some shades of grey to try and prevent accidental chroma.
|
||||
0x182828, 0x404040, 0x686868, 0x909090,
|
||||
0xB8B8B8, 0xE0E0E0, 0x800080, 0xFF0000,
|
||||
0x808000, 0x00FF00, 0x008080, 0x0000FF
|
||||
}
|
||||
local basePalT3 = {
|
||||
-- on T3 we provide the Tier 3 pal.
|
||||
0x0F0F0F, 0x1E1E1E, 0x2D2D2D, 0x3C3C3C,
|
||||
0x4B4B4B, 0x5A5A5A, 0x696969, 0x787878,
|
||||
0x878787, 0x969696, 0xA5A5A5, 0xB4B4B4,
|
||||
0xC3C3C3, 0xD2D2D2, 0xE1E1E1, 0xF0F0F0
|
||||
}
|
||||
|
||||
local function setSurfacePalette(surf, pal)
|
||||
if neo.dead then return 0 end
|
||||
local m = monitors[surf[1]]
|
||||
if not m then return 0 end
|
||||
local cb, rb = m[1]()
|
||||
if not cb then return end
|
||||
if not cb then return 0 end
|
||||
local dok, depth = pcall(cb.getDepth)
|
||||
if not dok then depth = 4 end
|
||||
if rb then
|
||||
monitorResetBF(m)
|
||||
end
|
||||
-- It is assumed basic type checks were handled earlier.
|
||||
if y < 1 then return end
|
||||
if y > target[5] then return end
|
||||
if x < 1 then return end
|
||||
-- Note the use of unicode.len here.
|
||||
-- It's assumed that if the app is using Unicode text, then it used safeTextFormat earlier.
|
||||
-- This works for a consistent safety check.
|
||||
local w = unicode.len(text)
|
||||
if ((x + w) - 1) > target[4] then return end
|
||||
-- Checks complete, now commence screen cropping...
|
||||
local worldY = ((y + target[3]) - 1)
|
||||
if worldY < 1 then return end
|
||||
if worldY > monitors[target[1]][4] then return end
|
||||
-- The actual draw loop
|
||||
local buildingSegmentWX = nil
|
||||
local buildingSegmentWY = nil
|
||||
local buildingSegment = nil
|
||||
local buildingSegmentE = nil
|
||||
local function submitSegment()
|
||||
if buildingSegment then
|
||||
base = unicode.sub(text, buildingSegment, buildingSegmentE)
|
||||
-- rely on undoSafeTextFormat for this transform now
|
||||
monitorGPUColours(m, cb, bg, fg)
|
||||
cb.set(buildingSegmentWX, buildingSegmentWY, unicode.undoSafeTextFormat(base))
|
||||
buildingSegment = nil
|
||||
local ko = -1
|
||||
local unlocked = false
|
||||
if not rawequal(pal, nil) then
|
||||
neo.ensureType(pal, "table")
|
||||
if depth < 8 then
|
||||
ko = 3 -- start overriding at indexes 4+
|
||||
end
|
||||
elseif depth < 8 then
|
||||
pal = basePalT2
|
||||
unlocked = true
|
||||
else
|
||||
pal = basePalT3
|
||||
unlocked = true
|
||||
end
|
||||
for k, v in ipairs(pal) do
|
||||
-- prevent graphical glitches for
|
||||
-- critical system colours on T3
|
||||
local av = v % 0x1000000
|
||||
if unlocked or (av ~= 0xFFFFFF and
|
||||
av ~= 0x000000 and
|
||||
av ~= 0x0080FF and
|
||||
av ~= 0x000040) then
|
||||
local ok = pcall(cb.setPaletteColor, k + ko, v)
|
||||
if not ok then return k - 1 end
|
||||
end
|
||||
end
|
||||
for i = 1, w do
|
||||
local rWX = (i - 1) + (x - 1) + target[2]
|
||||
local rWY = (y - 1) + target[3]
|
||||
local s = surfaceAt(target[1], rWX, rWY)
|
||||
local ok = false
|
||||
if s then
|
||||
ok = surfaces[s] == target
|
||||
end
|
||||
if ok then
|
||||
if not buildingSegment then
|
||||
buildingSegmentWX = rWX
|
||||
buildingSegmentWY = rWY
|
||||
buildingSegment = i
|
||||
end
|
||||
buildingSegmentE = i
|
||||
else
|
||||
submitSegment()
|
||||
end
|
||||
end
|
||||
submitSegment()
|
||||
return #pal
|
||||
end
|
||||
|
||||
local function changeFocus(oldSurface, optcache)
|
||||
|
@ -360,10 +444,12 @@ local function changeFocus(oldSurface, optcache)
|
|||
optcache = optcache or {}
|
||||
if ns1 ~= oldSurface then
|
||||
if oldSurface then
|
||||
oldSurface[6]("focus", false)
|
||||
setSurfacePalette(oldSurface, nil)
|
||||
oldSurface[6](oldSurface[8], "focus", false)
|
||||
end
|
||||
if ns1 then
|
||||
ns1[6]("focus", true)
|
||||
setSurfacePalette(ns1, nil)
|
||||
ns1[6](ns1[8], "focus", true)
|
||||
end
|
||||
updateStatus()
|
||||
if oldSurface then
|
||||
|
@ -402,69 +488,11 @@ everestProvider(function (pkg, pid, sendSig)
|
|||
else
|
||||
title = base .. ":" .. title
|
||||
end
|
||||
local m = 0
|
||||
if renderingAllowed() then m = 1 end
|
||||
local surf = {math.min(#monitors, math.max(1, lIM)), 1, 2, w, h}
|
||||
local focusState = false
|
||||
local llid = lid
|
||||
lid = lid + 1
|
||||
local specialDragHandler
|
||||
surf[6] = function (ev, a, b, c, d, e)
|
||||
-- Must forward surface events
|
||||
if ev == "focus" then
|
||||
focusState = a
|
||||
end
|
||||
if ev == "touch" then
|
||||
specialDragHandler = nil
|
||||
if math.floor(b) == 1 then
|
||||
if e == 1 then
|
||||
sendSig(llid, "close")
|
||||
return
|
||||
end
|
||||
specialDragHandler = function (x, y)
|
||||
local ofsX, ofsY = math.floor(x) - math.floor(a), math.floor(y) - math.floor(b)
|
||||
if (ofsX == 0) and (ofsY == 0) then return end
|
||||
ofsSurface(surf, ofsX, ofsY)
|
||||
end
|
||||
return
|
||||
end
|
||||
b = b - 1
|
||||
end
|
||||
if ev == "drag" then
|
||||
if specialDragHandler then
|
||||
specialDragHandler(a, b)
|
||||
return
|
||||
end
|
||||
b = b - 1
|
||||
end
|
||||
if ev == "scroll" or ev == "drop" then
|
||||
specialDragHandler = nil
|
||||
b = b - 1
|
||||
end
|
||||
if ev == "line" then
|
||||
if a == 1 then
|
||||
local lw = surf[4]
|
||||
local bg = 0x0080FF
|
||||
local fg = 0x000000
|
||||
local tx = "-"
|
||||
if focusState then
|
||||
bg = 0x000000
|
||||
fg = 0x0080FF
|
||||
tx = "+"
|
||||
end
|
||||
local vtitle = title
|
||||
local vto = unicode.len(vtitle)
|
||||
if vto < lw then
|
||||
vtitle = vtitle .. (tx):rep(lw - vto)
|
||||
else
|
||||
vtitle = unicode.sub(vtitle, 1, lw)
|
||||
end
|
||||
handleSpan(surf, 1, 1, vtitle, bg, fg)
|
||||
return
|
||||
end
|
||||
a = a - 1
|
||||
end
|
||||
sendSig(llid, ev, a, b, c, d, e)
|
||||
local surf = {math.min(#monitors, math.max(1, lIM)), 1, 2, w, h, sendSig, title, llid}
|
||||
if h >= monitors[surf[1]][4] then
|
||||
surf[3] = 1
|
||||
end
|
||||
local osrf = surfaces[1]
|
||||
table.insert(surfaces, 1, surf)
|
||||
|
@ -481,11 +509,11 @@ everestProvider(function (pkg, pid, sendSig)
|
|||
return w, (h - 1)
|
||||
end,
|
||||
getDepth = function ()
|
||||
if neo.dead then return false end
|
||||
if neo.dead then return 1 end
|
||||
local m = monitors[surf[1]]
|
||||
if not m then return false end
|
||||
if not m then return 1 end
|
||||
local cb, rb = m[1]()
|
||||
if not cb then return false end
|
||||
if not cb then return 1 end
|
||||
if rb then
|
||||
monitorResetBF(m)
|
||||
end
|
||||
|
@ -493,15 +521,15 @@ everestProvider(function (pkg, pid, sendSig)
|
|||
end,
|
||||
span = function (x, y, text, bg, fg)
|
||||
if neo.dead then error("everest died") end
|
||||
if type(x) ~= "number" then error("X must be number.") end
|
||||
if type(y) ~= "number" then error("Y must be number.") end
|
||||
if type(bg) ~= "number" then error("Background must be number.") end
|
||||
if type(fg) ~= "number" then error("Foreground must be number.") end
|
||||
if type(text) ~= "string" then error("Text must be string.") end
|
||||
checkArg(3, text, "string")
|
||||
x, y, bg, fg = math.floor(x), math.floor(y), math.floor(bg), math.floor(fg)
|
||||
if y == 0 then return end
|
||||
handleSpan(surf, x, y + 1, text, bg, fg)
|
||||
end,
|
||||
recommendPalette = function (pal)
|
||||
if surfaces[1] ~= surf then return 0 end
|
||||
return setSurfacePalette(surf, pal)
|
||||
end,
|
||||
close = function ()
|
||||
if neo.dead then return end
|
||||
local os1 = surfaces[1]
|
||||
|
@ -522,79 +550,113 @@ everestProvider(function (pkg, pid, sendSig)
|
|||
end
|
||||
end)
|
||||
-- THE EVEREST USER API ENDS (now for the session API, which just does boring stuff)
|
||||
-- used later on for lost monitor, too
|
||||
local function disclaimMonitor(mon)
|
||||
checkArg(1, mon, "string")
|
||||
screens.disclaim(mon)
|
||||
for k, v in ipairs(monitors) do
|
||||
if v[2] == mon then
|
||||
table.remove(monitors, k)
|
||||
reconcileAll()
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
everestSessionProvider(function (pkg, pid, sendSig)
|
||||
return {
|
||||
endSession = function (gotoBristol)
|
||||
checkArg(1, gotoBristol, "boolean")
|
||||
shuttingDown = true
|
||||
if gotoBristol then
|
||||
suggestAppsStop()
|
||||
dying()
|
||||
end
|
||||
end
|
||||
end,
|
||||
getMonitors = function ()
|
||||
local details = {}
|
||||
for k, v in ipairs(monitors) do
|
||||
details[k] = v[2]
|
||||
end
|
||||
return details
|
||||
end,
|
||||
disclaimMonitor = disclaimMonitor
|
||||
}
|
||||
end)
|
||||
-- THE EVEREST SESSION API ENDS
|
||||
|
||||
local function startLauncher()
|
||||
if not waitingShutdownCallback then
|
||||
local lApp = "app-launcher"
|
||||
if savingThrow then
|
||||
lApp = savingThrow.getSetting("sys-everest.launcher") or lApp
|
||||
end
|
||||
if lApp then
|
||||
neo.executeAsync(lApp)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- WM shortcuts are:
|
||||
-- Alt-Z: Switch surface
|
||||
-- Alt-Enter: Launcher
|
||||
-- Alt-Up/Down/Left/Right: Move surface
|
||||
local isAltDown = false
|
||||
local isCtrDown = false
|
||||
local function key(ka, kc, down)
|
||||
local focus = surfaces[1]
|
||||
if kc == 29 then isCtrDown = down end
|
||||
if kc == 56 then isAltDown = down end
|
||||
if isAltDown then
|
||||
if ka == 120 then
|
||||
if focus and down then ofsMSurface(focus, 1) end return
|
||||
end
|
||||
if kc == 200 then
|
||||
if focus and down then ofsSurface(focus, 0, -1) end return
|
||||
end
|
||||
if kc == 208 then
|
||||
if focus and down then ofsSurface(focus, 0, 1) end return
|
||||
end
|
||||
if kc == 203 then
|
||||
if focus and down then ofsSurface(focus, -1, 0) end return
|
||||
end
|
||||
if kc == 205 then
|
||||
if focus and down then ofsSurface(focus, 1, 0) end return
|
||||
end
|
||||
if ka == 122 then
|
||||
if focus and down then
|
||||
local n = table.remove(surfaces, 1)
|
||||
table.insert(surfaces, n)
|
||||
changeFocus(n)
|
||||
end return
|
||||
end
|
||||
if ka == 3 then
|
||||
-- Ctrl-Alt-C (!?!?!!)
|
||||
if isCtrDown then
|
||||
error("User-authorized Everest crash.")
|
||||
end
|
||||
end
|
||||
if ka == 99 then
|
||||
if down then
|
||||
if isCtrDown then
|
||||
error("User-authorized Everest crash.")
|
||||
else
|
||||
if focus then
|
||||
focus[6]("close")
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
if ka == 13 then
|
||||
if down and (not waitingShutdownCallback) then neo.executeAsync("app-launcher") end return
|
||||
local function key(ku, ka, kc, down)
|
||||
local ku, lin = screens.getMonitorByKeyboard(ku)
|
||||
for k, v in ipairs(monitors) do
|
||||
if ku and v[2] == ku then
|
||||
lin = k
|
||||
break
|
||||
end
|
||||
end
|
||||
if focus then
|
||||
if not lin then return end
|
||||
lIM = lin
|
||||
|
||||
local focus = surfaces[1]
|
||||
if kc == 29 then
|
||||
isCtrDown = down
|
||||
elseif kc == 56 then
|
||||
isAltDown = down
|
||||
end
|
||||
if isAltDown and ka == 122 then
|
||||
if focus and down then
|
||||
local n = table.remove(surfaces, 1)
|
||||
table.insert(surfaces, n)
|
||||
changeFocus(n)
|
||||
end
|
||||
elseif isAltDown and kc == 200 then
|
||||
if focus and down then ofsSurface(focus, 0, -1) end
|
||||
elseif isAltDown and kc == 208 then
|
||||
if focus and down then ofsSurface(focus, 0, 1) end
|
||||
elseif isAltDown and kc == 203 then
|
||||
if focus and down then ofsSurface(focus, -1, 0) end
|
||||
elseif isAltDown and kc == 205 then
|
||||
if focus and down then ofsSurface(focus, 1, 0) end
|
||||
elseif isAltDown and ka == 120 then
|
||||
if focus and down then ofsMSurface(focus, 1) end
|
||||
elseif isAltDown and ka == 97 then
|
||||
if not down then
|
||||
isAltDown = false
|
||||
end
|
||||
elseif isAltDown and (ka == 3 or ka == 99) then
|
||||
if down then
|
||||
if isCtrDown then
|
||||
error("User-authorized Everest crash.")
|
||||
elseif focus then
|
||||
focus[6](focus[8], "close")
|
||||
end
|
||||
end
|
||||
elseif isAltDown and ka == 13 then
|
||||
if down then
|
||||
startLauncher()
|
||||
end
|
||||
elseif focus then
|
||||
if kc ~= 56 then
|
||||
lIM = focus[1]
|
||||
end
|
||||
focus[6]("key", ka, kc, down)
|
||||
focus[6](focus[8], "key", ka, kc, down)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -605,6 +667,7 @@ local function performClaim(s3)
|
|||
if gpucb then
|
||||
local w, h = gpucb.getResolution()
|
||||
table.insert(monitors, {gpu, s3, w, h, -1, -1})
|
||||
setSurfacePalette({#monitors}, nil)
|
||||
-- This is required to ensure windows are moved off of the null monitor.
|
||||
-- Luckily, there's an obvious sign if they aren't - everest will promptly crash.
|
||||
reconcileAll()
|
||||
|
@ -619,20 +682,14 @@ while not shuttingDown do
|
|||
local s = {coroutine.yield()}
|
||||
if renderingAllowed() then
|
||||
if s[1] == "h.key_down" then
|
||||
local m = screens.getMonitorByKeyboard(s[2])
|
||||
for k, v in ipairs(monitors) do
|
||||
if v[2] == m then
|
||||
lIM = k
|
||||
end
|
||||
end
|
||||
key(s[3], s[4], true)
|
||||
key(s[2], s[3], s[4], true)
|
||||
end
|
||||
if s[1] == "h.key_up" then
|
||||
key(s[3], s[4], false)
|
||||
key(s[2], s[3], s[4], false)
|
||||
end
|
||||
if s[1] == "h.clipboard" then
|
||||
if surfaces[1] then
|
||||
surfaces[1][6]("clipboard", s[3])
|
||||
surfaces[1][6](surfaces[1][8], "clipboard", s[3])
|
||||
end
|
||||
end
|
||||
-- next on my list: high-res coordinates
|
||||
|
@ -640,17 +697,26 @@ while not shuttingDown do
|
|||
for k, v in ipairs(monitors) do
|
||||
if v[2] == s[2] then
|
||||
lIM = k
|
||||
local x, y = math.floor(s[3]), math.floor(s[4])
|
||||
local ix, iy = s[3] - x, s[4] - y
|
||||
local x, y = math.ceil(s[3]), math.ceil(s[4])
|
||||
local ix, iy = s[3] - math.floor(x), s[4] - math.floor(y)
|
||||
local sid, lx, ly = surfaceAt(k, x, y)
|
||||
if sid then
|
||||
local os = surfaces[1]
|
||||
local ns = table.remove(surfaces, sid)
|
||||
table.insert(surfaces, 1, ns)
|
||||
changeFocus(os)
|
||||
ns[6]("touch", lx, ly, ix, iy, s[5])
|
||||
draggingWindowX, draggingWindowY = nil
|
||||
if ly == 1 then
|
||||
if s[5] == 1 then
|
||||
ns[6](ns[8], "close")
|
||||
else
|
||||
draggingWindowX, draggingWindowY = lx, ly
|
||||
end
|
||||
else
|
||||
ns[6](ns[8], "touch", lx, ly - 1, ix, iy, s[5])
|
||||
end
|
||||
else
|
||||
if s[5] == 1 and not waitingShutdownCallback then neo.executeAsync("app-launcher") end
|
||||
if s[5] == 1 then startLauncher() end
|
||||
end
|
||||
break
|
||||
end
|
||||
|
@ -663,10 +729,18 @@ while not shuttingDown do
|
|||
for k, v in ipairs(monitors) do
|
||||
if v[2] == s[2] then
|
||||
if k == focus[1] then
|
||||
local x, y = (math.floor(s[3]) - focus[2]) + 1, (math.floor(s[4]) - focus[3]) + 1
|
||||
local x, y = (math.ceil(s[3]) - focus[2]) + 1, (math.ceil(s[4]) - focus[3]) + 1
|
||||
local ix, iy = s[3] - math.floor(s[3]), s[4] - math.floor(s[4])
|
||||
-- Ok, so let's see...
|
||||
focus[6](s[1]:sub(3), x, y, ix, iy, s[5])
|
||||
if s[1] == "h.drag" and draggingWindowX then
|
||||
local ofsX, ofsY = x - draggingWindowX, y - draggingWindowY
|
||||
if ofsX ~= 0 or ofsY ~= 0 then
|
||||
ofsSurface(focus, ofsX, ofsY)
|
||||
end
|
||||
else
|
||||
draggingWindowX, draggingWindowY = nil
|
||||
-- Ok, so let's see...
|
||||
focus[6](focus[8], s[1]:sub(3), x, y - 1, ix, iy, s[5])
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
|
@ -689,23 +763,24 @@ while not shuttingDown do
|
|||
end
|
||||
for k, v in ipairs(tags) do
|
||||
local surf = table.remove(surfaces, v - (k - 1))
|
||||
if os1 == surf then
|
||||
os1 = nil
|
||||
end
|
||||
updateRegion(surf[1], surf[2], surf[3], surf[4], surf[5], {})
|
||||
end
|
||||
checkWSC()
|
||||
changeFocus(os1)
|
||||
if os1 then
|
||||
changeFocus(os1)
|
||||
else
|
||||
changeFocus()
|
||||
end
|
||||
end
|
||||
if s[1] == "x.neo.sys.screens" then
|
||||
if s[2] == "available" then
|
||||
performClaim(s[3])
|
||||
end
|
||||
if s[2] == "lost" then
|
||||
for k, v in ipairs(monitors) do
|
||||
if v[2] == s[3] then
|
||||
table.remove(monitors, k)
|
||||
reconcileAll()
|
||||
break
|
||||
end
|
||||
end
|
||||
disclaimMonitor(s[3])
|
||||
end
|
||||
end
|
||||
if s[1] == "x.neo.sys.manage" then
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- s-donkonit : config, shutdown, screens
|
||||
|
||||
|
@ -8,18 +12,12 @@ local donkonitSPProvider = neo.requireAccess("r.neo.sys.manage", "creating NEO c
|
|||
local donkonitRDProvider = neo.requireAccess("r.neo.sys.screens", "creating NEO core APIs")
|
||||
local glacierDCProvider = neo.requireAccess("r.neo.pub.globals", "creating NEO core APIs")
|
||||
|
||||
local computer = neo.requireAccess("k.computer", "shutting down")
|
||||
local fs = neo.requireAccess("c.filesystem", "settings I/O")
|
||||
local gpus = neo.requireAccess("c.gpu", "screen control")
|
||||
local screens = neo.requireAccess("c.screen", "screen control")
|
||||
local shutdownFin = neo.requireAccess("k.computer", "shutting down").shutdown
|
||||
local primary = neo.requireAccess("c.filesystem", "settings I/O").primary
|
||||
local gpus = neo.requireAccess("c.gpu", "screen control").list
|
||||
local screens = neo.requireAccess("c.screen", "screen control").list
|
||||
neo.requireAccess("s.h.component_added", "HW management")
|
||||
neo.requireAccess("s.h.component_removed", "HW management")
|
||||
neo.requireAccess("s.h.key_down", "Keymap guesswork")
|
||||
|
||||
local function shutdownFin(reboot)
|
||||
-- any final actions donkonit needs to take here
|
||||
computer.shutdown(reboot)
|
||||
end
|
||||
|
||||
-- keys are pids
|
||||
local targs = {} -- settings notify targs
|
||||
|
@ -42,20 +40,21 @@ local settings = {
|
|||
password = "",
|
||||
["pub.clipboard"] = "",
|
||||
["sys-init.shell"] = "sys-everest",
|
||||
["sys-everest.launcher"] = "app-launcher",
|
||||
["run.sys-icecap"] = "yes",
|
||||
-- scr.w/h/d/t.<uuid>
|
||||
}
|
||||
|
||||
local function loadSettings()
|
||||
pcall(function ()
|
||||
local fw = require("sys-filewrap")
|
||||
local se = require("serial")
|
||||
local st = fw(fs.primary, "data/sys-glacier/sysconf.lua", false)
|
||||
local fw = require("sys-filewrap").create
|
||||
local se = require("serial").deserialize
|
||||
local st = fw(primary, "data/sys-glacier/sysconf.lua", false)
|
||||
local cfg = st.read("*a")
|
||||
st.close()
|
||||
st = nil
|
||||
fw = nil
|
||||
cfg = se.deserialize(cfg)
|
||||
cfg = se(cfg)
|
||||
for k, v in pairs(cfg) do
|
||||
if type(k) == "string" then
|
||||
if type(v) == "string" then
|
||||
|
@ -65,12 +64,13 @@ local function loadSettings()
|
|||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function saveSettings()
|
||||
local fw = require("sys-filewrap")
|
||||
local se = require("serial")
|
||||
fs.primary.makeDirectory("data/sys-glacier")
|
||||
local st = fw(fs.primary, "data/sys-glacier/sysconf.lua", true)
|
||||
st.write(se.serialize(settings))
|
||||
local fw = require("sys-filewrap").create
|
||||
local se = require("serial").serialize
|
||||
primary.makeDirectory("data/sys-glacier")
|
||||
local st = fw(primary, "data/sys-glacier/sysconf.lua", true)
|
||||
st.write(se(settings))
|
||||
st.close()
|
||||
end
|
||||
|
||||
|
@ -83,7 +83,7 @@ local currentGPUBinding = {}
|
|||
-- [gpuAddr] = userCount
|
||||
local currentGPUUsers = {}
|
||||
|
||||
-- Thanks to Skye for this!
|
||||
-- Thanks to Skye for this design!
|
||||
local keyboardMonCacheK, keyboardMonCacheV = nil
|
||||
|
||||
local function announceFreeMonitor(address, except)
|
||||
|
@ -94,38 +94,13 @@ local function announceFreeMonitor(address, except)
|
|||
end
|
||||
end
|
||||
|
||||
local function getGPU(monitor)
|
||||
local bestG
|
||||
local bestD = 0
|
||||
for v in gpus.list() do
|
||||
v.bind(monitor.address, false)
|
||||
currentGPUBinding[v.address] = nil
|
||||
local w, h = v.maxResolution()
|
||||
local d = v.maxDepth() * w * h
|
||||
if d > bestD then
|
||||
bestG = v
|
||||
bestD = d
|
||||
bestU = currentGPUUsers[v.address] or 0
|
||||
elseif d == bestD then
|
||||
if (currentGPUUsers[v.address] or 0) < bestU then
|
||||
bestG = v
|
||||
bestD = d
|
||||
bestU = currentGPUUsers[v.address] or 0
|
||||
end
|
||||
end
|
||||
end
|
||||
return bestG
|
||||
end
|
||||
|
||||
local function sRattle(name, val)
|
||||
for _, v in pairs(targs) do
|
||||
v("set_setting", name, val)
|
||||
end
|
||||
if name:sub(1, 4) == "scr." or name:sub(1, 4) == "pub." then
|
||||
for k, v in pairs(targsDC) do
|
||||
if not targs[k] then
|
||||
v("set_setting", name, val)
|
||||
end
|
||||
v("set_setting", name, val)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -139,96 +114,77 @@ local function getMonitorSettings(a)
|
|||
w, h, d = math.floor(w), math.floor(h), math.floor(d)
|
||||
return w, h, d, t
|
||||
end
|
||||
local function setupMonitor(gpu, monitor)
|
||||
monitor.setPrecise(true)
|
||||
monitor.turnOn()
|
||||
gpu.bind(monitor.address, false)
|
||||
currentGPUBinding[gpu.address] = monitor.address
|
||||
local maxW, maxH = gpu.maxResolution()
|
||||
local maxD = gpu.maxDepth()
|
||||
local w, h, d, t = getMonitorSettings(monitor.address)
|
||||
w, h, d = math.min(w, maxW), math.min(h, maxH), math.min(d, maxD)
|
||||
if monitor.setTouchModeInverted then
|
||||
monitor.setTouchModeInverted(t == "yes")
|
||||
else
|
||||
t = "no"
|
||||
|
||||
-- Settings API
|
||||
|
||||
local mBase = {
|
||||
getSetting = function (name)
|
||||
neo.ensureType(name, "string")
|
||||
return settings[name]
|
||||
end,
|
||||
listSettings = function ()
|
||||
local s = {}
|
||||
for k, v in pairs(settings) do
|
||||
table.insert(s, k)
|
||||
end
|
||||
return s
|
||||
end,
|
||||
delSetting = function (name)
|
||||
neo.ensureType(name, "string")
|
||||
local val = nil
|
||||
if name == "password" or name == "pub.clipboard" then val = "" end
|
||||
settings[name] = val
|
||||
sRattle(name, val)
|
||||
pcall(saveSettings)
|
||||
end,
|
||||
setSetting = function (name, val)
|
||||
neo.ensureType(name, "string")
|
||||
neo.ensureType(val, "string")
|
||||
settings[name] = val
|
||||
-- NOTE: Either a monitor is under application control,
|
||||
-- or it's not under any control.
|
||||
-- Monitor settings are applied on the transition to control.
|
||||
sRattle(name, val)
|
||||
pcall(saveSettings)
|
||||
end,
|
||||
shutdown = function (reboot)
|
||||
neo.ensureType(reboot, "boolean")
|
||||
if shuttingDown then return end
|
||||
shuttingDown = true
|
||||
shutdownMode = reboot
|
||||
local counter = 0
|
||||
neo.scheduleTimer(os.uptime() + 5) -- in case the upcoming code fails in some way
|
||||
for f, v in pairs(targsSD) do
|
||||
counter = counter + 1
|
||||
v("shutdown", reboot, function ()
|
||||
counter = counter - 1
|
||||
if counter == 0 then
|
||||
shutdownFin(shutdownMode)
|
||||
end
|
||||
end)
|
||||
end
|
||||
if counter == 0 then
|
||||
shutdownFin(shutdownMode)
|
||||
end
|
||||
-- donkonit will shutdown when the timer is hit.
|
||||
end
|
||||
settings["scr.w." .. monitor.address] = tostring(w)
|
||||
settings["scr.h." .. monitor.address] = tostring(h)
|
||||
settings["scr.d." .. monitor.address] = tostring(d)
|
||||
settings["scr.t." .. monitor.address] = t
|
||||
sRattle("scr.w." .. monitor.address, tostring(w))
|
||||
sRattle("scr.h." .. monitor.address, tostring(h))
|
||||
sRattle("scr.d." .. monitor.address, tostring(d))
|
||||
sRattle("scr.t." .. monitor.address, t)
|
||||
gpu.setResolution(w, h)
|
||||
gpu.setDepth(d)
|
||||
pcall(saveSettings)
|
||||
end
|
||||
}
|
||||
|
||||
donkonitSPProvider(function (pkg, pid, sendSig)
|
||||
targs[pid] = sendSig
|
||||
return {
|
||||
listSettings = function ()
|
||||
local s = {}
|
||||
for k, v in pairs(settings) do
|
||||
table.insert(s, k)
|
||||
end
|
||||
return s
|
||||
end,
|
||||
-- NOTE: REPLICATED IN GB
|
||||
getSetting = function (name)
|
||||
if type(name) ~= "string" then error("Setting name must be string") end
|
||||
return settings[name]
|
||||
end,
|
||||
delSetting = function (name)
|
||||
if type(name) ~= "string" then error("Setting name must be string") end
|
||||
local val = nil
|
||||
if name == "password" or name == "pub.clipboard" then val = "" end
|
||||
settings[name] = val
|
||||
sRattle(name, val)
|
||||
pcall(saveSettings)
|
||||
end,
|
||||
setSetting = function (name, val)
|
||||
if type(name) ~= "string" then error("Setting name must be string") end
|
||||
if type(val) ~= "string" then error("Setting value must be string") end
|
||||
settings[name] = val
|
||||
-- NOTE: Either a monitor is under application control,
|
||||
-- or it's not under any control.
|
||||
-- Monitor settings are applied on the transition to control.
|
||||
sRattle(name, val)
|
||||
pcall(saveSettings)
|
||||
end,
|
||||
--
|
||||
local n = {
|
||||
registerForShutdownEvent = function ()
|
||||
targsSD[pid] = sendSig
|
||||
end,
|
||||
registerSavingThrow = function (st)
|
||||
if type(st) ~= "function" then error("Saving throw function must be a function") end
|
||||
neo.ensureType(st, "function")
|
||||
targsST[pid] = st
|
||||
end,
|
||||
shutdown = function (reboot)
|
||||
if type(reboot) ~= "boolean" then error("Shutdown parameter must be a boolean (reboot)") end
|
||||
if shuttingDown then return end
|
||||
shuttingDown = true
|
||||
shutdownMode = reboot
|
||||
local counter = 0
|
||||
neo.scheduleTimer(os.uptime() + 5) -- in case the upcoming code fails in some way
|
||||
for f, v in pairs(targsSD) do
|
||||
counter = counter + 1
|
||||
v("shutdown", reboot, function ()
|
||||
counter = counter - 1
|
||||
if counter == 0 then
|
||||
shutdownFin(reboot)
|
||||
end
|
||||
end)
|
||||
end
|
||||
if counter == 0 then
|
||||
shutdownFin(reboot)
|
||||
end
|
||||
-- donkonit will shutdown when the timer is hit.
|
||||
end
|
||||
}
|
||||
return setmetatable(n, {
|
||||
__index = mBase,
|
||||
__metatable = 0
|
||||
})
|
||||
end)
|
||||
|
||||
donkonitRDProvider(function (pkg, pid, sendSig)
|
||||
|
@ -241,69 +197,34 @@ donkonitRDProvider(function (pkg, pid, sendSig)
|
|||
end}
|
||||
return {
|
||||
getMonitorByKeyboard = function (kb)
|
||||
if keyboardMonCacheK == kb.address then
|
||||
if keyboardMonCacheK == kb then
|
||||
return keyboardMonCacheV
|
||||
end
|
||||
for v in screens.list() do
|
||||
for v in screens() do
|
||||
for _, v2 in ipairs(v.getKeyboards()) do
|
||||
if v2 == kb then
|
||||
keyboardMonCacheK, keyboardMonCacheV = kb.address, v.address
|
||||
keyboardMonCacheK, keyboardMonCacheV = kb, v.address
|
||||
return v.address
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
getClaimable = function ()
|
||||
local c = {}
|
||||
-- do we have gpu?
|
||||
if not gpus.list()() then return c end
|
||||
if not gpus()() then return c end
|
||||
for _, v in ipairs(monitorPool) do
|
||||
table.insert(c, v.address)
|
||||
end
|
||||
return c
|
||||
end,
|
||||
claim = function (address)
|
||||
if type(address) ~= "string" then error("Address must be string.") end
|
||||
for k, v in ipairs(monitorPool) do
|
||||
if v.address == address then
|
||||
local gpu = getGPU(v)
|
||||
if gpu then
|
||||
setupMonitor(gpu, v)
|
||||
gpu = gpu.address
|
||||
currentGPUBinding[gpu] = address
|
||||
currentGPUUsers[gpu] = (currentGPUUsers[gpu] or 0) + 1
|
||||
local disclaimer = function (wasDevLoss)
|
||||
-- we lost it
|
||||
monitorClaims[address] = nil
|
||||
claimed[address] = nil
|
||||
if not wasDevLoss then
|
||||
currentGPUUsers[gpu] = currentGPUUsers[gpu] - 1
|
||||
table.insert(monitorPool, v)
|
||||
announceFreeMonitor(address, pid)
|
||||
else
|
||||
sendSig("lost", address)
|
||||
end
|
||||
end
|
||||
claimed[address] = disclaimer
|
||||
monitorClaims[address] = {gpu, disclaimer}
|
||||
table.remove(monitorPool, k)
|
||||
return function ()
|
||||
for v in gpus.list() do
|
||||
if v.address == gpu then
|
||||
local didBind = false
|
||||
if currentGPUBinding[gpu] ~= address then
|
||||
v.bind(address, false)
|
||||
didBind = true
|
||||
end
|
||||
currentGPUBinding[gpu] = address
|
||||
return v, didBind
|
||||
end
|
||||
end
|
||||
end, v
|
||||
end
|
||||
end
|
||||
end
|
||||
claim = function (...) -- see sys-gpualloc
|
||||
return require("sys-gpualloc")(
|
||||
gpus, screens,
|
||||
getMonitorSettings, settings, sRattle, saveSettings,
|
||||
announceFreeMonitor, pid, claimed, sendSig,
|
||||
monitorClaims, monitorPool, currentGPUUsers, currentGPUBinding,
|
||||
...)
|
||||
end,
|
||||
disclaim = function (address)
|
||||
if not address then error("Cannot disclaim nothing.") end
|
||||
|
@ -321,14 +242,13 @@ local function rescanDevs()
|
|||
currentGPUBinding = {}
|
||||
currentGPUUsers = {}
|
||||
keyboardMonCacheK, keyboardMonCacheV = nil, nil
|
||||
local hasGPU = gpus.list()()
|
||||
for k, v in pairs(monitorClaims) do
|
||||
v[2](true)
|
||||
end
|
||||
monitorClaims = {}
|
||||
for m in screens.list() do
|
||||
for m in screens() do
|
||||
table.insert(monitorPool, m)
|
||||
if hasGPU then
|
||||
if gpus()() then
|
||||
announceFreeMonitor(m.address)
|
||||
end
|
||||
end
|
||||
|
@ -336,11 +256,16 @@ end
|
|||
rescanDevs()
|
||||
|
||||
-- Save any settings made during the above (or just the language)
|
||||
saveSettings()
|
||||
pcall(saveSettings)
|
||||
-- --
|
||||
|
||||
glacierDCProvider(function (pkg, pid, sendSig)
|
||||
targsDC[pid] = sendSig
|
||||
local function sWrap(f)
|
||||
return function (s, ...)
|
||||
return f("pub." .. s, ...)
|
||||
end
|
||||
end
|
||||
return {
|
||||
getKnownMonitors = function ()
|
||||
local tbl = {}
|
||||
|
@ -379,25 +304,9 @@ glacierDCProvider(function (pkg, pid, sendSig)
|
|||
end,
|
||||
forceRescan = rescanDevs,
|
||||
-- NOTE: "pub." prefixed version of functions in sys.manage
|
||||
getSetting = function (name)
|
||||
if type(name) ~= "string" then error("Setting name must be string") end
|
||||
return settings["pub." .. name]
|
||||
end,
|
||||
delSetting = function (name)
|
||||
if type(name) ~= "string" then error("Setting name must be string") end
|
||||
local val = nil
|
||||
if name == "clipboard" then val = "" end
|
||||
settings["pub." .. name] = val
|
||||
sRattle("pub." .. name, val)
|
||||
pcall(saveSettings)
|
||||
end,
|
||||
setSetting = function (name, val)
|
||||
if type(name) ~= "string" then error("Setting name must be string") end
|
||||
if type(val) ~= "string" then error("Setting value must be string") end
|
||||
settings["pub." .. name] = val
|
||||
sRattle("pub." .. name, val)
|
||||
pcall(saveSettings)
|
||||
end
|
||||
getSetting = sWrap(mBase.getSetting),
|
||||
delSetting = sWrap(mBase.delSetting),
|
||||
setSetting = sWrap(mBase.setSetting)
|
||||
}
|
||||
end)
|
||||
|
||||
|
@ -409,13 +318,8 @@ while true do
|
|||
-- always shutdown
|
||||
shutdownFin(shutdownMode)
|
||||
end
|
||||
if s[1] == "h.component_added" then
|
||||
-- Before doing anything, is it worth it?
|
||||
if s[3] == "gpu" or s[3] == "screen" then
|
||||
rescanDevs()
|
||||
end
|
||||
end
|
||||
if s[1] == "h.component_removed" then
|
||||
if s[1] == "h.component_added" or s[1] == "h.component_removed" then
|
||||
-- Anything important?
|
||||
if s[3] == "gpu" or s[3] == "screen" then
|
||||
rescanDevs()
|
||||
end
|
||||
|
|
|
@ -1,87 +1,103 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- s-icecap : Responsible for x.neo.pub API, crash dialogs, and security policy that isn't "sys- has ALL access, anything else has none"
|
||||
-- In general, this is what userspace will be interacting with in some way or another to get stuff done
|
||||
|
||||
local rootAccess = neo.requireAccess("k.root", "installing GUI integration")
|
||||
local settings = neo.requireAccess("x.neo.sys.manage", "security sysconf access")
|
||||
|
||||
local fs = neo.requireAccess("c.filesystem", "file managers")
|
||||
|
||||
local donkonitDFProvider = neo.requireAccess("r.neo.pub.base", "creating basic NEO APIs")
|
||||
|
||||
local targsDH = {} -- data disposal
|
||||
|
||||
local todo = {}
|
||||
|
||||
local onEverest = {}
|
||||
-- Specific registration callbacks
|
||||
local onReg = {}
|
||||
local everestWindows = {}
|
||||
|
||||
local nexus
|
||||
|
||||
local function resumeWF(...)
|
||||
local ok, e = coroutine.resume(...)
|
||||
if not ok then
|
||||
e = tostring(e)
|
||||
neo.emergency(e)
|
||||
nexus.startDialog(e, "ice")
|
||||
end
|
||||
return ok
|
||||
local theEventHandler
|
||||
|
||||
local function addOnReg(p, f)
|
||||
onReg[p] = onReg[p] or {}
|
||||
table.insert(onReg[p], f)
|
||||
end
|
||||
|
||||
nexus = {
|
||||
createNexusThread = function (f, ...)
|
||||
local t = coroutine.create(f)
|
||||
if not resumeWF(t, ...) then return end
|
||||
local early = neo.requestAccess("x.neo.pub.window")
|
||||
if early then
|
||||
onEverest[#onEverest] = nil
|
||||
resumeWF(t, early)
|
||||
end
|
||||
return function ()
|
||||
for k, v in ipairs(onEverest) do
|
||||
if v == t then
|
||||
table.remove(onEverest, k)
|
||||
create = function (w, h, t, c)
|
||||
local function cb()
|
||||
local e = neo.requestAccess("x.neo.pub.window", theEventHandler)
|
||||
if e then
|
||||
if onReg["x.neo.pub.window"] then
|
||||
neo.emergency("icecap nexus prereg issue")
|
||||
theEventHandler("k.registration", "x.neo.pub.window")
|
||||
end
|
||||
local dwo, dw = pcall(e, w, h, t)
|
||||
if not dwo then
|
||||
addOnReg("x.neo.pub.window", cb)
|
||||
return
|
||||
end
|
||||
c(dw)
|
||||
everestWindows[dw.id] = function (...)
|
||||
return c(dw, ...)
|
||||
end
|
||||
else
|
||||
addOnReg("x.neo.pub.window", cb)
|
||||
end
|
||||
end
|
||||
cb()
|
||||
end,
|
||||
create = function (w, h, t)
|
||||
local thr = coroutine.running()
|
||||
table.insert(onEverest, thr)
|
||||
local everest = coroutine.yield()
|
||||
local dw = everest(w, h, title)
|
||||
everestWindows[dw.id] = thr
|
||||
return dw
|
||||
end,
|
||||
windows = everestWindows,
|
||||
startDialog = function (tx, ti)
|
||||
local fmt = require("fmttext")
|
||||
local txl = fmt.fmtText(unicode.safeTextFormat(tx), 40)
|
||||
fmt = nil
|
||||
nexus.createNexusThread(function ()
|
||||
local w = nexus.create(40, #txl, ti)
|
||||
while true do
|
||||
local ev, a = coroutine.yield()
|
||||
if ev == "line" then
|
||||
w.span(1, a, txl[a], 0xFFFFFF, 0)
|
||||
elseif ev == "close" then
|
||||
w.close()
|
||||
return
|
||||
local txl = require("fmttext").fmtText(unicode.safeTextFormat(tx), 40)
|
||||
nexus.create(40, #txl, ti, function (w, ev, a)
|
||||
if ev == "line" then
|
||||
if not pcall(w.span, 1, a, txl[a], 0xFFFFFF, 0) then
|
||||
everestWindows[w.id] = nil
|
||||
end
|
||||
elseif ev == "close" then
|
||||
w.close()
|
||||
everestWindows[w.id] = nil
|
||||
end
|
||||
end)
|
||||
end,
|
||||
close = function (wnd)
|
||||
wnd.close()
|
||||
everestWindows[wnd.id] = nil
|
||||
end
|
||||
}
|
||||
|
||||
local function getPfx(xd, pkg)
|
||||
-- This is to ensure the prefix naming scheme is FOLLOWED!
|
||||
-- sys- : System, part of KittenOS NEO and thus tries to present a "unified fragmented interface" in 'neo'
|
||||
-- app- : Application - these can have ad-hoc relationships. It is EXPECTED these have a GUI
|
||||
-- svc- : Service - Same as Application but with no expectation of desktop usability
|
||||
-- Libraries "have no rights" as they are essentially loadable blobs of Lua code.
|
||||
-- They have access via the calling program, and have a subset of the NEO Kernel API
|
||||
-- Apps can register with their own name, w/ details
|
||||
local pfx = nil
|
||||
if pkg:sub(1, 4) == "app-" then pfx = "app" end
|
||||
if pkg:sub(1, 4) == "svc-" then pfx = "svc" end
|
||||
if pfx then
|
||||
return xd .. pfx .. "." .. pkg:sub(5)
|
||||
end
|
||||
end
|
||||
|
||||
local function splitAC(ac)
|
||||
local sb = ac:match("/[a-z0-9/%.]*$")
|
||||
if sb then
|
||||
return ac:sub(1, #ac - #sb), sb
|
||||
end
|
||||
return ac
|
||||
end
|
||||
|
||||
donkonitDFProvider(function (pkg, pid, sendSig)
|
||||
local prefixNS = "data/" .. pkg
|
||||
local prefixWS = "data/" .. pkg .. "/"
|
||||
fs.primary.makeDirectory(prefixNS)
|
||||
local prefixWS = prefixNS .. "/"
|
||||
local fs = rootAccess.primaryDisk
|
||||
fs.makeDirectory(prefixNS)
|
||||
local openHandles = {}
|
||||
targsDH[pid] = function ()
|
||||
for k, v in pairs(openHandles) do
|
||||
|
@ -89,51 +105,75 @@ donkonitDFProvider(function (pkg, pid, sendSig)
|
|||
end
|
||||
end
|
||||
return {
|
||||
showFileDialogAsync = function (forWrite)
|
||||
showFileDialogAsync = function (forWrite, defName)
|
||||
if not rawequal(forWrite, nil) then
|
||||
require("sys-filewrap").ensureMode(forWrite)
|
||||
end
|
||||
if not rawequal(defName, nil) then
|
||||
defName = tostring(defName)
|
||||
end
|
||||
-- Not hooked into the event API, so can't safely interfere
|
||||
-- Thus, this is async and uses a return event.
|
||||
local tag = {}
|
||||
neo.scheduleTimer(0)
|
||||
table.insert(todo, function ()
|
||||
-- sys-filedialog is yet another "library to control memory usage".
|
||||
local closer = require("sys-filedialog")(event, nexus, function (res) openHandles[tag] = nil sendSig("filedialog", tag, res) end, fs, pkg, forWrite)
|
||||
local closer = require("sys-filedialog")(event, nexus, function (res) openHandles[tag] = nil sendSig("filedialog", tag, res) end, neo.requireAccess("c.filesystem", "file managers"), pkg, forWrite, defName)
|
||||
openHandles[tag] = closer
|
||||
end)
|
||||
return tag
|
||||
end,
|
||||
myApi = getPfx("", pkg),
|
||||
lockPerm = function (perm)
|
||||
-- Are we allowed to?
|
||||
local permPfx, detail = splitAC(perm)
|
||||
if getPfx("x.", pkg) ~= permPfx then
|
||||
return false, "You don't own this permission."
|
||||
end
|
||||
local set = "perm|*|" .. perm
|
||||
if settings.getSetting(set) then
|
||||
-- Silently ignored, to stop apps trying to sense this & be annoying.
|
||||
-- The user is allowed to choose.
|
||||
-- You are only allowed to suggest.
|
||||
return true
|
||||
end
|
||||
settings.setSetting(set, "ask")
|
||||
return true
|
||||
end,
|
||||
-- Paths must begin with / implicitly
|
||||
list = function (path)
|
||||
if type(path) ~= "string" then error("Expected path to be string") end
|
||||
neo.ensureType(path, "string")
|
||||
path = prefixNS .. path
|
||||
neo.ensurePath(path, prefixWS)
|
||||
if path:sub(#path, #path) ~= "/" then error("Expected / at end") end
|
||||
return fs.primary.list(path:sub(1, #path - 1))
|
||||
return fs.list(path:sub(1, #path - 1))
|
||||
end,
|
||||
makeDirectory = function (path)
|
||||
if type(path) ~= "string" then error("Expected path to be string") end
|
||||
neo.ensureType(path, "string")
|
||||
path = prefixNS .. path
|
||||
neo.ensurePath(path, prefixWS)
|
||||
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
||||
return fs.primary.makeDirectory(path)
|
||||
return fs.makeDirectory(path)
|
||||
end,
|
||||
rename = function (path1, path2)
|
||||
if type(path1) ~= "string" then error("Expected path to be string") end
|
||||
if type(path2) ~= "string" then error("Expected path to be string") end
|
||||
neo.ensureType(path1, "string")
|
||||
neo.ensureType(path2, "string")
|
||||
path1 = prefixNS .. path1
|
||||
path2 = prefixNS .. path2
|
||||
neo.ensurePath(path1, prefixWS)
|
||||
neo.ensurePath(path2, prefixWS)
|
||||
if path:sub(#path1, #path1) == "/" then error("Expected no / at end") end
|
||||
if path:sub(#path2, #path2) == "/" then error("Expected no / at end") end
|
||||
return fs.primary.rename(path1, path2)
|
||||
return fs.rename(path1, path2)
|
||||
end,
|
||||
open = function (path, mode)
|
||||
if type(path) ~= "string" then error("Expected path to be string") end
|
||||
if type(mode) ~= "boolean" then error("Expected mode to be boolean (writing)") end
|
||||
neo.ensureType(path, "string")
|
||||
-- mode verified by filewrap
|
||||
path = prefixNS .. path
|
||||
neo.ensurePath(path, prefixWS)
|
||||
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
||||
local fw, closer = require("sys-filewrap")(fs.primary, path, mode)
|
||||
local fw, closer = require("sys-filewrap").create(fs, path, mode)
|
||||
if not fw then return nil, closer end
|
||||
local oc = fw.close
|
||||
fw.close = function ()
|
||||
oc()
|
||||
|
@ -143,39 +183,32 @@ donkonitDFProvider(function (pkg, pid, sendSig)
|
|||
return fw
|
||||
end,
|
||||
remove = function (path)
|
||||
if type(path) ~= "string" then error("Expected path to be string") end
|
||||
neo.ensureType(path, "string")
|
||||
path = prefixNS .. path
|
||||
neo.ensurePath(path, prefixWS)
|
||||
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
||||
return fs.primary.remove(path)
|
||||
return fs.remove(path)
|
||||
end,
|
||||
stat = function (path)
|
||||
if type(path) ~= "string" then error("Expected path to be string") end
|
||||
neo.ensureType(path, "string")
|
||||
path = prefixNS .. path
|
||||
neo.ensurePath(path, prefixWS)
|
||||
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
||||
if not fs.primary.exists(path) then return nil end
|
||||
if not fs.exists(path) then return nil end
|
||||
return {
|
||||
fs.primary.isDirectory(path),
|
||||
fs.primary.size(path),
|
||||
fs.primary.lastModified(path)
|
||||
fs.isDirectory(path),
|
||||
fs.size(path),
|
||||
fs.lastModified(path)
|
||||
}
|
||||
end,
|
||||
-- getLabel/setLabel have nothing to do with this
|
||||
spaceUsed = fs.primary.spaceUsed,
|
||||
spaceTotal = fs.primary.spaceTotal,
|
||||
isReadOnly = fs.primary.isReadOnly
|
||||
spaceUsed = fs.spaceUsed,
|
||||
spaceTotal = fs.spaceTotal,
|
||||
isReadOnly = fs.isReadOnly
|
||||
}
|
||||
end)
|
||||
|
||||
-- Connect in security policy now
|
||||
local rootAccess = neo.requireAccess("k.root", "installing GUI integration")
|
||||
local backup = rootAccess.securityPolicyINIT or rootAccess.securityPolicy
|
||||
rootAccess.securityPolicyINIT = backup
|
||||
rootAccess.securityPolicy = function (pid, proc, perm, req)
|
||||
if neo.dead then
|
||||
return backup(pid, proc, perm, req)
|
||||
end
|
||||
local function secPolicyStage2(pid, proc, perm, req)
|
||||
local def = proc.pkg:sub(1, 4) == "sys-"
|
||||
local secpol, err = require("sys-secpolicy")
|
||||
if not secpol then
|
||||
|
@ -187,7 +220,11 @@ rootAccess.securityPolicy = function (pid, proc, perm, req)
|
|||
-- Push to ICECAP thread to avoid deadlock b/c wrong event-pull context
|
||||
neo.scheduleTimer(0)
|
||||
table.insert(todo, function ()
|
||||
local ok, err = pcall(secpol, nexus, settings, proc.pkg, pid, perm, req)
|
||||
local fPerm = perm
|
||||
if fPerm:sub(1, 2) == "r." then
|
||||
fPerm = splitAC(fPerm)
|
||||
end
|
||||
local ok, err = pcall(secpol, nexus, settings, proc.pkg, pid, fPerm, req, getPfx("", proc.pkg))
|
||||
if not ok then
|
||||
neo.emergency("Used fallback policy because of run-err: " .. err)
|
||||
req(def)
|
||||
|
@ -195,8 +232,66 @@ rootAccess.securityPolicy = function (pid, proc, perm, req)
|
|||
end)
|
||||
end
|
||||
|
||||
while true do
|
||||
local ev = {coroutine.yield()}
|
||||
-- Connect in security policy now
|
||||
local backup = rootAccess.securityPolicyINIT or rootAccess.securityPolicy
|
||||
rootAccess.securityPolicyINIT = backup
|
||||
rootAccess.securityPolicy = function (pid, proc, perm, req)
|
||||
if neo.dead then
|
||||
return backup(pid, proc, perm, req)
|
||||
end
|
||||
local function finish()
|
||||
secPolicyStage2(pid, proc, perm, req)
|
||||
end
|
||||
-- Do we need to start it?
|
||||
if perm:sub(1, 6) == "x.svc." and not neo.usAccessExists(perm) then
|
||||
local appAct = splitAC(perm:sub(7))
|
||||
-- Prepare for success
|
||||
onReg[perm] = onReg[perm] or {}
|
||||
local orp = onReg[perm]
|
||||
local function kme()
|
||||
if finish then
|
||||
finish()
|
||||
finish = nil
|
||||
end
|
||||
end
|
||||
table.insert(orp, kme)
|
||||
pcall(neo.executeAsync, "svc-" .. appAct)
|
||||
-- Fallback "quit now"
|
||||
local time = os.uptime() + 30
|
||||
neo.scheduleTimer(time)
|
||||
local f
|
||||
function f()
|
||||
if finish then
|
||||
if os.uptime() >= time then
|
||||
-- we've given up
|
||||
if onReg[perm] == orp then
|
||||
for k, v in ipairs(orp) do
|
||||
if v == kme then
|
||||
table.remove(orp, k)()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(todo, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(todo, f)
|
||||
return
|
||||
else
|
||||
finish()
|
||||
end
|
||||
end
|
||||
|
||||
local function dcall(c, ...)
|
||||
local ok, e = pcall(...)
|
||||
if not ok then
|
||||
nexus.startDialog(tostring(e), c .. "err")
|
||||
end
|
||||
end
|
||||
function theEventHandler(...)
|
||||
local ev = {...}
|
||||
if ev[1] == "k.procdie" then
|
||||
local _, pkg, pid, reason = table.unpack(ev)
|
||||
if targsDH[pid] then
|
||||
|
@ -210,26 +305,24 @@ while true do
|
|||
local nt = todo
|
||||
todo = {}
|
||||
for _, v in ipairs(nt) do
|
||||
local ok, e = pcall(v)
|
||||
if not ok then
|
||||
nexus.startDialog(tostring(e), "terr")
|
||||
end
|
||||
dcall("t", v)
|
||||
end
|
||||
elseif ev[1] == "k.registration" then
|
||||
if ev[2] == "x.neo.pub.window" then
|
||||
local nt = onEverest
|
||||
onEverest = {}
|
||||
for _, v in ipairs(nt) do
|
||||
coroutine.resume(v, neo.requestAccess("x.neo.pub.window"))
|
||||
if onReg[ev[2]] then
|
||||
local tmp = onReg[ev[2]]
|
||||
onReg[ev[2]] = nil
|
||||
for _, v in ipairs(tmp) do
|
||||
dcall("r", v)
|
||||
end
|
||||
end
|
||||
elseif ev[1] == "x.neo.pub.window" then
|
||||
local v = everestWindows[ev[2]]
|
||||
if v then
|
||||
resumeWF(v, table.unpack(ev, 3))
|
||||
if coroutine.status(v) == "dead" then
|
||||
everestWindows[ev[2]] = nil
|
||||
end
|
||||
dcall("w", v, table.unpack(ev, 3))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
theEventHandler(coroutine.yield())
|
||||
end
|
||||
|
|
|
@ -1,21 +1,27 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- s-bristol splash screen, login agent
|
||||
-- Named to allude to Plymouth (splash screen used in some linux distros)
|
||||
|
||||
local callerPkg, callerPid, callerScr = ...
|
||||
|
||||
local gpuG, screen = nil, nil
|
||||
local shutdownEmergency = neo.requestAccess("k.computer").shutdown
|
||||
neo.requestAccess("s.h.key_down")
|
||||
neo.requestAccess("s.h._kosneo_syslog")
|
||||
|
||||
local scrW, scrH
|
||||
local warnings = {
|
||||
"",
|
||||
"",
|
||||
""
|
||||
}
|
||||
-- gpuG/performDisclaim are GPU management, while screen is used for prioritization
|
||||
local gpuG, performDisclaim, screen = nil
|
||||
local scrW, scrH = 1, 1
|
||||
local nssInst
|
||||
|
||||
local console = {}
|
||||
local helpActive = false
|
||||
local buttonsActive = false
|
||||
|
||||
-- Attempts to call upon nsm for a safe shutdown
|
||||
local function shutdown(reboot)
|
||||
|
@ -30,30 +36,53 @@ local function shutdown(reboot)
|
|||
end
|
||||
end
|
||||
|
||||
local function rstfbDraw(gpu)
|
||||
gpu.setBackground(0xFFFFFF)
|
||||
gpu.setForeground(0x000000)
|
||||
end
|
||||
|
||||
local function basicDraw(gpu)
|
||||
scrW, scrH = gpu.getResolution()
|
||||
gpu.fill(1, 1, scrW, scrH, " ")
|
||||
gpu.set(2, 2, "KittenOS NEO")
|
||||
end
|
||||
|
||||
local function advDraw(gpu)
|
||||
basicDraw(gpu)
|
||||
local function basicDraw(bg)
|
||||
local gpu = gpuG()
|
||||
pcall(gpu.setBackground, bg or 0xFFFFFF)
|
||||
pcall(gpu.setForeground, 0x000000)
|
||||
local ok, sw, sh = pcall(gpu.getResolution)
|
||||
if not ok then return end
|
||||
scrW, scrH = sw, sh
|
||||
pcall(gpu.fill, 1, 1, scrW, scrH, " ")
|
||||
pcall(gpu.set, 2, 2, "KittenOS NEO")
|
||||
local usage = math.floor((os.totalMemory() - os.freeMemory()) / 1024)
|
||||
gpu.set(2, 3, "RAM Usage: " .. usage .. "K / " .. math.floor(os.totalMemory() / 1024) .. "K")
|
||||
for i = 1, #warnings do
|
||||
gpu.set(2, 6 + i, warnings[i])
|
||||
pcall(gpu.set, 2, 3, "RAM Usage: " .. usage .. "K / " .. math.floor(os.totalMemory() / 1024) .. "K")
|
||||
local cut = 7
|
||||
if buttonsActive then cut = 9 end
|
||||
local areaSize = scrH - cut
|
||||
local n2 = 0
|
||||
if helpActive then
|
||||
if _VERSION == "Lua 5.2" then
|
||||
table.insert(console, "WARNING: Lua 5.2 memory usage issue!")
|
||||
table.insert(console, "Shift-right-click while holding the CPU/APU.")
|
||||
n2 = 2
|
||||
end
|
||||
table.insert(console, "TAB to change option, ENTER to select.")
|
||||
n2 = n2 + 1
|
||||
end
|
||||
for i = 1, areaSize do
|
||||
pcall(gpu.set, 2, 6 + i, console[#console + i - areaSize] or "")
|
||||
end
|
||||
for i = 1, n2 do
|
||||
table.remove(console, #console)
|
||||
end
|
||||
return gpu
|
||||
end
|
||||
|
||||
local function consoleEventHandler(ev)
|
||||
if ev[1] == "h._kosneo_syslog" then
|
||||
local text = ""
|
||||
for i = 3, #ev do
|
||||
if i ~= 3 then text = text .. " " end
|
||||
text = text .. tostring(ev[i])
|
||||
end
|
||||
table.insert(console, text)
|
||||
end
|
||||
end
|
||||
|
||||
-- Callback setup by finalPrompt to disclaim the main monitor first
|
||||
local performDisclaim = nil
|
||||
|
||||
local function retrieveNssMonitor(nss)
|
||||
-- Attempts to get an NSS monitor with a priority list of screens
|
||||
local function retrieveNssMonitor(...)
|
||||
local spc = {...}
|
||||
gpuG = nil
|
||||
local subpool = {}
|
||||
while not gpuG do
|
||||
|
@ -65,19 +94,23 @@ local function retrieveNssMonitor(nss)
|
|||
-- If no monitors are available, shut down now.
|
||||
-- NSS monitor pool output is smaller than, but similar to, Everest monitor data:
|
||||
-- {gpu, screenAddr}
|
||||
local pool = nss.getClaimable()
|
||||
local pool = nssInst.getClaimable()
|
||||
while not pool[1] do
|
||||
coroutine.yield() -- wait for presumably a NSS notification
|
||||
pool = nss.getClaimable()
|
||||
-- wait for presumably a NSS notification
|
||||
consoleEventHandler({coroutine.yield()})
|
||||
pool = nssInst.getClaimable()
|
||||
end
|
||||
subpool = {}
|
||||
-- Specifies which element to elevate to top priority
|
||||
local optimalSwap = nil
|
||||
local optimal = #spc + 1
|
||||
if screen then
|
||||
for k, v in ipairs(pool) do
|
||||
if v == screen then
|
||||
optimalSwap = k
|
||||
break
|
||||
for k2, v2 in ipairs(spc) do
|
||||
if v == v2 and optimal > k2 then
|
||||
optimalSwap, optimal = k, k2
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -87,65 +120,66 @@ local function retrieveNssMonitor(nss)
|
|||
pool[1] = swapA
|
||||
end
|
||||
for _, v in ipairs(pool) do
|
||||
local gpu = nss.claim(v)
|
||||
local gpu = nssInst.claim(v)
|
||||
if gpu then
|
||||
local gcb = gpu()
|
||||
if gcb then
|
||||
table.insert(subpool, {gpu, v})
|
||||
gcb.setBackground(0x000020)
|
||||
local w, h = gcb.getResolution()
|
||||
gcb.fill(1, 1, w, h, " ")
|
||||
pcall(function ()
|
||||
gcb.setBackground(0x000020)
|
||||
local w, h = gcb.getResolution()
|
||||
gcb.fill(1, 1, w, h, " ")
|
||||
table.insert(subpool, {gpu, v})
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not subpool[1] then
|
||||
error("None of the GPUs we got were actually usable")
|
||||
if subpool[1] then
|
||||
gpuG, screen = table.unpack(subpool[1])
|
||||
end
|
||||
gpuG = subpool[1][1]
|
||||
screen = subpool[1][2]
|
||||
end
|
||||
-- done with search
|
||||
local gpu = gpuG()
|
||||
scrW, scrH = gpu.getResolution()
|
||||
rstfbDraw(gpu)
|
||||
gpu.fill(1, 1, scrW, scrH, " ")
|
||||
performDisclaim = function (full)
|
||||
nss.disclaim(subpool[1][2])
|
||||
nssInst.disclaim(subpool[1][2])
|
||||
if full then
|
||||
for _, v in ipairs(subpool) do
|
||||
nss.disclaim(v[2])
|
||||
nssInst.disclaim(v[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
return gpu
|
||||
end
|
||||
|
||||
local function sleep(t)
|
||||
neo.scheduleTimer(os.uptime() + t)
|
||||
while true do
|
||||
local ev = {coroutine.yield()}
|
||||
consoleEventHandler(ev)
|
||||
if ev[1] == "k.timer" then
|
||||
break
|
||||
end
|
||||
if ev[1] == "x.neo.sys.screens" then
|
||||
-- This implies we have and can use nss, but check anyway
|
||||
local nss = neo.requestAccess("x.neo.sys.screens")
|
||||
if nss then
|
||||
local gpu = retrieveNssMonitor(nss)
|
||||
basicDraw(gpu)
|
||||
end
|
||||
retrieveNssMonitor(screen)
|
||||
basicDraw()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function alert(s)
|
||||
console = {s}
|
||||
helpActive, buttonsActive = false, false
|
||||
basicDraw()
|
||||
sleep(1)
|
||||
buttonsActive = true
|
||||
end
|
||||
|
||||
local function finalPrompt()
|
||||
local nss = neo.requestAccess("x.neo.sys.screens")
|
||||
if nss then
|
||||
retrieveNssMonitor(nss)
|
||||
else
|
||||
error("no glacier to provide GPU for the prompt")
|
||||
nssInst = neo.requestAccess("x.neo.sys.screens")
|
||||
if not nssInst then
|
||||
console = {"sys-glacier not available"}
|
||||
basicDraw()
|
||||
error("no nssInst")
|
||||
end
|
||||
retrieveNssMonitor()
|
||||
helpActive, buttonsActive = true, true
|
||||
-- This is nsm's final chance to make itself available and thus allow the password to be set
|
||||
local nsm = neo.requestAccess("x.neo.sys.manage")
|
||||
local waiting = true
|
||||
|
@ -157,24 +191,11 @@ local function finalPrompt()
|
|||
return false
|
||||
end
|
||||
end
|
||||
if _VERSION == "Lua 5.2" then
|
||||
warnings[1] = "NOTE: It's recommended you use Lua 5.3."
|
||||
warnings[2] = "Shift-right-click while holding the CPU item."
|
||||
else
|
||||
warnings[1] = "TAB to change option,"
|
||||
warnings[2] = "ENTER to select..."
|
||||
end
|
||||
-- The actual main prompt loop
|
||||
while waiting do
|
||||
local gpu = gpuG()
|
||||
rstfbDraw(gpu)
|
||||
advDraw(gpu)
|
||||
local entry = ""
|
||||
local entry2 = ""
|
||||
local active = true
|
||||
local shButton = "<Shutdown>"
|
||||
local rbButton = "<Reboot>"
|
||||
local smButton = "<Safe Mode>"
|
||||
local pw = {function ()
|
||||
return "Password: " .. entry2
|
||||
end, function (key)
|
||||
|
@ -186,10 +207,7 @@ local function finalPrompt()
|
|||
if entry == password then
|
||||
waiting = false
|
||||
else
|
||||
local gpu = gpuG()
|
||||
rstfbDraw(gpu)
|
||||
advDraw(gpu)
|
||||
sleep(1)
|
||||
alert("Incorrect password")
|
||||
end
|
||||
active = false
|
||||
end
|
||||
|
@ -206,68 +224,69 @@ local function finalPrompt()
|
|||
end
|
||||
local controls = {
|
||||
{function ()
|
||||
return shButton
|
||||
return "<Shutdown>"
|
||||
end, function (key)
|
||||
if key == 13 then
|
||||
local gpu = gpuG()
|
||||
rstfbDraw(gpu)
|
||||
basicDraw(gpu)
|
||||
gpu.set(2, 4, "Shutting down...")
|
||||
alert("Shutting down...")
|
||||
shutdown(false)
|
||||
end
|
||||
end, 2, scrH - 1, unicode.len(shButton)},
|
||||
end, 2, scrH - 1, 10},
|
||||
{function ()
|
||||
return rbButton
|
||||
return "<Reboot>"
|
||||
end, function (key)
|
||||
if key == 13 then
|
||||
local gpu = gpuG()
|
||||
rstfbDraw(gpu)
|
||||
basicDraw(gpu)
|
||||
gpu.set(2, 4, "Rebooting...")
|
||||
alert("Rebooting...")
|
||||
shutdown(true)
|
||||
end
|
||||
end, 3 + unicode.len(shButton), scrH - 1, unicode.len(rbButton)},
|
||||
end, 13, scrH - 1, 8},
|
||||
{function ()
|
||||
return smButton
|
||||
return "<Safe Mode>"
|
||||
end, function (key)
|
||||
if key == 13 then
|
||||
local gpu = gpuG()
|
||||
rstfbDraw(gpu)
|
||||
basicDraw(gpu)
|
||||
gpu.set(2, 4, "Login to activate Safe Mode.")
|
||||
sleep(1)
|
||||
gpu = gpuG()
|
||||
safeModeActive = true
|
||||
rstfbDraw(gpu)
|
||||
advDraw(gpu)
|
||||
alert("Login to activate Safe Mode.")
|
||||
end
|
||||
end, 4 + unicode.len(shButton) + unicode.len(rbButton), scrH - 1, unicode.len(smButton)},
|
||||
end, 22, scrH - 1, 11},
|
||||
pw,
|
||||
}
|
||||
local control = #controls
|
||||
local lastKeyboard
|
||||
while active do
|
||||
local gpu = gpuG()
|
||||
for k, v in ipairs(controls) do
|
||||
if k == control then
|
||||
gpu.setBackground(0x000000)
|
||||
gpu.setForeground(0xFFFFFF)
|
||||
else
|
||||
gpu.setBackground(0xFFFFFF)
|
||||
gpu.setForeground(0x000000)
|
||||
local gpu = basicDraw()
|
||||
if gpu then
|
||||
for k, v in ipairs(controls) do
|
||||
if k == control then
|
||||
pcall(gpu.setBackground, 0x000000)
|
||||
pcall(gpu.setForeground, 0xFFFFFF)
|
||||
else
|
||||
pcall(gpu.setBackground, 0xFFFFFF)
|
||||
pcall(gpu.setForeground, 0x000000)
|
||||
end
|
||||
pcall(gpu.fill, v[3], v[4], v[5], 1, " ")
|
||||
pcall(gpu.set, v[3], v[4], v[1]())
|
||||
end
|
||||
gpu.fill(v[3], v[4], v[5], 1, " ")
|
||||
gpu.set(v[3], v[4], v[1]())
|
||||
end
|
||||
-- event handling...
|
||||
local sig = {coroutine.yield()}
|
||||
consoleEventHandler(sig)
|
||||
if sig[1] == "x.neo.sys.screens" then
|
||||
-- We need to reinit screens no matter what.
|
||||
retrieveNssMonitor(nss)
|
||||
retrieveNssMonitor(screen)
|
||||
active = false
|
||||
end
|
||||
if sig[1] == "h.key_down" then
|
||||
if sig[2] ~= lastKeyboard then
|
||||
lastKeyboard = sig[2]
|
||||
local nScreen = nssInst.getMonitorByKeyboard(lastKeyboard)
|
||||
if nScreen and nScreen ~= screen then
|
||||
neo.emergency("new primary:", nScreen)
|
||||
retrieveNssMonitor(nScreen, screen)
|
||||
active = false
|
||||
end
|
||||
end
|
||||
if sig[4] == 15 then
|
||||
-- this makes sense in context
|
||||
control = control % (#controls)
|
||||
control = control % #controls
|
||||
control = control + 1
|
||||
else
|
||||
controls[control][2](sig[3])
|
||||
|
@ -275,24 +294,21 @@ local function finalPrompt()
|
|||
end
|
||||
end
|
||||
end
|
||||
local gpu = gpuG()
|
||||
rstfbDraw(gpu)
|
||||
advDraw(gpu)
|
||||
helpActive, buttonsActive = false, false
|
||||
return safeModeActive
|
||||
end
|
||||
local function postPrompt()
|
||||
local gpu = gpuG()
|
||||
local nsm = neo.requestAccess("x.neo.sys.manage")
|
||||
local sh = "sys-everest"
|
||||
warnings = {"Unable to get sys-init.shell due to no NSM, using sys-everest"}
|
||||
console = {"Unable to get shell (no sys-glacier)"}
|
||||
if nsm then
|
||||
sh = nsm.getSetting("sys-init.shell") or sh
|
||||
warnings = {"Starting " .. sh}
|
||||
console = {"Starting " .. sh}
|
||||
end
|
||||
rstfbDraw(gpu)
|
||||
advDraw(gpu)
|
||||
basicDraw()
|
||||
performDisclaim()
|
||||
neo.executeAsync(sh)
|
||||
-- There's a delay here to allow taking the monitor.
|
||||
sleep(0.5)
|
||||
for i = 1, 9 do
|
||||
local v = neo.requestAccess("x.neo.sys.session")
|
||||
|
@ -302,16 +318,16 @@ local function postPrompt()
|
|||
end
|
||||
end
|
||||
-- ...oh. hope this works then?
|
||||
warnings = {"That wasn't a shell. Try Safe Mode."}
|
||||
rstfbDraw(gpu)
|
||||
advDraw(gpu)
|
||||
console = {"x.neo.sys.session not found, try Safe Mode."}
|
||||
retrieveNssMonitor(screen)
|
||||
basicDraw()
|
||||
sleep(1)
|
||||
shutdown(true)
|
||||
end
|
||||
|
||||
local function initializeSystem()
|
||||
-- System has just booted, bristol is in charge
|
||||
-- Firstly, since we don't know scrcfg, let's work out something sensible.
|
||||
-- No screen configuration, so just guess.
|
||||
-- Note that we should try to keep going with this if there's no reason to do otherwise.
|
||||
local gpuAc = neo.requestAccess("c.gpu")
|
||||
local screenAc = neo.requestAccess("c.screen")
|
||||
|
@ -336,57 +352,38 @@ local function initializeSystem()
|
|||
gpu.bind(screen, true)
|
||||
local gW, gH = gpu.maxResolution()
|
||||
gW, gH = math.min(80, gW), math.min(25, gH)
|
||||
gpu.setResolution(gW, gH)
|
||||
pcall(gpu.setResolution, gW, gH)
|
||||
pcall(gpu.setDepth, gpu.maxDepth()) -- can crash on OCEmu if done at the "wrong time"
|
||||
gpu.setForeground(0x000000)
|
||||
end
|
||||
-- Setup the new GPU provider
|
||||
gpuG = function () return gpu end
|
||||
--
|
||||
local w = 1
|
||||
local steps = {
|
||||
"sys-glacier", -- (Glacier : Config, Screen, Power)
|
||||
-- Let that start, and system GC
|
||||
"WAIT",
|
||||
"WAIT",
|
||||
"WAIT",
|
||||
"WAIT",
|
||||
-- Start services
|
||||
"INJECT",
|
||||
-- extra GC time
|
||||
"WAIT",
|
||||
"WAIT",
|
||||
"WAIT",
|
||||
"WAIT",
|
||||
"WAIT",
|
||||
"WAIT",
|
||||
"WAIT"
|
||||
}
|
||||
local steps = {"sys-glacier"}
|
||||
for i = 1, 4 do table.insert(steps, "WAIT") end
|
||||
table.insert(steps, "INJECT")
|
||||
for i = 1, 8 do table.insert(steps, "WAIT") end
|
||||
|
||||
local stepCount = #steps
|
||||
|
||||
neo.scheduleTimer(os.uptime())
|
||||
while true do
|
||||
local ev = {coroutine.yield()}
|
||||
if ev[1] == "k.procnew" then
|
||||
table.insert(warnings, ev[2] .. "/" .. ev[3] .. " UP")
|
||||
end
|
||||
if ev[1] == "k.procdie" then
|
||||
table.insert(warnings, ev[2] .. "/" .. ev[3] .. " DOWN")
|
||||
table.insert(warnings, tostring(ev[4]))
|
||||
end
|
||||
consoleEventHandler(ev)
|
||||
if ev[1] == "k.timer" then
|
||||
if gpu then
|
||||
gpu.setForeground(0x000000)
|
||||
local bg = 0xFFFFFF
|
||||
if w < stepCount then
|
||||
local n = math.floor((w / stepCount) * 255)
|
||||
gpu.setBackground((n * 0x10000) + (n * 0x100) + n)
|
||||
else
|
||||
gpu.setBackground(0xFFFFFF)
|
||||
bg = (n * 0x10000) + (n * 0x100) + n
|
||||
end
|
||||
basicDraw(gpu)
|
||||
basicDraw(bg)
|
||||
end
|
||||
if steps[w] then
|
||||
if steps[w] == "INJECT" then
|
||||
local nsm = neo.requestAccess("x.neo.sys.manage")
|
||||
if not nsm then
|
||||
table.insert(warnings, "Settings not available for INJECT.")
|
||||
table.insert(console, "Settings not available for INJECT.")
|
||||
else
|
||||
local nextstepsA = {}
|
||||
local nextstepsB = {}
|
||||
|
@ -410,8 +407,8 @@ local function initializeSystem()
|
|||
else
|
||||
local v, err = neo.executeAsync(steps[w])
|
||||
if not v then
|
||||
table.insert(warnings, steps[w] .. " STF")
|
||||
table.insert(warnings, err)
|
||||
neo.emergency("failed start:", steps[w])
|
||||
neo.emergency(err)
|
||||
end
|
||||
end
|
||||
else
|
||||
|
@ -433,12 +430,10 @@ end
|
|||
-- System initialized
|
||||
if finalPrompt() then
|
||||
-- Safe Mode
|
||||
local gpu = gpuG()
|
||||
rstfbDraw(gpu)
|
||||
basicDraw(gpu)
|
||||
local nsm = neo.requestAccess("x.neo.sys.manage")
|
||||
if nsm then
|
||||
gpu.set(2, 4, "Rebooting for Safe Mode...")
|
||||
console = {"Rebooting for Safe Mode..."}
|
||||
basicDraw()
|
||||
for _, v in ipairs(nsm.listSettings()) do
|
||||
if v ~= "password" then
|
||||
nsm.delSetting(v)
|
||||
|
@ -446,12 +441,14 @@ if finalPrompt() then
|
|||
end
|
||||
else
|
||||
-- assume sysconf.lua did something very bad
|
||||
gpu.set(2, 4, "No NSM. Wiping configuration completely.")
|
||||
console = {"No NSM. Wiping configuration completely."}
|
||||
local fs = neo.requestAccess("c.filesystem")
|
||||
if not fs then
|
||||
gpu.set(2, 4, "Failed to get permission, you're doomed.")
|
||||
table.insert(console, "Failed to get permission, you're doomed.")
|
||||
else
|
||||
fs.primary.remove("/data/sys-glacier/sysconf.lua")
|
||||
end
|
||||
fs.primary.remove("/data/sys-glacier/sysconf.lua")
|
||||
basicDraw()
|
||||
end
|
||||
-- Do not give anything a chance to alter the new configuration
|
||||
shutdownEmergency(true)
|
||||
|
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
100
code/init.lua
100
code/init.lua
|
@ -1,6 +1,10 @@
|
|||
-- KittenOS N.E.O Kernel: "Tell Mettaton I said hi."
|
||||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- NOTE: local is considered unnecessary in kernel since 21 March
|
||||
|
||||
|
@ -8,13 +12,27 @@
|
|||
readBufSize = 2048
|
||||
|
||||
-- A function used for logging, usable by programs.
|
||||
emergencyFunction = function () end
|
||||
emergencyFunction = function (...)
|
||||
computer.pushSignal("_kosneo_syslog", "kernel", ...)
|
||||
if ocemu and ocemu.log then
|
||||
pcall(ocemu.log, ...)
|
||||
end
|
||||
end
|
||||
-- Comment this out if you don't want programs to have
|
||||
-- access to ocemu's logger.
|
||||
ocemu = component.list("ocemu", true)()
|
||||
ocemu = (component.list("ocemu", true)()) or (component.list("sandbox", true)())
|
||||
if ocemu then
|
||||
ocemu = component.proxy(ocemu)
|
||||
emergencyFunction = ocemu.log
|
||||
end
|
||||
|
||||
-- It is a really bad idea to remove this.
|
||||
-- If the code inside this block even executes, then removing it is a security risk.
|
||||
if load(string.dump(function()end)) then
|
||||
emergencyFunction("detected bytecode access, preventing (only remove this block if you trust every app ever on your KittenOS NEO system)")
|
||||
local oldLoad = load
|
||||
load = function (c, n, m, ...)
|
||||
return oldLoad(c, n, "t", ...)
|
||||
end
|
||||
end
|
||||
|
||||
primaryDisk = component.proxy(computer.getBootAddress())
|
||||
|
@ -118,13 +136,13 @@ function wrapMeta(t)
|
|||
__call = function (_, ...)
|
||||
return t(...)
|
||||
end,
|
||||
__pairs = function (a)
|
||||
__pairs = function ()
|
||||
return function (x, key)
|
||||
local k, v = next(t, k)
|
||||
local k, v = next(t, key)
|
||||
if k then return k, wrapMeta(v) end
|
||||
end, 9, nil
|
||||
end,
|
||||
__ipairs = function (a)
|
||||
__ipairs = function ()
|
||||
return function (x, key)
|
||||
key = key + 1
|
||||
if t[key] then
|
||||
|
@ -132,6 +150,9 @@ function wrapMeta(t)
|
|||
end
|
||||
end, 9, 0
|
||||
end,
|
||||
__len = function ()
|
||||
return #t
|
||||
end,
|
||||
__metatable = uniqueNEOProtectionObject
|
||||
-- Don't protect this table - it'll make things worse
|
||||
})
|
||||
|
@ -149,7 +170,7 @@ function ensureType(a, t)
|
|||
end
|
||||
|
||||
function ensurePathComponent(s)
|
||||
if not string.match(s, "^[a-zA-Z0-9_%-%+%,%#%~%@%'%;%[%]%(%)%&%%%$%! %=%{%}%^]+") then error("chars disallowed") end
|
||||
if not string.match(s, "^[a-zA-Z0-9_%-%+%,%.%#%~%@%'%;%[%]%(%)%&%%%$%! %=%{%}%^\x80-\xFF]+$") then error("chars disallowed: " .. s) end
|
||||
if s == "." then error("single dot disallowed") end
|
||||
if s == ".." then error("double dot disallowed") end
|
||||
end
|
||||
|
@ -245,6 +266,7 @@ function loadLibraryInner(library)
|
|||
if l then
|
||||
local ok, al = pcall(l)
|
||||
if ok then
|
||||
al = wrapMeta(al)
|
||||
libraries[library] = al
|
||||
return al
|
||||
else
|
||||
|
@ -259,13 +281,17 @@ wrapTable = wrapMeta(table)
|
|||
wrapString = wrapMeta(string)
|
||||
wrapUnicode = wrapMeta(unicode)
|
||||
wrapCoroutine = wrapMeta(coroutine)
|
||||
wrapOs = wrapMeta({
|
||||
totalMemory = computer.totalMemory, freeMemory = computer.freeMemory,
|
||||
energy = computer.energy, maxEnergy = computer.maxEnergy,
|
||||
clock = os.clock, date = os.date, difftime = os.difftime,
|
||||
time = os.time, uptime = computer.uptime
|
||||
})
|
||||
-- inject stuff into os
|
||||
os.totalMemory = computer.totalMemory
|
||||
os.freeMemory = computer.freeMemory
|
||||
os.energy = computer.energy
|
||||
os.maxEnergy = computer.maxEnergy
|
||||
os.uptime = computer.uptime
|
||||
os.address = computer.address
|
||||
wrapOs = wrapMeta(os)
|
||||
wrapDebug = wrapMeta(debug)
|
||||
wrapBit32 = wrapMeta(bit32)
|
||||
wrapUtf8 = wrapMeta(utf8)
|
||||
|
||||
baseProcEnvCore = {
|
||||
_VERSION = _VERSION,
|
||||
|
@ -276,9 +302,10 @@ baseProcEnvCore = {
|
|||
coroutine = wrapCoroutine,
|
||||
os = wrapOs,
|
||||
debug = wrapDebug,
|
||||
bit32 = wrapBit32,
|
||||
utf8 = wrapUtf8,
|
||||
require = loadLibraryInner,
|
||||
assert = assert, ipairs = ipairs,
|
||||
load = load,
|
||||
next = function (t, k)
|
||||
local mt = getmetatable(t)
|
||||
if mt == uniqueNEOProtectionObject then error("NEO-Protected Object") end
|
||||
|
@ -290,14 +317,15 @@ baseProcEnvCore = {
|
|||
tonumber = tonumber, tostring = tostring,
|
||||
setmetatable = setmetatable, getmetatable = function (n)
|
||||
local mt = getmetatable(n)
|
||||
if mt == uniqueNEOProtectionObject then return "NEO-Protected Object" end
|
||||
if rawequal(mt, uniqueNEOProtectionObject) then return "NEO-Protected Object" end
|
||||
return mt
|
||||
end,
|
||||
rawset = function (t, i, v)
|
||||
local mt = getmetatable(t)
|
||||
if mt == uniqueNEOProtectionObject then error("NEO-Protected Object") end
|
||||
if rawequal(mt, uniqueNEOProtectionObject) then error("NEO-Protected Object") end
|
||||
return rawset(t, i, v)
|
||||
end, rawget = rawget, rawlen = rawlen, rawequal = rawequal,
|
||||
checkArg = checkArg
|
||||
}
|
||||
baseProcNeo = {
|
||||
emergency = emergencyFunction,
|
||||
|
@ -312,6 +340,10 @@ baseProcNeo = {
|
|||
end,
|
||||
listApps = lister("apps/"),
|
||||
listLibs = lister("libs/"),
|
||||
usAccessExists = function (accessName)
|
||||
ensureType(accessName, "string")
|
||||
return not not accesses[accessName]
|
||||
end,
|
||||
totalIdleTime = function () return idleTime end,
|
||||
ensurePath = ensurePath,
|
||||
ensurePathComponent = ensurePathComponent,
|
||||
|
@ -329,9 +361,14 @@ baseProcNeoMT = {
|
|||
|
||||
function baseProcEnv()
|
||||
local pe = setmetatable({}, baseProcEnvMT)
|
||||
pe.load = function (a, b, c, d, ...)
|
||||
if rawequal(d, nil) then
|
||||
d = pe
|
||||
end
|
||||
return load(a, b, c, d, ...)
|
||||
end
|
||||
pe.neo = setmetatable({}, baseProcNeoMT)
|
||||
pe._G = pe
|
||||
pe._ENV = pe
|
||||
return pe
|
||||
end
|
||||
|
||||
|
@ -362,7 +399,7 @@ function retrieveAccess(perm, pkg, pid)
|
|||
-- "c.<hw>": Component
|
||||
-- "s.<event>": Signal receiver (with responsibilities for Security Request watchers)
|
||||
-- "s.k.<...>": Kernel stuff
|
||||
-- "s.k.procnew" : New process (pkg, pid)
|
||||
-- "s.k.procnew" : New process (pkg, pid, ppkg, ppid)
|
||||
-- "s.k.procdie" : Process dead (pkg, pid, reason, usage)
|
||||
-- "s.k.registration" : Registration of service alert ("x." .. etc)
|
||||
-- "s.k.deregistration" : Registration of service alert ("x." .. etc)
|
||||
|
@ -426,7 +463,7 @@ function retrieveAccess(perm, pkg, pid)
|
|||
accesses[uid] = function (pkg, pid)
|
||||
return nil
|
||||
end
|
||||
return function (f)
|
||||
return function (f, secret)
|
||||
-- Registration function
|
||||
ensureType(f, "function")
|
||||
local accessObjectCache = {}
|
||||
|
@ -448,8 +485,10 @@ function retrieveAccess(perm, pkg, pid)
|
|||
end
|
||||
-- returns nil and fails
|
||||
end
|
||||
-- Announce registration
|
||||
distEvent(nil, "k.registration", uid)
|
||||
if not secret then
|
||||
-- Announce registration
|
||||
distEvent(nil, "k.registration", uid)
|
||||
end
|
||||
end, function ()
|
||||
-- Registration becomes null (access is held but other processes cannot retrieve object)
|
||||
if accesses[uid] then
|
||||
|
@ -460,14 +499,16 @@ function retrieveAccess(perm, pkg, pid)
|
|||
end
|
||||
end
|
||||
|
||||
function start(pkg, ...)
|
||||
function start(pkg, ppkg, ppid, ...)
|
||||
local proc = {}
|
||||
local pid = lastPID
|
||||
emergencyFunction("starting:", pkg)
|
||||
lastPID = lastPID + 1
|
||||
|
||||
local function startFromUser(ipkg, ...)
|
||||
ensureType(ipkg, "string")
|
||||
ensurePathComponent(ipkg .. ".lua")
|
||||
local ok, n = pcall(ensurePathComponent, ipkg .. ".lua")
|
||||
if not ok then return nil, n end
|
||||
local k, r = runProgramPolicy(ipkg, pkg, pid, ...)
|
||||
if k then
|
||||
return start(ipkg, pkg, pid, ...)
|
||||
|
@ -522,6 +563,7 @@ function start(pkg, ...)
|
|||
end
|
||||
local env = baseProcEnv()
|
||||
env.neo.pid = pid
|
||||
env.neo.pkg = pkg
|
||||
env.neo.executeAsync = startFromUser
|
||||
env.neo.execute = function (...)
|
||||
return osExecuteCore(function () end, ...)
|
||||
|
@ -530,7 +572,7 @@ function start(pkg, ...)
|
|||
env.neo.requestAccessAsync = requestAccessAsync
|
||||
env.neo.requestAccess = function (perm, handler)
|
||||
requestAccessAsync(perm)
|
||||
if not handler then handler = function() end end
|
||||
handler = handler or function() end
|
||||
while true do
|
||||
local n = {coroutine.yield()}
|
||||
handler(table.unpack(n))
|
||||
|
@ -549,7 +591,7 @@ function start(pkg, ...)
|
|||
env.neo.scheduleTimer = function (time)
|
||||
ensureType(time, "number")
|
||||
local tag = {}
|
||||
table.insert(timers, {time, execEvent, pid, "k.timer", tag, time, ofs})
|
||||
table.insert(timers, {time, execEvent, pid, "k.timer", tag, time})
|
||||
return tag
|
||||
end
|
||||
|
||||
|
@ -565,9 +607,9 @@ function start(pkg, ...)
|
|||
proc.deathCBs = {function () pcall(function () env.neo.dead = true end) end}
|
||||
proc.cpuUsage = 0
|
||||
-- Note the target process doesn't get the procnew (the dist occurs before it's creation)
|
||||
pcall(distEvent, nil, "k.procnew", pkg, pid)
|
||||
pcall(distEvent, nil, "k.procnew", pkg, pid, ppkg, ppid)
|
||||
processes[pid] = proc
|
||||
table.insert(timers, {0, execEvent, pid, ...})
|
||||
table.insert(timers, {0, execEvent, pid, ppkg, ppid, ...})
|
||||
return pid
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- bmp: Portable OC BMP/DIB library
|
||||
-- Flexible: Reading can be set to
|
||||
-- ignore first 14 bytes,
|
||||
-- allowing reuse of the library on
|
||||
-- ICO/CUR data. Yes, really.
|
||||
|
||||
-- handle(i, valDiv, valMod)
|
||||
local function bitsCore(i, fieldOfs, fieldWidth, handle)
|
||||
local adv = math.floor(fieldOfs / 8)
|
||||
i = i + adv
|
||||
fieldOfs = fieldOfs - (adv * 8)
|
||||
-- above 3 lines are a removable optimization
|
||||
while fieldWidth > 0 do
|
||||
local bitsHere = math.min(fieldWidth, math.max(0, 8 - fieldOfs))
|
||||
if bitsHere > 0 then
|
||||
local pow1 = math.floor(2 ^ bitsHere)
|
||||
-- offset
|
||||
-- pixels are "left to right" in the MostSigBitFirst stream
|
||||
local aFieldOfs = 8 - (fieldOfs + bitsHere)
|
||||
local pow2 = math.floor(2 ^ aFieldOfs)
|
||||
handle(i, pow2, pow1)
|
||||
fieldWidth = fieldWidth - bitsHere
|
||||
fieldOfs = 0
|
||||
else
|
||||
-- in case the 'adv' opt. gets removed
|
||||
fieldOfs = fieldOfs - 8
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function encode16(t)
|
||||
return string.char(t % 0x100) .. string.char(math.floor(t / 0x100))
|
||||
end
|
||||
|
||||
local function encode32(t)
|
||||
return encode16(t % 0x10000) .. encode16(math.floor(t / 0x10000))
|
||||
end
|
||||
|
||||
-- This & the BMP equivalent return a header,
|
||||
-- a buffer size, and a 1-based data pointer,
|
||||
-- and are used to initially create the image.
|
||||
-- Notably, for bpp <= 8, a paletteSize of 0 is illegal.
|
||||
-- topDown adjusts the order of scanlines.
|
||||
-- cMode adjusts some values.
|
||||
-- IT IS RECOMMENDED YOU PROPERLY SET THE MASK UP.
|
||||
local function prepareDIB(w, h, p, bpp, paletteSize, topDown, cMode)
|
||||
if bpp <= 8 then
|
||||
if paletteSize == 0 then
|
||||
error("A palette size of 0 is invalid for <= 8-bit images. Use 16-bit or 32-bit for no palette, or specify the amount of palette entries.")
|
||||
end
|
||||
end
|
||||
|
||||
local scanWB = math.ceil((bpp * w) / 32) * 4
|
||||
local palSize = paletteSize * 4
|
||||
local bufSize = scanWB * h * p
|
||||
|
||||
local aH = h
|
||||
if cMode then
|
||||
-- O.o why change format? who knows!
|
||||
aH = aH * 2
|
||||
bufSize = bufSize + ((math.ceil(w / 32) * 4) * h * p)
|
||||
end
|
||||
if topDown then
|
||||
aH = 0x100000000 - aH
|
||||
end
|
||||
return
|
||||
"\x28\x00\x00\x00" .. -- 0x0E
|
||||
encode32(w) .. -- 0x12
|
||||
encode32(aH) .. -- 0x16
|
||||
encode16(p) .. -- 0x1A
|
||||
encode16(bpp) .. -- 0x1C
|
||||
"\x00\x00\x00\x00" .. -- 0x1E
|
||||
encode32(bufSize) .. -- 0x22
|
||||
"\x00\x00\x00\x00" .. -- 0x26
|
||||
"\x00\x00\x00\x00" .. -- 0x2A
|
||||
encode32(paletteSize) .. -- 0x2E
|
||||
encode32(paletteSize), -- 0x32 then EOH
|
||||
-- -14 here to move back into headless units
|
||||
0x36 + palSize + bufSize - 14,
|
||||
0x36 + palSize + 1 - 14 -- 1-based data pointer
|
||||
end
|
||||
|
||||
return {
|
||||
headerMinSzBMP = 0x36,
|
||||
headerMinSzDIB = 0x36 - 14,
|
||||
-- get/set are (index) and (index, value) respectively
|
||||
-- they are 1-based
|
||||
-- If "packed" is used, two things happen:
|
||||
-- 1. The +1 offeset for 1-based is replaced
|
||||
-- with packed (so -13 is pure packed-DIB)
|
||||
-- 2. We don't try to use the BMP header
|
||||
connect = function (get, set, cMode, packed)
|
||||
-- NOTE: Internally, BMP addresses are used,
|
||||
-- so that the Wikipedia page can be used
|
||||
-- as a valid reference for header fields.
|
||||
-- verify cMode
|
||||
if cMode ~= nil and cMode ~= "mask" and cMode ~= "colour" then
|
||||
error("Unknown cMode " .. cMode)
|
||||
end
|
||||
-- NOTE: 0-base is used
|
||||
local function get8(i)
|
||||
return get(i + (packed or 1))
|
||||
end
|
||||
local function get16(i)
|
||||
return get8(i) + (256 * get8(i + 1))
|
||||
end
|
||||
local function get32(i)
|
||||
return get16(i) + (65536 * get16(i + 2))
|
||||
end
|
||||
local function set8(i, v)
|
||||
set(i + (packed or 1), v)
|
||||
end
|
||||
local function set32(i, v)
|
||||
local st = encode32(v)
|
||||
set8(i, st:byte(1))
|
||||
set8(i + 1, st:byte(2))
|
||||
set8(i + 2, st:byte(3))
|
||||
set8(i + 3, st:byte(4))
|
||||
end
|
||||
local function getBits(i, fieldOfs, fieldWidth)
|
||||
local v = 0
|
||||
local vp = 1
|
||||
bitsCore(i, fieldOfs, fieldWidth, function (i, valDiv, valMod)
|
||||
local data = math.floor(get8(i) / valDiv) % valMod
|
||||
v = v + (data * vp)
|
||||
vp = vp * valMod
|
||||
end)
|
||||
return v
|
||||
end
|
||||
local function setBits(i, fieldOfs, fieldWidth, v)
|
||||
return bitsCore(i, fieldOfs, fieldWidth, function (i, valDiv, valMod)
|
||||
local data = get8(i)
|
||||
-- Firstly need to eliminate the old data
|
||||
data = data - ((math.floor(data / valDiv) % valMod) * valDiv)
|
||||
-- Now to insert the new data
|
||||
data = data + ((v % valMod) * valDiv)
|
||||
set8(i, data)
|
||||
-- Advance
|
||||
v = math.floor(v / valMod)
|
||||
end)
|
||||
end
|
||||
-- direct header reads (all of them)
|
||||
local hdrSize = get32(0x0E)
|
||||
if hdrSize < 0x28 then
|
||||
error("OS/2 Bitmaps Incompatible")
|
||||
end
|
||||
local width = get32(0x12)
|
||||
local height = get32(0x16)
|
||||
local planes = get16(0x1A)
|
||||
local bpp = get16(0x1C)
|
||||
local compression = get32(0x1E)
|
||||
local paletteCol = get32(0x2E)
|
||||
|
||||
-- negative height means sane coords
|
||||
local upDown = true
|
||||
if height >= 0x80000000 then
|
||||
height = height - 0x100000000
|
||||
height = -height
|
||||
upDown = false
|
||||
end
|
||||
|
||||
-- postprocess
|
||||
|
||||
-- The actual values used for addressing, for cMode to mess with
|
||||
local basePtr = 14 + hdrSize + (paletteCol * 4)
|
||||
local scanWB = math.ceil((bpp * width) / 32) * 4
|
||||
local monoWB = math.ceil(width / 32) * 4
|
||||
local planeWB = scanWB * height
|
||||
|
||||
if not packed then
|
||||
basePtr = get32(0x0A) -- 'BM' header
|
||||
end
|
||||
|
||||
-- Cursor/Icon
|
||||
if cMode then
|
||||
height = math.floor(height / 2)
|
||||
assert(planes == 1, "planes ~= 1 for cursor")
|
||||
planeWB = planeWB + (monoWB * height)
|
||||
end
|
||||
if cMode == "mask" then
|
||||
if upDown then
|
||||
basePtr = basePtr + (scanWB * height)
|
||||
end
|
||||
bpp = 1
|
||||
scanWB = monoWB
|
||||
paletteCol = 0
|
||||
compression = 3
|
||||
elseif cMode == "colour" then
|
||||
if not upDown then
|
||||
basePtr = basePtr + (monoWB * height)
|
||||
end
|
||||
end
|
||||
-- Check compression
|
||||
if (compression ~= 0) and (compression ~= 3) and (compression ~= 6) then
|
||||
error("compression " .. compression .. " unavailable")
|
||||
end
|
||||
-- paletteSize correction for comp == 0
|
||||
if (bpp <= 8) and (paletteCol == 0) and (compression == 0) then
|
||||
paletteCol = math.floor(2 ^ bpp)
|
||||
end
|
||||
return {
|
||||
width = width,
|
||||
height = height,
|
||||
planes = planes,
|
||||
bpp = bpp,
|
||||
ignoresPalette = (compression ~= 0) or (paletteCol == 0) or (cMode == "mask"),
|
||||
paletteCol = paletteCol,
|
||||
paletteAddress = 14 + hdrSize + (packed or 1),
|
||||
dataAddress = basePtr + (packed or 1),
|
||||
dsFull = get32(0x22),
|
||||
dsSpan = scanWB,
|
||||
dsPlane = planeWB,
|
||||
getPalette = function (i)
|
||||
return get32(14 + hdrSize + (i * 4))
|
||||
end,
|
||||
setPalette = function (i, xrgb)
|
||||
set32(14 + hdrSize + (i * 4), xrgb)
|
||||
end,
|
||||
-- Coordinates are 0-based for sanity. Returns raw colour value.
|
||||
getPixel = function (x, y, p)
|
||||
if upDown then
|
||||
y = height - (1 + y)
|
||||
end
|
||||
local i = basePtr + (y * scanWB) + (p * planeWB)
|
||||
return getBits(i, x * bpp, bpp)
|
||||
end,
|
||||
-- Uses raw colour value.
|
||||
setPixel = function (x, y, p, v)
|
||||
if upDown then
|
||||
y = height - (1 + y)
|
||||
end
|
||||
local i = basePtr + (y * scanWB) + (p * planeWB)
|
||||
setBits(i, x * bpp, bpp, v)
|
||||
end
|
||||
}
|
||||
end,
|
||||
-- See prepareDIB above for format.
|
||||
prepareBMP = function (...)
|
||||
local head, tLen, dtPtr = prepareDIB(...)
|
||||
tLen = tLen + 14 -- add BM header
|
||||
dtPtr = dtPtr + 14
|
||||
head = "BM" .. encode32(tLen) .. "mRWH" .. encode32(dtPtr - 1) .. head
|
||||
return head, tLen, dtPtr
|
||||
end,
|
||||
prepareDIB = prepareDIB
|
||||
}
|
|
@ -1,10 +1,17 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- Braille Neoux Component
|
||||
-- Callbacks :
|
||||
-- Braille Converter & NeoUX component
|
||||
-- While the Neoux part isn't OS-independent,
|
||||
-- the converter itself is. Enjoy.
|
||||
|
||||
-- Braille Neoux Component Callbacks :
|
||||
-- selectable (boolean)
|
||||
-- key(window, update, a, c, d)
|
||||
-- key(window, update, a, c, d, keyFlags)
|
||||
-- touch(window, update, x, y, button)
|
||||
-- drag(window, update, x, y, button)
|
||||
-- drop(window, update, x, y, button)
|
||||
|
@ -17,35 +24,41 @@
|
|||
-- 25
|
||||
-- 67
|
||||
local function dotDist(ra, ga, ba, rb, gb, bb)
|
||||
local dR, dG, dB = math.abs(ra - rb), math.abs(ga - gb), math.abs(ba - bb)
|
||||
return (dR * 0.299) + (dG * 0.587) + (dB * 0.114)
|
||||
local dR, dG, dB = math.abs(ra - rb)^2, math.abs(ga - gb)^2, math.abs(ba - bb)^2
|
||||
return (dR * 0.2126) + (dG * 0.7152) + (dB * 0.0722)
|
||||
end
|
||||
local function dotGet(p, ra, ga, ba, rb, gb, bb, rc, gc, bc, pos, col)
|
||||
local function ditherResult(pos, pos2, luma)
|
||||
local res = false
|
||||
if luma >= 217 then
|
||||
res = true
|
||||
elseif luma >= 158 then
|
||||
res = not pos2
|
||||
elseif luma >= 96 then
|
||||
res = pos
|
||||
elseif luma >= 32 then
|
||||
res = pos2
|
||||
end
|
||||
return res
|
||||
end
|
||||
local function dotGet(p, ra, ga, ba, rb, gb, bb, rc, gc, bc, pos, pos2, col)
|
||||
if not col then
|
||||
-- Use our own magic
|
||||
local res = false
|
||||
local luma = (ra * 0.299) + (ga * 0.587) + (ba * 0.114)
|
||||
if luma > 96 and luma < 160 then
|
||||
res = pos
|
||||
elseif luma >= 160 then
|
||||
res = true
|
||||
end
|
||||
return (res and p) or 0
|
||||
return (ditherResult(pos, pos2, luma) and p) or 0
|
||||
end
|
||||
local distA = dotDist(ra, ga, ba, rb, gb, bb)
|
||||
local distB = dotDist(ra, ga, ba, rc, gc, bc)
|
||||
local distAB = dotDist(rb, gb, bb, rc, gc, bc)
|
||||
local distC = dotDist(ra, ga, ba, (rb + rc) / 2, (gb + gc) / 2, (bb + bc) / 2)
|
||||
-- If A and B are close,
|
||||
if (distAB < 32) and (distC < (math.min(distA, distB) * 4)) then
|
||||
return (pos and p) or 0
|
||||
if (distC / 2) < math.min(distA, distB) then
|
||||
return (ditherResult(pos, pos2, (distA / math.max(distA, distB, 0.1)) * 255) and p) or 0
|
||||
end
|
||||
return ((distB < distA) and p) or 0
|
||||
end
|
||||
local function cTransform(core)
|
||||
return function (window, update, x, y, xI, yI, blah)
|
||||
x = x + math.ceil(xI - 0.5)
|
||||
y = y + math.ceil((yI - 0.25) * 4)
|
||||
x = (x * 2) + math.ceil(xI - 0.5)
|
||||
y = (y * 4) + math.ceil((yI - 0.25) * 4)
|
||||
core(window, update, x, y, blah)
|
||||
end
|
||||
end
|
||||
|
@ -59,9 +72,7 @@ local function colourize(mark, ...)
|
|||
for i = 1, #t do
|
||||
local luma = (t[i][1] * 0.299) + (t[i][2] * 0.587) + (t[i][3] * 0.114)
|
||||
if luma > nLuma then
|
||||
bCR = t[i][1]
|
||||
bCG = t[i][2]
|
||||
bCB = t[i][3]
|
||||
bCR, bCG, bCB = table.unpack(t[i])
|
||||
nLuma = luma
|
||||
end
|
||||
end
|
||||
|
@ -92,12 +103,10 @@ end
|
|||
-- NOTE: xo/yo are 0-based!
|
||||
local function calcLine(x, y, w, span, get, colour)
|
||||
local str = ""
|
||||
local bgR = 0
|
||||
local bgG = 0
|
||||
local bgB = 0
|
||||
local fgR = 255
|
||||
local fgG = 255
|
||||
local fgB = 255
|
||||
-- *g* : actual colour com.
|
||||
-- *g : RGB mirror of colour
|
||||
local bgR, bgG, bgB = 0, 0, 0
|
||||
local fgR, fgG, fgB = 255, 255, 255
|
||||
local bg = 0
|
||||
local fg = 0xFFFFFF
|
||||
local ca = 0
|
||||
|
@ -149,14 +158,14 @@ local function calcLine(x, y, w, span, get, colour)
|
|||
fgR, fgG, fgB = ofgR, ofgG, ofgB
|
||||
end
|
||||
end
|
||||
i = i + dotGet(1, dot0R, dot0G, dot0B, bgR, bgG, bgB, fgR, fgG, fgB, true, colour)
|
||||
i = i + dotGet(2, dot1R, dot1G, dot1B, bgR, bgG, bgB, fgR, fgG, fgB, false, colour)
|
||||
i = i + dotGet(4, dot2R, dot2G, dot2B, bgR, bgG, bgB, fgR, fgG, fgB, true, colour)
|
||||
i = i + dotGet(8, dot3R, dot3G, dot3B, bgR, bgG, bgB, fgR, fgG, fgB, false, colour)
|
||||
i = i + dotGet(16, dot4R, dot4G, dot4B, bgR, bgG, bgB, fgR, fgG, fgB, true, colour)
|
||||
i = i + dotGet(32, dot5R, dot5G, dot5B, bgR, bgG, bgB, fgR, fgG, fgB, false, colour)
|
||||
i = i + dotGet(64, dot6R, dot6G, dot6B, bgR, bgG, bgB, fgR, fgG, fgB, false, colour)
|
||||
i = i + dotGet(128, dot7R, dot7G, dot7B, bgR, bgG, bgB, fgR, fgG, fgB, true, colour)
|
||||
i = i + dotGet(1, dot0R, dot0G, dot0B, bgR, bgG, bgB, fgR, fgG, fgB, true, false, colour)
|
||||
i = i + dotGet(2, dot1R, dot1G, dot1B, bgR, bgG, bgB, fgR, fgG, fgB, false, false, colour)
|
||||
i = i + dotGet(4, dot2R, dot2G, dot2B, bgR, bgG, bgB, fgR, fgG, fgB, true, false, colour)
|
||||
i = i + dotGet(8, dot3R, dot3G, dot3B, bgR, bgG, bgB, fgR, fgG, fgB, false, false, colour)
|
||||
i = i + dotGet(16, dot4R, dot4G, dot4B, bgR, bgG, bgB, fgR, fgG, fgB, true, true, colour)
|
||||
i = i + dotGet(32, dot5R, dot5G, dot5B, bgR, bgG, bgB, fgR, fgG, fgB, false, false, colour)
|
||||
i = i + dotGet(64, dot6R, dot6G, dot6B, bgR, bgG, bgB, fgR, fgG, fgB, false, false, colour)
|
||||
i = i + dotGet(128, dot7R, dot7G, dot7B, bgR, bgG, bgB, fgR, fgG, fgB, true, true, colour)
|
||||
str = str .. unicode.char(i)
|
||||
ca = ca + 1
|
||||
end
|
||||
|
@ -164,7 +173,8 @@ local function calcLine(x, y, w, span, get, colour)
|
|||
span(x, y, str, bg, fg)
|
||||
end
|
||||
end
|
||||
heldRef = neo.wrapMeta({
|
||||
|
||||
heldRef = {
|
||||
calcLine = calcLine,
|
||||
new = function (x, y, w, h, cbs, colour)
|
||||
local control
|
||||
|
@ -181,7 +191,11 @@ heldRef = neo.wrapMeta({
|
|||
drop = cbs.drop and cTransform(cbs.drop),
|
||||
scroll = cbs.scroll and cTransform(cbs.scroll),
|
||||
line = function (window, x, y, iy, bg, fg, selected)
|
||||
local colour = ((window.getDepth() <= 1) or nil) and colour
|
||||
local colour = colour
|
||||
local depth = window.getDepth()
|
||||
if depth <= 1 then
|
||||
colour = nil
|
||||
end
|
||||
calcLine(x, y, control.w, window.span, function (xb, yb)
|
||||
return cbs.get(window, xb + 1, yb + (iy * 4) - 3, bg, fg, selected, colour)
|
||||
end, colour)
|
||||
|
@ -189,5 +203,5 @@ heldRef = neo.wrapMeta({
|
|||
}
|
||||
return control
|
||||
end,
|
||||
})
|
||||
}
|
||||
return heldRef
|
||||
|
|
|
@ -1,184 +0,0 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
-- claw: assistant to app-claw
|
||||
-- should only ever be one app-claw at a time
|
||||
local lock = false
|
||||
return function ()
|
||||
if lock then
|
||||
error("libclaw safety lock in use")
|
||||
end
|
||||
lock = true
|
||||
local sourceList = {}
|
||||
local sources = {}
|
||||
-- 1 2 3 4 5 6
|
||||
-- dst entries: writeFile(fn), mkdir(fn), exists(fn), isDirectory(fn), remove(fn), rename(fna, fnb)
|
||||
-- writeFile(fn) -> function (data/nil to close)
|
||||
local function saveInfo(dn)
|
||||
sources[dn][2][2]("data")
|
||||
sources[dn][2][2]("data/app-claw")
|
||||
local cb, _, r = sources[dn][2][1]("data/app-claw/local.lua")
|
||||
if not cb then return false, r end
|
||||
_, r = cb(require("serial").serialize(sources[dn][3]))
|
||||
if not _ then return false, r end
|
||||
_, r = cb(nil)
|
||||
if not _ then return false, r end
|
||||
return true
|
||||
end
|
||||
local remove, installTo
|
||||
-- NOTE: Functions in this must return something due to the checked-call wrapper,
|
||||
-- but should all use error() for consistency.
|
||||
-- Operations
|
||||
installTo = function (dstName, pkg, srcName, checked, yielder)
|
||||
local installed = {pkg}
|
||||
local errs = {}
|
||||
if srcName == dstName then
|
||||
error("Invalid API use")
|
||||
end
|
||||
-- preliminary checks
|
||||
if checked then
|
||||
for _, v in ipairs(sources[srcName][3][pkg].deps) do
|
||||
if not sources[dstName][3][v] then
|
||||
if not sources[srcName][3][v] then
|
||||
table.insert(errs, pkg .. " depends on " .. v .. "\n")
|
||||
elseif #errs == 0 then
|
||||
installTo(dstName, v, srcName, checked, yielder)
|
||||
else
|
||||
table.insert(errs, pkg .. " depends on " .. v .. " (can autoinstall)\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Files from previous versions to get rid of
|
||||
local ignFiles = {}
|
||||
if sources[dstName][3][pkg] then
|
||||
for _, v in ipairs(sources[dstName][3][pkg].files) do
|
||||
ignFiles[v] = true
|
||||
end
|
||||
end
|
||||
for _, v in ipairs(sources[srcName][3][pkg].files) do
|
||||
if not ignFiles[v] then
|
||||
if sources[dstName][2][3](v) then
|
||||
table.insert(errs, v .. " already exists (corrupt system?)")
|
||||
end
|
||||
end
|
||||
end
|
||||
if #errs > 0 then
|
||||
error(table.concat(errs))
|
||||
end
|
||||
for _, v in ipairs(sources[srcName][3][pkg].dirs) do
|
||||
sources[dstName][2][2](v)
|
||||
if not sources[dstName][2][4](v) then
|
||||
error("Unable to create directory " .. v)
|
||||
end
|
||||
end
|
||||
for _, v in ipairs(sources[srcName][3][pkg].files) do
|
||||
local ok, r = sources[srcName][1](v, sources[dstName][2][1](v .. ".claw-tmp"))
|
||||
if ok then
|
||||
yielder()
|
||||
else
|
||||
-- Cleanup...
|
||||
for _, v in ipairs(sources[srcName][3][pkg].files) do
|
||||
sources[dstName][2][5](v .. ".claw-tmp")
|
||||
end
|
||||
error(r)
|
||||
end
|
||||
end
|
||||
-- PAST THIS POINT, ERRORS CORRUPT!
|
||||
sources[dstName][3][pkg] = nil
|
||||
saveInfo(dstName)
|
||||
for k, _ in pairs(ignFiles) do
|
||||
yielder()
|
||||
sources[dstName][2][5](k)
|
||||
end
|
||||
for _, v in ipairs(sources[srcName][3][pkg].files) do
|
||||
yielder()
|
||||
sources[dstName][2][6](v .. ".claw-tmp", v)
|
||||
end
|
||||
sources[dstName][3][pkg] = sources[srcName][3][pkg]
|
||||
saveInfo(dstName)
|
||||
return true
|
||||
end
|
||||
remove = function (dstName, pkg, checked)
|
||||
if checked then
|
||||
local errs = {}
|
||||
for dpsName, dpsV in pairs(sources[dstName][3]) do
|
||||
for _, v in ipairs(dpsV.deps) do
|
||||
if v == pkg then
|
||||
table.insert(errs, dpsName .. " depends on " .. pkg .. "\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
if #errs > 0 then
|
||||
return nil, table.concat(errs)
|
||||
end
|
||||
end
|
||||
for _, v in ipairs(sources[dstName][3][pkg].files) do
|
||||
sources[dstName][2][5](v)
|
||||
end
|
||||
sources[dstName][3][pkg] = nil
|
||||
saveInfo(dstName)
|
||||
return true
|
||||
end
|
||||
return {
|
||||
-- Gets the latest info, or if given a source just gives that source's info.
|
||||
-- Do not modify output.
|
||||
getInfo = function (pkg, source, oldest)
|
||||
if source then return sources[source][3][pkg] end
|
||||
local bestI = {
|
||||
v = -1,
|
||||
desc = "An unknown package.",
|
||||
deps = {}
|
||||
}
|
||||
if oldest then bestI.v = 10000 end
|
||||
for _, v in pairs(sources) do
|
||||
if v[3][pkg] then
|
||||
if ((not oldest) and (v[3][pkg].v > bestI.v)) or (oldest and (v[3][pkg].v > bestI.v)) then
|
||||
bestI = v[3][pkg]
|
||||
end
|
||||
end
|
||||
end
|
||||
return bestI
|
||||
end,
|
||||
-- Provides an ordered list of sources, with writable.
|
||||
-- Do not modify output.
|
||||
getSources = function ()
|
||||
return sourceList
|
||||
end,
|
||||
-- NOTE: If a source is writable, it's added anyway despite any problems.
|
||||
addSource = function (name, src, dst)
|
||||
local ifo = ""
|
||||
local ifok, e = src("data/app-claw/local.lua", function (t)
|
||||
ifo = ifo .. (t or "")
|
||||
return true
|
||||
end)
|
||||
e = e or "local.lua parse error"
|
||||
ifo = ifok and require("serial").deserialize(ifo)
|
||||
if not (dst or ifo) then return false, e end
|
||||
table.insert(sourceList, {name, not not dst})
|
||||
sources[name] = {src, dst, ifo or {}}
|
||||
return not not ifo, e
|
||||
end,
|
||||
remove = remove,
|
||||
installTo = installTo,
|
||||
|
||||
-- Gets a total list of packages, as a table of strings. You can modify output.
|
||||
getList = function ()
|
||||
local n = {}
|
||||
local seen = {}
|
||||
for k, v in pairs(sources) do
|
||||
for kb, vb in pairs(v[3]) do
|
||||
if not seen[kb] then
|
||||
seen[kb] = true
|
||||
table.insert(n, kb)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(n)
|
||||
return n
|
||||
end,
|
||||
unlock = function ()
|
||||
lock = false
|
||||
end
|
||||
}
|
||||
end
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- event: Implements pull/listen semantics in a consistent way for a given process.
|
||||
-- This is similar in API to OpenOS's event framework, but is per-process.
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
local fmt
|
||||
fmt = {
|
||||
pad = function (t, len, centre, cut)
|
||||
pad = function (t, len, centre, cut, ra)
|
||||
local l = unicode.len(t)
|
||||
local add = len - l
|
||||
if add > 0 then
|
||||
|
@ -11,10 +18,15 @@ fmt = {
|
|||
end
|
||||
end
|
||||
if cut then
|
||||
t = unicode.sub(t, 1, len)
|
||||
local i = 0
|
||||
if ra then
|
||||
i = -math.min(0, add)
|
||||
end
|
||||
t = unicode.sub(t, i + 1, len + i)
|
||||
end
|
||||
return t
|
||||
end,
|
||||
-- Expects safeTextFormat'd input
|
||||
fmtText = function (text, w)
|
||||
local nl = text:find("\n")
|
||||
if nl then
|
||||
|
@ -64,4 +76,4 @@ fmt = {
|
|||
return {fmt.pad(text, w)}
|
||||
end
|
||||
}
|
||||
return neo.wrapMeta(fmt)
|
||||
return fmt
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
return {
|
||||
-- note: everything must already be unicode.safeTextFormat'd
|
||||
draw = function (sW, line, cursorX, rX)
|
||||
-- if no camera, provide a default
|
||||
rX = rX or math.max(1, (cursorX or 1) - math.floor(sW * 2 / 3))
|
||||
-- transform into area-relative
|
||||
local tl = unicode.sub(line, rX, rX + sW - 1)
|
||||
-- inject cursor
|
||||
if cursorX then
|
||||
cursorX = (cursorX - rX) + 1
|
||||
if cursorX >= 1 then
|
||||
if cursorX <= sW then
|
||||
tl = unicode.sub(tl, 1, cursorX - 1) .. "_" .. unicode.sub(tl, cursorX + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
return tl .. (" "):rep(sW - unicode.len(tl))
|
||||
end,
|
||||
clamp = function (tl, cursorX)
|
||||
tl = unicode.len(tl)
|
||||
if tl < cursorX - 1 then
|
||||
return tl + 1
|
||||
end
|
||||
return cursorX
|
||||
end,
|
||||
-- returns line, cursorX, special
|
||||
-- return values may be nil if irrelevant
|
||||
key = function (ks, kc, line, cursorX)
|
||||
local cS = unicode.sub(line, 1, cursorX - 1)
|
||||
local cE = unicode.sub(line, cursorX)
|
||||
local ll = unicode.len(line)
|
||||
if kc == 203 then -- navi <
|
||||
if cursorX > 1 then
|
||||
return nil, cursorX - 1
|
||||
else
|
||||
-- cline underflow
|
||||
return nil, nil, "l<"
|
||||
end
|
||||
elseif kc == 205 then -- navi >
|
||||
if cursorX > ll then
|
||||
-- cline overflow
|
||||
return nil, nil, "l>"
|
||||
end
|
||||
return nil, cursorX + 1
|
||||
elseif kc == 199 then -- home
|
||||
return nil, 1
|
||||
elseif kc == 207 then -- end
|
||||
return nil, unicode.len(line) + 1
|
||||
elseif ks == "\8" then -- del
|
||||
if cursorX == 1 then
|
||||
-- weld prev
|
||||
return nil, nil, "w<"
|
||||
else
|
||||
return unicode.sub(cS, 1, unicode.len(cS) - 1) .. cE, cursorX - 1
|
||||
end
|
||||
elseif kc == 211 then -- del
|
||||
if cursorX > ll then
|
||||
return nil, nil, "w>"
|
||||
end
|
||||
return cS .. unicode.sub(cE, 2)
|
||||
elseif ks then -- standard letters
|
||||
if ks == "\r" then
|
||||
-- new line
|
||||
return nil, nil, "nl"
|
||||
end
|
||||
return cS .. ks .. cE, cursorX + unicode.len(ks)
|
||||
end
|
||||
-- :(
|
||||
end
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- neoux: Implements utilities on top of Everest & event:
|
||||
-- Everest crash protection
|
||||
|
@ -7,7 +11,7 @@
|
|||
-- Control reference
|
||||
-- x/y/w/h: ints, position/size, 1,1 TL
|
||||
-- selectable: boolean
|
||||
-- key(window, update, char, code, down)
|
||||
-- key(window, update, char, code, down, keyFlags) (If this returns something truthy, defaults are inhibited)
|
||||
-- touch(window, update, x, y, xI, yI, button)
|
||||
-- drag(window, update, x, y, xI, yI, button)
|
||||
-- drop(window, update, x, y, xI, yI, button)
|
||||
|
@ -25,7 +29,7 @@ newNeoux = function (event, neo)
|
|||
end
|
||||
end)
|
||||
local neoux = {}
|
||||
neoux.fileDialog = function (forWrite, callback)
|
||||
neoux.fileDialog = function (forWrite, callback, dfn)
|
||||
local sync = false
|
||||
local rtt = nil
|
||||
if not callback then
|
||||
|
@ -35,7 +39,7 @@ newNeoux = function (event, neo)
|
|||
rtt = rt
|
||||
end
|
||||
end
|
||||
local tag = neo.requestAccess("x.neo.pub.base").showFileDialogAsync(forWrite)
|
||||
local tag = neo.requireAccess("x.neo.pub.base", "filedialog").showFileDialogAsync(forWrite, dfn)
|
||||
local f
|
||||
f = function (_, fd, tg, re)
|
||||
if fd == "filedialog" then
|
||||
|
@ -67,13 +71,14 @@ newNeoux = function (event, neo)
|
|||
window.getSize = function ()
|
||||
return w, h
|
||||
end
|
||||
window.getDepth = windowCore.getDepth
|
||||
window.setSize = function (nw, nh)
|
||||
w = nw
|
||||
h = nh
|
||||
windowCore.setSize(w, h)
|
||||
end
|
||||
window.getDepth = windowCore.getDepth
|
||||
window.span = windowCore.span
|
||||
window.recommendPalette = windowCore.recommendPalette
|
||||
window.close = function ()
|
||||
windowCore.close()
|
||||
lclEvToW[windowCore.id] = nil
|
||||
|
@ -82,7 +87,10 @@ newNeoux = function (event, neo)
|
|||
return window
|
||||
end
|
||||
-- Padding function
|
||||
neoux.pad = require("fmttext").pad
|
||||
neoux.pad = function (...)
|
||||
local fmt = require("fmttext")
|
||||
return fmt.pad(...)
|
||||
end
|
||||
-- Text dialog formatting function.
|
||||
-- Assumes you've run unicode.safeTextFormat if need be
|
||||
neoux.fmtText = function (...)
|
||||
|
@ -90,7 +98,7 @@ newNeoux = function (event, neo)
|
|||
return fmt.fmtText(...)
|
||||
end
|
||||
-- UI FRAMEWORK --
|
||||
neoux.tcwindow = function (w, h, controls, closing, bg, fg, selIndex)
|
||||
neoux.tcwindow = function (w, h, controls, closing, bg, fg, selIndex, keyFlags)
|
||||
local function rotateSelIndex()
|
||||
local original = selIndex
|
||||
while true do
|
||||
|
@ -115,6 +123,7 @@ newNeoux = function (event, neo)
|
|||
end
|
||||
rotateSelIndex()
|
||||
end
|
||||
keyFlags = keyFlags or {}
|
||||
local function moveIndex(vertical, negative)
|
||||
if not controls[selIndex] then return end
|
||||
local currentMA, currentOA = controls[selIndex].y, controls[selIndex].x
|
||||
|
@ -226,34 +235,45 @@ newNeoux = function (event, neo)
|
|||
end
|
||||
end
|
||||
elseif ev == "key" then
|
||||
if b == 203 then
|
||||
if c then
|
||||
moveIndexAU(window, false, true)
|
||||
if controls[selIndex] and controls[selIndex].key then
|
||||
if controls[selIndex].key(window, function () doZone(window, controls[selIndex]) end, a, b, c, keyFlags) then
|
||||
return
|
||||
end
|
||||
elseif b == 205 then
|
||||
if c then
|
||||
moveIndexAU(window, false, false)
|
||||
end
|
||||
elseif b == 200 then
|
||||
if c then
|
||||
moveIndexAU(window, true, true)
|
||||
end
|
||||
elseif b == 208 then
|
||||
if c then
|
||||
moveIndexAU(window, true, false)
|
||||
end
|
||||
elseif a == 9 then
|
||||
if c then
|
||||
local c1 = controls[selIndex]
|
||||
rotateSelIndex()
|
||||
local c2 = controls[selIndex]
|
||||
local cache = {}
|
||||
if c1 then doZone(window, c1, cache) end
|
||||
if c2 then doZone(window, c2, cache) end
|
||||
end
|
||||
elseif controls[selIndex] then
|
||||
if controls[selIndex].key then
|
||||
controls[selIndex].key(window, function () doZone(window, controls[selIndex]) end, a, b, c)
|
||||
end
|
||||
if b == 29 then
|
||||
keyFlags.ctrl = c
|
||||
elseif b == 157 then
|
||||
keyFlags.rctrl = c
|
||||
elseif b == 42 then
|
||||
keyFlags.shift = c
|
||||
elseif b == 54 then
|
||||
keyFlags.rshift = c
|
||||
elseif not (keyFlags.ctrl or keyFlags.rctrl or keyFlags.shift or keyFlags.rshift) then
|
||||
if b == 203 then
|
||||
if c then
|
||||
moveIndexAU(window, false, true)
|
||||
end
|
||||
elseif b == 205 then
|
||||
if c then
|
||||
moveIndexAU(window, false, false)
|
||||
end
|
||||
elseif b == 200 then
|
||||
if c then
|
||||
moveIndexAU(window, true, true)
|
||||
end
|
||||
elseif b == 208 then
|
||||
if c then
|
||||
moveIndexAU(window, true, false)
|
||||
end
|
||||
elseif a == 9 then
|
||||
if c then
|
||||
local c1 = controls[selIndex]
|
||||
rotateSelIndex()
|
||||
local c2 = controls[selIndex]
|
||||
local cache = {}
|
||||
if c1 then doZone(window, c1, cache) end
|
||||
if c2 then doZone(window, c2, cache) end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif ev == "clipboard" then
|
||||
|
@ -267,7 +287,7 @@ newNeoux = function (event, neo)
|
|||
elseif ev == "close" then
|
||||
closing(window)
|
||||
end
|
||||
end, doZone
|
||||
end
|
||||
end
|
||||
neoux.tcrawview = function (x, y, lines)
|
||||
return {
|
||||
|
@ -300,10 +320,11 @@ newNeoux = function (event, neo)
|
|||
w = unicode.len(text),
|
||||
h = 1,
|
||||
selectable = true,
|
||||
key = function (window, update, a, c, d)
|
||||
key = function (window, update, a, c, d, f)
|
||||
if d then
|
||||
if a == 13 or a == 32 then
|
||||
callback(window)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
@ -322,36 +343,52 @@ newNeoux = function (event, neo)
|
|||
end
|
||||
-- Note: w should be at least 2 - this is similar to buttons.
|
||||
neoux.tcfield = function (x, y, w, textprop)
|
||||
-- compat. workaround for apps which nuke tcfields
|
||||
local p = unicode.len(textprop()) + 1
|
||||
return {
|
||||
x = x,
|
||||
y = y,
|
||||
w = w,
|
||||
h = 1,
|
||||
selectable = true,
|
||||
key = function (window, update, a, c, d)
|
||||
key = function (window, update, a, c, d, f)
|
||||
if d then
|
||||
if a == 13 then
|
||||
elseif a == 8 then
|
||||
local str = textprop()
|
||||
textprop(unicode.sub(str, 1, unicode.len(str) - 1))
|
||||
update()
|
||||
elseif a ~= 0 then
|
||||
textprop(textprop() .. unicode.char(a))
|
||||
local ot = textprop()
|
||||
local le = require("lineedit")
|
||||
p = le.clamp(ot, p)
|
||||
if c == 63 then
|
||||
neo.requireAccess("x.neo.pub.globals", "clipboard").setSetting("clipboard", ot)
|
||||
elseif c == 64 then
|
||||
local contents = neo.requireAccess("x.neo.pub.globals", "clipboard").getSetting("clipboard")
|
||||
contents = contents:match("^[^\r\n]*")
|
||||
textprop(contents)
|
||||
update()
|
||||
elseif a ~= 9 then
|
||||
local lT, lC, lX = le.key(a ~= 0 and unicode.char(a), c, ot, p)
|
||||
if lT or lC then
|
||||
if lT then textprop(lT) end
|
||||
p = lC or p
|
||||
update()
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
clipboard = function (window, update, contents)
|
||||
contents = contents:match("^[^\r\n]*")
|
||||
textprop(contents)
|
||||
update()
|
||||
end,
|
||||
line = function (window, x, y, lind, bg, fg, selected)
|
||||
local fg1 = fg
|
||||
if selected then
|
||||
fg = bg
|
||||
bg = fg1
|
||||
end
|
||||
local text = unicode.safeTextFormat(textprop())
|
||||
local txl = unicode.len(text)
|
||||
local start = math.max(1, (txl - (w - 2)) + 1)
|
||||
text = "[" .. neoux.pad(unicode.sub(text, start, start + (w - 3)), w - 2, false, true) .. "]"
|
||||
window.span(x, y, text, bg, fg)
|
||||
local t, e, r = textprop(), require("lineedit")
|
||||
p = e.clamp(t, p)
|
||||
t, r = unicode.safeTextFormat(t, p)
|
||||
window.span(x, y, "[" .. e.draw(w - 2, t, selected and r) .. "]", bg, fg)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
local doSerialize = nil
|
||||
function doSerialize(s)
|
||||
if type(s) == "table" then
|
||||
|
@ -26,12 +31,16 @@ function doSerialize(s)
|
|||
end
|
||||
error("Cannot serialize " .. type(s))
|
||||
end
|
||||
return neo.wrapMeta({
|
||||
return {
|
||||
serialize = function (x) return "return " .. doSerialize(x) end,
|
||||
deserialize = function (s)
|
||||
local r1, r2 = pcall(function() return load(s, "=serial", "t", {})() end)
|
||||
local r1, r2 = pcall(function()
|
||||
return load(s, "=serial", "t", {})()
|
||||
end)
|
||||
if r1 then
|
||||
return r2
|
||||
else
|
||||
return nil, tostring(r2)
|
||||
end
|
||||
end
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- just don't bother with proper indent here
|
||||
return function (event, nexus, retFunc, fs, pkg, mode)
|
||||
return function (event, nexus, retFunc, fs, pkg, mode, defName)
|
||||
|
||||
local fmt = require("fmttext")
|
||||
local class = "manage"
|
||||
if mode ~= nil then
|
||||
|
@ -13,17 +18,120 @@ if mode ~= nil then
|
|||
end
|
||||
end
|
||||
|
||||
local prepareNode
|
||||
-- State
|
||||
local w, h, ctrl = 30, 8, false
|
||||
local l, selection, unknownTx
|
||||
local node, wnd
|
||||
|
||||
local ccb = nil
|
||||
local function cb(...)
|
||||
local res, e = pcall(ccb, ...)
|
||||
local function prepareNode(n)
|
||||
node = n
|
||||
l = node.list()
|
||||
selection, unknownTx = 1, ""
|
||||
wnd.setSize(w, h)
|
||||
end
|
||||
|
||||
local function format(a)
|
||||
if a <= 1 then
|
||||
return false, fmt.pad(unicode.safeTextFormat(node.name), w, true, true)
|
||||
end
|
||||
local camY = math.max(1, selection - 3)
|
||||
local idx = a + camY - 2
|
||||
local utx = (" "):rep(w)
|
||||
if node.unknownAvailable and idx == #l + 1 then
|
||||
utx = "<OK>[" .. fmt.pad(unicode.safeTextFormat(unknownTx), w - 6, false, true, true) .. "]"
|
||||
end
|
||||
if l[idx] then
|
||||
utx = "<" .. fmt.pad(unicode.safeTextFormat(l[idx][1]), w - 2, false, true) .. ">"
|
||||
end
|
||||
return selection == idx, utx
|
||||
end
|
||||
|
||||
local function updateLine(a)
|
||||
local colA, colB = 0xFFFFFF, 0
|
||||
local sel, text = format(a)
|
||||
if sel then
|
||||
colB, colA = 0xFFFFFF, 0
|
||||
end
|
||||
wnd.span(1, a, text, colA, colB)
|
||||
end
|
||||
|
||||
local function flush()
|
||||
for i = 1, h do
|
||||
updateLine(i)
|
||||
end
|
||||
end
|
||||
|
||||
local function key(ka, kc, down)
|
||||
if kc == 29 then
|
||||
ctrl = down
|
||||
end
|
||||
if not down then return end
|
||||
if ctrl then
|
||||
if kc == 200 then
|
||||
h = math.max(2, h - 1)
|
||||
elseif kc == 208 then
|
||||
h = h + 1
|
||||
elseif kc == 203 then
|
||||
w = math.max(6, w - 1)
|
||||
elseif kc == 205 then
|
||||
w = w + 1
|
||||
else
|
||||
return
|
||||
end
|
||||
wnd.setSize(w, h)
|
||||
return
|
||||
elseif (ka == 9) or (kc == 208) then
|
||||
local lo = selection
|
||||
selection = selection + 1
|
||||
local max = #l
|
||||
if node.unknownAvailable then
|
||||
max = max + 1
|
||||
end
|
||||
if selection > max then
|
||||
selection = 1
|
||||
end
|
||||
elseif kc == 200 then
|
||||
local lo = selection
|
||||
selection = selection - 1
|
||||
local max = #l
|
||||
if node.unknownAvailable then
|
||||
max = max + 1
|
||||
end
|
||||
if selection == 0 then
|
||||
selection = max
|
||||
end
|
||||
elseif ka == 13 then
|
||||
local aResult, res
|
||||
if selection ~= #l + 1 then
|
||||
aResult, res = l[selection][2]()
|
||||
else
|
||||
aResult, res = node.selectUnknown(unknownTx)
|
||||
end
|
||||
if aResult then
|
||||
retFunc(res)
|
||||
nexus.windows[wnd.id] = nil
|
||||
wnd.close()
|
||||
else
|
||||
prepareNode(res)
|
||||
end
|
||||
elseif selection == #l + 1 then
|
||||
if ka == 8 or kc == 211 then
|
||||
unknownTx = unicode.sub(unknownTx, 1, unicode.len(unknownTx) - 1)
|
||||
elseif ka ~= 0 then
|
||||
unknownTx = unknownTx .. unicode.char(ka)
|
||||
end
|
||||
end
|
||||
flush()
|
||||
end
|
||||
|
||||
local function key2(...)
|
||||
local res, e = pcall(key, ...)
|
||||
if not res then
|
||||
prepareNode({
|
||||
name = "F.M. Error",
|
||||
list = function ()
|
||||
local l = {}
|
||||
for k, v in ipairs(fmt.fmtText(unicode.safeTextFormat(e), 25)) do
|
||||
for k, v in ipairs(fmt.fmtText(unicode.safeTextFormat(e), w)) do
|
||||
l[k] = {v, function () return true end}
|
||||
end
|
||||
return l
|
||||
|
@ -34,151 +142,44 @@ local function cb(...)
|
|||
end
|
||||
end
|
||||
|
||||
local function prepareNodeI(node)
|
||||
local l = node.list()
|
||||
local w, h = 30, 8
|
||||
-- Local State
|
||||
-- Selection. Having this equal to #l + 1 means typing area ('unknown')
|
||||
local selection = 1
|
||||
local unknownTx = ""
|
||||
--
|
||||
local function format(a)
|
||||
if a <= 1 then
|
||||
return true, true, node.name
|
||||
end
|
||||
local camY = math.max(1, selection - 3)
|
||||
local idx = a + camY - 2
|
||||
nexus.create(w, h, class .. " " .. pkg, function (w, ev, a, b, c)
|
||||
if not wnd then
|
||||
wnd = w
|
||||
prepareNode(require("sys-filevfs")(fs, mode, defName))
|
||||
end
|
||||
if ev == "key" then
|
||||
key2(a, b, c)
|
||||
end
|
||||
if ev == "touch" then
|
||||
local ns = b + math.max(1, selection - 3) - 2
|
||||
local max = #l
|
||||
if node.unknownAvailable then
|
||||
if idx == #l + 1 then
|
||||
return selection == #l + 1, false, ":" .. unknownTx
|
||||
end
|
||||
max = max + 1
|
||||
end
|
||||
if l[idx] then
|
||||
return selection == idx, false, l[idx][1]
|
||||
end
|
||||
return true, true, "~~~"
|
||||
end
|
||||
local function updateLine(wnd, a)
|
||||
local colA, colB = 0xFFFFFF, 0
|
||||
local sel, cen, text = format(a)
|
||||
if sel then
|
||||
colB, colA = 0xFFFFFF, 0
|
||||
end
|
||||
wnd.span(1, a, fmt.pad(unicode.safeTextFormat(text), w, cen, true), colA, colB)
|
||||
end
|
||||
local function flush(wnd)
|
||||
for i = 1, h do
|
||||
updateLine(wnd, i)
|
||||
if ns == selection and ((selection ~= #l + 1) or (a <= 4)) then
|
||||
key2(13, 0, true)
|
||||
else
|
||||
selection = math.min(math.max(1, ns), max)
|
||||
flush()
|
||||
end
|
||||
end
|
||||
local function key(wnd, ka, kc, down)
|
||||
if not down then return end
|
||||
if (ka == 9) or (kc == 208) then
|
||||
local lo = selection
|
||||
selection = selection + 1
|
||||
local max = #l
|
||||
if node.unknownAvailable then
|
||||
max = max + 1
|
||||
end
|
||||
if selection > max then
|
||||
selection = 1
|
||||
end
|
||||
flush(wnd)
|
||||
return
|
||||
end
|
||||
if kc == 200 then
|
||||
local lo = selection
|
||||
selection = selection - 1
|
||||
local max = #l
|
||||
if node.unknownAvailable then
|
||||
max = max + 1
|
||||
end
|
||||
if selection == 0 then
|
||||
selection = max
|
||||
end
|
||||
flush(wnd)
|
||||
return
|
||||
end
|
||||
if ka == 13 then
|
||||
local aResult, res
|
||||
if selection ~= #l + 1 then
|
||||
aResult, res = l[selection][2]()
|
||||
else
|
||||
aResult, res = node.selectUnknown(unknownTx)
|
||||
end
|
||||
if aResult then
|
||||
retFunc(res)
|
||||
nexus.close(wnd)
|
||||
else
|
||||
prepareNode(res)
|
||||
end
|
||||
return
|
||||
end
|
||||
if selection == #l + 1 then
|
||||
if ka == 8 then
|
||||
unknownTx = unicode.sub(unknownTx, 1, unicode.len(unknownTx) - 1)
|
||||
flush(wnd)
|
||||
return
|
||||
end
|
||||
if ka ~= 0 then
|
||||
unknownTx = unknownTx .. unicode.char(ka)
|
||||
flush(wnd)
|
||||
end
|
||||
end
|
||||
if ev == "line" then
|
||||
updateLine(a)
|
||||
end
|
||||
return w, h, function (wnd, evt, a, b, c)
|
||||
if evt == "key" then
|
||||
key(wnd, a, b, c)
|
||||
end
|
||||
if evt == "touch" then
|
||||
local ns = b + math.max(1, selection - 3) - 2
|
||||
local max = #l
|
||||
if node.unknownAvailable then
|
||||
max = max + 1
|
||||
end
|
||||
if ns == selection and selection ~= #l + 1 then
|
||||
key(wnd, 13, 0, true)
|
||||
else
|
||||
selection = math.min(math.max(1, ns), max)
|
||||
flush(wnd)
|
||||
end
|
||||
end
|
||||
if evt == "line" then
|
||||
updateLine(wnd, a)
|
||||
end
|
||||
if evt == "close" then
|
||||
retFunc(nil)
|
||||
nexus.close(wnd)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local text = class .. " " .. pkg
|
||||
local window
|
||||
|
||||
function prepareNode(node)
|
||||
local w, h, c = prepareNodeI(node)
|
||||
ccb = c
|
||||
window.setSize(w, h)
|
||||
end
|
||||
|
||||
local closer = nexus.createNexusThread(function ()
|
||||
window = nexus.create(25, 10, text)
|
||||
prepareNode(require("sys-filevfs")(fs, mode))
|
||||
while window do
|
||||
cb(window, coroutine.yield())
|
||||
if ev == "close" then
|
||||
retFunc(nil)
|
||||
nexus.windows[wnd.id] = nil
|
||||
wnd.close()
|
||||
end
|
||||
end)
|
||||
if not closer then
|
||||
retFunc()
|
||||
return
|
||||
end
|
||||
|
||||
return function ()
|
||||
retFunc()
|
||||
closer()
|
||||
if window then
|
||||
nexus.close(window)
|
||||
window = nil
|
||||
if wnd then
|
||||
nexus.windows[wnd.id] = nil
|
||||
wnd.close()
|
||||
wnd = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- Used by filedialog to provide a sane relative environment.
|
||||
-- Essentially, the filedialog is just a 'thin' UI wrapper over this.
|
||||
|
@ -19,7 +23,7 @@ local setupCopyNode
|
|||
function setupCopyNode(parent, myRoot, op, complete)
|
||||
local function handleResult(aRes, res)
|
||||
if aRes then
|
||||
return complete(res)
|
||||
return complete(res, true)
|
||||
else
|
||||
return nil, setupCopyNode(parent, res, op, complete)
|
||||
end
|
||||
|
@ -29,6 +33,7 @@ function setupCopyNode(parent, myRoot, op, complete)
|
|||
list = function ()
|
||||
local l = {}
|
||||
table.insert(l, {"Cancel Operation: " .. op, function ()
|
||||
complete(nil, false)
|
||||
return false, parent
|
||||
end})
|
||||
for _, v in ipairs(myRoot.list()) do
|
||||
|
@ -44,15 +49,19 @@ function setupCopyNode(parent, myRoot, op, complete)
|
|||
end
|
||||
}
|
||||
end
|
||||
local function setupCopyVirtualEnvironment(fs, parent, fwrap)
|
||||
local function setupCopyVirtualEnvironment(fs, parent, fwrap, impliedName)
|
||||
if not fwrap then
|
||||
return false, dialog("Could not open source", parent)
|
||||
end
|
||||
local myRoot = getRoot(fs, true)
|
||||
local myRoot = getRoot(fs, true, impliedName)
|
||||
-- Setup wrapping node
|
||||
return setupCopyNode(parent, myRoot, "Copy", function (fwrap2)
|
||||
return setupCopyNode(parent, myRoot, "Copy", function (fwrap2, intent)
|
||||
if not fwrap2 then
|
||||
return false, dialog("Could not open dest.", parent)
|
||||
fwrap.close()
|
||||
if intent then
|
||||
return false, dialog("Could not open dest.", parent)
|
||||
end
|
||||
return false, parent
|
||||
end
|
||||
local data = fwrap.read(neo.readBufSize)
|
||||
while data do
|
||||
|
@ -64,31 +73,74 @@ local function setupCopyVirtualEnvironment(fs, parent, fwrap)
|
|||
return false, dialog("Completed copy.", parent)
|
||||
end)
|
||||
end
|
||||
getFsNode = function (fs, parent, fsc, path, mode)
|
||||
function getFsNode(fs, parent, fsc, path, mode, impliedName)
|
||||
local va = fsc.address:sub(1, 4)
|
||||
if path:sub(#path, #path) == "/" then
|
||||
local t
|
||||
local confirmedDel = false
|
||||
t = {
|
||||
name = "DIR : " .. va .. path,
|
||||
list = function ()
|
||||
local n = {}
|
||||
n[1] = {"..", function ()
|
||||
return nil, parent
|
||||
end}
|
||||
local fscrw = not fsc.isReadOnly()
|
||||
local dir = path:sub(#path, #path) == "/"
|
||||
local confirmedDel = false
|
||||
local t
|
||||
local function selectUnknown(text)
|
||||
-- Relies on text being nil if used in leaf node
|
||||
local rt, re = require("sys-filewrap").create(fsc, path .. (text or ""), mode)
|
||||
if not rt then
|
||||
return false, dialog("Open Error: " .. tostring(re), parent)
|
||||
end
|
||||
return true, rt
|
||||
end
|
||||
t = {
|
||||
name = ((dir and "DIR: ") or "FILE: ") .. va .. path,
|
||||
list = function ()
|
||||
local n = {}
|
||||
n[1] = {"..", function ()
|
||||
return nil, parent
|
||||
end}
|
||||
if dir then
|
||||
for k, v in ipairs(fsc.list(path)) do
|
||||
local nm = "[F] " .. v
|
||||
local nm = "F: " .. v
|
||||
local fp = path .. v
|
||||
if fsc.isDirectory(fp) then
|
||||
nm = "[D] " .. v
|
||||
local cDir = fsc.isDirectory(fp)
|
||||
if cDir then
|
||||
nm = "D: " .. v
|
||||
end
|
||||
if (not cDir) and (fscrw or mode == false) and (mode ~= nil) then
|
||||
local vn = v
|
||||
n[k + 1] = {nm, function () return selectUnknown(vn) end}
|
||||
else
|
||||
n[k + 1] = {nm, function () return nil, getFsNode(fs, t, fsc, fp, mode, impliedName) end}
|
||||
end
|
||||
n[k + 1] = {nm, function () return nil, getFsNode(fs, t, fsc, fp, mode) end}
|
||||
end
|
||||
local delText = "Delete"
|
||||
if confirmedDel then
|
||||
delText = "Delete <ARMED>"
|
||||
else
|
||||
table.insert(n, {"Copy", function ()
|
||||
local rt, re = require("sys-filewrap").create(fsc, path, false)
|
||||
if not rt then
|
||||
return false, dialog("Open Error: " .. tostring(re), parent)
|
||||
end
|
||||
return nil, setupCopyVirtualEnvironment(fs, parent, rt, path:match("[^/]*$") or "")
|
||||
end})
|
||||
end
|
||||
if fscrw then
|
||||
if dir then
|
||||
table.insert(n, {"Mk. Directory", function ()
|
||||
return nil, {
|
||||
name = "MKDIR...",
|
||||
list = function () return {{
|
||||
"Cancel", function ()
|
||||
return false, t
|
||||
end
|
||||
}} end,
|
||||
unknownAvailable = true,
|
||||
selectUnknown = function (text)
|
||||
fsc.makeDirectory(path .. text)
|
||||
return nil, dialog("Done!", t)
|
||||
end
|
||||
}
|
||||
end})
|
||||
end
|
||||
if path ~= "/" then
|
||||
local delText = "Delete"
|
||||
if confirmedDel then
|
||||
delText = "Delete <ARMED>"
|
||||
end
|
||||
table.insert(n, {delText, function ()
|
||||
if not confirmedDel then
|
||||
confirmedDel = true
|
||||
|
@ -97,65 +149,39 @@ getFsNode = function (fs, parent, fsc, path, mode)
|
|||
fsc.remove(path)
|
||||
return nil, dialog("Done.", parent)
|
||||
end})
|
||||
else
|
||||
table.insert(n, {"Relabel Disk", function ()
|
||||
return nil, {
|
||||
name = "Disk Relabel...",
|
||||
list = function () return {{
|
||||
fsc.getLabel() or "Cancel",
|
||||
function ()
|
||||
return false, t
|
||||
end
|
||||
}} end,
|
||||
unknownAvailable = true,
|
||||
selectUnknown = function (tx)
|
||||
fsc.setLabel(tx)
|
||||
return false, t
|
||||
end
|
||||
}
|
||||
end})
|
||||
end
|
||||
table.insert(n, {"Mk. Directory", function ()
|
||||
return nil, {
|
||||
name = "MKDIR...",
|
||||
list = function () return {} end,
|
||||
unknownAvailable = true,
|
||||
selectUnknown = function (text)
|
||||
fsc.makeDirectory(path .. text)
|
||||
return nil, dialog("Done!", t)
|
||||
end
|
||||
}
|
||||
end})
|
||||
return n
|
||||
end,
|
||||
unknownAvailable = mode ~= nil,
|
||||
selectUnknown = function (text)
|
||||
local rt, re = require("sys-filewrap")(fsc, path .. text, mode)
|
||||
if not rt then
|
||||
return false, dialog("Open Error: " .. tostring(re), parent)
|
||||
end
|
||||
return true, rt
|
||||
end
|
||||
}
|
||||
return t
|
||||
end
|
||||
return {
|
||||
name = "FILE: " .. va .. path,
|
||||
list = function ()
|
||||
local n = {}
|
||||
table.insert(n, {"Back", function ()
|
||||
return nil, parent
|
||||
end})
|
||||
if mode ~= nil then
|
||||
table.insert(n, {"Open", function ()
|
||||
local rt, re = require("sys-filewrap")(fsc, path, mode)
|
||||
if not rt then
|
||||
return false, dialog("Open Error: " .. tostring(re), parent)
|
||||
end
|
||||
return true, rt
|
||||
if not dir then
|
||||
elseif impliedName then
|
||||
table.insert(n, {"Implied: " .. impliedName, function ()
|
||||
return selectUnknown(impliedName)
|
||||
end})
|
||||
end
|
||||
table.insert(n, {"Copy", function ()
|
||||
local rt, re = require("sys-filewrap")(fsc, path, false)
|
||||
if not rt then
|
||||
return false, dialog("Open Error: " .. tostring(re), parent)
|
||||
end
|
||||
return nil, setupCopyVirtualEnvironment(fs, parent, rt)
|
||||
end})
|
||||
table.insert(n, {"Delete", function ()
|
||||
fsc.remove(path)
|
||||
return nil, dialog("Done.", parent)
|
||||
end})
|
||||
return n
|
||||
end,
|
||||
unknownAvailable = false,
|
||||
selectUnknown = function (text) end
|
||||
unknownAvailable = dir and (mode ~= nil) and ((mode == false) or fscrw),
|
||||
selectUnknown = selectUnknown
|
||||
}
|
||||
return t
|
||||
end
|
||||
function getRoot(fs, mode)
|
||||
function getRoot(fs, mode, defName)
|
||||
local t
|
||||
t = {
|
||||
name = "DRVS:",
|
||||
|
@ -163,32 +189,31 @@ function getRoot(fs, mode)
|
|||
local l = {}
|
||||
for fsi in fs.list() do
|
||||
local id = fsi.getLabel()
|
||||
if not id then
|
||||
id = " Disk"
|
||||
else
|
||||
id = ":" .. id
|
||||
end
|
||||
if fsi == fs.primary then
|
||||
id = "NEO" .. id
|
||||
id = "NEO" .. ((id and (":" .. id)) or " Disk")
|
||||
elseif fsi == fs.temporary then
|
||||
id = "RAM" .. id
|
||||
id = "RAM" .. ((id and (" " .. id)) or "Disk")
|
||||
else
|
||||
id = id or "Disk"
|
||||
end
|
||||
local used, total = fsi.spaceUsed(), fsi.spaceTotal()
|
||||
local amount = string.format("%02i", math.ceil((used / total) * 100))
|
||||
local mb = math.floor(total / (1024 * 1024))
|
||||
if fsi.isReadOnly() then
|
||||
id = amount .. "% RO " .. mb .. "M " .. id
|
||||
id = "RO " .. amount .. "% " .. mb .. "M " .. id
|
||||
else
|
||||
id = amount .. "% RW " .. mb .. "M " .. id
|
||||
id = "RW " .. amount .. "% " .. mb .. "M " .. id
|
||||
end
|
||||
table.insert(l, {fsi.address:sub(1, 4) .. ": " .. id, function ()
|
||||
return nil, getFsNode(fs, t, fsi, "/", mode)
|
||||
table.insert(l, {fsi.address:sub(1, 4) .. " " .. id, function ()
|
||||
return nil, getFsNode(fs, t, fsi, "/", mode, defName)
|
||||
end})
|
||||
end
|
||||
return l
|
||||
end,
|
||||
unknownAvailable = false,
|
||||
selectUnknown = function (text) end
|
||||
selectUnknown = function (text)
|
||||
return false, dialog("Ow, that hurt...", t)
|
||||
end
|
||||
}
|
||||
return t
|
||||
end
|
||||
|
|
|
@ -1,9 +1,29 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
return function(dev, file, mode)
|
||||
local function ensureMode(mode)
|
||||
local n = "rb"
|
||||
if mode then n = "wb" end
|
||||
if type(mode) == "boolean" then
|
||||
if mode then
|
||||
n = "wb"
|
||||
end
|
||||
elseif type(mode) == "string" then
|
||||
if mode == "append" then
|
||||
n = "ab"
|
||||
else
|
||||
error("Invalid fmode " .. mode)
|
||||
end
|
||||
else
|
||||
error("Invalid fmode")
|
||||
end
|
||||
return n
|
||||
end
|
||||
local function create(dev, file, mode)
|
||||
local n = ensureMode(mode)
|
||||
local handle, r = dev.open(file, n)
|
||||
if not handle then return nil, r end
|
||||
local open = true
|
||||
|
@ -14,37 +34,45 @@ return function(dev, file, mode)
|
|||
dev.close(handle)
|
||||
end)
|
||||
end
|
||||
local function reader(len)
|
||||
if not open then return end
|
||||
if len == "*a" then
|
||||
local ch = ""
|
||||
local c = dev.read(handle, neo.readBufSize)
|
||||
while c do
|
||||
ch = ch .. c
|
||||
c = dev.read(handle, neo.readBufSize)
|
||||
end
|
||||
return ch
|
||||
end
|
||||
if type(len) ~= "number" then error("Length of read must be number or '*a'") end
|
||||
return dev.read(handle, len)
|
||||
end
|
||||
local function writer(txt)
|
||||
if not open then return end
|
||||
neo.ensureType(txt, "string")
|
||||
return dev.write(handle, txt)
|
||||
end
|
||||
local function seeker(whence, point)
|
||||
if not open then return end
|
||||
return dev.seek(handle, whence, point)
|
||||
end
|
||||
if not mode then
|
||||
if mode == "rb" then
|
||||
return {
|
||||
close = closer,
|
||||
seek = seeker,
|
||||
read = function (len)
|
||||
if len == "*a" then
|
||||
local ch = ""
|
||||
local c = dev.read(handle, neo.readBufSize)
|
||||
while c do
|
||||
ch = ch .. c
|
||||
c = dev.read(handle, neo.readBufSize)
|
||||
end
|
||||
return ch
|
||||
end
|
||||
if type(len) ~= "number" then error("Length of read must be number or '*a'") end
|
||||
return dev.read(handle, len)
|
||||
end
|
||||
read = reader
|
||||
}, closer
|
||||
else
|
||||
return {
|
||||
close = closer,
|
||||
seek = seeker,
|
||||
write = function (txt)
|
||||
if type(txt) ~= "string" then error("Write data must be string-bytearray") end
|
||||
local ok, b = dev.write(handle, txt)
|
||||
if not ok then error(tostring(b)) end
|
||||
end
|
||||
read = reader,
|
||||
write = writer
|
||||
}, closer
|
||||
end
|
||||
end
|
||||
return {
|
||||
ensureMode = ensureMode,
|
||||
create = create
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
return function (
|
||||
gpus, screens,
|
||||
getMonitorSettings, settings, sRattle, saveSettings,
|
||||
announceFreeMonitor, pid, claimed, sendSig,
|
||||
monitorClaims, monitorPool, currentGPUUsers, currentGPUBinding,
|
||||
address
|
||||
)
|
||||
neo.ensureType(address, "string")
|
||||
for k, monitor in ipairs(monitorPool) do
|
||||
if monitor.address == address then
|
||||
-- find GPU
|
||||
local gpu, bestStats = nil, {-math.huge, -math.huge, -math.huge}
|
||||
for a, _ in pairs(currentGPUBinding) do
|
||||
currentGPUBinding[a] = nil
|
||||
end
|
||||
for v in gpus() do
|
||||
v.bind(monitor.address, false)
|
||||
local w, h = v.maxResolution()
|
||||
local quality = w * h * v.maxDepth()
|
||||
local users = (currentGPUUsers[v.address] or 0)
|
||||
local gquality = 0
|
||||
for scr in screens() do
|
||||
v.bind(scr.address, false)
|
||||
w, h = v.maxResolution()
|
||||
local squality = w * h * v.maxDepth()
|
||||
gquality = math.max(gquality, squality)
|
||||
end
|
||||
local stats = {quality, -users, -gquality}
|
||||
for i = 1, #stats do
|
||||
if stats[i] > bestStats[i] then
|
||||
gpu = v
|
||||
bestStats = stats
|
||||
break
|
||||
elseif stats[i] < bestStats[i] then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- setup monitor
|
||||
if gpu then
|
||||
monitor.setPrecise(true)
|
||||
monitor.turnOn()
|
||||
gpu.bind(address, false)
|
||||
currentGPUBinding[gpu.address] = address
|
||||
local maxW, maxH = gpu.maxResolution()
|
||||
local maxD = gpu.maxDepth()
|
||||
local w, h, d, t = getMonitorSettings(address)
|
||||
w, h, d = math.min(w, maxW), math.min(h, maxH), math.min(d, maxD)
|
||||
if monitor.setTouchModeInverted then
|
||||
monitor.setTouchModeInverted(t == "yes")
|
||||
else
|
||||
t = "no"
|
||||
end
|
||||
settings["scr.w." .. monitor.address] = tostring(w)
|
||||
settings["scr.h." .. monitor.address] = tostring(h)
|
||||
settings["scr.d." .. monitor.address] = tostring(d)
|
||||
settings["scr.t." .. monitor.address] = t
|
||||
sRattle("scr.w." .. monitor.address, tostring(w))
|
||||
sRattle("scr.h." .. monitor.address, tostring(h))
|
||||
sRattle("scr.d." .. monitor.address, tostring(d))
|
||||
sRattle("scr.t." .. monitor.address, t)
|
||||
gpu.setResolution(w, h)
|
||||
gpu.setDepth(d)
|
||||
pcall(saveSettings)
|
||||
-- finish up
|
||||
gpu = gpu.address
|
||||
currentGPUUsers[gpu] = (currentGPUUsers[gpu] or 0) + 1
|
||||
local disclaimer = function (wasDevLoss)
|
||||
-- we lost it
|
||||
monitorClaims[address] = nil
|
||||
claimed[address] = nil
|
||||
if not wasDevLoss then
|
||||
currentGPUUsers[gpu] = currentGPUUsers[gpu] - 1
|
||||
table.insert(monitorPool, monitor)
|
||||
announceFreeMonitor(address, pid)
|
||||
else
|
||||
sendSig("lost", address)
|
||||
end
|
||||
end
|
||||
claimed[address] = disclaimer
|
||||
monitorClaims[address] = {gpu, disclaimer}
|
||||
table.remove(monitorPool, k)
|
||||
return function ()
|
||||
for v in gpus() do
|
||||
if v.address == gpu then
|
||||
local didBind = false
|
||||
if currentGPUBinding[gpu] ~= address then
|
||||
v.bind(address, false)
|
||||
didBind = true
|
||||
end
|
||||
currentGPUBinding[gpu] = address
|
||||
return v, didBind
|
||||
end
|
||||
end
|
||||
end, monitor
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- CRITICAL FILE!
|
||||
-- This file defines how your KittenOS NEO system responds to access requests.
|
||||
|
@ -11,36 +15,36 @@
|
|||
-- IRC is usually pretty safe, but no guarantees.
|
||||
|
||||
-- Returns "allow", "deny", or "ask".
|
||||
local actualPolicy = function (pkg, pid, perm)
|
||||
local function actualPolicy(pkg, pid, perm, pkgSvcPfx)
|
||||
-- System stuff is allowed.
|
||||
if pkg:sub(1, 4) == "sys-" then
|
||||
return "allow"
|
||||
end
|
||||
-- svc-t's job is solely to emulate terminals
|
||||
-- TO INSTALL YOUR OWN TERMINAL EMULATOR:
|
||||
-- perm|app-yourterm|r.neo.t
|
||||
if pkg == "svc-t" and perm == "r.neo.pub.t" then
|
||||
return "allow"
|
||||
end
|
||||
-- <The following is for apps & services>
|
||||
-- x.neo.pub (aka Icecap) is open to all
|
||||
-- x.neo.pub.* is open to all
|
||||
if perm:sub(1, 10) == "x.neo.pub." then
|
||||
return "allow"
|
||||
end
|
||||
-- This is to ensure the prefix naming scheme is FOLLOWED!
|
||||
-- sys- : System, part of KittenOS NEO and thus tries to present a "unified fragmented interface" in 'neo'
|
||||
-- app- : Application - these can have ad-hoc relationships. It is EXPECTED these have a GUI
|
||||
-- svc- : Service - Same as Application but with no expectation of desktop usability
|
||||
-- Libraries "have no rights" as they are essentially loadable blobs of Lua code.
|
||||
-- They have access via the calling program, and have a subset of the NEO Kernel API
|
||||
local pfx = nil
|
||||
if pkg:sub(1, 4) == "app-" then pfx = "app" end
|
||||
if pkg:sub(1, 4) == "svc-" then pfx = "svc" end
|
||||
if pfx then
|
||||
-- Apps can register with their own name
|
||||
if perm == "r." .. pfx .. "." .. pkg:sub(5) then
|
||||
return "allow"
|
||||
end
|
||||
-- These signals are harmless, though they identify HW (as does everything in OC...)
|
||||
if perm == "s.h.component_added" or perm == "s.h.component_removed" or perm == "s.h.tablet_use" or perm == "c.tablet" then
|
||||
return "allow"
|
||||
end
|
||||
-- Userlevel can register for itself
|
||||
if perm == "r." .. pkgSvcPfx then
|
||||
return "allow"
|
||||
end
|
||||
-- Userlevel has no other registration rights
|
||||
if perm:sub(1, 2) == "r." then
|
||||
return "deny"
|
||||
end
|
||||
-- app/svc stuff is world-accessible
|
||||
-- app/svc stuff is world-accessible,
|
||||
-- but note perm|*| overrides this
|
||||
if perm:sub(1, 6) == "x.app." then
|
||||
return "allow"
|
||||
end
|
||||
|
@ -51,10 +55,11 @@ local actualPolicy = function (pkg, pid, perm)
|
|||
return "ask"
|
||||
end
|
||||
|
||||
return function (nexus, settings, pkg, pid, perm, rsp)
|
||||
local res = actualPolicy(pkg, pid, perm)
|
||||
if res == "ask" and settings then
|
||||
res = settings.getSetting("perm|" .. pkg .. "|" .. perm) or "ask"
|
||||
return function (nexus, settings, pkg, pid, perm, rsp, pkgSvcPfx)
|
||||
local res = actualPolicy(pkg, pid, perm, pkgSvcPfx)
|
||||
if settings then
|
||||
res = settings.getSetting("perm|" .. pkg .. "|" .. perm) or
|
||||
settings.getSetting("perm|*|" .. perm) or res
|
||||
end
|
||||
if res == "ask" and nexus then
|
||||
local totalW = 3 + 6 + 2 + 8
|
||||
|
@ -62,31 +67,31 @@ return function (nexus, settings, pkg, pid, perm, rsp)
|
|||
local buttons = {
|
||||
{"<No>", function (w)
|
||||
rsp(false)
|
||||
nexus.close(w)
|
||||
nexus.windows[w.id] = nil
|
||||
w.close()
|
||||
end},
|
||||
{"<Always>", function (w)
|
||||
if settings then
|
||||
settings.setSetting("perm|" .. pkg .. "|" .. perm, "allow")
|
||||
end
|
||||
rsp(true)
|
||||
nexus.close(w)
|
||||
nexus.windows[w.id] = nil
|
||||
w.close()
|
||||
end},
|
||||
{"<Yes>", function (w)
|
||||
rsp(true)
|
||||
nexus.close(w)
|
||||
nexus.windows[w.id] = nil
|
||||
w.close()
|
||||
end}
|
||||
}
|
||||
nexus.createNexusThread(function ()
|
||||
local window = nexus.create(totalW, #fmt, "security")
|
||||
local cButton = 0
|
||||
local ev, a, b, c
|
||||
while true do
|
||||
if not ev then
|
||||
ev, a, b, c = coroutine.yield()
|
||||
end
|
||||
local cButton = 0
|
||||
nexus.create(totalW, #fmt, "security", function (window, ev, a, b, c)
|
||||
while ev do
|
||||
if ev == "line" or ev == "touch" then
|
||||
local cor = b
|
||||
if ev == "line" then
|
||||
local iev = ev
|
||||
ev = nil
|
||||
if iev == "line" then
|
||||
cor = a
|
||||
if fmt[a] then
|
||||
window.span(1, a, fmt[a], 0xFFFFFF, 0)
|
||||
|
@ -95,7 +100,7 @@ return function (nexus, settings, pkg, pid, perm, rsp)
|
|||
if cor == #fmt then
|
||||
local x = 1
|
||||
for k, v in ipairs(buttons) do
|
||||
if ev == "line" then
|
||||
if iev == "line" then
|
||||
if k ~= cButton + 1 then
|
||||
window.span(x, a, v[1], 0xFFFFFF, 0)
|
||||
else
|
||||
|
@ -114,10 +119,10 @@ return function (nexus, settings, pkg, pid, perm, rsp)
|
|||
end
|
||||
elseif ev == "close" then
|
||||
rsp(false)
|
||||
nexus.close(window)
|
||||
return
|
||||
end
|
||||
if ev == "key" then
|
||||
nexus.windows[window.id] = nil
|
||||
window.close()
|
||||
ev = nil
|
||||
elseif ev == "key" then
|
||||
if c and (a == 9 or b == 205) then
|
||||
cButton = (cButton + 1) % #buttons
|
||||
ev = "line"
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
-- BDIVIDE
|
||||
-- format:
|
||||
-- 0-127 for constants
|
||||
-- <block + 128>, <(len - 3) * 2, + lowest bit is upper bit of position>, <position - 1>
|
||||
|
||||
io.write("\x00") -- initiation character
|
||||
|
||||
local blockCache = {}
|
||||
local window = 0
|
||||
local blockUse = 128
|
||||
for i = 128, 128 + blockUse - 1 do
|
||||
blockCache[i] = ("\x00"):rep(512)
|
||||
end
|
||||
|
||||
local function runBlock(blk)
|
||||
-- firstly, get current block index
|
||||
local blockIndex = window + 128
|
||||
window = (window + 1) % blockUse
|
||||
blockCache[blockIndex] = ""
|
||||
-- ok, now work on the problem
|
||||
local i = 1
|
||||
while i <= #blk do
|
||||
local bestData = blk:sub(i, i)
|
||||
local bestRes = bestData
|
||||
local bestScore = 1
|
||||
for bid = 128, 128 + blockUse - 1 do
|
||||
for lm = 0, 127 do
|
||||
local al = lm + 3
|
||||
local pfx = blk:sub(i, i + al - 1)
|
||||
if #pfx ~= al then
|
||||
break
|
||||
end
|
||||
local p = blockCache[bid]:find(pfx, 1, true)
|
||||
if not p then
|
||||
break
|
||||
end
|
||||
local score = al / 3
|
||||
if score > bestScore then
|
||||
bestData = string.char(bid) .. string.char((lm * 2) + math.floor((p - 1) / 256)) .. string.char((p - 1) % 256)
|
||||
bestRes = pfx
|
||||
bestScore = score
|
||||
end
|
||||
end
|
||||
end
|
||||
-- ok, encode!
|
||||
io.write(bestData)
|
||||
blockCache[blockIndex] = blockCache[blockIndex] .. bestRes
|
||||
i = i + #bestRes
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
local blkd = io.read(512)
|
||||
runBlock(blkd)
|
||||
if #blkd < 512 then
|
||||
return
|
||||
end
|
||||
end
|
|
@ -1,69 +0,0 @@
|
|||
-- This is released into the public domain. XX
|
||||
-- No warranty is provided, implied or otherwise. XX
|
||||
local sector = io.write -- XX
|
||||
-- BUNDIVIDE reference implementation for integration XX
|
||||
local Cs,Cbu,Cb,Cw,Cp,Ct,Ci,CP,CB,CD={},128,"",128,"",""
|
||||
CP=function(d,b)
|
||||
Ct=Ct..d
|
||||
while#Ct>2 do
|
||||
b=Ct:byte()
|
||||
Ct=Ct:sub(2)
|
||||
if b==127then
|
||||
b=Ct:byte()
|
||||
Ct=Ct:sub(2)
|
||||
if b==127then
|
||||
b=Ct:byte()+254
|
||||
if b>255then
|
||||
b=b-256
|
||||
end
|
||||
Ct=Ct:sub(2)
|
||||
else
|
||||
b=b+127
|
||||
end
|
||||
end
|
||||
Cp=Cp..string.char(b)
|
||||
if #Cp==512then
|
||||
sector(Cp)
|
||||
Cp=""
|
||||
end
|
||||
end
|
||||
end
|
||||
for i=128,127+Cbu do Cs[i]=("\x00"):rep(512) end
|
||||
Cs[Cw]=""
|
||||
CB=function(d,i,d2,x,y)
|
||||
i=1
|
||||
while i<=#d-2 do
|
||||
b=d:byte(i)
|
||||
d2=d:sub(i,i)
|
||||
i=i+1
|
||||
if not Ci then
|
||||
if b==0then
|
||||
Ci=1
|
||||
end
|
||||
else
|
||||
if b>=128then
|
||||
x=d:byte(i)i=i+1
|
||||
y=(d:byte(i)+((x%2)*256))i=i+1
|
||||
d2=Cs[b]:sub(y+1,y+3+math.floor(x/2))
|
||||
end
|
||||
Cs[Cw]=Cs[Cw]..d2
|
||||
if #Cs[Cw]>=512then
|
||||
CP(Cs[Cw])
|
||||
Cw=((Cw-127)%Cbu)+128
|
||||
Cs[Cw]=""
|
||||
end
|
||||
end
|
||||
end
|
||||
return i
|
||||
end
|
||||
CD=function(d)
|
||||
Cb=Cb..d
|
||||
Cb=Cb:sub(CB(Cb))
|
||||
end
|
||||
CD(io.read("*a")) -- XX
|
||||
--D.remove("init-bdivide.lua")--
|
||||
--D.rename("init.lua","init-bdivide.lua")--
|
||||
--local Ch=D.open("init-bdivide.lua","rb")--
|
||||
--dieCB=function()D.close(Ch)D.remove("init-bdivide.lua")end--
|
||||
--while true do local t=D.read(Ch, 64)if not t then break end CD(t)end--
|
||||
CD("\x00\x00")CP(Cs[Cw] .. "\x00\x00")
|
|
@ -1,20 +0,0 @@
|
|||
-- PREPROC: preprocess input to be 7-bit
|
||||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
while true do
|
||||
local c = io.read(1)
|
||||
if not c then return end
|
||||
local bc = c:byte()
|
||||
if bc < 127 then
|
||||
io.write(c)
|
||||
else
|
||||
if bc <= 253 then
|
||||
-- 127(0) through 253(126)
|
||||
io.write("\x7F" .. string.char(bc - 127))
|
||||
else
|
||||
-- 254(0) through 255 (1)
|
||||
io.write("\x7F\x7F" .. string.char(bc - 254))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
-- KittenOS NEO Repository Compliance Check Tool
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- I'm still not a lawyer
|
||||
local filesAccountedFor = {
|
||||
["repository/data/app-claw/local.lua"] = 0,
|
||||
["repository/inst.lua"] = 0
|
||||
}
|
||||
local f = io.popen("find repository/docs/repoauthors -type f", "r")
|
||||
while true do
|
||||
local s = f:read()
|
||||
if not s then f:close() break end
|
||||
filesAccountedFor[s] = s
|
||||
local f2 = io.open(s, "r")
|
||||
while true do
|
||||
local s2 = f2:read()
|
||||
if not s2 then
|
||||
f2:close()
|
||||
break
|
||||
end
|
||||
local st = s2:match("^[^:]+")
|
||||
if st then
|
||||
filesAccountedFor[st] = s
|
||||
end
|
||||
end
|
||||
end
|
||||
f = io.popen("find repository -type f", "r")
|
||||
while true do
|
||||
local s = f:read()
|
||||
if not s then f:close() return end
|
||||
if not filesAccountedFor[s] then
|
||||
print("File wasn't accounted for: " .. s)
|
||||
end
|
||||
end
|
30
heroes.lua
30
heroes.lua
|
@ -1,30 +0,0 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
-- arg is the size of the code.tar file
|
||||
local arg = ...
|
||||
os.execute("lua com2/preproc.lua < code.tar | lua com2/bdivide.lua > com2/code.tar.bd")
|
||||
os.execute("cat insthead.lua")
|
||||
print("sC=" .. math.ceil(tonumber(arg) / 512))
|
||||
local src = io.open("com2/bundiv.lua", "r")
|
||||
while true do
|
||||
local line = src:read()
|
||||
if not line then
|
||||
src:close()
|
||||
io.write("--[[")
|
||||
io.flush()
|
||||
os.execute("cat com2/code.tar.bd")
|
||||
io.write("]]")
|
||||
return
|
||||
end
|
||||
local endix = line:sub(#line-1,#line)
|
||||
if endix ~= "XX" then
|
||||
if endix == "--" then
|
||||
-- included
|
||||
print(line:sub(3,#line-2))
|
||||
else
|
||||
print(line)
|
||||
end
|
||||
end
|
||||
-- XX means ignored.
|
||||
end
|
34
imitclaw.lua
34
imitclaw.lua
|
@ -1,34 +0,0 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
-- Imitation CLAW
|
||||
local done = {}
|
||||
|
||||
local f, e = loadfile("code/data/app-claw/local.lua")
|
||||
if not f then error(e) end
|
||||
f = f()
|
||||
if not os.execute("mkdir work") then
|
||||
error("Delete 'work'")
|
||||
end
|
||||
for k, v in pairs(f) do
|
||||
for _, vd in ipairs(v.dirs) do
|
||||
os.execute("mkdir work/" .. vd .. " 2> /dev/null")
|
||||
end
|
||||
for _, vf in ipairs(v.files) do
|
||||
-- not totally proofed but will do
|
||||
if not os.execute("cp code/" .. vf .. " work/" .. vf) then
|
||||
error("Could not copy " .. vf .. " in " .. k)
|
||||
end
|
||||
if done[vf] then
|
||||
error("duplicate " .. vf .. " in " .. k)
|
||||
end
|
||||
print(vf .. "\t\t" .. k)
|
||||
done[vf] = true
|
||||
end
|
||||
end
|
||||
os.execute("mkdir -p work/data/app-claw")
|
||||
os.execute("cp code/data/app-claw/local.lua work/data/app-claw/local.lua")
|
||||
os.execute("cd code ; find . > ../imitclaw.treecode")
|
||||
os.execute("cd work ; find . > ../imitclaw.treework")
|
||||
os.execute("diff -u imitclaw.treecode imitclaw.treework")
|
||||
os.execute("rm imitclaw.treecode imitclaw.treework")
|
|
@ -0,0 +1,40 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
local bga = {}
|
||||
|
||||
local str = io.read("*a")
|
||||
|
||||
for i = 1, #str - 1 do
|
||||
local bg = str:sub(i, i + 1)
|
||||
bga[bg] = (bga[bg] or 0) + 1
|
||||
end
|
||||
|
||||
local first = {}
|
||||
local second = {}
|
||||
|
||||
local mode = ...
|
||||
|
||||
for k, v in pairs(bga) do
|
||||
if mode == "combined" then
|
||||
print(string.format("%08i: %02x%02x : %s", v, k:byte(1), k:byte(2), k))
|
||||
end
|
||||
first[k:sub(1, 1)] = (first[k:sub(1, 1)] or 0) + v
|
||||
second[k:sub(1, 1)] = (second[k:sub(1, 1)] or 0) + v
|
||||
end
|
||||
|
||||
for k, v in pairs(first) do
|
||||
if mode == "first" then
|
||||
print(string.format("%08i: %s", v, k))
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(second) do
|
||||
if mode == "second" then
|
||||
print(string.format("%08i: %s", v, k))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,89 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- PREPROC (r9 edition): preprocess input to be 7-bit
|
||||
|
||||
local frw = require("libs.frw")
|
||||
|
||||
local
|
||||
-- SHARED WITH DECOMPRESSION ENGINE
|
||||
function p(x, y)
|
||||
if x == 126 then
|
||||
return string.char(y), 3
|
||||
elseif x == 127 then
|
||||
return string.char(128 + y), 3
|
||||
elseif x >= 32 then
|
||||
return string.char(x), 2
|
||||
elseif x == 31 then
|
||||
return "\n", 2
|
||||
elseif x == 30 then
|
||||
return "\x00", 2
|
||||
end
|
||||
return string.char(("enart"):byte(x % 5 + 1), ("ndtelh"):byte((x - x % 5) / 5 + 1)), 2
|
||||
end
|
||||
|
||||
local preprocParts = {}
|
||||
local preprocMaxLen = 0
|
||||
for i = 0, 127 do
|
||||
for j = 0, 127 do
|
||||
local d, l = p(i, j)
|
||||
if d then
|
||||
preprocMaxLen = math.max(preprocMaxLen, #d)
|
||||
l = l - 1
|
||||
if (not preprocParts[d]) or (#preprocParts[d] > l) then
|
||||
if l == 2 then
|
||||
preprocParts[d] = string.char(i, j)
|
||||
else
|
||||
preprocParts[d] = string.char(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function preprocWithPadding(blk, p)
|
||||
local out = ""
|
||||
local needsPadding = false
|
||||
while blk ~= "" do
|
||||
p(blk)
|
||||
local len = math.min(preprocMaxLen, #blk)
|
||||
while len > 0 do
|
||||
local seg = blk:sub(1, len)
|
||||
if preprocParts[seg] then
|
||||
out = out .. preprocParts[seg]
|
||||
needsPadding = #preprocParts[seg] < 2
|
||||
blk = blk:sub(#seg + 1)
|
||||
break
|
||||
end
|
||||
len = len - 1
|
||||
end
|
||||
assert(len ~= 0)
|
||||
end
|
||||
-- This needsPadding bit is just sort of quickly added in
|
||||
-- to keep this part properly maintained
|
||||
-- even though it might never get used
|
||||
if needsPadding then
|
||||
return out .. "\x00"
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
local bdCore = require("bdivide.core")
|
||||
|
||||
return function (data, lexCrunch)
|
||||
io.stderr:write("preproc: ")
|
||||
local pi = frw.progress()
|
||||
local function p(b)
|
||||
pi(1 - (#b / #data))
|
||||
end
|
||||
data = preprocWithPadding(data, p)
|
||||
io.stderr:write("\nbdivide: ")
|
||||
pi = frw.progress()
|
||||
data = bdCore.bdividePad(bdCore.bdivide(data, p))
|
||||
io.stderr:write("\n")
|
||||
return lexCrunch.process(frw.read("bdivide/instdeco.lua"), {}), data
|
||||
end
|
|
@ -0,0 +1,76 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- BDIVIDE r5 edition
|
||||
-- Algorithm simplified for smaller implementation and potentially better compression
|
||||
-- format:
|
||||
-- 0-127 for constants
|
||||
-- <128 + (length - 4)>, <position high>, <position low>
|
||||
-- Position is where in the window it was found, minus 1.
|
||||
-- windowSize must be the same between the encoder and decoder,
|
||||
-- and is the amount of data preserved after cropping.
|
||||
|
||||
local bdivCore = {}
|
||||
|
||||
function bdivCore.bdivide(blk, p)
|
||||
local out = ""
|
||||
|
||||
local windowSize = 0x10000
|
||||
local windowData = ("\x00"):rep(windowSize)
|
||||
|
||||
while blk ~= "" do
|
||||
p(blk)
|
||||
local bestData = blk:sub(1, 1)
|
||||
assert(blk:byte() < 128, "BDIVIDE does not handle 8-bit data")
|
||||
local bestRes = bestData
|
||||
for lm = 0, 127 do
|
||||
local al = lm + 4
|
||||
local pfx = blk:sub(1, al)
|
||||
if #pfx ~= al then
|
||||
break
|
||||
end
|
||||
local p = windowData:find(pfx, 1, true)
|
||||
if not p then
|
||||
break
|
||||
end
|
||||
local pm = p - 1
|
||||
local thirdByte = pm % 256
|
||||
bestData = string.char(128 + lm, math.floor(pm / 256), thirdByte)
|
||||
bestRes = pfx
|
||||
end
|
||||
-- ok, encode!
|
||||
out = out .. bestData
|
||||
-- crop window
|
||||
windowData = (windowData .. bestRes):sub(-windowSize)
|
||||
blk = blk:sub(#bestRes + 1)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
-- Adds padding if required
|
||||
function bdivCore.bdividePad(data)
|
||||
local i = 1
|
||||
-- Basically, if it ends on a literal,
|
||||
-- then the literal won't get read without two padding bytes.
|
||||
-- Otherwise (including if no data) it's fine.
|
||||
local needsPadding = false
|
||||
while i <= #data do
|
||||
if data:byte(i) > 127 then
|
||||
i = i + 3
|
||||
needsPadding = false
|
||||
else
|
||||
i = i + 1
|
||||
needsPadding = true
|
||||
end
|
||||
end
|
||||
if needsPadding then
|
||||
return data .. "\x00\x00"
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
return bdivCore
|
|
@ -0,0 +1,68 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- BDIVIDE (r5 edition) and PREPROC (r9 edition)
|
||||
-- decompression engine for installer
|
||||
|
||||
$bdPPBuffer = ""
|
||||
$bdBDBuffer = ""
|
||||
$bdBDWindow = ("\x00"):rep(2^16)
|
||||
-- High-level breakdown:
|
||||
-- q is unescaper.
|
||||
-- It'll only begin to input if at least 3 bytes are available,
|
||||
-- so you'll want to throw in 2 extra zeroes at the end of stream as done here.
|
||||
-- It uses t (input buffer) and p (output buffer).
|
||||
-- Ignore its second argument, as that's a lie, it's just there for a local.
|
||||
-- L is the actual decompressor. It has the same quirk as q, wanting two more bytes.
|
||||
-- It stores to c (compressed), and w (window).
|
||||
-- It outputs that which goes to the window to q also.
|
||||
-- And it also uses a fake local.
|
||||
|
||||
-- SEE compress.lua FOR THIS FUNCTION
|
||||
function $bdPP(x, y)
|
||||
if x == 126 then
|
||||
return string.char(y), 3
|
||||
elseif x == 127 then
|
||||
return string.char(128 + y), 3
|
||||
elseif x >= 32 then
|
||||
return string.char(x), 2
|
||||
elseif x == 31 then
|
||||
return "\n", 2
|
||||
elseif x == 30 then
|
||||
return "\x00", 2
|
||||
end
|
||||
return string.char(("enart"):byte(x % 5 + 1), ("ndtelh"):byte((x - x % 5) / 5 + 1)), 2
|
||||
end
|
||||
|
||||
${
|
||||
function $engineInput($L|lData)
|
||||
$bdBDBuffer = $bdBDBuffer .. $lData
|
||||
while #$bdBDBuffer > 2 do
|
||||
$lData = $bdBDBuffer:byte()
|
||||
if $lData < 128 then
|
||||
$lData = $bdBDBuffer:sub(1, 1)
|
||||
$bdBDBuffer = $bdBDBuffer:sub(2)
|
||||
else
|
||||
${
|
||||
$L|bdBDPtr = $bdBDBuffer:byte(2) * 256 + $bdBDBuffer:byte(3) + 1
|
||||
$lData = $bdBDWindow:sub($bdBDPtr, $bdBDPtr + $lData - 125)
|
||||
$bdBDBuffer = $bdBDBuffer:sub(4)
|
||||
$}
|
||||
end
|
||||
$bdPPBuffer = $bdPPBuffer .. $lData
|
||||
$bdBDWindow = ($bdBDWindow .. $lData):sub(-2^16)
|
||||
while #$bdPPBuffer > 1 do
|
||||
${
|
||||
$lData, $L|bdPPAdv = $bdPP($bdPPBuffer:byte(), $bdPPBuffer:byte(2))
|
||||
$bdPPBuffer = $bdPPBuffer:sub($bdPPAdv)
|
||||
$}
|
||||
$engineOutput($lData)
|
||||
end
|
||||
end
|
||||
end
|
||||
$}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
local frw = require("libs.frw")
|
||||
|
||||
local bdCore = require("bdivide.core")
|
||||
|
||||
return function (data, lexCrunch)
|
||||
io.stderr:write("\nbdivide: ")
|
||||
local pi = frw.progress()
|
||||
local function p(b)
|
||||
pi(1 - (#b / #data))
|
||||
end
|
||||
data = bdCore.bdividePad(bdCore.bdivide(data, p))
|
||||
io.stderr:write("\n")
|
||||
return lexCrunch.process(frw.read("bdvlite/instdeco.lua"), {}), data
|
||||
end
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- BDIVIDE r5 edition
|
||||
-- Algorithm simplified for smaller implementation and potentially better compression
|
||||
-- format:
|
||||
-- 0-127 for constants
|
||||
-- <128 + (length - 4)>, <position high>, <position low>
|
||||
-- Position is where in the window it was found, minus 1.
|
||||
-- windowSize must be the same between the encoder and decoder,
|
||||
-- and is the amount of data preserved after cropping.
|
||||
|
||||
local bdivCore = {}
|
||||
|
||||
function bdivCore.bdivide(blk, p)
|
||||
local out = ""
|
||||
|
||||
local windowSize = 0x10000
|
||||
local windowData = ("\x00"):rep(windowSize)
|
||||
|
||||
while blk ~= "" do
|
||||
p(blk)
|
||||
local bestData = blk:sub(1, 1)
|
||||
assert(blk:byte() < 128, "BDIVIDE does not handle 8-bit data")
|
||||
local bestRes = bestData
|
||||
for lm = 0, 127 do
|
||||
local al = lm + 4
|
||||
local pfx = blk:sub(1, al)
|
||||
if #pfx ~= al then
|
||||
break
|
||||
end
|
||||
local p = windowData:find(pfx, 1, true)
|
||||
if not p then
|
||||
break
|
||||
end
|
||||
local pm = p - 1
|
||||
local thirdByte = pm % 256
|
||||
bestData = string.char(128 + lm, math.floor(pm / 256), thirdByte)
|
||||
bestRes = pfx
|
||||
end
|
||||
-- ok, encode!
|
||||
out = out .. bestData
|
||||
-- crop window
|
||||
windowData = (windowData .. bestRes):sub(-windowSize)
|
||||
blk = blk:sub(#bestRes + 1)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
-- Adds padding if required
|
||||
function bdivCore.bdividePad(data)
|
||||
local i = 1
|
||||
-- Basically, if it ends on a literal,
|
||||
-- then the literal won't get read without two padding bytes.
|
||||
-- Otherwise (including if no data) it's fine.
|
||||
local needsPadding = false
|
||||
while i <= #data do
|
||||
if data:byte(i) > 127 then
|
||||
i = i + 3
|
||||
needsPadding = false
|
||||
else
|
||||
i = i + 1
|
||||
needsPadding = true
|
||||
end
|
||||
end
|
||||
if needsPadding then
|
||||
return data .. "\x00\x00"
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
return bdivCore
|
|
@ -0,0 +1,34 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- BDIVIDE (r5 edition)
|
||||
-- decompression engine used to decompress DEFLATE decompression engine
|
||||
|
||||
$bdBDBuffer = ""
|
||||
$bdBDWindow = ("\x00"):rep(2^16)
|
||||
|
||||
${
|
||||
function $engineInput($L|lData)
|
||||
$bdBDBuffer = $bdBDBuffer .. $lData
|
||||
while #$bdBDBuffer > 2 do
|
||||
$lData = $bdBDBuffer:byte()
|
||||
if $lData < 128 then
|
||||
$lData = $bdBDBuffer:sub(1, 1)
|
||||
$bdBDBuffer = $bdBDBuffer:sub(2)
|
||||
else
|
||||
${
|
||||
$L|bdBDPtr = $bdBDBuffer:byte(2) * 256 + $bdBDBuffer:byte(3) + 1
|
||||
$lData = $bdBDWindow:sub($bdBDPtr, $bdBDPtr + $lData - 125)
|
||||
$bdBDBuffer = $bdBDBuffer:sub(4)
|
||||
$}
|
||||
end
|
||||
$bdBDWindow = ($bdBDWindow .. $lData):sub(-2^16)
|
||||
$engineOutput($lData)
|
||||
end
|
||||
end
|
||||
$}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- KittenOS NEO Installer Generator --
|
||||
local args = {...}
|
||||
|
||||
local cid = args[1]
|
||||
local tarName = args[2]
|
||||
local algorithmsInReverseOrder = {}
|
||||
for i = 3, #args do
|
||||
table.insert(algorithmsInReverseOrder, 1, args[i])
|
||||
end
|
||||
|
||||
local u = require("libs.frw")
|
||||
|
||||
local instSize = 0
|
||||
local function put(data)
|
||||
io.write(data)
|
||||
instSize = instSize + #data
|
||||
end
|
||||
|
||||
-- Installer Lexcrunch Context --
|
||||
local lexCrunch = require("libs.lexcrunch")()
|
||||
|
||||
-- Installer Core --
|
||||
|
||||
-- installerFinalized:
|
||||
-- Stuff that's already finished and put at the end of RISM. Prepend to this.
|
||||
-- installerPayload / installerProgramLength:
|
||||
-- The next-outer chunk that hasn't been written to the end of RISM
|
||||
-- as the compression scheme (if one) has not been applied yet.
|
||||
-- Really, installerProgramLength is only necessary because of the innermost chunk,
|
||||
-- as that chunk has the TAR; additional data that's part of the same effective compression block,
|
||||
-- but requires the program length to avoid it.
|
||||
local installerPayload
|
||||
local installerProgramLength
|
||||
local installerFinalized = ""
|
||||
|
||||
do
|
||||
local tarData = u.read(tarName)
|
||||
local tarSectors = math.floor(#tarData / 512)
|
||||
local installerCore = lexCrunch.process(u.read("instcore.lua"), {["$$SECTORS"] = tostring(tarSectors)})
|
||||
installerPayload = installerCore .. tarData
|
||||
installerProgramLength = #installerCore
|
||||
end
|
||||
|
||||
-- Installer Compression --
|
||||
for _, v in ipairs(algorithmsInReverseOrder) do
|
||||
io.stderr:write("compressing (" .. v .. ")\n")
|
||||
local algImpl = require(v .. ".compress")
|
||||
local algEngine, algData = algImpl(installerPayload, lexCrunch)
|
||||
io.stderr:write("result: " .. #installerPayload .. " -> " .. #algData .. "\n")
|
||||
-- prepend the program length of the last section
|
||||
algEngine = lexCrunch.process("$iBlockingLen = " .. installerProgramLength .. " " .. algEngine, {})
|
||||
-- commit
|
||||
installerPayload = algEngine
|
||||
installerProgramLength = #installerPayload
|
||||
installerFinalized = algData .. installerFinalized
|
||||
end
|
||||
|
||||
-- Installer Final --
|
||||
|
||||
-- This is a special case, so the program length/payload/etc. business has to be repeated.
|
||||
put("--" .. cid .. "\n")
|
||||
put("--This is released into the public domain. No warranty is provided, implied or otherwise.\n")
|
||||
put(lexCrunch.process(u.read("insthead.lua"), {["$$CORESIZE"] = tostring(installerProgramLength)}))
|
||||
|
||||
local RISM = installerPayload .. installerFinalized
|
||||
RISM = RISM:gsub("\xFE", "\xFE\xFE")
|
||||
RISM = RISM:gsub("]]", "]\xFE]")
|
||||
RISM = "\x00" .. RISM
|
||||
put("--[[" .. RISM .. "]]")
|
||||
|
||||
-- Dumping debug info --
|
||||
local dbg = io.open("iSymTab", "wb")
|
||||
lexCrunch.dump(dbg)
|
||||
dbg:close()
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- This is a wrapper around (i.e. does not contain) Zopfli.
|
||||
local frw = require("libs.frw")
|
||||
|
||||
return function (data, lexCrunch)
|
||||
frw.write("tempData.bin", data)
|
||||
os.execute("zopfli --i1 --deflate -c tempData.bin > tempZopf.bin")
|
||||
local res = frw.read("tempZopf.bin")
|
||||
os.execute("rm tempData.bin tempZopf.bin")
|
||||
return lexCrunch.process(frw.read("deflate/instdeco.lua"), {}), res
|
||||
end
|
||||
|
|
@ -0,0 +1,291 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- THIS NEXT LINE IS CLEARLY AWFUL
|
||||
$bdBDWindow = nil
|
||||
|
||||
-- THE DEFLATE DECOMPRESSOR OF MADNESS --
|
||||
|
||||
-- Core I/O --
|
||||
|
||||
-- $dfAlignToByteRemaining is
|
||||
-- set to 8 in the outer engine
|
||||
|
||||
${
|
||||
function $dfGetIntField($L|lLen, $L|lVal)
|
||||
$lVal = 0
|
||||
for $L|lI = 0, $lLen - 1 do
|
||||
if coroutine.yield() then
|
||||
$lVal = $lVal + 2^$lI
|
||||
end
|
||||
end
|
||||
return $lVal
|
||||
end
|
||||
$}
|
||||
|
||||
-- Huffman Core --
|
||||
-- The approach here is based around 1-prefixed integers.
|
||||
-- These are stored in a flat table.
|
||||
-- So 0b1000 is the pattern 000.
|
||||
|
||||
${
|
||||
function $dfReadHuffmanSymbol($L|lTree, $L|lVal)
|
||||
$lVal = 1
|
||||
while not $lTree[$lVal] do
|
||||
$lVal = ($lVal * 2) + $dfGetIntField(1)
|
||||
end
|
||||
return $lTree[$lVal]
|
||||
end
|
||||
$}
|
||||
|
||||
${
|
||||
function $dfGenHuffmanTree($L|lCodeLens)
|
||||
-- $L|lI (used everywhere; defining inside creates a bug because it gets globalized)
|
||||
$L|lNextCode = {}
|
||||
${
|
||||
-- To explain:
|
||||
-- lNextCode is needed all the way to the end.
|
||||
-- But lBLCount isn't needed after it's used to
|
||||
-- generate lNextCode.
|
||||
-- And lCode is very, very temporary.
|
||||
-- Hence this massive block.
|
||||
$L|lBLCount = {}
|
||||
-- Note the 0
|
||||
for $lI = 0, 15 do
|
||||
$lBLCount[$lI] = 0
|
||||
end
|
||||
for $lI = 0, #$lCodeLens do
|
||||
${
|
||||
$L|lCodeLen = $lCodeLens[$lI]
|
||||
if $lCodeLen ~= 0 then
|
||||
$lBLCount[$lCodeLen] = $lBLCount[$lCodeLen] + 1
|
||||
end
|
||||
$}
|
||||
end
|
||||
|
||||
$L|lCode = 0
|
||||
for $lI = 1, 15 do
|
||||
$lCode = ($lCode + $lBLCount[$lI - 1]) * 2
|
||||
$lNextCode[$lI] = $lCode
|
||||
end
|
||||
$}
|
||||
|
||||
$L|lTree = {}
|
||||
for $lI = 0, #$lCodeLens do
|
||||
${
|
||||
$L|lCodeLen = $lCodeLens[$lI]
|
||||
if $lCodeLen ~= 0 then
|
||||
$L|lPow = math.floor(2 ^ $lCodeLen)
|
||||
assert($lNextCode[$lCodeLen] < $lPow, "Tl" .. $lCodeLen)
|
||||
$L|lK = $lNextCode[$lCodeLen] + $lPow
|
||||
assert(not $lTree[$lK], "conflict @ " .. $lK)
|
||||
$lTree[$lK] = $lI
|
||||
$lNextCode[$lCodeLen] = $lNextCode[$lCodeLen] + 1
|
||||
end
|
||||
$}
|
||||
end
|
||||
return $lTree
|
||||
end
|
||||
$}
|
||||
|
||||
-- DEFLATE fixed trees --
|
||||
${
|
||||
$L|dfFixedTL = {}
|
||||
for $L|lI = 0, 143 do $dfFixedTL[$lI] = 8 end
|
||||
for $lI = 144, 255 do $dfFixedTL[$lI] = 9 end
|
||||
for $lI = 256, 279 do $dfFixedTL[$lI] = 7 end
|
||||
for $lI = 280, 287 do $dfFixedTL[$lI] = 8 end
|
||||
$dfFixedLit = $dfGenHuffmanTree($dfFixedTL)
|
||||
-- last line possibly destroyed dfFixedTL, but that's alright
|
||||
$dfFixedTL = {}
|
||||
for $lI = 0, 31 do $dfFixedTL[$lI] = 5 end
|
||||
$dfFixedDst = $dfGenHuffmanTree($dfFixedTL)
|
||||
$}
|
||||
|
||||
-- DEFLATE LZ Core --
|
||||
|
||||
$dfWindow = ("\x00"):rep(2^15)
|
||||
$dfPushBuf = ""
|
||||
${
|
||||
function $dfOutput($L|lData)
|
||||
$dfWindow = ($dfWindow .. $lData):sub(-2^15)
|
||||
$dfPushBuf = $dfPushBuf .. $lData
|
||||
end
|
||||
$}
|
||||
|
||||
$dfBittblLength = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 4, 4, 4, 4,
|
||||
5, 5, 5, 5, 0
|
||||
}
|
||||
$dfBasetblLength = {
|
||||
3, 4, 5, 6, 7, 8, 9, 10,
|
||||
11, 13, 15, 17, 19, 23, 27, 31,
|
||||
35, 43, 51, 59, 67, 83, 99, 115,
|
||||
131, 163, 195, 227, 258
|
||||
}
|
||||
$dfBittblDist = {
|
||||
0, 0, 0, 0, 1, 1, 2, 2,
|
||||
3, 3, 4, 4, 5, 5, 6, 6,
|
||||
7, 7, 8, 8, 9, 9, 10, 10,
|
||||
11, 11, 12, 12, 13, 13
|
||||
}
|
||||
$dfBasetblDist = {
|
||||
1, 2, 3, 4, 5, 7, 9, 13,
|
||||
17, 25, 33, 49, 65, 97, 129, 193,
|
||||
257, 385, 513, 769, 1025, 1537, 2049, 3073,
|
||||
4097, 6145, 8193, 12289, 16385, 24577
|
||||
}
|
||||
|
||||
${
|
||||
function $dfReadBlockBody($L|lLit, $L|lDst, $L|lLitSym, $L|lLen, $L|lDCode, $L|lPtr)
|
||||
while true do
|
||||
$lLitSym = $dfReadHuffmanSymbol($lLit)
|
||||
if $lLitSym <= 255 then
|
||||
$dfOutput(string.char($lLitSym))
|
||||
elseif $lLitSym == 256 then
|
||||
return
|
||||
elseif $lLitSym <= 285 then
|
||||
$lLen = $dfBasetblLength[$lLitSym - 256] + $dfGetIntField($dfBittblLength[$lLitSym - 256])
|
||||
$lDCode = $dfReadHuffmanSymbol($lDst)
|
||||
$lPtr = 32769 - ($dfBasetblDist[$lDCode + 1] + $dfGetIntField($dfBittblDist[$lDCode + 1]))
|
||||
for $L|lI = 1, $lLen do
|
||||
$dfOutput($dfWindow:sub($lPtr, $lPtr))
|
||||
end
|
||||
else
|
||||
error("nt" .. v)
|
||||
end
|
||||
end
|
||||
end
|
||||
$}
|
||||
|
||||
-- Huffman Dynamics --
|
||||
|
||||
${
|
||||
function $dfReadHuffmanDynamicSubcodes($L|lDistLens, $L|lCount, $L|lMetatree, $L|lCode)
|
||||
-- used a lot: $L|lI
|
||||
$lCode = 0
|
||||
$lDistLens[-1] = 0
|
||||
while $lCode < $lCount do
|
||||
-- use a tmpglb since it's just for the chain
|
||||
$L|lInstr = $dfReadHuffmanSymbol($lMetatree)
|
||||
if $lInstr < 16 then
|
||||
$lDistLens[$lCode] = $lInstr
|
||||
$lCode = $lCode + 1
|
||||
elseif $lInstr == 16 then
|
||||
for $lI = 1, 3 + $dfGetIntField(2) do
|
||||
$lDistLens[$lCode] = $lDistLens[$lCode - 1]
|
||||
$lCode = $lCode + 1
|
||||
assert($lCode <= $lCount, "sc.over")
|
||||
end
|
||||
elseif $lInstr == 17 then
|
||||
for $lI = 1, 3 + $dfGetIntField(3) do
|
||||
$lDistLens[$lCode] = 0
|
||||
$lCode = $lCode + 1
|
||||
assert($lCode <= $lCount, "sc.over")
|
||||
end
|
||||
elseif $lInstr == 18 then
|
||||
for $lI = 1, 11 + $dfGetIntField(7) do
|
||||
$lDistLens[$lCode] = 0
|
||||
$lCode = $lCode + 1
|
||||
assert($lCode <= $lCount, "sc.over")
|
||||
end
|
||||
else
|
||||
-- is this even possible?
|
||||
error("sc.unki")
|
||||
end
|
||||
end
|
||||
$lDistLens[-1] = nil
|
||||
return $lDistLens
|
||||
end
|
||||
$}
|
||||
|
||||
$dfDynamicMetalensScramble = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
|
||||
|
||||
${
|
||||
function $dfReadHuffmanDynamic($L|lState, $L|lLiteralSize, $L|lDistanceSize, $L|lDistanceLens)
|
||||
-- $L|lI loop variable, used all over
|
||||
-- to save on locals, this is reused
|
||||
-- as metalens
|
||||
$lState = {}
|
||||
for $lI = 0, 18 do $lState[$lI] = 0 end
|
||||
$lLiteralSize = $dfGetIntField(5) + 257
|
||||
$lDistanceSize = $dfGetIntField(5) + 1
|
||||
for $lI = 1, $dfGetIntField(4) + 4 do
|
||||
$lState[$dfDynamicMetalensScramble[$lI]] = $dfGetIntField(3)
|
||||
end
|
||||
-- as metatree
|
||||
$lState = $dfGenHuffmanTree($lState)
|
||||
-- as concatenated subcodes
|
||||
$lState = $dfReadHuffmanDynamicSubcodes({}, $lLiteralSize + $lDistanceSize, $lState)
|
||||
-- The distance lengths are removed from lState,
|
||||
-- while being added to lDistanceLens
|
||||
-- The result is completion
|
||||
$lDistanceLens = {}
|
||||
for $lI = 0, $lDistanceSize - 1 do
|
||||
$lDistanceLens[$lI] = $lState[$lLiteralSize + $lI]
|
||||
$lState[$lLiteralSize + $lI] = nil
|
||||
end
|
||||
return $dfGenHuffmanTree($lState), $dfGenHuffmanTree($lDistanceLens)
|
||||
end
|
||||
$}
|
||||
|
||||
-- Main Thread --
|
||||
|
||||
${
|
||||
$dfThread = coroutine.create(function ($L|lFinal, $L|lLitLen)
|
||||
while true do
|
||||
$lFinal = coroutine.yield()
|
||||
$L|lBlockType = $dfGetIntField(2)
|
||||
if $lBlockType == 0 then
|
||||
-- literal
|
||||
$dfGetIntField($dfAlignToByteRemaining)
|
||||
$lLitLen = $dfGetIntField(16)
|
||||
-- this is weird, ignore it
|
||||
$dfGetIntField(16)
|
||||
for $L|lI = 1, $lLitLen do
|
||||
$dfOutput(string.char($dfGetIntField(8)))
|
||||
end
|
||||
elseif $lBlockType == 1 then
|
||||
-- fixed Huffman
|
||||
$dfReadBlockBody($dfFixedLit, $dfFixedDst)
|
||||
elseif $lBlockType == 2 then
|
||||
-- dynamic Huffman
|
||||
$dfReadBlockBody($dfReadHuffmanDynamic())
|
||||
else
|
||||
error("b3")
|
||||
end
|
||||
while $lFinal do
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
end)
|
||||
$}
|
||||
|
||||
-- The Outer Engine --
|
||||
|
||||
coroutine.resume($dfThread)
|
||||
${
|
||||
function $engineInput($L|lData, $L|lByte)
|
||||
for $L|lI = 1, #$lData do
|
||||
$lByte = $lData:byte($lI)
|
||||
$dfAlignToByteRemaining = 8
|
||||
while $dfAlignToByteRemaining > 0 do
|
||||
-- If we're providing the first bit (v = 8), then there are 7 bits remaining.
|
||||
-- So this hits 0 when the *next* 8 yields will provide an as-is byte.
|
||||
$dfAlignToByteRemaining = $dfAlignToByteRemaining - 1
|
||||
assert(coroutine.resume($dfThread, $lByte % 2 == 1))
|
||||
$lByte = math.floor($lByte / 2)
|
||||
end
|
||||
end
|
||||
-- flush prepared buffer
|
||||
$engineOutput($dfPushBuf)
|
||||
$dfPushBuf = ""
|
||||
end
|
||||
$}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
-- KOSNEO installer base
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
$icScreen = $component.list("screen", true)()
|
||||
$icGPU = $component.list("gpu", true)()
|
||||
|
||||
$icFilename = "Starting..."
|
||||
$icBytesRemaining = 0
|
||||
|
||||
if $icScreen and $icGPU then
|
||||
$icGPU = $component.proxy($icGPU)
|
||||
$icGPU.bind($icScreen)
|
||||
$icGPU.setResolution(50, 5)
|
||||
$icGPU.setBackground(2^24-1)
|
||||
$icGPU.setForeground(0)
|
||||
$icGPU.fill(1, 1, 50, 5, "█")
|
||||
$icGPU.fill(1, 2, 50, 1, " ")
|
||||
$icGPU.set(2, 2, "KittenOS NEO Installer")
|
||||
end
|
||||
|
||||
function $icOctalToNumber($a0)
|
||||
if $a0 == "" then return 0 end
|
||||
return $icOctalToNumber($a0:sub(1, -2)) * 8 + ($a0:byte(#$a0) - 48)
|
||||
end
|
||||
|
||||
$icSectorsRead = 0
|
||||
$iBlockingLen = 512
|
||||
function $iBlockingHook($a0)
|
||||
if $icBytesRemaining > 0 then
|
||||
${
|
||||
$L|icByteAdv = math.min(512, $icBytesRemaining)
|
||||
$icBytesRemaining = $icBytesRemaining - $icByteAdv
|
||||
if $icFile then
|
||||
$filesystem.write($icFile, $a0:sub(1, $icByteAdv))
|
||||
if $icBytesRemaining <= 0 then
|
||||
$filesystem.close($icFile)
|
||||
$icFile = nil
|
||||
end
|
||||
end
|
||||
$}
|
||||
else
|
||||
$icFilename = $a0:sub(1, 100):gsub("\x00", "")
|
||||
-- this sets up the reading/skipping of data
|
||||
$icBytesRemaining = $icOctalToNumber($a0:sub(125, 135))
|
||||
if $icFilename:sub(1, 2) == "./" and $icFilename ~= "./" then
|
||||
$icFilename = $icFilename:sub(3)
|
||||
if $icFilename:sub(#$icFilename) == "/" then
|
||||
$filesystem.makeDirectory($icFilename)
|
||||
else
|
||||
$icFile = $filesystem.open($icFilename, "wb")
|
||||
if $icBytesRemaining == 0 then
|
||||
$filesystem.close($icFile)
|
||||
$icFile = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- UPDATE DISPLAY --
|
||||
$icSectorsRead = $icSectorsRead + 1
|
||||
if $icScreen and $icGPU then
|
||||
$icGPU.fill(1, 2, 50, 1, " ")
|
||||
$icGPU.set(2, 2, "KittenOS NEO Installer : " .. $icFilename)
|
||||
$icGPU.fill(2, 4, 48, 1, "█")
|
||||
$icGPU.fill(2, 4, math.ceil(48 * $icSectorsRead / $$SECTORS), 1, " ")
|
||||
end
|
||||
if $icSectorsRead % 16 == 0 then
|
||||
$computer.pullSignal(0.01)
|
||||
end
|
||||
if $icSectorsRead == $$SECTORS then
|
||||
$filesystem.close($readInFile)
|
||||
$filesystem.remove("init.neoi.lua")
|
||||
$computer.shutdown(true)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
-- KOSNEO installer base
|
||||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
$computer = computer
|
||||
$component = component
|
||||
assert($component, "KittenOS NEO installer: Copy as init.lua to the target disk, then remove other disks & reboot.")
|
||||
|
||||
$filesystem = $component.proxy($computer.getBootAddress())
|
||||
|
||||
$filesystem.remove("init.neoi.lua")
|
||||
$filesystem.rename("init.lua", "init.neoi.lua")
|
||||
$readInFile = $filesystem.open("init.neoi.lua", "rb")
|
||||
|
||||
$iBlockingBuffer = ""
|
||||
$iBlockingLen = $$CORESIZE
|
||||
${
|
||||
function $iBlockingHook($L|lBlock)
|
||||
-- Run the next script (replacement compression engine,)
|
||||
assert(load($lBlock))()
|
||||
end
|
||||
$}
|
||||
|
||||
${
|
||||
function $engineOutput($L|lBlock)
|
||||
$iBlockingBuffer = $iBlockingBuffer .. $lBlock
|
||||
while #$iBlockingBuffer >= $iBlockingLen do
|
||||
$lBlock = $iBlockingBuffer:sub(1, $iBlockingLen)
|
||||
$iBlockingBuffer = $iBlockingBuffer:sub($iBlockingLen + 1)
|
||||
$iBlockingHook($lBlock)
|
||||
end
|
||||
end
|
||||
$}
|
||||
$engineInput = $engineOutput
|
||||
|
||||
while true do
|
||||
$readInBlock = $filesystem.read($readInFile, 1024)
|
||||
${
|
||||
for i = 1, #$readInBlock do
|
||||
-- Read-in state machine
|
||||
|
||||
-- IT IS VERY IMPORTANT that read-in be performed char-by-char.
|
||||
-- This is because of compression chain-loading; if the switch between engines isn't "clean",
|
||||
-- bad stuff happens.
|
||||
|
||||
-- This character becomes invalid once
|
||||
-- it gets passed to engineInput,
|
||||
-- but that's the last step, so it's ok!
|
||||
$L|readInChar = $readInBlock:sub(i, i)
|
||||
if not $readInState then
|
||||
if $readInChar == "\x00" then
|
||||
$readInState = 0
|
||||
end
|
||||
elseif $readInState == 0 then
|
||||
if $readInChar == "\xFE" then
|
||||
$readInState = 1
|
||||
else
|
||||
$engineInput($readInChar)
|
||||
end
|
||||
else
|
||||
$engineInput($readInChar)
|
||||
$readInState = 0
|
||||
end
|
||||
end
|
||||
$}
|
||||
end
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
return {
|
||||
read = function (fn)
|
||||
local f = io.open(fn, "rb")
|
||||
local d = f:read("*a")
|
||||
f:close()
|
||||
return d
|
||||
end,
|
||||
write = function (fn, data)
|
||||
local f = io.open(fn, "wb")
|
||||
f:write(data)
|
||||
f:close()
|
||||
end,
|
||||
progress = function ()
|
||||
io.stderr:write("00% \\")
|
||||
local lastPercent = 0
|
||||
local chr = 0
|
||||
return function (fraction)
|
||||
local percent = math.ceil(fraction * 100)
|
||||
if percent ~= lastPercent then
|
||||
lastPercent = percent
|
||||
chr = (chr + 1) % 4
|
||||
io.stderr:write(string.format("\8\8\8\8\8%02i%% %s", percent, ("\\|/-"):sub(chr + 1, chr + 1)))
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- This library helps in crunching down the installer a bit further.
|
||||
local sequences = {
|
||||
{"\n", " "},
|
||||
{" ", " "},
|
||||
{" #", "#"},
|
||||
{"# ", "#"},
|
||||
{" ,", ","},
|
||||
{", ", ","},
|
||||
{" (", "("},
|
||||
{"( ", "("},
|
||||
{" )", ")"},
|
||||
{") ", ")"},
|
||||
{" {", "{"},
|
||||
{"{ ", "{"},
|
||||
{" }", "}"},
|
||||
{"} ", "}"},
|
||||
{" <", "<"},
|
||||
{"< ", "<"},
|
||||
{" >", ">"},
|
||||
{"> ", ">"},
|
||||
{" *", "*"},
|
||||
{"* ", "*"},
|
||||
{" ~", "~"},
|
||||
{"~ ", "~"},
|
||||
{" /", "/"},
|
||||
{"/ ", "/"},
|
||||
{" %", "%"},
|
||||
{"% ", "%"},
|
||||
{" =", "="},
|
||||
{"= ", "="},
|
||||
{" -", "-"},
|
||||
{"- ", "-"},
|
||||
{" +", "+"},
|
||||
{"+ ", "+"},
|
||||
{".. ", ".."},
|
||||
{" ..", ".."},
|
||||
{"\"\" ", "\"\""},
|
||||
{"=0 t", "=0t"},
|
||||
{">0 t", ">0t"},
|
||||
{">1 t", ">1t"},
|
||||
{"=1 w", "=1w"},
|
||||
{"=380 l", "=380l"},
|
||||
{"=127 t", "=127t"},
|
||||
{"<128 t", "<128t"},
|
||||
{"=128 t", "=128t"},
|
||||
{">255 t", ">255t"},
|
||||
{"=512 t", "=512t"}
|
||||
}
|
||||
|
||||
local function pass(buffer)
|
||||
local ob = ""
|
||||
local smode = false
|
||||
while #buffer > 0 do
|
||||
if not smode then
|
||||
local ds = true
|
||||
while ds do
|
||||
ds = false
|
||||
for _, v in ipairs(sequences) do
|
||||
if buffer:sub(1, #(v[1])) == v[1] then
|
||||
buffer = v[2] .. buffer:sub(#(v[1]) + 1)
|
||||
ds = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local ch = buffer:sub(1, 1)
|
||||
buffer = buffer:sub(2)
|
||||
ob = ob .. ch
|
||||
if ch == "\"" then
|
||||
smode = not smode
|
||||
end
|
||||
end
|
||||
return ob
|
||||
end
|
||||
|
||||
-- Context creation --
|
||||
return function ()
|
||||
local forwardSymTab = {}
|
||||
local reverseSymTab = {}
|
||||
|
||||
local temporaryPool = {}
|
||||
|
||||
local stackFrames = {}
|
||||
|
||||
local log = {}
|
||||
|
||||
local possible = {}
|
||||
for i = 1, 52 do
|
||||
possible[i] = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"):sub(i, i)
|
||||
end
|
||||
|
||||
local function allocate(reason)
|
||||
for _, v in pairs(possible) do
|
||||
if not reverseSymTab[v] then
|
||||
reverseSymTab[v] = reason
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function allocTmp(id)
|
||||
assert(not forwardSymTab[id], "var already exists: " .. id)
|
||||
local val = table.remove(temporaryPool, 1)
|
||||
if not val then val = allocate("temporary") end
|
||||
forwardSymTab[id] = val
|
||||
table.insert(log, "allocTmp " .. id .. " -> " .. val)
|
||||
return val
|
||||
end
|
||||
|
||||
local lexCrunch = {}
|
||||
function lexCrunch.dump(file)
|
||||
file:write("forward table:\n")
|
||||
for k, v in pairs(forwardSymTab) do
|
||||
file:write(k .. " -> " .. v .. "\n")
|
||||
end
|
||||
file:write("reverse table (where differing):\n")
|
||||
for k, v in pairs(reverseSymTab) do
|
||||
if forwardSymTab[v] ~= k then
|
||||
file:write(v .. " -> " .. k .. "\n")
|
||||
end
|
||||
end
|
||||
file:write("log:\n")
|
||||
for k, v in ipairs(log) do
|
||||
file:write(v .. "\n")
|
||||
end
|
||||
end
|
||||
function lexCrunch.process(op, defines)
|
||||
-- symbol replacement
|
||||
op = op:gsub("%$[%$a-z%{%}%|A-Z0-9]*", function (str)
|
||||
if str:sub(2, 2) == "$" then
|
||||
-- defines
|
||||
assert(defines[str], "no define " .. str)
|
||||
return defines[str]
|
||||
end
|
||||
local com = {}
|
||||
for v in str:sub(2):gmatch("[^%|]*") do
|
||||
table.insert(com, v)
|
||||
end
|
||||
if com[1] == "L" then
|
||||
assert(#com == 2)
|
||||
local id = "$" .. com[2]
|
||||
assert(stackFrames[1], "allocation of " .. id .. " outside of stack frame")
|
||||
table.insert(stackFrames[1], id)
|
||||
return allocTmp(id)
|
||||
elseif com[1] == "{" then
|
||||
assert(#com == 1)
|
||||
table.insert(stackFrames, 1, {})
|
||||
return ""
|
||||
elseif com[1] == "}" then
|
||||
assert(#com == 1)
|
||||
for _, id in ipairs(table.remove(stackFrames, 1)) do
|
||||
table.insert(temporaryPool, forwardSymTab[id])
|
||||
forwardSymTab[id] = nil
|
||||
end
|
||||
return ""
|
||||
else
|
||||
assert(#com == 1)
|
||||
local id = "$" .. com[1]
|
||||
-- normal handling
|
||||
if forwardSymTab[id] then
|
||||
return forwardSymTab[id]
|
||||
end
|
||||
local v = allocate(id)
|
||||
forwardSymTab[id] = v
|
||||
return v
|
||||
end
|
||||
end)
|
||||
-- comment removal
|
||||
while true do
|
||||
local np = op:gsub("%-%-[^\n]*\n", " ")
|
||||
np = np:gsub("%-%-[^\n]*$", "")
|
||||
if np == op then
|
||||
break
|
||||
end
|
||||
op = np
|
||||
end
|
||||
-- stripping
|
||||
while true do
|
||||
local np = pass(op)
|
||||
if np == op then
|
||||
return np
|
||||
end
|
||||
op = np
|
||||
end
|
||||
return op
|
||||
end
|
||||
return lexCrunch
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- Status Screen --
|
||||
local target = ...
|
||||
local u = require("libs.frw")
|
||||
local instSize = #u.read(target)
|
||||
|
||||
local status = ""
|
||||
local statusDetail = ""
|
||||
local blinkI = ""
|
||||
if instSize > 65536 then
|
||||
blinkI = "5;31;"
|
||||
status = " DO NOT SHIP "
|
||||
statusDetail = "The installer is too big to ship safely.\nIt's possible it may crash on Tier 1 systems.\nUpgrade the compression system or remove existing code."
|
||||
elseif instSize > 64000 then
|
||||
blinkI = "33;"
|
||||
status = " Shippable * "
|
||||
statusDetail = "The installer is getting dangerously large.\nReserve further room for bugfixes."
|
||||
else
|
||||
blinkI = "32;"
|
||||
status = " All Green "
|
||||
statusDetail = "The installer is well within budget with room for features.\nDevelop as normal."
|
||||
end
|
||||
io.stderr:write("\n")
|
||||
local ctS, ctM, ctE = " \x1b[1;" .. blinkI .. "7m", "\x1b[0;7m", "\x1b[0m\n"
|
||||
io.stderr:write(ctS .. " " .. ctM .. " " .. ctE)
|
||||
io.stderr:write(ctS .. status .. ctM .. string.format(" %07i ", 65536 - instSize) .. ctE)
|
||||
io.stderr:write(ctS .. " " .. ctM .. " " .. ctE)
|
||||
io.stderr:write("\n")
|
||||
io.stderr:write(statusDetail .. "\n")
|
||||
io.stderr:write("\n")
|
||||
io.stderr:write("Size: " .. instSize .. "\n")
|
||||
io.stderr:write(" max. 65536\n")
|
||||
io.stderr:write("\n")
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# The Symbol Guide
|
||||
|
||||
## lexCrunch commands
|
||||
|
||||
The following prefixes are really special,
|
||||
and are lexcrunch's responsibility:
|
||||
|
||||
"$$THING" : These are defines.
|
||||
"$Thing" : Writes a global into stream. If not already allocated, is allocated a global.
|
||||
|
||||
"${" : Opens a frame.
|
||||
"$}" : Closes a frame. (Attached temps are released.)
|
||||
"$L|THING" : Allocates THING from temp pool, attaches to stack frame, writes to stream.
|
||||
Use inside a comment to erase the written symbol
|
||||
|
||||
## Conventions
|
||||
|
||||
The rest are convention:
|
||||
"$iThing" symbols are Installer Wrapper.
|
||||
"$icThing" symbols are Installer Core.
|
||||
"$dfThing" symbols are DEFLATE Engine.
|
||||
"$bdThing" symbols are BDIVIDE Engine.
|
||||
|
||||
"$a0", "$a1", etc. are Local Symbols.
|
||||
DEPRECATED, THESE ARE AN OLD MECHANISM, USE FRAMED TEMPS INSTEAD.
|
||||
These are reserved only for use in locals.
|
||||
(For loops count.)
|
||||
|
||||
"$lThing" symbols are used to name Local Symbols using aliases.
|
||||
|
||||
NO THEY ARE NOW USED FOR ALL TEMPS, INCLUDING LOCAL SYMBOLS
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
-- THIS SOFTWARE.
|
||||
|
||||
-- Example compression engine.
|
||||
-- Given: data, lexCrunch
|
||||
-- returns compressionEngine, compressedData
|
||||
return function (data, lexCrunch)
|
||||
return lexCrunch.process(" $engineInput = $engineOutput ", {}), data
|
||||
end
|
||||
|
103
insthead.lua
103
insthead.lua
|
@ -1,103 +0,0 @@
|
|||
-- KOSNEO inst.
|
||||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
local C, O, G, D = component, computer
|
||||
local sa = C.list("screen", true)()
|
||||
if sa then
|
||||
G = C.list("gpu", true)()
|
||||
if G then
|
||||
G = C.proxy(G)
|
||||
G.bind(sa)
|
||||
G.setForeground(0xFFFFFF)
|
||||
G.setBackground(0x000000)
|
||||
G.setResolution(50, 5)
|
||||
G.setDepth(1)
|
||||
G.fill(1, 1, 50, 5, " ")
|
||||
G.setBackground(0xFFFFFF)
|
||||
G.setForeground(0x000000)
|
||||
G.fill(1, 2, 50, 1, " ")
|
||||
G.set(2, 2, "KittenOS NEO Installer")
|
||||
end
|
||||
end
|
||||
|
||||
D = C.proxy(O.getBootAddress())
|
||||
|
||||
local tF = nil
|
||||
local tFN = "Starting..."
|
||||
local tFSR = 0
|
||||
local tW = 0
|
||||
|
||||
local convoct
|
||||
convoct = function (oct)
|
||||
local v = oct:byte(#oct) - 0x30
|
||||
if #oct > 1 then
|
||||
return (convoct(oct:sub(1, #oct - 1)) * 8) + v
|
||||
end
|
||||
return v
|
||||
end
|
||||
local function tA(s)
|
||||
if tW > 0 then
|
||||
tW = tW - 1
|
||||
return
|
||||
end
|
||||
if tF then
|
||||
local ta = math.min(512, tFSR)
|
||||
D.write(tF, s:sub(1, ta))
|
||||
tFSR = tFSR - ta
|
||||
if tFSR == 0 then
|
||||
D.close(tF)
|
||||
tF = nil
|
||||
end
|
||||
else
|
||||
tFN = s:sub(1, 100):gsub("\x00", "")
|
||||
local sz = convoct(s:sub(125, 135))
|
||||
if tFN:sub(1, 5) ~= "code/" then
|
||||
tW = math.ceil(sz / 512)
|
||||
else
|
||||
tFN = tFN:sub(6)
|
||||
if tFN == "" then
|
||||
return
|
||||
end
|
||||
if tFN:sub(#tFN) == "/" then
|
||||
tW = math.ceil(sz / 512)
|
||||
D.makeDirectory(tFN)
|
||||
else
|
||||
tF = D.open(tFN, "wb")
|
||||
tFSR = sz
|
||||
if tFSR == 0 then
|
||||
D.close(tF)
|
||||
tF = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dieCB = function () end
|
||||
|
||||
local sN = 0
|
||||
local sC = 0
|
||||
|
||||
local function sector(n)
|
||||
tA(n)
|
||||
sN = sN + 1
|
||||
if G then
|
||||
local a = sN / sC
|
||||
G.fill(1, 2, 50, 1, " ")
|
||||
G.set(2, 2, "KittenOS NEO Installer : " .. tFN)
|
||||
G.setForeground(0xFFFFFF)
|
||||
G.setBackground(0x000000)
|
||||
G.fill(2, 4, 48, 1, " ")
|
||||
G.setBackground(0xFFFFFF)
|
||||
G.setForeground(0x000000)
|
||||
G.fill(2, 4, math.ceil(48 * a), 1, " ")
|
||||
end
|
||||
if sN % 8 == 0 then
|
||||
O.pullSignal(0.05)
|
||||
end
|
||||
if sN == sC then
|
||||
dieCB()
|
||||
O.shutdown(true)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
["components"]={
|
||||
{"screen","67c66973-51ff-4aec-29cd-baabf2fbe346",},
|
||||
{"gpu","9b7fe47d-77d0-1623-0dc5-126752e3f2c7",44800,},
|
||||
{"eeprom","94a9cc26-347e-cc46-7faf-0c131bb7d8b7",4096,256,"EEPROM",},
|
||||
{"computer","36bc34ad-8c4a-d19a-10e3-0162c6f3295a",1.04858e+06,},
|
||||
{"filesystem","tmpfs",true,"tmpfs",},
|
||||
{"filesystem","c1-sda",},
|
||||
{"filesystem","c1-sdb",},
|
||||
{"keyboard","dd142450-613c-435d-6049-743b27434cc1","67c66973-51ff-4aec-29cd-baabf2fbe346",},
|
||||
{"modem","460ae893-9aa9-f10d-6bb0-462199da7376",56000,8192,8,},
|
||||
{"internet","ef97c750-d30a-ad33-5321-6e7a64ba3baa",true,true,},
|
||||
{"data","c5243e5f-cd2f-6c39-dfb2-5a788dcdef7c",["tier"]=1,},
|
||||
{"sandbox","ece7b9ba-1625-f3f6-d74d-6e641a5de07f",},
|
||||
},
|
||||
["system"]={["allowBytecode"]=false,["allowGC"]=false,["maxTcpConnections"]=4,["timeout"]=math.huge,},
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This is released into the public domain.
|
||||
# No warranty is provided, implied or otherwise.
|
||||
|
||||
XPWD=`pwd`
|
||||
export XPWD
|
||||
|
||||
cd "$OCEMU/src"
|
||||
./boot.lua "$XPWD"
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
# THIS SOFTWARE.
|
||||
|
||||
ocvm .
|
|
@ -0,0 +1,28 @@
|
|||
ocemu {
|
||||
emulator {
|
||||
components {
|
||||
{"gpu", "c1-gpu-tier3", 0, 160, 50, 3},
|
||||
{"gpu", "c1-gpu-tier1", 0, 50, 16, 1},
|
||||
{"screen_sdl2", "c1-screen-tier3", -1, 160, 50, 3},
|
||||
-- {"screen_sdl2", "c1-screen-tier1", -1, 50, 16, 1},
|
||||
{"modem", "c1-modem", 1, false},
|
||||
{"eeprom", "c1-eeprom", 9, "lua/bios.lua"},
|
||||
{"filesystem", "c1-tmpfs", -1, "tmpfs", "tmpfs", false, 5},
|
||||
{"filesystem", "c1-sda", 5, nil, "Workbench", false, 4},
|
||||
{"filesystem", "c1-sdb", 5, nil, "Repository", false, 4},
|
||||
{"filesystem", "openos", 0,"loot/openos","openos",true,1},
|
||||
{"internet", "c1-internet", 2},
|
||||
{"computer", "c1-computer", -1},
|
||||
{"ocemu", "c1-ocemu", -1},
|
||||
{"keyboard_sdl2", "c1-keyboard", -1}
|
||||
}
|
||||
debug=false
|
||||
fast=false
|
||||
vague=false
|
||||
}
|
||||
internet {
|
||||
enableHttp=true
|
||||
enableTcp=true
|
||||
}
|
||||
version=3
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This is released into the public domain.
|
||||
# No warranty is provided, implied or otherwise.
|
||||
|
||||
cp ocemu.cfg.default ocemu.cfg && rm -rf c1-sda c1-sdb tmpfs
|
||||
mkdir c1-sda c1-sdb
|
||||
echo -n c1-sda > c1-eeprom/data.bin
|
||||
cd ..
|
||||
./package.sh $*
|
||||
cp inst.lua laboratory/c1-sda/init.lua
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This is released into the public domain.
|
||||
# No warranty is provided, implied or otherwise.
|
||||
|
||||
cp ocemu.cfg.default ocemu.cfg && rm -rf c1-sda c1-sdb
|
||||
mkdir c1-sda c1-sdb
|
||||
echo -n c1-sda > c1-eeprom/data.bin
|
||||
|
||||
./update.sh
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This is released into the public domain.
|
||||
# No warranty is provided, implied or otherwise.
|
||||
|
||||
cd ..
|
||||
cp -r code/* laboratory/c1-sdb/
|
||||
cp -r repository/* laboratory/c1-sdb/
|
||||
lua claw/clawconv.lua laboratory/c1-sdb/data/app-claw/ < claw/code-claw.lua > /dev/null
|
||||
lua claw/clawconv.lua laboratory/c1-sdb/data/app-claw/ < claw/repo-claw.lua >> /dev/null
|
||||
cp -r laboratory/c1-sdb/* laboratory/c1-sda/
|
27
mkucinst.lua
27
mkucinst.lua
|
@ -1,27 +0,0 @@
|
|||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
os.execute("tar -cf code.tar code")
|
||||
os.execute("cat insthead.lua > inst.lua")
|
||||
local f = io.open("inst.lua", "ab")
|
||||
|
||||
local df = io.open("code.tar", "rb")
|
||||
local sc = 0
|
||||
while true do
|
||||
local d = df:read(512)
|
||||
if not d then break end
|
||||
sc = sc + 1
|
||||
end
|
||||
df:close()
|
||||
local df = io.open("code.tar", "rb")
|
||||
f:write("sC = " .. sc .. "\n")
|
||||
while true do
|
||||
local d = df:read(512)
|
||||
if d then
|
||||
f:write(string.format("sector(%q)", d))
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
df:close()
|
||||
f:close()
|
Binary file not shown.
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
# THIS SOFTWARE.
|
||||
|
||||
./package.sh bdvlite deflate
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
# THIS SOFTWARE.
|
||||
|
||||
# Package repository using supplied inst.lua (use inst-gold.lua for repository branch)
|
||||
|
||||
# this is a guard check to avoid removing repobuild if it's blatantly
|
||||
# not the actual repobuild directory (this is an rm -rf after all)
|
||||
stat repobuild/data/app-claw 1>/dev/null 2>/dev/null && rm -rf repobuild
|
||||
|
||||
mkdir -p repobuild
|
||||
cp -r code/* repobuild/
|
||||
cp -r repository/* repobuild/
|
||||
cp $1 repobuild/inst.lua
|
||||
lua claw/clawconv.lua repobuild/data/app-claw/ < claw/code-claw.lua > repobuild/data/app-claw/local.c2l
|
||||
lua claw/clawconv.lua repobuild/data/app-claw/ < claw/repo-claw.lua >> repobuild/data/app-claw/local.c2l
|
30
package.sh
30
package.sh
|
@ -1,17 +1,27 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This is released into the public domain.
|
||||
# No warranty is provided, implied or otherwise.
|
||||
# Copyright (C) 2018-2021 by KittenOS NEO contributors
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
# THIS SOFTWARE.
|
||||
|
||||
rm code/data/app-claw/*
|
||||
mkdir -p code/data/app-claw
|
||||
lua claw/clawconv.lua code/data/app-claw/ < claw/code-claw.lua > /dev/null
|
||||
rm code.tar
|
||||
# Hey, look behind you, there's nothing to see here.
|
||||
# ... ok, are they seriously all named "Mann"?
|
||||
tar --owner=gray:0 --group=mann:0 -cf code.tar code
|
||||
lua heroes.lua `wc -c code.tar` > inst.lua
|
||||
cd code
|
||||
tar --mtime=0 --owner=gray:0 --group=mann:0 -cf ../code.tar .
|
||||
cd ..
|
||||
|
||||
stat repobuild/data/app-claw/local.lua && rm -rf repobuild
|
||||
mkdir repobuild
|
||||
cp -r code/* repobuild/
|
||||
cp -r repository/* repobuild/
|
||||
cp inst.lua repobuild/
|
||||
lua clawmerge.lua repository/data/app-claw/local.lua code/data/app-claw/local.lua > repobuild/data/app-claw/local.lua
|
||||
# The Installer Creator
|
||||
cd inst
|
||||
lua build.lua `git status --porcelain=2 --branch | grep branch.oid | grep -E -o "[0-9a-f]*$" -` ../code.tar $* > ../inst.lua
|
||||
lua status.lua ../inst.lua
|
||||
cd ..
|
||||
|
||||
# Common Repository Setup Code
|
||||
./package-repo.sh inst.lua
|
||||
|
|
|
@ -1,609 +0,0 @@
|
|||
-- KOSNEO inst.
|
||||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
-- PADPADPADPADPADPADPADPAD
|
||||
|
||||
local C, O, G, D = component, computer
|
||||
local sAddr = C.list("screen", true)()
|
||||
if sAddr then
|
||||
G = C.list("gpu", true)()
|
||||
if G then
|
||||
G = C.proxy(G)
|
||||
G.bind(sAddr)
|
||||
G.setForeground(0xFFFFFF)
|
||||
G.setBackground(0x000000)
|
||||
G.setResolution(50, 5)
|
||||
G.setDepth(1)
|
||||
G.fill(1, 1, 50, 5, " ")
|
||||
G.setBackground(0xFFFFFF)
|
||||
G.setForeground(0x000000)
|
||||
G.fill(1, 2, 50, 1, " ")
|
||||
G.set(2, 2, "KittenOS NEO Installer")
|
||||
end
|
||||
end
|
||||
|
||||
D = C.proxy(O.getBootAddress())
|
||||
|
||||
local file = nil
|
||||
local fileName = "Starting..."
|
||||
local fileSizeRm = 0
|
||||
local ws = 0
|
||||
|
||||
local convoct
|
||||
convoct = function (oct)
|
||||
local v = oct:byte(#oct) - 0x30
|
||||
if #oct > 1 then
|
||||
return (convoct(oct:sub(1, #oct - 1)) * 8) + v
|
||||
end
|
||||
return v
|
||||
end
|
||||
local function sectorCore(sector)
|
||||
if ws > 0 then
|
||||
ws = ws - 1
|
||||
return
|
||||
end
|
||||
if file then
|
||||
local takeaway = math.min(512, fileSizeRm)
|
||||
D.write(file, sector:sub(1, takeaway))
|
||||
fileSizeRm = fileSizeRm - takeaway
|
||||
if fileSizeRm == 0 then
|
||||
D.close(file)
|
||||
file = nil
|
||||
end
|
||||
else
|
||||
local name = sector:sub(1, 100):gsub("\x00", "")
|
||||
local sz = convoct(sector:sub(125, 135))
|
||||
if name:sub(1, 5) ~= "code/" then
|
||||
ws = math.ceil(sz / 512)
|
||||
else
|
||||
if name:sub(#name, #name) == "/" then
|
||||
ws = math.ceil(sz / 512)
|
||||
D.makeDirectory(name:sub(6))
|
||||
else
|
||||
fileName = name:sub(6)
|
||||
file = D.open(fileName, "wb")
|
||||
fileSizeRm = sz
|
||||
if file then
|
||||
if fileSizeRm == 0 then
|
||||
D.close(file)
|
||||
file = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dieCB = function () end
|
||||
|
||||
local sectorNum = 0
|
||||
local sectorCount = 0
|
||||
|
||||
local function sector(n)
|
||||
sectorCore(n)
|
||||
sectorNum = sectorNum + 1
|
||||
if G then
|
||||
local a = sectorNum / sectorCount
|
||||
G.fill(1, 2, 50, 1, " ")
|
||||
G.set(2, 2, "KittenOS NEO Installer : " .. fileName)
|
||||
G.setForeground(0xFFFFFF)
|
||||
G.setBackground(0x000000)
|
||||
G.fill(2, 4, 48, 1, " ")
|
||||
G.setBackground(0xFFFFFF)
|
||||
G.setForeground(0x000000)
|
||||
G.fill(2, 4, math.ceil(48 * a), 1, " ")
|
||||
end
|
||||
if sectorNum % 8 == 0 then
|
||||
O.pullSignal(0.05)
|
||||
end
|
||||
if sectorNum == sectorCount then
|
||||
dieCB()
|
||||
O.shutdown(true)
|
||||
end
|
||||
end
|
||||
|
||||
sectorCount = 260
|
||||
D.remove("init-symsear.lua")
|
||||
D.rename("init.lua", "init-symsear.lua")
|
||||
local instHandle = D.open("init-symsear.lua", "rb")
|
||||
dieCB = function ()
|
||||
D.close(instHandle)
|
||||
D.remove("init-symsear.lua")
|
||||
end
|
||||
local syms = {" ","s","e","t","a","i","\24","(","r",".","\
|
||||
","p","\"","o","c","m",", ","\1","l","n",")","d","u","\17","\9","x","-","\7","\6","\8","\4","\13","\2","\3","\5","g","\14","\21","\11"," then\
|
||||
","1","w"," ","\12","\18","\22","f","F","y",",","\20","re","b","k"," = ","\23","return "," local ","\16","v"," if ","\
|
||||
","\15","\19","\
|
||||
"," ","[","en","/","0","]","path","nction (","\
|
||||
","se","h"," =","or","S","T","le","\
|
||||
local ",")\
|
||||
","= fu","on"," == ","ne",")\
|
||||
","functio","\
|
||||
end\
|
||||
","D","{"," t","n ","oc","lo"," end\
|
||||
","\0\0\0\0\0\0\0\0\0","un"," i","W","\0\0\0\0\0\0","fu","et"," end\
|
||||
","then ","nd","ni","A","ing"," tab","loca","etting",")\
|
||||
","ct","C","P","}","\
|
||||
end\
|
||||
",")\
|
||||
","onit","= ","end\
|
||||
","\0","I","Y"," do\
|
||||
","return\
|
||||
","le.inser"," = func","= \"","al"," r"," e","in","he","nc"," e","j","donkonit","tion ()",") do\
|
||||
"," the","\
|
||||
",")\
|
||||
","ur","#","+","N","cursor",".inse",", v ","nction (w"," neo.req"," for "," then\
|
||||
","\0\0\0\0\0\0\0","nd\
|
||||
","()\
|
||||
","neo.","ath","table","l ","do","2","3",":","H"," = nil\
|
||||
","nd\
|
||||
e","hen\
|
||||
","\7local ","indowCore",")\
|
||||
end\
|
||||
","d\") end\
|
||||
"," = false","if ","pairs(","dow","string\"","ti","O","uestAcces","nd\
|
||||
if ","icode."," if","v in ","pkg, pid","000","750\0000001","end\
|
||||
e","750\0000000"," else\
|
||||
","window","end","00",".neo.sys.","neoux.tc","= {}\
|
||||
","(window, ","5","=","E","R",") end\
|
||||
"," cursor","request","ode/app"," return e","\" the","equire("," "," then",".neo.pub.","hile true"," end"," en","\0\0","B","M"," ret","for ","in ipai"," true\
|
||||
","close","code.sub","error(\"","return t","oroutine"," end\
|
||||
if","end\
|
||||
en","tion ","\", funct",":sub(","vailable"," end\
|
||||
"," l"," == \"","prima"," if type(","ryWindow","window.s"," end\
|
||||
","L","X","~","on ("," in ipair"," for _, v"," for k","\0\0\0\0\0cod","lose()\
|
||||
"," = funct","rror(\"Exp","nsurePath","s.primary","primary","buildingS","unic"," "," re","surfaces","disallowe","ackground","neoux.","ccess","selectUnk"," end\
|
||||
"," = uni","\
|
||||
end\
|
||||
"," f","7","Z","z","for k","rue\
|
||||
end\
|
||||
","ode.len("," end\
|
||||
","lse\
|
||||
",") end","end\
|
||||
"," fu",":sub(1, ","sub(1, 4)"," ","neo","the","if not "," th","tion","unknownA","end\
|
||||
","[1] == \"","= unico","Acces","\
|
||||
end\
|
||||
\
|
||||
l","n\
|
||||
","\") end\
|
||||
","de.len("," end","\" then\
|
||||
","0\0000001","urface","nd\
|
||||
","()\
|
||||
","(surfaces"," if ","\0\0\0\0\0\0\0\0","unc","urn","\
|
||||
"," end\
|
||||
"," neoux","ction","\
|
||||
end\
|
||||
","\
|
||||
","neoux","\24\0\0\0\0\0\0\0\0","\
|
||||
end"," end\
|
||||
end\
|
||||
","\
|
||||
end\
|
||||
"," if "," err","s[1] == \"","unicode."," window"," if ","ocal fun","= false","\0\0\0\0\0\0\0000","\0\0\0\0\0\0\00000","code","_, v in i","()\
|
||||
","d\
|
||||
","f ev == \""," return",", bg, fg"," end"," neoux.","\0\0\0","w.close(","\
|
||||
if ev"," i"," if not ","if kc == ",")\
|
||||
end\
|
||||
","indow","onkonit","00175","table.",", bg, fg)"," l"," end\
|
||||
",") == \"","d\
|
||||
"," return ","rn","ca","q"," tabl"," error(\"","end\
|
||||
re"," in ","hen err","nd\
|
||||
en","nd\
|
||||
","\
|
||||
end","turn ","x.neo.","ion ("," table","false","string","e.len(","d\
|
||||
"," re","1, unicod","cursorX ","local ","end\
|
||||
end\
|
||||
","cal","\" then e","nd\
|
||||
if k","ion","00644\00000"," end\
|
||||
","\
|
||||
end"," window."," e","if not","== \"",", func"," neo.",", functi","\
|
||||
end\
|
||||
end"," r","end\
|
||||
","ion (w)\
|
||||
","\
|
||||
end"," = false\
|
||||
"," ret","tu"," end\
|
||||
","f ","unction","= nil\
|
||||
","th","n\
|
||||
","6","U","_","requir","eturn\
|
||||
"," funct","eturn tr","\
|
||||
\
|
||||
local"," table.","eques","rn ",")\
|
||||
r",".insert(","ode.",")\
|
||||
end","== ","\0\0\0\0\0\0\0c","n\
|
||||
","\0\0\0\0\0","()\
|
||||
"," = nil","able.","n\
|
||||
"," = n","return","000644\0000","Access","able","etu","d\
|
||||
","\
|
||||
if ","end\
|
||||
end","nction (p","ub(1, 4) "," return "," return","lse\
|
||||
","abl","= uni","for k, ","\0\0\0\0\0\0\0\0c","cursorY","ble","bg, fg)\
|
||||
","\
|
||||
e","ind","ret","tring\"","000000"," neo","pairs","then\
|
||||
",")\
|
||||
","le.inse","loc","\
|
||||
end","n error","\
|
||||
end\
|
||||
\
|
||||
","if s[1] =","lse\
|
||||
","turn","ursor","function","neou","\0\0\0\0\0\0c","function ","\0000001","end\
|
||||
e","eo.","Access(\""," re"," lo","\
|
||||
e",")\
|
||||
end","urn ","\
|
||||
re"," end\
|
||||
e"," = f","\0\0\0\0","cal ","\0000000644\0","in ip","\
|
||||
en"," return",")\
|
||||
i","ction ","\
|
||||
end\
|
||||
","ode","equire(\"","r(\"Expect","ctio"," cursorY","if","%","4","<","G","\
|
||||
local f","\
|
||||
ret"," for ","d\
|
||||
if"," erro","true\
|
||||
end","ed\") end","hen ","nd\
|
||||
en","al ","on ","] then\
|
||||
","string\")","for _, ","unicode.l","[1] == ","true\
|
||||
","uestAc","nd\
|
||||
end\
|
||||
"," for k, "," if ev ","then\
|
||||
","\
|
||||
en",")\
|
||||
en","do\
|
||||
","e.ins",".primary"," true","coroutine","return\
|
||||
","airs(",") end\
|
||||
","nd\
|
||||
end\
|
||||
\
|
||||
","\
|
||||
return","window, ","end\
|
||||
e","end\
|
||||
end","750\0000","eturn t",", functio"," e","d\") end","en\
|
||||
","ocal","icode.len","e\
|
||||
","n\
|
||||
","w.close()"," uni","sub(","n ipairs("," for ","end\
|
||||
","ion ()","end\
|
||||
en","quest","cursorY ","eturn","unicode.s","x.neo.pub"," = neo","hen\
|
||||
","then\
|
||||
","string\") ","in ipa"," retur","indowCor"," re","750\00000"," do\
|
||||
",")\
|
||||
if "," then ret","\") end"," en","n\
|
||||
","ipairs"," then err","d\
|
||||
en","= f"," local",".neo.","\24\0\0\0\0\0\0\0","func","()\
|
||||
",")\
|
||||
en","curs","\
|
||||
loca","ode/apps","nd\
|
||||
if "," i"," = neo.r","kg, pid, ","\
|
||||
i","win","code.sub(","require","wind"," else\
|
||||
","close()"," end\
|
||||
e","\
|
||||
\
|
||||
local "," then re","\
|
||||
e",".request","wnAvailab","tion (","\
|
||||
local ","error(","\0000001750\0","n\
|
||||
","d\
|
||||
"," ","uestA","reques","end\
|
||||
end\
|
||||
\
|
||||
","ion ()\
|
||||
"," curso","then\
|
||||
","0001750","surfac",".close("," function","= neo.","if type("," loc","d\
|
||||
e","oroutine."," do\
|
||||
"," else\
|
||||
","tion ()\
|
||||
",", v in ","window.","neo.re","0000","\000000","\
|
||||
end\
|
||||
","ipairs("," ret","\"x.neo.pu","error","\
|
||||
i","\
|
||||
end","()\
|
||||
","= nil\
|
||||
","nsert(","n erro","rror(\"","nil\
|
||||
","cursorX ="," do\
|
||||
"," for","turn f","en\
|
||||
"," table.","\
|
||||
","\") end\
|
||||
","\") end\
|
||||
",".close()\
|
||||
","\
|
||||
re","d\
|
||||
e","\
|
||||
end\
|
||||
","ownAvaila","000644\00000"," false\
|
||||
","\
|
||||
re","false\
|
||||
","ion ()\
|
||||
",")\
|
||||
e","urn end\
|
||||
",")\
|
||||
if","x.neo.sys","indow, ","then\
|
||||
",")\
|
||||
if t","cti","ion ","ion (w","\" then"," = nil\
|
||||
","then","e\" then\
|
||||
"," if","on (w)\
|
||||
",") do\
|
||||
"," en","tAccess(\"","surePath"," end\
|
||||
end","\
|
||||
end\
|
||||
","e.inse"," table.i"," local","oca","n (","0 then\
|
||||
",") end\
|
||||
"," loc","se\
|
||||
","do\
|
||||
","nd\
|
||||
end\
|
||||
"," then e","0000644\0","ion ()\
|
||||
","ocal ","end\
|
||||
if ","e\
|
||||
"," then\
|
||||
"," local","icode.le","\" then "," ==","] == \"","eoux","ndow","\0\0\0\0\0c","nownAvail"," functi","0\000000000","eoux.tc","hen error"," = neo.","table.ins"," window","(window","d\
|
||||
if ","\
|
||||
if ","\
|
||||
local","end\
|
||||
e","nd\
|
||||
end","insert("," local ","k, v i","surface","eturn ","\
|
||||
loc","sub(1, ","end\
|
||||
if ","io","!","$","&","'","*","8","9",";",">","?","@","J","K","Q","V","\\","^","|","<22>","…","ˆ","‰","Œ","<22>","Ž","<22>","<22>","’","“","”","˜","¦","¬","¼","½","Â","ï",}
|
||||
local bytBuf = ""
|
||||
local bitBuf = ""
|
||||
local function getByte()
|
||||
if bytBuf == "" then
|
||||
bytBuf = D.read(instHandle, 64)
|
||||
end
|
||||
local r = bytBuf:byte()
|
||||
bytBuf = bytBuf:sub(2)
|
||||
return r
|
||||
end
|
||||
while true do
|
||||
if getByte() == 0 then break end
|
||||
end
|
||||
local function pb(c, p) if c % (p * 2) ~= c % p then bitBuf = bitBuf .. "1" else bitBuf = bitBuf .. "0" end end
|
||||
local stn = 0
|
||||
local function getBit()
|
||||
if bitBuf == "" then
|
||||
local c = getByte()
|
||||
c = (c - stn) % 256 stn = stn + 3
|
||||
pb(c, 1)
|
||||
pb(c, 2)
|
||||
pb(c, 4)
|
||||
pb(c, 8)
|
||||
pb(c, 16)
|
||||
pb(c, 32)
|
||||
pb(c, 64)
|
||||
pb(c, 128)
|
||||
end
|
||||
local bit = bitBuf:sub(1, 1) == "1"
|
||||
bitBuf = bitBuf:sub(2)
|
||||
return bit
|
||||
end
|
||||
local buf = ""
|
||||
local mode = false
|
||||
local bc2 = 10
|
||||
while true do
|
||||
local bc = getBit()
|
||||
local v = 0
|
||||
if bc then bc = bc2 v = 64 else bc = 6 end
|
||||
for bit = 0, bc - 1 do
|
||||
if getBit() then v = v + (2 ^ bit) end
|
||||
end
|
||||
buf = buf .. syms[v]
|
||||
if mode then
|
||||
while #buf >= 512 do
|
||||
sector(buf:sub(1, 512))
|
||||
buf = buf:sub(513)
|
||||
end
|
||||
else
|
||||
if #buf == 27939 then
|
||||
stn = 0
|
||||
bc2 = 11
|
||||
bitBuf = ""
|
||||
syms = {}
|
||||
while #buf > 0 do
|
||||
local len = buf:byte()
|
||||
if len > 127 then error("symlen") end
|
||||
buf = buf:sub(2)
|
||||
local ch = buf:sub(1, len)
|
||||
buf = buf:sub(len + 1)
|
||||
table.insert(syms, ch)
|
||||
end
|
||||
mode = true
|
||||
end
|
||||
end
|
||||
end
|
||||
--[[ |