mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-12-14 18:15:44 +00:00
Compare commits
192 commits
v0.0.6-alp
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2445175248 | |||
| c19fb78b1f | |||
| 5d0b6e864a | |||
| b2c3158d72 | |||
| 1ba7185939 | |||
| e0afdf177c | |||
| 993b7f43f3 | |||
| 74c9e25ad6 | |||
| f5d6bbfaae | |||
| 2bf4b17577 | |||
| 990b30b7bb | |||
| 3326d0e73d | |||
| f7d4b728f5 | |||
| bf24c6e989 | |||
|
|
2b8eb54a8b | ||
|
|
b85adb960b | ||
| d8d6e76729 | |||
| 3fa0a73ac6 | |||
| fe9446e2e7 | |||
| 2f7c4fb032 | |||
| 55c4f70533 | |||
| 4880fea8e3 | |||
|
|
59bbf10e78 | ||
|
|
b44f943456 | ||
|
|
efe510d0da | ||
| 589042ca38 | |||
|
|
86c433e966 | ||
|
|
ad8a0dcb84 | ||
|
|
74b799a0b4 | ||
| e152203afd | |||
| 97161f0c85 | |||
| 7c799426a9 | |||
|
|
fa39689e3a | ||
|
|
b09ab4bdff | ||
|
|
756b8b35dd | ||
| e520383da3 | |||
| f5887c63c0 | |||
| 544025290b | |||
| 4b21daa095 | |||
| 1d28f07a7e | |||
| e07f79c31a | |||
| e926bd6d6b | |||
| f124520a2f | |||
| 451abe6b86 | |||
| 8121920ad7 | |||
| f782dbddb2 | |||
| 57195bcf0b | |||
| 63838e97c7 | |||
| 44437e84f1 | |||
| 4a9d2bad34 | |||
| 9fadf6d064 | |||
| 9abb828dbd | |||
| 7b07ec4f09 | |||
| a84aedbe38 | |||
|
|
b2e7969793 | ||
|
|
6d4161e251 | ||
|
|
b7a956bfd6 | ||
|
|
f9a88f39bb | ||
|
|
7628d4a29e | ||
|
|
d56a0d1a2a | ||
| c5fa0d44ca | |||
| 16223c9986 | |||
| 9aadc35329 | |||
| d137c46ed9 | |||
| 0e4d3c135a | |||
| 341ed38897 | |||
| 67d3ea6154 | |||
| 07da11fe40 | |||
| 9ed2cc1687 | |||
| eb58bdda14 | |||
| e6c70d43a0 | |||
| 085e2c2947 | |||
| 311b705fa9 | |||
| 96e1fb9223 | |||
| 1cf706f633 | |||
| d9f947c406 | |||
| 240efdc493 | |||
| 7090bc842c | |||
|
|
444652da12 | ||
|
|
4c78f8afff | ||
| fd8b38932b | |||
| 1c6419ff10 | |||
| 2a84c8cb35 | |||
| 191a2954c6 | |||
| f9b0a38fd2 | |||
| aee3434b44 | |||
| dd1834ad2b | |||
| 12272f13ab | |||
| e662e77fa5 | |||
| 8b32d524de | |||
|
|
70ff5ba20b | ||
|
|
adaf63c7fc | ||
|
|
ed66911489 | ||
|
|
65cb15f6c8 | ||
|
|
3f9f036b49 | ||
|
|
001ce2f626 | ||
|
|
ac9081cf89 | ||
|
|
dee550b28d | ||
|
|
03e0992328 | ||
|
|
1dbbb23ff6 | ||
|
|
aa63b19929 | ||
|
|
38478b3359 | ||
|
|
e606dfee95 | ||
|
|
40295112fe | ||
|
|
b86d330532 | ||
|
|
0e5a4895fa | ||
|
|
19d65651bd | ||
|
|
0a365f8b72 | ||
|
|
03f82a6796 | ||
|
|
fffbb76a5e | ||
| 6fd4f6ec39 | |||
|
|
9cc8377503 | ||
|
|
8703733b91 | ||
|
|
32d5299a3f | ||
|
|
9907232af7 | ||
|
|
9dd01041d0 | ||
|
|
dba8f8c600 | ||
| ee44873e07 | |||
|
|
2f701a6338 | ||
| 5045fa757c | |||
|
|
f7591e0d35 | ||
|
|
6cce546749 | ||
|
|
4017296426 | ||
|
|
dc51203d3e | ||
|
|
8cdf2148ed | ||
|
|
d744432a50 | ||
|
|
e37ed56a43 | ||
|
|
e3616e4ac6 | ||
|
|
5e95e74545 | ||
|
|
8e08c80d91 | ||
|
|
e24192fb26 | ||
|
|
d3bbedb30e | ||
|
|
6d9dbda7b5 | ||
|
|
34cf7a6bc4 | ||
|
|
1ca007b8d5 | ||
|
|
33037a40fc | ||
|
|
1b2ad30759 | ||
|
|
b477e74141 | ||
|
|
963d47afb2 | ||
|
|
4143417947 | ||
|
|
f9f56c8063 | ||
|
|
5dddc42910 | ||
|
|
d318523bde | ||
|
|
7254ba638c | ||
|
|
b84db27f83 | ||
|
|
5a510c6eb6 | ||
|
|
392c1b9aca | ||
|
|
ffa223b5b3 | ||
|
|
b212f41fea | ||
|
|
38c62c8f45 | ||
|
|
23d062682a | ||
|
|
cf12454a83 | ||
|
|
8b5fa96a74 | ||
|
|
bcb358ed9d | ||
|
|
6240179118 | ||
|
|
3502da4889 | ||
| e6082c9ea8 | |||
| 4eec297c69 | |||
|
|
8191c7dc85 | ||
|
|
1187a262d9 | ||
|
|
b12eac4bed | ||
|
|
3e3693bc2e | ||
|
|
bf6424c0d1 | ||
|
|
15e700008e | ||
|
|
aec372feb8 | ||
|
|
e0f3dc578b | ||
|
|
a298f1630f | ||
|
|
2949239598 | ||
|
|
68f4f17f35 | ||
|
|
9b5d463f4a | ||
|
|
732311a730 | ||
|
|
c6858f6bc0 | ||
|
|
dc7958d206 | ||
|
|
2ed070f7ab | ||
|
|
2544659d0c | ||
|
|
1fbcef2146 | ||
|
|
5af217ac1f | ||
|
|
0068509357 | ||
|
|
e5e2d58f62 | ||
|
|
dc7d6bde27 | ||
|
|
05228dbb17 | ||
|
|
322ce54443 | ||
|
|
10646eb13b | ||
|
|
72692be21a | ||
|
|
8eddd69616 | ||
|
|
50461558e4 | ||
|
|
4385a9e2fc | ||
|
|
2a84bf8fec | ||
|
|
17056b3269 | ||
|
|
c278d997ff | ||
|
|
38ff61f614 | ||
|
|
ad34d6185b |
149 changed files with 8595 additions and 3236 deletions
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Side**
|
||||
- [ ] Lua side
|
||||
- [ ] C# side
|
||||
- [ ] Other
|
||||
- [ ] I don't know
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. Linux, Windows, OSX]
|
||||
- OS Version: [e.g. 22]
|
||||
- Capy64 Version: [e.g. 0.0.9-alpha]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
26
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
26
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Side**
|
||||
- [ ] Lua side
|
||||
- [ ] C# side
|
||||
- [ ] Other
|
||||
- [ ] I don't know
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
76
.github/workflows/codeql.yml
vendored
Normal file
76
.github/workflows/codeql.yml
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '25 14 * * 2'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'csharp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
81
.github/workflows/dotnet.yml
vendored
81
.github/workflows/dotnet.yml
vendored
|
|
@ -5,42 +5,59 @@ name: .NET
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
branches: ["main"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal
|
||||
|
||||
- name: Publish for Windows x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release --no-self-contained -a x64 --os win -p:PublishSingleFile=true -o publish/win-x64
|
||||
|
||||
- name: Upload Windows x64 artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: Windows-x64
|
||||
path: publish/win-x64/**
|
||||
if-no-files-found: error
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal
|
||||
|
||||
- name: Publish for Linux x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release --no-self-contained -a x64 --os linux -p:PublishSingleFile=true -o publish/linux-x64
|
||||
- name: Publish for Windows x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release --no-self-contained -a x64 --os win -p:PublishSingleFile=true -o publish/win-x64
|
||||
|
||||
- name: Upload Linux x64 artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: Linux-x64
|
||||
path: publish/linux-x64/**
|
||||
if-no-files-found: error
|
||||
- name: Upload Windows x64 artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: Windows-x64
|
||||
path: publish/win-x64/**
|
||||
if-no-files-found: error
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal
|
||||
|
||||
- name: Publish for Linux x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release --no-self-contained -a x64 --os linux -p:PublishSingleFile=true -o publish/linux-x64
|
||||
|
||||
- name: Upload Linux x64 artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: Linux-x64
|
||||
path: publish/linux-x64/**
|
||||
if-no-files-found: error
|
||||
|
|
|
|||
69
.github/workflows/main.yml
vendored
69
.github/workflows/main.yml
vendored
|
|
@ -1,69 +0,0 @@
|
|||
name: Publish release draft
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Release tag
|
||||
required: true
|
||||
prerelease:
|
||||
description: Prerelease
|
||||
type: boolean
|
||||
generatenotes:
|
||||
description: Generate release notes
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal
|
||||
|
||||
- name: Publish for Linux x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release --no-self-contained -a x64 --os linux -p:PublishSingleFile=true -o publish/linux-x64
|
||||
- name: Publish for Windows x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release --no-self-contained -a x64 --os win -p:PublishSingleFile=true -o publish/win-x64
|
||||
|
||||
- name: Upload Linux x64 artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: Linux-x64
|
||||
path: publish/linux-x64/**
|
||||
if-no-files-found: error
|
||||
- name: Upload Windows x64 artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: Windows-x64
|
||||
path: publish/win-x64/**
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Zip Linux x64
|
||||
run: 7z a capy64-linux-x64.zip publish/linux-x64/**
|
||||
- name: Zip Windows x64
|
||||
run: 7z a capy64-windows-x64.zip publish/win-x64/**
|
||||
|
||||
- name: Create Release
|
||||
uses: ncipollo/release-action@v1.12.0
|
||||
with:
|
||||
commit: ${{ github.ref_name }}
|
||||
tag: ${{ inputs.tag }}
|
||||
allowUpdates: true
|
||||
artifactErrorsFailBuild: true
|
||||
artifacts: "capy64-linux-x64.zip,capy64-windows-x64.zip"
|
||||
draft: true
|
||||
generateReleaseNotes: ${{ inputs.generatenotes }}
|
||||
makeLatest: true
|
||||
prerelease: ${{ inputs.prerelease }}
|
||||
97
.github/workflows/publish.yml
vendored
Normal file
97
.github/workflows/publish.yml
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
name: Publish release draft
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Release tag
|
||||
required: true
|
||||
prerelease:
|
||||
description: Prerelease
|
||||
type: boolean
|
||||
generatenotes:
|
||||
description: Generate release notes
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
release-windows:
|
||||
runs-on: windows-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal
|
||||
|
||||
- name: Publish for Windows x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release -a x64 --os win -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:TieredCompilation=false -o capy64-windows-x64 --self-contained
|
||||
|
||||
- name: Upload Windows x64 artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: Windows-x64
|
||||
path: capy64-windows-x64/**
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Zip Windows x64
|
||||
run: 7z a capy64-windows-x64.zip capy64-windows-x64/**
|
||||
|
||||
release-linux:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal
|
||||
|
||||
- name: Publish for Linux x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release -a x64 --os linux -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:TieredCompilation=false -o capy64-linux-x64 --self-contained
|
||||
|
||||
- name: Upload Linux x64 artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: Linux-x64
|
||||
path: capy64-linux-x64/**
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Zip Linux x64
|
||||
run: 7z a capy64-linux-x64.zip capy64-linux-x64/**
|
||||
|
||||
publish:
|
||||
needs: [release-windows, release-linux]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Create Release
|
||||
uses: ncipollo/release-action@v1.12.0
|
||||
with:
|
||||
commit: ${{ github.ref_name }}
|
||||
tag: ${{ inputs.tag }}
|
||||
allowUpdates: true
|
||||
artifactErrorsFailBuild: true
|
||||
artifacts: "capy64-linux-x64.zip,capy64-windows-x64.zip"
|
||||
draft: true
|
||||
generateReleaseNotes: ${{ inputs.generatenotes }}
|
||||
makeLatest: true
|
||||
prerelease: ${{ inputs.prerelease }}
|
||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
capy64@alexdevs.me.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-mgcb": {
|
||||
"version": "3.8.1.303",
|
||||
"commands": [
|
||||
"mgcb"
|
||||
]
|
||||
},
|
||||
"dotnet-mgcb-editor": {
|
||||
"version": "3.8.1.303",
|
||||
"commands": [
|
||||
"mgcb-editor"
|
||||
]
|
||||
},
|
||||
"dotnet-mgcb-editor-linux": {
|
||||
"version": "3.8.1.303",
|
||||
"commands": [
|
||||
"mgcb-editor-linux"
|
||||
]
|
||||
},
|
||||
"dotnet-mgcb-editor-windows": {
|
||||
"version": "3.8.1.303",
|
||||
"commands": [
|
||||
"mgcb-editor-windows"
|
||||
]
|
||||
},
|
||||
"dotnet-mgcb-editor-mac": {
|
||||
"version": "3.8.1.303",
|
||||
"commands": [
|
||||
"mgcb-editor-mac"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
179
Capy64/.editorconfig
Normal file
179
Capy64/.editorconfig
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
[*.{cs,vb,lua}]
|
||||
#### Top of file license comment
|
||||
file_header_template = This file is part of Capy64 - https://github.com/Ale32bit/Capy64\nCopyright 2023 Alessandro "AlexDevs" Proto\n\nLicensed under the Apache License, Version 2.0 (the "License").\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an "AS IS" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.
|
||||
|
||||
[*.cs]
|
||||
#### Stili di denominazione ####
|
||||
|
||||
# Regole di denominazione
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
# Specifiche dei simboli
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# Stili di denominazione
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:silent
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
csharp_style_prefer_null_check_over_type_check = true:suggestion
|
||||
csharp_style_prefer_index_operator = true:suggestion
|
||||
csharp_style_prefer_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
|
||||
csharp_style_prefer_range_operator = true:suggestion
|
||||
csharp_style_prefer_utf8_string_literals = true:suggestion
|
||||
csharp_style_prefer_tuple_swap = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
csharp_style_namespace_declarations = block_scoped:silent
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_style_prefer_method_group_conversion = true:silent
|
||||
csharp_style_prefer_top_level_statements = true:silent
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
csharp_style_prefer_pattern_matching = true:silent
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_prefer_not_pattern = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_prefer_extended_property_pattern = true:suggestion
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
csharp_prefer_static_local_function = true:suggestion
|
||||
csharp_style_prefer_readonly_struct = true:suggestion
|
||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
|
||||
csharp_style_var_for_built_in_types = false:silent
|
||||
csharp_style_var_when_type_is_apparent = false:silent
|
||||
csharp_style_var_elsewhere = false:silent
|
||||
|
||||
[*.vb]
|
||||
#### Stili di denominazione ####
|
||||
|
||||
# Regole di denominazione
|
||||
|
||||
dotnet_naming_rule.interface_should_be_inizia_con_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_inizia_con_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_inizia_con_i.style = inizia_con_i
|
||||
|
||||
dotnet_naming_rule.tipi_should_be_notazione_pascal.severity = suggestion
|
||||
dotnet_naming_rule.tipi_should_be_notazione_pascal.symbols = tipi
|
||||
dotnet_naming_rule.tipi_should_be_notazione_pascal.style = notazione_pascal
|
||||
|
||||
dotnet_naming_rule.membri_non_di_campo_should_be_notazione_pascal.severity = suggestion
|
||||
dotnet_naming_rule.membri_non_di_campo_should_be_notazione_pascal.symbols = membri_non_di_campo
|
||||
dotnet_naming_rule.membri_non_di_campo_should_be_notazione_pascal.style = notazione_pascal
|
||||
|
||||
# Specifiche dei simboli
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.tipi.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.tipi.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||
dotnet_naming_symbols.tipi.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.membri_non_di_campo.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.membri_non_di_campo.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||
dotnet_naming_symbols.membri_non_di_campo.required_modifiers =
|
||||
|
||||
# Stili di denominazione
|
||||
|
||||
dotnet_naming_style.inizia_con_i.required_prefix = I
|
||||
dotnet_naming_style.inizia_con_i.required_suffix =
|
||||
dotnet_naming_style.inizia_con_i.word_separator =
|
||||
dotnet_naming_style.inizia_con_i.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.notazione_pascal.required_prefix =
|
||||
dotnet_naming_style.notazione_pascal.required_suffix =
|
||||
dotnet_naming_style.notazione_pascal.word_separator =
|
||||
dotnet_naming_style.notazione_pascal.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.notazione_pascal.required_prefix =
|
||||
dotnet_naming_style.notazione_pascal.required_suffix =
|
||||
dotnet_naming_style.notazione_pascal.word_separator =
|
||||
dotnet_naming_style.notazione_pascal.capitalization = pascal_case
|
||||
|
||||
[*.{cs,vb}]
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
dotnet_style_prefer_auto_properties = true:silent
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_compound_assignment = true:suggestion
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||
dotnet_style_namespace_match_folder = true:suggestion
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = false:silent
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
|
||||
dotnet_code_quality_unused_parameters = all:suggestion
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
||||
dotnet_style_predefined_type_for_member_access = true:silent
|
||||
dotnet_style_qualification_for_field = false:silent
|
||||
dotnet_style_qualification_for_property = false:silent
|
||||
dotnet_style_qualification_for_method = false:silent
|
||||
dotnet_style_qualification_for_event = false:silent
|
||||
23
Capy64/API/IComponent.cs
Normal file
23
Capy64/API/IComponent.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using KeraLua;
|
||||
|
||||
namespace Capy64.API;
|
||||
|
||||
public interface IComponent
|
||||
{
|
||||
void LuaInit(Lua L) { }
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
using KeraLua;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Capy64.API;
|
||||
|
||||
public interface IPlugin
|
||||
{
|
||||
void ConfigureServices(IServiceCollection services) { }
|
||||
void LuaInit(Lua L) { }
|
||||
|
||||
}
|
||||
7
Capy64/Assets/Lua/CapyOS/home/.shrc
Normal file
7
Capy64/Assets/Lua/CapyOS/home/.shrc
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
alias ll "ls -al"
|
||||
alias la "ls -a"
|
||||
alias rmdir "rm -r"
|
||||
alias reboot "shutdown -r"
|
||||
|
||||
# Comment or remove the line below to disable the MOTD
|
||||
motd
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
local version = "0.1.1"
|
||||
local systemDirectory = "/sys"
|
||||
|
||||
print("Starting CapyOS")
|
||||
|
||||
local term = require("term")
|
||||
local fs = require("fs")
|
||||
local gpu = require("gpu")
|
||||
local machine = require("machine")
|
||||
|
||||
local nPrint = print
|
||||
local function showError(err)
|
||||
|
|
@ -20,28 +23,24 @@ local function showError(err)
|
|||
end
|
||||
|
||||
function os.version()
|
||||
return "CapyOS 0.0.1"
|
||||
return "CapyOS " .. version
|
||||
end
|
||||
|
||||
term.setSize(51, 19)
|
||||
gpu.setScale(2)
|
||||
|
||||
term.setPos(1, 1)
|
||||
term.write(_HOST)
|
||||
term.write(machine.version())
|
||||
term.setPos(1, 3)
|
||||
|
||||
local files = fs.list("/boot")
|
||||
for k, v in ipairs(files) do
|
||||
local func, err = loadfile("/boot/" .. v)
|
||||
local files = fs.list(fs.combine(systemDirectory, "boot/autorun"))
|
||||
for i = 1, #files do
|
||||
local func, err = loadfile(fs.combine(systemDirectory, "boot/autorun", files[i]))
|
||||
if not func then
|
||||
showError(err)
|
||||
break
|
||||
end
|
||||
|
||||
local ok, err = pcall(func)
|
||||
|
||||
if not ok then
|
||||
showError(debug.traceback(err))
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
28
Capy64/Assets/Lua/CapyOS/sys/bin/alias.lua
Normal file
28
Capy64/Assets/Lua/CapyOS/sys/bin/alias.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
local argparser = require("argparser")
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
|
||||
if options.l or options.list then
|
||||
for alias, value in pairs(shell.aliases) do
|
||||
print(string.format("%s = \"%s\"", alias, value))
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local alias = args[1]
|
||||
|
||||
if not alias or options.h or options.help then
|
||||
print("Usage: alias [option...] <alias> [command]")
|
||||
print("Options:")
|
||||
print(" -l --list: List aliases")
|
||||
return false
|
||||
end
|
||||
|
||||
local command = table.pack(select(2, ...))
|
||||
if #command == 0 then
|
||||
shell.aliases[alias] = nil
|
||||
return
|
||||
end
|
||||
|
||||
shell.aliases[alias] = table.concat(command, " ")
|
||||
4
Capy64/Assets/Lua/CapyOS/sys/bin/bg.lua
Normal file
4
Capy64/Assets/Lua/CapyOS/sys/bin/bg.lua
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
local scheduler = require("scheduler")
|
||||
scheduler.spawn(function()
|
||||
shell.run(arg.string)
|
||||
end)
|
||||
9
Capy64/Assets/Lua/CapyOS/sys/bin/cat.lua
Normal file
9
Capy64/Assets/Lua/CapyOS/sys/bin/cat.lua
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
local fs = require("fs")
|
||||
|
||||
local args = {...}
|
||||
|
||||
local path = shell.resolve(args[1])
|
||||
|
||||
local f<close> = fs.open(path, "r")
|
||||
print(f:read("a"))
|
||||
f:close()
|
||||
4
Capy64/Assets/Lua/CapyOS/sys/bin/echo.lua
Normal file
4
Capy64/Assets/Lua/CapyOS/sys/bin/echo.lua
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
local argparser = require("argparser")
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
print(table.concat(args, " "))
|
||||
1
Capy64/Assets/Lua/CapyOS/sys/bin/exit.lua
Normal file
1
Capy64/Assets/Lua/CapyOS/sys/bin/exit.lua
Normal file
|
|
@ -0,0 +1 @@
|
|||
shell.exit()
|
||||
40
Capy64/Assets/Lua/CapyOS/sys/bin/fun/donuts.lua
Normal file
40
Capy64/Assets/Lua/CapyOS/sys/bin/fun/donuts.lua
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
local gpu = require("gpu")
|
||||
local event = require("event")
|
||||
local donuts = {}
|
||||
local limit = 100
|
||||
|
||||
local w, h = gpu.getSize()
|
||||
local function insert()
|
||||
local donut = {
|
||||
x = math.random(-20, w + 20),
|
||||
y = math.random(-20, h + 20),
|
||||
d = math.random() * math.pi*2,
|
||||
dir = math.random(0, 1),
|
||||
c = math.random(0xffffff),
|
||||
life = math.random(100, 1000),
|
||||
}
|
||||
table.insert(donuts, donut)
|
||||
end
|
||||
|
||||
while true do
|
||||
if #donuts < limit then
|
||||
insert()
|
||||
end
|
||||
gpu.clear(0)
|
||||
for k, donut in ipairs(donuts) do
|
||||
if donut.life <= 0 then
|
||||
table.remove(donuts, k)
|
||||
end
|
||||
local doReverse = math.random(0, 1000) > 950
|
||||
donut.x = donut.x + math.cos(donut.d) * 4
|
||||
donut.y = donut.y + math.sin(donut.d) * 4
|
||||
donut.d = donut.d + (donut.dir == 1 and 0.05 or -0.05)
|
||||
gpu.drawCircle(donut.x, donut.y, 20, donut.c, 10)
|
||||
if doReverse then
|
||||
donut.dir = donut.dir == 1 and 0 or 1
|
||||
end
|
||||
donut.life = donut.life - 1
|
||||
end
|
||||
event.push("donuts")
|
||||
event.pull("donuts")
|
||||
end
|
||||
|
|
@ -38,20 +38,24 @@ local function iter(cr, ci)
|
|||
end
|
||||
|
||||
local function draw()
|
||||
local buffer <close> = gpu.newBuffer()
|
||||
local size = w * h
|
||||
local canvas = { string.unpack(("B"):rep(size), ("\0"):rep(size)) }
|
||||
canvas[#canvas] = nil
|
||||
|
||||
|
||||
for y = 0, h - 1 do
|
||||
for x = 0, w - 1 do
|
||||
local _, _, i = iter((x - cx + dx * pscale) * px, (y - cy + dy * pscale) * px)
|
||||
buffer[y * w + x] = colorUnit * (iterations - i)
|
||||
canvas[y * w + x] = colorUnit * (iterations - i)
|
||||
end
|
||||
end
|
||||
|
||||
local buffer <close> = gpu.bufferFrom(canvas, w, h)
|
||||
gpu.setBuffer(buffer)
|
||||
end
|
||||
|
||||
-- no idea why it's needed
|
||||
timer.sleep(1)
|
||||
timer.sleep(0)
|
||||
|
||||
draw()
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ local function melt()
|
|||
end
|
||||
gpu.setBuffer(buffer)
|
||||
|
||||
timer.sleep(10)
|
||||
timer.delay(0):await()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ local function random()
|
|||
)
|
||||
end
|
||||
|
||||
timer.sleep(100)
|
||||
timer.delay(0.1):await()
|
||||
end
|
||||
end
|
||||
|
||||
209
Capy64/Assets/Lua/CapyOS/sys/bin/fun/paint.lua
Normal file
209
Capy64/Assets/Lua/CapyOS/sys/bin/fun/paint.lua
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
local event = require("event")
|
||||
local gpu = require("gpu")
|
||||
local colors = require("colors")
|
||||
local term = require("term")
|
||||
local timer = require("timer")
|
||||
|
||||
local w, h = gpu.getSize()
|
||||
local tw, th = term.getSize()
|
||||
|
||||
local selectedColor = 1
|
||||
local thickness = 4
|
||||
|
||||
local canvasW, canvasH = term.toRealPos(tw - 1, th + 1)
|
||||
canvasW = canvasW - 3
|
||||
local size = canvasW * canvasH
|
||||
local canvas = {string.unpack(("B"):rep(size), ("\0"):rep(size))}
|
||||
canvas[#canvas] = nil
|
||||
|
||||
local function drawCircle(buffer, x, y, radius, color)
|
||||
radius = math.max(0, radius)
|
||||
if radius == 0 then
|
||||
buffer[x + buffer.width * y] = color
|
||||
return
|
||||
end
|
||||
local width = buffer.width
|
||||
local height = buffer.height
|
||||
|
||||
local index = function(x, y)
|
||||
return y * width + x
|
||||
end
|
||||
|
||||
local isValid = function(x, y)
|
||||
return x >= 0 and x < width and y >= 0 and y < height
|
||||
end
|
||||
|
||||
local setPixel = function(x, y, color)
|
||||
if isValid(x, y) then
|
||||
buffer[index(x, y)] = color
|
||||
end
|
||||
end
|
||||
|
||||
local drawFilledCirclePoints = function(cx, cy, x, y)
|
||||
for dx = -x, x do
|
||||
for dy = -y, y do
|
||||
setPixel(cx + dx, cy + dy, color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local drawCircleBresenham = function(cx, cy, radius)
|
||||
local x = 0
|
||||
local y = radius
|
||||
local d = 3 - 2 * radius
|
||||
drawFilledCirclePoints(cx, cy, x, y)
|
||||
while y >= x do
|
||||
x = x + 1
|
||||
if d > 0 then
|
||||
y = y - 1
|
||||
d = d + 4 * (x - y) + 10
|
||||
else
|
||||
d = d + 4 * x + 6
|
||||
end
|
||||
drawFilledCirclePoints(cx, cy, x, y)
|
||||
end
|
||||
end
|
||||
|
||||
drawCircleBresenham(x, y, radius)
|
||||
end
|
||||
|
||||
local function drawLine(buffer, x0, y0, x1, y1, color, thickness)
|
||||
local width = canvasW
|
||||
local height = canvasH
|
||||
|
||||
local index = function(x, y)
|
||||
return y * width + x
|
||||
end
|
||||
|
||||
local isValid = function(x, y)
|
||||
return x >= 0 and x < width and y >= 0 and y < height
|
||||
end
|
||||
|
||||
local setPixel = function(x, y)
|
||||
if isValid(x, y) then
|
||||
buffer[index(x, y)] = color
|
||||
end
|
||||
end
|
||||
|
||||
local drawLineBresenham = function()
|
||||
local i = 0
|
||||
local dx = math.abs(x1 - x0)
|
||||
local dy = math.abs(y1 - y0)
|
||||
local sx = x0 < x1 and 1 or -1
|
||||
local sy = y0 < y1 and 1 or -1
|
||||
local err = dx - dy
|
||||
|
||||
local majorAxis = dx > dy
|
||||
|
||||
while x0 ~= x1 or y0 ~= y1 do
|
||||
for i = 0, thickness - 1 do
|
||||
if majorAxis then
|
||||
setPixel(x0, y0 + i)
|
||||
else
|
||||
setPixel(x0 + i, y0)
|
||||
end
|
||||
end
|
||||
|
||||
local err2 = 2 * err
|
||||
if err2 > -dy then
|
||||
err = err - dy
|
||||
x0 = x0 + sx
|
||||
end
|
||||
if err2 < dx then
|
||||
err = err + dx
|
||||
y0 = y0 + sy
|
||||
end
|
||||
|
||||
if i % 1024 == 0 then
|
||||
--event.push("paint")
|
||||
--event.pull("paint")
|
||||
--timer.sleep(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
drawLineBresenham()
|
||||
end
|
||||
|
||||
local function drawUI()
|
||||
term.setBackground(0)
|
||||
term.clear()
|
||||
for y = 1, 16 do
|
||||
term.setPos(tw - 1, y)
|
||||
term.setBackground(0)
|
||||
term.setForeground(colors[y])
|
||||
if selectedColor == y then
|
||||
term.setBackground(colors[y])
|
||||
term.write(" ")
|
||||
else
|
||||
term.write("##")
|
||||
end
|
||||
end
|
||||
term.setPos(tw - 1, 17)
|
||||
|
||||
if selectedColor == 0 then
|
||||
term.setBackground(colors.white)
|
||||
term.setForeground(0)
|
||||
else
|
||||
term.setBackground(0)
|
||||
term.setForeground(colors.white)
|
||||
end
|
||||
term.write("XX")
|
||||
|
||||
term.setPos(tw - 1, 18)
|
||||
term.setBackground(colors.black)
|
||||
term.setForeground(colors.white)
|
||||
term.write(thickness)
|
||||
|
||||
gpu.drawLine(canvasW + 1, 0, canvasW, canvasH, colors.gray, 2)
|
||||
|
||||
local b<close> = gpu.bufferFrom(canvas, canvasW, canvasH)
|
||||
gpu.drawBuffer(b, 0, 0, {
|
||||
source = {
|
||||
0, 0, canvasW, canvasH
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
local function contains(arr, val)
|
||||
for i, v in ipairs(arr) do
|
||||
if v == val then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local oldX, oldY
|
||||
while true do
|
||||
drawUI()
|
||||
|
||||
local ev, b, x, y = event.pull("mouse_down", "mouse_up", "mouse_move", "mouse_scroll")
|
||||
local tx, ty = term.fromRealPos(x, y)
|
||||
if ev == "mouse_up" then
|
||||
if x >= canvasW then
|
||||
if ty <= 16 then
|
||||
selectedColor = ty
|
||||
elseif ty == 17 then
|
||||
selectedColor = 0
|
||||
end
|
||||
end
|
||||
oldX, oldY = nil, nil
|
||||
elseif ev == "mouse_down" or (ev == "mouse_move" and contains(b, 1)) then
|
||||
if x < canvasW and y < canvasH then
|
||||
--canvas[x + y * canvasW] = colors[selectedColor] or 0
|
||||
--drawCircle(canvas, x, y, thickness - 2, colors[selectedColor])
|
||||
|
||||
drawLine(canvas, x, y, oldX or x, oldY or y, colors[selectedColor] or 0, thickness)
|
||||
--gpu.drawLine(x, y, oldX or x, oldY or y, colors[selectedColor] or 0)
|
||||
--canvas = gpu.getBuffer()
|
||||
|
||||
oldX, oldY = x, y
|
||||
end
|
||||
elseif ev == "mouse_scroll" then
|
||||
local x, y, b = b, x, y
|
||||
local tx, ty = term.fromRealPos(x, y)
|
||||
if x >= canvasW and ty == 18 then
|
||||
thickness = math.min(99, math.max(0, thickness - b))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -11,7 +11,13 @@ local function slowPrint(text, delay)
|
|||
print()
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
local text = "Hello, World!"
|
||||
if #args > 0 then
|
||||
text = table.concat(args, " ")
|
||||
end
|
||||
|
||||
local color = colors[math.random(1, #colors)]
|
||||
|
||||
term.setForeground(color)
|
||||
slowPrint("Hello, World!", 50)
|
||||
slowPrint(text, 0.05)
|
||||
11
Capy64/Assets/Lua/CapyOS/sys/bin/help.lua
Normal file
11
Capy64/Assets/Lua/CapyOS/sys/bin/help.lua
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
local fs = require("fs")
|
||||
local helpPath = "/sys/share/help"
|
||||
|
||||
local topicName = arg[1] or "index"
|
||||
|
||||
if not fs.exists(fs.combine(helpPath, topicName)) then
|
||||
print(string.format("Topic \"%s\" not found.", topicName))
|
||||
return false
|
||||
end
|
||||
|
||||
shell.run("/sys/bin/less.lua", fs.combine(helpPath, topicName))
|
||||
78
Capy64/Assets/Lua/CapyOS/sys/bin/less.lua
Normal file
78
Capy64/Assets/Lua/CapyOS/sys/bin/less.lua
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
local term = require("term")
|
||||
local keys = require("keys")
|
||||
local event = require("event")
|
||||
local fs = require("fs")
|
||||
local timer = require("timer")
|
||||
local colors = require("colors")
|
||||
|
||||
local filename = shell.resolve(arg[1])
|
||||
|
||||
local f<close> = fs.open(filename, "r")
|
||||
local lines = {}
|
||||
local lineMax = 0
|
||||
for line in f:lines() do
|
||||
table.insert(lines, line)
|
||||
lineMax = math.max(lineMax, #line)
|
||||
end
|
||||
f:close()
|
||||
|
||||
|
||||
local width, height = term.getSize()
|
||||
height = height - 1
|
||||
local posx, posy = 0, 0
|
||||
|
||||
local function redraw()
|
||||
term.clear()
|
||||
term.setForeground(colors.white)
|
||||
for i = 1, height do
|
||||
if i + posy > #lines then
|
||||
break
|
||||
end
|
||||
term.setPos(-posx + 1, i)
|
||||
term.write(lines[i + posy])
|
||||
end
|
||||
|
||||
term.setForeground(colors.yellow)
|
||||
term.setPos(1, height + 1)
|
||||
term.write("Use arrow keys to move or press Q to exit.")
|
||||
end
|
||||
|
||||
while true do
|
||||
redraw()
|
||||
|
||||
local _, key = event.pull("key_down")
|
||||
|
||||
if key == keys.enter or key == keys.down then
|
||||
posy = posy + 1
|
||||
elseif key == keys.up then
|
||||
posy = posy - 1
|
||||
elseif key == keys.right then
|
||||
posx = posx + 1
|
||||
elseif key == keys.left then
|
||||
posx = posx - 1
|
||||
elseif key == keys.q or key == keys.escape then
|
||||
-- Clear event queue
|
||||
timer.sleep(0)
|
||||
term.clear()
|
||||
term.setPos(1, 1)
|
||||
break
|
||||
end
|
||||
|
||||
|
||||
|
||||
if posy > #lines - height then
|
||||
posy = #lines - height
|
||||
end
|
||||
|
||||
if posy < 0 then
|
||||
posy = 0
|
||||
end
|
||||
|
||||
if posx + width > lineMax + 1 then
|
||||
posx = lineMax - width + 1
|
||||
end
|
||||
|
||||
if posx < 0 then
|
||||
posx = 0
|
||||
end
|
||||
end
|
||||
88
Capy64/Assets/Lua/CapyOS/sys/bin/ls.lua
Normal file
88
Capy64/Assets/Lua/CapyOS/sys/bin/ls.lua
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
local fs = require("fs")
|
||||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
local argparser = require("argparser")
|
||||
|
||||
local theme = {
|
||||
directory = colors.lightBlue,
|
||||
file = colors.white,
|
||||
lua = colors.yellow,
|
||||
}
|
||||
|
||||
local function humanizeBytes(n)
|
||||
local prefixes = {
|
||||
[0] = "",
|
||||
"k",
|
||||
"M",
|
||||
"G",
|
||||
"T",
|
||||
}
|
||||
local block = 1024
|
||||
local prefixIndex = 0
|
||||
|
||||
while n >= block do
|
||||
n = n / 1024
|
||||
prefixIndex = prefixIndex + 1
|
||||
end
|
||||
|
||||
return string.format("%.0f%s", n, prefixes[prefixIndex])
|
||||
end
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
if options.h or options.help then
|
||||
print("Usage: ls [option...] [path]")
|
||||
print("List files (current directory by default)")
|
||||
print("Options:")
|
||||
print(" -a: Include hidden files")
|
||||
print(" -l: Use long listing format")
|
||||
return
|
||||
end
|
||||
local path = shell.getDir()
|
||||
|
||||
if args[1] then
|
||||
path = shell.resolve(args[1])
|
||||
end
|
||||
|
||||
if not fs.isDir(path) then
|
||||
error("No such directory: " .. path, 0)
|
||||
return false
|
||||
end
|
||||
|
||||
local entries = fs.list(path)
|
||||
|
||||
if options.l then
|
||||
print(string.format("total %d", #entries))
|
||||
end
|
||||
local printed = 0
|
||||
for i, entry in ipairs(entries) do
|
||||
if entry:sub(1, 1) ~= "." or options.a then
|
||||
printed = printed + 1
|
||||
local attributes = fs.attributes(fs.combine(path, entry))
|
||||
local size = humanizeBytes(attributes.size)
|
||||
local date = os.date("%x %H:%m", attributes.modified // 1000)
|
||||
|
||||
local entryType
|
||||
if attributes.isDirectory then
|
||||
entryType = "directory"
|
||||
else
|
||||
entryType = "file"
|
||||
if string.match(entry, "%.lua$") then
|
||||
entryType = "lua"
|
||||
end
|
||||
end
|
||||
|
||||
if options.l then
|
||||
term.setForeground(colors.white)
|
||||
term.write(string.format("%s %5s %s ", attributes.isDirectory and "d" or "-", size, date))
|
||||
end
|
||||
term.setForeground(theme[entryType])
|
||||
io.write(entry)
|
||||
|
||||
io.write(options.l and "\n" or "\t")
|
||||
end
|
||||
end
|
||||
|
||||
if not options.l and printed > 0 then
|
||||
print()
|
||||
end
|
||||
|
|
@ -1,49 +1,16 @@
|
|||
local term = require("term")
|
||||
local io = require("io")
|
||||
local colors = require("colors")
|
||||
local colours = colors
|
||||
local argparser = require("argparser")
|
||||
local tableutils = require("tableutils")
|
||||
|
||||
local tArgs = { ... }
|
||||
if #tArgs > 0 then
|
||||
print("This is an interactive Lua prompt.")
|
||||
print("To run a lua program, just type its name.")
|
||||
return
|
||||
end
|
||||
|
||||
--local pretty = require "cc.pretty"
|
||||
|
||||
local bRunning = true
|
||||
local tCommandHistory = {}
|
||||
local tEnv = {
|
||||
["exit"] = setmetatable({}, {
|
||||
__tostring = function() return "Call exit() to exit." end,
|
||||
__call = function() bRunning = false end,
|
||||
}),
|
||||
}
|
||||
setmetatable(tEnv, { __index = _ENV })
|
||||
|
||||
for k, v in pairs(package.loaded) do
|
||||
tEnv[k] = v
|
||||
end
|
||||
|
||||
term.setForeground(colours.yellow)
|
||||
print(_VERSION .. " interactive prompt")
|
||||
print("Call exit() to exit.")
|
||||
term.setForeground(colours.white)
|
||||
|
||||
while bRunning do
|
||||
term.setForeground(colours.yellow)
|
||||
io.write("> ")
|
||||
term.setForeground(colours.white)
|
||||
|
||||
local s = io.read(nil, tCommandHistory)
|
||||
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
|
||||
table.insert(tCommandHistory, s)
|
||||
end
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
local function evaluate(str, env, chunkname)
|
||||
chunkname = chunkname or "=lua"
|
||||
local nForcePrint = 0
|
||||
local func, e = load(s, "=lua", "t", tEnv)
|
||||
local func2 = load("return " .. s, "=lua", "t", tEnv)
|
||||
local func, e = load(str, chunkname, "t", env)
|
||||
local func2 = load("return " .. str, chunkname, "t", env)
|
||||
if not func then
|
||||
if func2 then
|
||||
func = func2
|
||||
|
|
@ -62,14 +29,70 @@ while bRunning do
|
|||
local n = 1
|
||||
while n < tResults.n or n <= nForcePrint do
|
||||
local value = tResults[n + 1]
|
||||
print(tostring(value))
|
||||
print(tableutils.pretty(value))
|
||||
n = n + 1
|
||||
end
|
||||
else
|
||||
io.stderr.print(tResults[2])
|
||||
return false
|
||||
end
|
||||
else
|
||||
io.stderr.print(e)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function createEnvironment()
|
||||
return setmetatable({}, { __index = _ENV })
|
||||
end
|
||||
|
||||
local function loadPackages(env)
|
||||
for k, v in pairs(package.loaded) do
|
||||
env[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
if options.e then
|
||||
local env = createEnvironment()
|
||||
loadPackages(env)
|
||||
return evaluate(table.concat(args, " "), env)
|
||||
end
|
||||
|
||||
if #args > 0 then
|
||||
print("This is an interactive Lua prompt.")
|
||||
print("To run a lua program, just type its name.")
|
||||
return
|
||||
end
|
||||
|
||||
--local pretty = require "cc.pretty"
|
||||
|
||||
local bRunning = true
|
||||
local tCommandHistory = {}
|
||||
|
||||
|
||||
local tEnv = createEnvironment()
|
||||
tEnv.exit = setmetatable({}, {
|
||||
__tostring = function() return "Call exit() to exit." end,
|
||||
__call = function() bRunning = false end,
|
||||
})
|
||||
loadPackages(tEnv)
|
||||
|
||||
term.setForeground(colors.yellow)
|
||||
print(_VERSION .. " interactive prompt")
|
||||
print("Call exit() to exit.")
|
||||
term.setForeground(colors.white)
|
||||
|
||||
while bRunning do
|
||||
term.setForeground(colors.yellow)
|
||||
io.write("> ")
|
||||
term.setForeground(colors.white)
|
||||
|
||||
local s = io.read(nil, tCommandHistory)
|
||||
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
|
||||
table.insert(tCommandHistory, s)
|
||||
end
|
||||
|
||||
evaluate(s, tEnv)
|
||||
|
||||
end
|
||||
20
Capy64/Assets/Lua/CapyOS/sys/bin/motd.lua
Normal file
20
Capy64/Assets/Lua/CapyOS/sys/bin/motd.lua
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
local fs = require("fs")
|
||||
|
||||
local date = os.date("*t")
|
||||
|
||||
if date.month == 4 and date.day == 28 then
|
||||
print("Ed Balls")
|
||||
return
|
||||
end
|
||||
|
||||
local motdList = {}
|
||||
|
||||
local f<close> = fs.open("/sys/share/motd.txt", "r")
|
||||
for line in f:lines() do
|
||||
table.insert(motdList, line)
|
||||
end
|
||||
f:close()
|
||||
|
||||
local motdIndex = math.random(1, #motdList)
|
||||
|
||||
print(motdList[motdIndex])
|
||||
16
Capy64/Assets/Lua/CapyOS/sys/bin/mv.lua
Normal file
16
Capy64/Assets/Lua/CapyOS/sys/bin/mv.lua
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
local fs = require("fs")
|
||||
local argparser = require("argparser")
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
if not args[1] or not args[2] or options.h or options.help then
|
||||
print("Usage: mv [option...] <source> <target>")
|
||||
print("Options:")
|
||||
print(" -h --help: Display help")
|
||||
return
|
||||
end
|
||||
|
||||
local source = shell.resolve(args[1])
|
||||
local destination = shell.resolve(args[2])
|
||||
|
||||
fs.move(source, destination)
|
||||
10
Capy64/Assets/Lua/CapyOS/sys/bin/programs.lua
Normal file
10
Capy64/Assets/Lua/CapyOS/sys/bin/programs.lua
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
local fs = require("fs")
|
||||
local programs = fs.list("/sys/bin", function(name, attr)
|
||||
return not attr.isDirectory
|
||||
end)
|
||||
|
||||
for i, v in ipairs(programs) do
|
||||
programs[i] = string.gsub(v, "%.lua$", "")
|
||||
end
|
||||
|
||||
print(table.concat(programs, " "))
|
||||
16
Capy64/Assets/Lua/CapyOS/sys/bin/rm.lua
Normal file
16
Capy64/Assets/Lua/CapyOS/sys/bin/rm.lua
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
local fs = require("fs")
|
||||
local argparser = require("argparser")
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
if not args[1] or options.h or options.help then
|
||||
print("Usage: rm [option...] <path>")
|
||||
print("Options:")
|
||||
print(" -r --recursive: Delete non-empty directories")
|
||||
print(" -h --help: Display help")
|
||||
return
|
||||
end
|
||||
|
||||
local file = shell.resolve(args[1])
|
||||
|
||||
fs.delete(file, options.recursive or options.r)
|
||||
|
|
@ -1,36 +1,36 @@
|
|||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
local fs = require("fs")
|
||||
local machine = require("machine")
|
||||
local argparser = require("argparser")
|
||||
local scheduler = require("scheduler")
|
||||
local createPackageEnvironment = require("shell.package")
|
||||
|
||||
local useScheduler = false
|
||||
local exit = false
|
||||
local parentShell = shell
|
||||
local isStartupShell = parentShell == nil
|
||||
local shell = {}
|
||||
|
||||
shell.path = "./?;./?.lua;/bin/?.lua"
|
||||
shell.homePath = "/home"
|
||||
shell.path = parentShell and parentShell.path or "./?;./?.lua;/bin/?.lua;/sys/bin/?.lua"
|
||||
shell.homePath = parentShell and parentShell.home or "/home"
|
||||
shell.aliases = parentShell and parentShell.aliases or {}
|
||||
|
||||
local currentDir = shell.homePath
|
||||
local currentDir = parentShell and parentShell.getDir() or shell.homePath
|
||||
|
||||
local function buildEnvironment(command, filepath, args, argf)
|
||||
local arg = { table.unpack(args, 2) }
|
||||
arg[0] = command
|
||||
arg.string = argf
|
||||
|
||||
local envPackage = createPackageEnvironment(filepath)
|
||||
envPackage.loaded.scheduler = scheduler
|
||||
|
||||
local function buildEnvironment()
|
||||
return setmetatable({
|
||||
shell = shell,
|
||||
}, { __index = _G })
|
||||
end
|
||||
|
||||
local function tokenise(...)
|
||||
local sLine = table.concat({ ... }, " ")
|
||||
local tWords = {}
|
||||
local bQuoted = false
|
||||
for match in string.gmatch(sLine .. "\"", "(.-)\"") do
|
||||
if bQuoted then
|
||||
table.insert(tWords, match)
|
||||
else
|
||||
for m in string.gmatch(match, "[^ \t]+") do
|
||||
table.insert(tWords, m)
|
||||
end
|
||||
end
|
||||
bQuoted = not bQuoted
|
||||
end
|
||||
return tWords
|
||||
arg = arg,
|
||||
scheduler = scheduler,
|
||||
}, { __index = envPackage.loaded._G })
|
||||
end
|
||||
|
||||
function shell.getDir()
|
||||
|
|
@ -67,16 +67,24 @@ function shell.resolveProgram(path)
|
|||
end
|
||||
|
||||
function shell.run(...)
|
||||
local args = tokenise(...)
|
||||
local args = argparser.tokenize(...)
|
||||
local argf = table.concat({...}, " ")
|
||||
local command = args[1]
|
||||
|
||||
argf = argf:sub(#command + 2)
|
||||
|
||||
local path = shell.resolveProgram(command)
|
||||
|
||||
if not path then
|
||||
io.stderr.print("Command not found: " .. command)
|
||||
return false
|
||||
if shell.aliases[command] then
|
||||
return shell.run(shell.aliases[command], select(2, table.unpack(args)))
|
||||
else
|
||||
io.stderr.print("Command not found: " .. command)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local env = buildEnvironment()
|
||||
local env = buildEnvironment(command, path, args, argf)
|
||||
|
||||
local func, err = loadfile(path, "t", env)
|
||||
|
||||
|
|
@ -85,7 +93,21 @@ function shell.run(...)
|
|||
return false
|
||||
end
|
||||
|
||||
local ok, err = pcall(func, table.unpack(args, 2))
|
||||
local ok, err
|
||||
local function run()
|
||||
ok, err = pcall(func, table.unpack(args, 2))
|
||||
end
|
||||
|
||||
if useScheduler then
|
||||
local programTask, yielded = scheduler.spawn(run)
|
||||
|
||||
if yielded then
|
||||
coroutine.yield("scheduler_task_end")
|
||||
end
|
||||
else
|
||||
run()
|
||||
end
|
||||
|
||||
if not ok then
|
||||
io.stderr.print(err)
|
||||
return false
|
||||
|
|
@ -102,9 +124,26 @@ if not fs.exists(shell.homePath) then
|
|||
fs.makeDir(shell.homePath)
|
||||
end
|
||||
|
||||
term.setForeground(colors.white)
|
||||
term.setBackground(colors.black)
|
||||
|
||||
if isStartupShell then
|
||||
if fs.exists(fs.combine(shell.homePath, ".shrc")) then
|
||||
local f <close> = fs.open(fs.combine(shell.homePath, ".shrc"), "r")
|
||||
for line in f:lines() do
|
||||
if line:match("%S") and not line:match("^%s-#") then
|
||||
shell.run(line)
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
|
||||
local history = {}
|
||||
local lastExecSuccess = true
|
||||
while not exit do
|
||||
machine.setRPC(os.version(), "On shell")
|
||||
|
||||
term.setBackground(colors.black)
|
||||
term.setForeground(colors.white)
|
||||
io.write(":")
|
||||
|
|
@ -130,6 +169,7 @@ while not exit do
|
|||
end
|
||||
|
||||
if line:match("%S") then
|
||||
machine.setRPC(os.version(), "Running: " .. line)
|
||||
lastExecSuccess = shell.run(line)
|
||||
end
|
||||
end
|
||||
33
Capy64/Assets/Lua/CapyOS/sys/bin/shutdown.lua
Normal file
33
Capy64/Assets/Lua/CapyOS/sys/bin/shutdown.lua
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
local machine = require("machine")
|
||||
local scheduler = require("scheduler")
|
||||
local argparser = require("argparser")
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
if options.h or options.help then
|
||||
print("Usage: shutdown [option...]")
|
||||
print("Shutdown or restart Capy64.")
|
||||
print("Options:")
|
||||
print(" -s --shutdown: Shutdown and exit Capy64. (default)")
|
||||
print(" -r --reboot: Restart Capy64.")
|
||||
print(" -t --time: Time to wait in seconds. (\"now\" is 0 seconds, default)")
|
||||
return
|
||||
end
|
||||
|
||||
local time = 0
|
||||
if options.t or options.time then
|
||||
time = options.t or options.time
|
||||
end
|
||||
if time == "now" then
|
||||
time = 0
|
||||
else
|
||||
time = tonumber(time)
|
||||
if not time then
|
||||
error("Invalid time option: " .. (options.t or options.time), 0)
|
||||
end
|
||||
end
|
||||
|
||||
scheduler.ipc(1, "power", {
|
||||
reboot = options.r or options.reboot,
|
||||
time = time,
|
||||
})
|
||||
2
Capy64/Assets/Lua/CapyOS/sys/bin/version.lua
Normal file
2
Capy64/Assets/Lua/CapyOS/sys/bin/version.lua
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
local machine = require("machine")
|
||||
print(string.format("%s @ %s - %s", os.version(), machine.version(), _VERSION))
|
||||
|
|
@ -19,7 +19,9 @@ if not http.checkURL(args[1]) then
|
|||
error("Invalid URL", 0)
|
||||
end
|
||||
|
||||
local response <close>, err = http.get(args[1], nil, {
|
||||
print("Connecting...")
|
||||
|
||||
local response, err = http.get(args[1], nil, {
|
||||
binary = true,
|
||||
})
|
||||
if not response then
|
||||
|
|
@ -27,8 +29,8 @@ if not response then
|
|||
end
|
||||
|
||||
local file <close> = fs.open(outputPath, "wb")
|
||||
file:write(response:readAll())
|
||||
file:write(response.content:read("a"))
|
||||
file:close()
|
||||
response:close()
|
||||
response.content:close()
|
||||
|
||||
print("File written to " .. outputPath)
|
||||
print("Downloaded to " .. outputPath)
|
||||
3
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/00_package.lua
Normal file
3
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/00_package.lua
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package.path = "/lib/?.lua;/lib/?/init.lua;/sys/lib/?.lua;/sys/lib/?/init.lua;" .. package.path
|
||||
|
||||
_G.io = require("io")
|
||||
28
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/02_fs.lua
Normal file
28
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/02_fs.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
local fs = require("fs")
|
||||
local expect = require("expect").expect
|
||||
|
||||
local fsList = fs.list
|
||||
|
||||
function fs.list(path, filter)
|
||||
expect(1, path, "string")
|
||||
expect(2, filter, "nil", "function")
|
||||
|
||||
if not fs.isDir(path) then
|
||||
error("directory not found", 2)
|
||||
end
|
||||
|
||||
local list = fsList(path)
|
||||
if not filter then
|
||||
return list
|
||||
end
|
||||
|
||||
local filteredList = {}
|
||||
for i = 1, #list do
|
||||
local attributes = fs.attributes(fs.combine(path, list[i]))
|
||||
if filter(list[i], attributes) then
|
||||
table.insert(filteredList, list[i])
|
||||
end
|
||||
end
|
||||
|
||||
return filteredList
|
||||
end
|
||||
|
|
@ -13,17 +13,8 @@ function http.request(url, body, headers, options)
|
|||
return nil, "Invalid URL"
|
||||
end
|
||||
|
||||
local requestId = http.requestAsync(url, body, headers, options)
|
||||
local ev, id, data, info
|
||||
repeat
|
||||
ev, id, data, info = event.pull("http_response", "http_failure")
|
||||
until id == requestId
|
||||
|
||||
if ev == "http_failure" then
|
||||
return nil, data
|
||||
end
|
||||
|
||||
return data, info
|
||||
local task<close> = http.requestAsync(url, body, headers, options)
|
||||
return task:await()
|
||||
end
|
||||
|
||||
function http.get(url, headers, options)
|
||||
|
|
@ -45,6 +36,9 @@ end
|
|||
|
||||
local WebSocketHandle
|
||||
local function buildWebsocketHandle(handle)
|
||||
if not handle then
|
||||
return nil
|
||||
end
|
||||
if not WebSocketHandle then
|
||||
WebSocketHandle = getmetatable(handle) or { __index = {} }
|
||||
function WebSocketHandle.__index:close()
|
||||
|
|
@ -76,15 +70,8 @@ function http.websocket(url, headers)
|
|||
return nil, "Invalid URL"
|
||||
end
|
||||
|
||||
local requestId = http.websocketAsync(url, headers)
|
||||
local ev, id, par
|
||||
repeat
|
||||
ev, id, par = event.pull("websocket_connect", "websocket_failure")
|
||||
until id == requestId
|
||||
local task<close> = http.websocketAsync(url, headers)
|
||||
local client, err = task:await()
|
||||
|
||||
if ev == "http_failure" then
|
||||
return nil, par
|
||||
end
|
||||
|
||||
return buildWebsocketHandle(par)
|
||||
return buildWebsocketHandle(client), err
|
||||
end
|
||||
|
|
@ -5,7 +5,6 @@ local range = require("expect").range
|
|||
|
||||
function timer.sleep(n)
|
||||
expect(1, n, "number")
|
||||
range(1, 1)
|
||||
|
||||
local timerId = timer.start(n)
|
||||
repeat
|
||||
47
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/50_os_manager.lua
Normal file
47
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/50_os_manager.lua
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
local machine = require("machine")
|
||||
local scheduler = require("scheduler")
|
||||
|
||||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
term.setForeground(0x59c9ff)
|
||||
term.setBackground(colors.black)
|
||||
term.clear()
|
||||
term.setPos(1, 1)
|
||||
|
||||
term.write(os.version())
|
||||
term.setPos(1, 2)
|
||||
|
||||
local function spawnShell()
|
||||
return scheduler.spawn(loadfile("/sys/bin/shell.lua"))
|
||||
end
|
||||
|
||||
local function main()
|
||||
local shellTask = spawnShell()
|
||||
while true do
|
||||
local ev = {coroutine.yield()}
|
||||
if ev[1] == "ipc_message" then
|
||||
local sender = ev[2]
|
||||
local call = ev[3]
|
||||
if call == "power" then
|
||||
local options = ev[4]
|
||||
--todo: handle time and cancels
|
||||
if options.reboot then
|
||||
machine.reboot()
|
||||
else
|
||||
machine.shutdown()
|
||||
end
|
||||
end
|
||||
elseif ev[1] == "scheduler_task_end" then
|
||||
if ev[2].pid == shellTask.pid then
|
||||
if not ev[3] then
|
||||
io.stderr.print(ev[4])
|
||||
end
|
||||
shellTask = spawnShell()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scheduler.spawn(main)
|
||||
|
||||
scheduler.init()
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("machine").shutdown()
|
||||
91
Capy64/Assets/Lua/CapyOS/sys/lib/argparser.lua
Normal file
91
Capy64/Assets/Lua/CapyOS/sys/lib/argparser.lua
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
local argparser = {}
|
||||
|
||||
function argparser.tokenize(...)
|
||||
local input = table.concat(table.pack(...), " ")
|
||||
local tokens = {}
|
||||
|
||||
-- surely there must be a better way
|
||||
local quoted = false
|
||||
local escaped = false
|
||||
local current = ""
|
||||
for i = 1, #input do
|
||||
local char = input:sub(i, i)
|
||||
if escaped then
|
||||
escaped = false
|
||||
current = current .. char
|
||||
else
|
||||
if char == "\\" then
|
||||
escaped = true
|
||||
elseif char == "\"" then
|
||||
if quoted then
|
||||
-- close quote
|
||||
table.insert(tokens, current)
|
||||
current = ""
|
||||
end
|
||||
quoted = not quoted
|
||||
elseif char == " " and not quoted then
|
||||
if #current > 0 then
|
||||
table.insert(tokens, current)
|
||||
end
|
||||
current = ""
|
||||
else
|
||||
current = current .. char
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if current ~= "" then
|
||||
table.insert(tokens, current)
|
||||
end
|
||||
|
||||
return tokens
|
||||
end
|
||||
|
||||
function argparser.parse(...)
|
||||
local tokens = { ... }
|
||||
local args = {}
|
||||
local options = {}
|
||||
local ignoreOptions = false
|
||||
|
||||
for i = 1, #tokens do
|
||||
local token = tokens[i]
|
||||
if not ignoreOptions then
|
||||
if token == "--" then
|
||||
ignoreOptions = true
|
||||
elseif token:sub(1, 2) == "--" then
|
||||
local opt, value = token:match("%-%-(.+)=(.+)")
|
||||
if not opt then
|
||||
opt = token:sub(3)
|
||||
if opt:sub(-1) == "=" then
|
||||
-- next token is value
|
||||
value = tokens[i + 1]
|
||||
opt = opt:sub(1, -2)
|
||||
options[opt] = value
|
||||
i = i + 1
|
||||
else
|
||||
options[opt] = true
|
||||
end
|
||||
else
|
||||
options[opt] = value
|
||||
end
|
||||
elseif token:sub(1, 1) == "-" then
|
||||
local opts = token:sub(2)
|
||||
for j = 1, #opts do
|
||||
options[opts:sub(j, j)] = true
|
||||
end
|
||||
else
|
||||
if #token > 0 then
|
||||
table.insert(args, token)
|
||||
end
|
||||
end
|
||||
else
|
||||
if #token > 0 then
|
||||
table.insert(args, token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return args, options
|
||||
end
|
||||
|
||||
return argparser
|
||||
616
Capy64/Assets/Lua/CapyOS/sys/lib/io.lua
Normal file
616
Capy64/Assets/Lua/CapyOS/sys/lib/io.lua
Normal file
|
|
@ -0,0 +1,616 @@
|
|||
local expect = require("expect").expect
|
||||
local event = require("event")
|
||||
local term = require("term")
|
||||
local keys = require("keys")
|
||||
local machine = require("machine")
|
||||
|
||||
local io = {}
|
||||
|
||||
function io.write(sText)
|
||||
sText = tostring(sText)
|
||||
|
||||
local w, h = term.getSize()
|
||||
local x, y = term.getPos()
|
||||
|
||||
local nLinesPrinted = 0
|
||||
local function newLine()
|
||||
if y + 1 <= h then
|
||||
term.setPos(1, y + 1)
|
||||
else
|
||||
term.setPos(1, h)
|
||||
term.scroll(1)
|
||||
end
|
||||
x, y = term.getPos()
|
||||
nLinesPrinted = nLinesPrinted + 1
|
||||
end
|
||||
|
||||
-- Print the line with proper word wrapping
|
||||
sText = tostring(sText)
|
||||
while #sText > 0 do
|
||||
local whitespace = string.match(sText, "^[ \t]+")
|
||||
if whitespace then
|
||||
-- Print whitespace
|
||||
term.write(whitespace)
|
||||
x, y = term.getPos()
|
||||
sText = string.sub(sText, #whitespace + 1)
|
||||
end
|
||||
|
||||
local newline = string.match(sText, "^\n")
|
||||
if newline then
|
||||
-- Print newlines
|
||||
newLine()
|
||||
sText = string.sub(sText, 2)
|
||||
end
|
||||
|
||||
local text = string.match(sText, "^[^ \t\n]+")
|
||||
if text then
|
||||
sText = string.sub(sText, #text + 1)
|
||||
if #text > w then
|
||||
-- Print a multiline word
|
||||
while #text > 0 do
|
||||
if x > w then
|
||||
newLine()
|
||||
end
|
||||
term.write(text)
|
||||
text = string.sub(text, w - x + 2)
|
||||
x, y = term.getPos()
|
||||
end
|
||||
else
|
||||
-- Print a word normally
|
||||
if x + #text - 1 > w then
|
||||
newLine()
|
||||
end
|
||||
term.write(text)
|
||||
x, y = term.getPos()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nLinesPrinted
|
||||
end
|
||||
|
||||
function io.read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
||||
expect(1, _sReplaceChar, "string", "nil")
|
||||
expect(2, _tHistory, "table", "nil")
|
||||
expect(3, _fnComplete, "function", "nil")
|
||||
expect(4, _sDefault, "string", "nil")
|
||||
|
||||
term.setBlink(true)
|
||||
|
||||
local sLine
|
||||
if type(_sDefault) == "string" then
|
||||
sLine = _sDefault
|
||||
else
|
||||
sLine = ""
|
||||
end
|
||||
local nHistoryPos
|
||||
local nPos, nScroll = #sLine, 0
|
||||
if _sReplaceChar then
|
||||
_sReplaceChar = string.sub(_sReplaceChar, 1, 1)
|
||||
end
|
||||
|
||||
local tCompletions
|
||||
local nCompletion
|
||||
local function recomplete()
|
||||
if _fnComplete and nPos == #sLine then
|
||||
tCompletions = _fnComplete(sLine)
|
||||
if tCompletions and #tCompletions > 0 then
|
||||
nCompletion = 1
|
||||
else
|
||||
nCompletion = nil
|
||||
end
|
||||
else
|
||||
tCompletions = nil
|
||||
nCompletion = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function uncomplete()
|
||||
tCompletions = nil
|
||||
nCompletion = nil
|
||||
end
|
||||
|
||||
local w = term.getSize()
|
||||
local sx = term.getPos()
|
||||
|
||||
local function redraw(_bClear)
|
||||
local cursor_pos = nPos - nScroll
|
||||
if sx + cursor_pos >= w then
|
||||
-- We've moved beyond the RHS, ensure we're on the edge.
|
||||
nScroll = sx + nPos - w
|
||||
elseif cursor_pos < 0 then
|
||||
-- We've moved beyond the LHS, ensure we're on the edge.
|
||||
nScroll = nPos
|
||||
end
|
||||
|
||||
local _, cy = term.getPos()
|
||||
term.setPos(sx, cy)
|
||||
local sReplace = _bClear and " " or _sReplaceChar
|
||||
if sReplace then
|
||||
term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
|
||||
else
|
||||
term.write(string.sub(sLine, nScroll + 1))
|
||||
end
|
||||
|
||||
if nCompletion then
|
||||
local sCompletion = tCompletions[nCompletion]
|
||||
local oldText, oldBg
|
||||
if not _bClear then
|
||||
oldText = term.getTextColor()
|
||||
oldBg = term.getBackgroundColor()
|
||||
term.setTextColor(colors.white)
|
||||
term.setBackgroundColor(colors.gray)
|
||||
end
|
||||
if sReplace then
|
||||
term.write(string.rep(sReplace, #sCompletion))
|
||||
else
|
||||
term.write(sCompletion)
|
||||
end
|
||||
if not _bClear then
|
||||
term.setTextColor(oldText)
|
||||
term.setBackgroundColor(oldBg)
|
||||
end
|
||||
end
|
||||
|
||||
term.setPos(sx + nPos - nScroll, cy)
|
||||
end
|
||||
|
||||
local function clear()
|
||||
redraw(true)
|
||||
end
|
||||
|
||||
recomplete()
|
||||
redraw()
|
||||
|
||||
local function acceptCompletion()
|
||||
if nCompletion then
|
||||
-- Clear
|
||||
clear()
|
||||
|
||||
-- Find the common prefix of all the other suggestions which start with the same letter as the current one
|
||||
local sCompletion = tCompletions[nCompletion]
|
||||
sLine = sLine .. sCompletion
|
||||
nPos = #sLine
|
||||
|
||||
-- Redraw
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
end
|
||||
while true do
|
||||
local sEvent, param, param1, param2 = event.pull()
|
||||
if sEvent == "char" then
|
||||
-- Typed key
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
||||
nPos = nPos + 1
|
||||
recomplete()
|
||||
redraw()
|
||||
|
||||
elseif sEvent == "paste" then
|
||||
-- Pasted text
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
||||
nPos = nPos + #param
|
||||
recomplete()
|
||||
redraw()
|
||||
|
||||
elseif sEvent == "key_down" then
|
||||
if param == keys.enter or param == keys.numPadEnter then
|
||||
-- Enter/Numpad Enter
|
||||
if nCompletion then
|
||||
clear()
|
||||
uncomplete()
|
||||
redraw()
|
||||
end
|
||||
break
|
||||
|
||||
elseif param == keys.left then
|
||||
-- Left
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
nPos = nPos - 1
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.right then
|
||||
-- Right
|
||||
if nPos < #sLine then
|
||||
-- Move right
|
||||
clear()
|
||||
nPos = nPos + 1
|
||||
recomplete()
|
||||
redraw()
|
||||
else
|
||||
-- Accept autocomplete
|
||||
acceptCompletion()
|
||||
end
|
||||
|
||||
elseif param == keys.up or param == keys.down then
|
||||
-- Up or down
|
||||
if nCompletion then
|
||||
-- Cycle completions
|
||||
clear()
|
||||
if param == keys.up then
|
||||
nCompletion = nCompletion - 1
|
||||
if nCompletion < 1 then
|
||||
nCompletion = #tCompletions
|
||||
end
|
||||
elseif param == keys.down then
|
||||
nCompletion = nCompletion + 1
|
||||
if nCompletion > #tCompletions then
|
||||
nCompletion = 1
|
||||
end
|
||||
end
|
||||
redraw()
|
||||
|
||||
elseif _tHistory then
|
||||
-- Cycle history
|
||||
clear()
|
||||
if param == keys.up then
|
||||
-- Up
|
||||
if nHistoryPos == nil then
|
||||
if #_tHistory > 0 then
|
||||
nHistoryPos = #_tHistory
|
||||
end
|
||||
elseif nHistoryPos > 1 then
|
||||
nHistoryPos = nHistoryPos - 1
|
||||
end
|
||||
else
|
||||
-- Down
|
||||
if nHistoryPos == #_tHistory then
|
||||
nHistoryPos = nil
|
||||
elseif nHistoryPos ~= nil then
|
||||
nHistoryPos = nHistoryPos + 1
|
||||
end
|
||||
end
|
||||
if nHistoryPos then
|
||||
sLine = _tHistory[nHistoryPos]
|
||||
nPos, nScroll = #sLine, 0
|
||||
else
|
||||
sLine = ""
|
||||
nPos, nScroll = 0, 0
|
||||
end
|
||||
uncomplete()
|
||||
redraw()
|
||||
|
||||
end
|
||||
|
||||
elseif param == keys.back then
|
||||
-- Backspace
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
|
||||
nPos = nPos - 1
|
||||
if nScroll > 0 then nScroll = nScroll - 1 end
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.home then
|
||||
-- Home
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
nPos = 0
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.delete then
|
||||
-- Delete
|
||||
if nPos < #sLine then
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys["end"] then
|
||||
-- End
|
||||
if nPos < #sLine then
|
||||
clear()
|
||||
nPos = #sLine
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.tab then
|
||||
-- Tab (accept autocomplete)
|
||||
acceptCompletion()
|
||||
|
||||
end
|
||||
|
||||
elseif sEvent == "mouse_down" or sEvent == "mouse_drag" and param == 1 then
|
||||
local _, cy = term.getPos()
|
||||
if param1 >= sx and param1 <= w and param2 == cy then
|
||||
-- Ensure we don't scroll beyond the current line
|
||||
nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine)
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif sEvent == "term_resize" then
|
||||
-- Terminal resized
|
||||
w = term.getSize()
|
||||
redraw()
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local _, cy = term.getPos()
|
||||
term.setBlink(false)
|
||||
term.setPos(w + 1, cy)
|
||||
print()
|
||||
|
||||
return sLine
|
||||
end
|
||||
|
||||
|
||||
--[[function io.write(text)
|
||||
text = tostring(text)
|
||||
|
||||
local lines = 0
|
||||
local w, h = term.getSize()
|
||||
|
||||
local function inc_cy(cy)
|
||||
lines = lines + 1
|
||||
|
||||
if cy > h - 1 then
|
||||
term.scroll(1)
|
||||
return cy
|
||||
else
|
||||
return cy + 1
|
||||
end
|
||||
end
|
||||
|
||||
while #text > 0 do
|
||||
local nl = text:find("\n") or #text
|
||||
local chunk = text:sub(1, nl)
|
||||
text = text:sub(#chunk + 1)
|
||||
|
||||
local has_nl = chunk:sub(-1) == "\n"
|
||||
if has_nl then chunk = chunk:sub(1, -2) end
|
||||
|
||||
local cx, cy = term.getPos()
|
||||
while #chunk > 0 do
|
||||
if cx > w then
|
||||
term.setPos(1, inc_cy(cy))
|
||||
cx, cy = term.getPos()
|
||||
end
|
||||
|
||||
local to_write = chunk:sub(1, w - cx + 1)
|
||||
term.write(to_write)
|
||||
|
||||
chunk = chunk:sub(#to_write + 1)
|
||||
cx, cy = term.getPos()
|
||||
end
|
||||
|
||||
if has_nl then
|
||||
term.setPos(1, inc_cy(cy))
|
||||
end
|
||||
end
|
||||
|
||||
return lines
|
||||
end
|
||||
|
||||
local empty = {}
|
||||
function io.read(replace, history, complete, default)
|
||||
expect(1, replace, "string", "nil")
|
||||
expect(2, history, "table", "nil")
|
||||
expect(3, complete, "function", "nil")
|
||||
expect(4, default, "string", "nil")
|
||||
|
||||
if replace then replace = replace:sub(1, 1) end
|
||||
local hist = history or {}
|
||||
history = {}
|
||||
for i = 1, #hist, 1 do
|
||||
history[i] = hist[i]
|
||||
end
|
||||
|
||||
local buffer = default or ""
|
||||
local prev_buf = buffer
|
||||
history[#history + 1] = buffer
|
||||
|
||||
local hist_pos = #history
|
||||
local cursor_pos = 0
|
||||
|
||||
local stx, sty = term.getPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
local dirty = false
|
||||
local completions = {}
|
||||
local comp_id = 0
|
||||
|
||||
local function clearCompletion()
|
||||
if completions[comp_id] then
|
||||
io.write((" "):rep(#completions[comp_id]))
|
||||
end
|
||||
end
|
||||
|
||||
local function full_redraw(force)
|
||||
if force or dirty then
|
||||
if complete and buffer ~= prev_buf then
|
||||
completions = complete(buffer) or empty
|
||||
comp_id = math.min(1, #completions)
|
||||
end
|
||||
prev_buf = buffer
|
||||
|
||||
term.setPos(stx, sty)
|
||||
local text = buffer
|
||||
if replace then text = replace:rep(#text) end
|
||||
local ln = io.write(text)
|
||||
|
||||
if completions[comp_id] then
|
||||
local oldfg = term.getForeground()
|
||||
local oldbg = term.getBackground()
|
||||
term.setForeground(colors.white)
|
||||
term.setBackground(colors.gray)
|
||||
ln = ln + write(completions[comp_id])
|
||||
term.setForeground(oldfg)
|
||||
term.setBackground(oldbg)
|
||||
else
|
||||
ln = ln + io.write(" ")
|
||||
end
|
||||
|
||||
if sty + ln > h then
|
||||
sty = sty - (sty + ln - h)
|
||||
end
|
||||
end
|
||||
|
||||
-- set cursor to the appropriate spot
|
||||
local cx, cy = stx, sty
|
||||
cx = cx + #buffer - cursor_pos -- + #(completions[comp_id] or "")
|
||||
while cx > w do
|
||||
cx = cx - w
|
||||
cy = cy + 1
|
||||
end
|
||||
term.setPos(cx, cy)
|
||||
end
|
||||
|
||||
term.setBlink(true)
|
||||
|
||||
while true do
|
||||
full_redraw()
|
||||
-- get input
|
||||
local evt, par1, par2, mods = event.pull()
|
||||
|
||||
if evt == "char" then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer .. par1
|
||||
elseif cursor_pos == #buffer then
|
||||
buffer = par1 .. buffer
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub(-cursor_pos)
|
||||
end
|
||||
elseif evt == "key_down" then
|
||||
if par1 == keys.back and #buffer > 0 then
|
||||
dirty = true
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer:sub(1, -2)
|
||||
clearCompletion()
|
||||
elseif cursor_pos < #buffer then
|
||||
buffer = buffer:sub(0, -cursor_pos - 2) .. buffer:sub(-cursor_pos)
|
||||
end
|
||||
elseif par1 == keys.delete and cursor_pos > 0 then
|
||||
dirty = true
|
||||
|
||||
if cursor_pos == #buffer then
|
||||
buffer = buffer:sub(2)
|
||||
elseif cursor_pos == 1 then
|
||||
buffer = buffer:sub(1, -2)
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1)
|
||||
end
|
||||
cursor_pos = cursor_pos - 1
|
||||
elseif par1 == keys.up then
|
||||
if #completions > 1 then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if comp_id > 1 then
|
||||
comp_id = comp_id - 1
|
||||
else
|
||||
comp_id = #completions
|
||||
end
|
||||
elseif hist_pos > 1 then
|
||||
cursor_pos = 0
|
||||
|
||||
history[hist_pos] = buffer
|
||||
hist_pos = hist_pos - 1
|
||||
|
||||
buffer = (" "):rep(#buffer)
|
||||
full_redraw(true)
|
||||
|
||||
buffer = history[hist_pos]
|
||||
dirty = true
|
||||
end
|
||||
elseif par1 == keys.down then
|
||||
if #completions > 1 then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if comp_id < #completions then
|
||||
comp_id = comp_id + 1
|
||||
else
|
||||
comp_id = 1
|
||||
end
|
||||
elseif hist_pos < #history then
|
||||
cursor_pos = 0
|
||||
|
||||
history[hist_pos] = buffer
|
||||
hist_pos = hist_pos + 1
|
||||
|
||||
buffer = (" "):rep(#buffer)
|
||||
full_redraw(true)
|
||||
|
||||
buffer = history[hist_pos]
|
||||
dirty = true
|
||||
end
|
||||
elseif par1 == keys.left then
|
||||
if cursor_pos < #buffer then
|
||||
clearCompletion()
|
||||
cursor_pos = cursor_pos + 1
|
||||
end
|
||||
elseif par1 == keys.right then
|
||||
if cursor_pos > 0 then
|
||||
cursor_pos = cursor_pos - 1
|
||||
elseif comp_id > 0 then
|
||||
dirty = true
|
||||
buffer = buffer .. completions[comp_id]
|
||||
end
|
||||
elseif par1 == keys.tab then
|
||||
if comp_id > 0 then
|
||||
dirty = true
|
||||
buffer = buffer .. completions[comp_id]
|
||||
end
|
||||
elseif par1 == keys.home then
|
||||
cursor_pos = #buffer
|
||||
elseif par1 == keys["end"] then
|
||||
cursor_pos = 0
|
||||
elseif par1 == keys.enter then
|
||||
clearCompletion()
|
||||
print()
|
||||
break
|
||||
elseif mods & keys.mods.ctrl ~= 0 then
|
||||
if par1 == keys.v then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
local text = machine.getClipboard()
|
||||
if text then
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer .. text
|
||||
elseif cursor_pos == #buffer then
|
||||
buffer = text .. buffer
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. text ..
|
||||
buffer:sub(-cursor_pos + (#text - 1))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
term.setBlink(false)
|
||||
|
||||
return buffer
|
||||
end
|
||||
]]
|
||||
|
||||
io.stderr = {}
|
||||
|
||||
function io.stderr.write(text)
|
||||
local fg = term.getForeground()
|
||||
term.setForeground(0xff0000)
|
||||
io.write(text)
|
||||
term.setForeground(fg)
|
||||
end
|
||||
|
||||
function io.stderr.print(...)
|
||||
local fg = term.getForeground()
|
||||
term.setForeground(0xff0000)
|
||||
print(...)
|
||||
term.setForeground(fg)
|
||||
end
|
||||
|
||||
return io
|
||||
388
Capy64/Assets/Lua/CapyOS/sys/lib/json.lua
Normal file
388
Capy64/Assets/Lua/CapyOS/sys/lib/json.lua
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\",
|
||||
[ "\"" ] = "\"",
|
||||
[ "\b" ] = "b",
|
||||
[ "\f" ] = "f",
|
||||
[ "\n" ] = "n",
|
||||
[ "\r" ] = "r",
|
||||
[ "\t" ] = "t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local res = ""
|
||||
local j = i + 1
|
||||
local k = j
|
||||
|
||||
while j <= #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
|
||||
elseif x == 92 then -- `\`: Escape
|
||||
res = res .. str:sub(k, j - 1)
|
||||
j = j + 1
|
||||
local c = str:sub(j, j)
|
||||
if c == "u" then
|
||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||
or str:match("^%x%x%x%x", j + 1)
|
||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||
res = res .. parse_unicode_escape(hex)
|
||||
j = j + #hex
|
||||
else
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
res = res .. escape_char_map_inv[c]
|
||||
end
|
||||
k = j + 1
|
||||
|
||||
elseif x == 34 then -- `"`: End of string
|
||||
res = res .. str:sub(k, j - 1)
|
||||
return res, j + 1
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
return json
|
||||
179
Capy64/Assets/Lua/CapyOS/sys/lib/keys.lua
Normal file
179
Capy64/Assets/Lua/CapyOS/sys/lib/keys.lua
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
local keys = {
|
||||
none = 0,
|
||||
back = 8,
|
||||
tab = 9,
|
||||
enter = 13,
|
||||
pause = 19,
|
||||
caps_lock = 20,
|
||||
kana = 21,
|
||||
kanji = 25,
|
||||
escape = 27,
|
||||
ime_convert = 28,
|
||||
ime_no_convert = 29,
|
||||
space = 32,
|
||||
page_up = 33,
|
||||
page_down = 34,
|
||||
["end"] = 35,
|
||||
home = 36,
|
||||
left = 37,
|
||||
up = 38,
|
||||
right = 39,
|
||||
down = 40,
|
||||
select = 41,
|
||||
print = 42,
|
||||
execute = 43,
|
||||
print_screen = 44,
|
||||
insert = 45,
|
||||
delete = 46,
|
||||
help = 47,
|
||||
zero = 48,
|
||||
one = 49,
|
||||
two = 50,
|
||||
three = 51,
|
||||
four = 52,
|
||||
five = 53,
|
||||
six = 54,
|
||||
seven = 55,
|
||||
eight = 56,
|
||||
nine = 57,
|
||||
a = 65,
|
||||
b = 66,
|
||||
c = 67,
|
||||
d = 68,
|
||||
e = 69,
|
||||
f = 70,
|
||||
g = 71,
|
||||
h = 72,
|
||||
i = 73,
|
||||
j = 74,
|
||||
k = 75,
|
||||
l = 76,
|
||||
m = 77,
|
||||
n = 78,
|
||||
o = 79,
|
||||
p = 80,
|
||||
q = 81,
|
||||
r = 82,
|
||||
s = 83,
|
||||
t = 84,
|
||||
u = 85,
|
||||
v = 86,
|
||||
w = 87,
|
||||
x = 88,
|
||||
y = 89,
|
||||
z = 90,
|
||||
left_windows = 91,
|
||||
right_windows = 92,
|
||||
apps = 93,
|
||||
sleep = 95,
|
||||
num_pad0 = 96,
|
||||
num_pad1 = 97,
|
||||
num_pad2 = 98,
|
||||
num_pad3 = 99,
|
||||
num_pad4 = 100,
|
||||
num_pad5 = 101,
|
||||
num_pad6 = 102,
|
||||
num_pad7 = 103,
|
||||
num_pad8 = 104,
|
||||
num_pad9 = 105,
|
||||
multiply = 106,
|
||||
add = 107,
|
||||
separator = 108,
|
||||
subtract = 109,
|
||||
decimal = 110,
|
||||
divide = 111,
|
||||
f1 = 112,
|
||||
f2 = 113,
|
||||
f3 = 114,
|
||||
f4 = 115,
|
||||
f5 = 116,
|
||||
f6 = 117,
|
||||
f7 = 118,
|
||||
f8 = 119,
|
||||
f9 = 120,
|
||||
f10 = 121,
|
||||
f11 = 122,
|
||||
f12 = 123,
|
||||
f13 = 124,
|
||||
f14 = 125,
|
||||
f15 = 126,
|
||||
f16 = 127,
|
||||
f17 = 128,
|
||||
f18 = 129,
|
||||
f19 = 130,
|
||||
f20 = 131,
|
||||
f21 = 132,
|
||||
f22 = 133,
|
||||
f23 = 134,
|
||||
f24 = 135,
|
||||
num_lock = 144,
|
||||
scroll = 145,
|
||||
left_shift = 160,
|
||||
right_shift = 161,
|
||||
left_control = 162,
|
||||
right_control = 163,
|
||||
left_alt = 164,
|
||||
right_alt = 165,
|
||||
browser_back = 166,
|
||||
browser_forward = 167,
|
||||
browser_refresh = 168,
|
||||
browser_stop = 169,
|
||||
browser_search = 170,
|
||||
browser_favorites = 171,
|
||||
browser_home = 172,
|
||||
volume_mute = 173,
|
||||
volume_down = 174,
|
||||
volume_up = 175,
|
||||
media_next_track = 176,
|
||||
media_previous_track = 177,
|
||||
media_stop = 178,
|
||||
media_play_pause = 179,
|
||||
launch_mail = 180,
|
||||
select_media = 181,
|
||||
launch_application1 = 182,
|
||||
launch_application2 = 183,
|
||||
semicolon = 186,
|
||||
plus = 187,
|
||||
comma = 188,
|
||||
minus = 189,
|
||||
period = 190,
|
||||
question = 191,
|
||||
tilde = 192,
|
||||
chat_pad_green = 202,
|
||||
chat_pad_orange = 203,
|
||||
open_brackets = 219,
|
||||
pipe = 220,
|
||||
close_brackets = 221,
|
||||
quotes = 222,
|
||||
oem8 = 223,
|
||||
backslash = 226,
|
||||
process_key = 229,
|
||||
copy = 242,
|
||||
auto = 243,
|
||||
enl_w = 244,
|
||||
attn = 246,
|
||||
crsel = 247,
|
||||
exsel = 248,
|
||||
erase_eof = 249,
|
||||
play = 250,
|
||||
zoom = 251,
|
||||
pa1 = 253,
|
||||
clear = 254,
|
||||
}
|
||||
|
||||
keys.mods = {
|
||||
none = 0,
|
||||
left_shift = 1,
|
||||
right_shift = 2,
|
||||
left_alt = 4,
|
||||
right_alt = 8,
|
||||
left_control = 16,
|
||||
right_control = 32,
|
||||
}
|
||||
|
||||
keys.mods.shift = keys.mods.left_shift | keys.mods.right_shift
|
||||
keys.mods.alt = keys.mods.left_alt | keys.mods.right_alt
|
||||
keys.mods.control = keys.mods.left_control | keys.mods.right_control
|
||||
keys.mods.ctrl = keys.mods.control
|
||||
|
||||
return keys
|
||||
3
Capy64/Assets/Lua/CapyOS/sys/lib/lib.lua
Normal file
3
Capy64/Assets/Lua/CapyOS/sys/lib/lib.lua
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
local x = math.random()
|
||||
|
||||
return x
|
||||
182
Capy64/Assets/Lua/CapyOS/sys/lib/scheduler.lua
Normal file
182
Capy64/Assets/Lua/CapyOS/sys/lib/scheduler.lua
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
local expect = require("expect").expect
|
||||
local tableutils = require("tableutils")
|
||||
local event = require("event")
|
||||
|
||||
local scheduler = {}
|
||||
|
||||
local function contains(array, value)
|
||||
for k, v in pairs(array) do
|
||||
if v == value then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local tasks = {}
|
||||
local processes = 0
|
||||
|
||||
local Task = {}
|
||||
local TaskMeta = {
|
||||
__index = Task,
|
||||
__name = "OS_TASK",
|
||||
__tostring = function(self)
|
||||
return string.format("OS_TASK[%s]: %d", self.source or "", self.pid or 0)
|
||||
end,
|
||||
}
|
||||
local function newTask()
|
||||
local task = {}
|
||||
return setmetatable(task, TaskMeta)
|
||||
end
|
||||
|
||||
function Task:queue(eventName, ...)
|
||||
expect(1, eventName, "string")
|
||||
event.push("scheduler", self.pid, eventName, ...)
|
||||
end
|
||||
|
||||
local function findParent()
|
||||
local i = 3
|
||||
|
||||
while true do
|
||||
local info = debug.getinfo(i)
|
||||
if not info then
|
||||
break
|
||||
end
|
||||
|
||||
for pid, task in pairs(tasks) do
|
||||
if task.uuid == tostring(info.func) then
|
||||
return task
|
||||
end
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function cascadeKill(pid, err)
|
||||
local task = tasks[pid]
|
||||
if not task then
|
||||
return
|
||||
end
|
||||
for i, cpid in ipairs(task.children) do
|
||||
cascadeKill(cpid, err)
|
||||
end
|
||||
if task.parent then
|
||||
local parent = tasks[task.parent]
|
||||
if parent then
|
||||
local index = tableutils.find(parent.children, task.pid)
|
||||
table.remove(parent.children, index)
|
||||
parent:queue("scheduler_task_end", task, err == nil, err)
|
||||
end
|
||||
else
|
||||
if err then
|
||||
error(err, 0)
|
||||
end
|
||||
end
|
||||
if task then
|
||||
task.killed = true
|
||||
coroutine.close(task.thread)
|
||||
tasks[pid] = nil
|
||||
processes = processes - 1
|
||||
end
|
||||
end
|
||||
|
||||
local function resumeTask(task, yieldPars)
|
||||
local pars = table.pack(coroutine.resume(task.thread, table.unpack(yieldPars)))
|
||||
if pars[1] then
|
||||
task.filters = table.pack(table.unpack(pars, 2))
|
||||
return coroutine.status(task.thread) ~= "dead"
|
||||
else
|
||||
cascadeKill(task.pid, pars[2])
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function scheduler.spawn(func, options)
|
||||
expect(1, func, "function")
|
||||
expect(2, options, "nil", "table")
|
||||
|
||||
options = options or {}
|
||||
options.args = options.args or {}
|
||||
|
||||
local source = debug.getinfo(2)
|
||||
|
||||
local task = newTask()
|
||||
local pid = #tasks + 1
|
||||
task.pid = pid
|
||||
task.options = options
|
||||
task.source = source.source
|
||||
task.uuid = tostring(func)
|
||||
task.thread = coroutine.create(func)
|
||||
local parent = findParent()
|
||||
if parent then
|
||||
task.parent = parent.pid
|
||||
table.insert(parent.children, pid)
|
||||
end
|
||||
task.filters = {}
|
||||
task.children = {}
|
||||
task.eventQueue = {}
|
||||
task.skip = true
|
||||
|
||||
tasks[pid] = task
|
||||
|
||||
processes = processes + 1
|
||||
|
||||
return task, resumeTask(task, task.options.args)
|
||||
end
|
||||
|
||||
function scheduler.kill(pid)
|
||||
expect(1, pid, "number")
|
||||
cascadeKill(pid)
|
||||
end
|
||||
|
||||
function scheduler.ipc(pid, ...)
|
||||
expect(1, pid, "number")
|
||||
if not tasks[pid] then
|
||||
error("process by pid " .. pid .. " does not exist.", 2)
|
||||
end
|
||||
|
||||
local sender = findParent()
|
||||
tasks[pid]:queue("ipc_message", sender, ...)
|
||||
end
|
||||
|
||||
local running = false
|
||||
function scheduler.init()
|
||||
if running then
|
||||
error("scheduler already running", 2)
|
||||
end
|
||||
running = true
|
||||
|
||||
local ev = { n = 0 }
|
||||
while processes > 0 do
|
||||
for pid, task in pairs(tasks) do
|
||||
local yieldPars = ev
|
||||
if ev[1] == "scheduler" and ev[2] == pid then
|
||||
yieldPars = table.pack(table.unpack(ev, 3))
|
||||
end
|
||||
if yieldPars[1] ~= "scheduler" and not task.filters or #task.filters == 0 or contains(task.filters, yieldPars[1]) or yieldPars[1] == "interrupt" then
|
||||
if task.skip then
|
||||
task.skip = false
|
||||
else
|
||||
resumeTask(task, yieldPars)
|
||||
end
|
||||
end
|
||||
|
||||
if coroutine.status(task.thread) == "dead" then
|
||||
cascadeKill(pid)
|
||||
end
|
||||
end
|
||||
|
||||
if processes <= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
ev = table.pack(coroutine.yield())
|
||||
end
|
||||
|
||||
running = false
|
||||
end
|
||||
|
||||
return scheduler
|
||||
112
Capy64/Assets/Lua/CapyOS/sys/lib/shell/package.lua
Normal file
112
Capy64/Assets/Lua/CapyOS/sys/lib/shell/package.lua
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
local expect = require("expect").expect
|
||||
local fs = require("fs")
|
||||
local nativePackage = package
|
||||
|
||||
local function copyTable(source, target)
|
||||
target = target or {}
|
||||
|
||||
for k, v in pairs(source) do
|
||||
target[k] = v
|
||||
end
|
||||
|
||||
return target
|
||||
end
|
||||
|
||||
local hostPackages = copyTable(nativePackage._host)
|
||||
|
||||
local function createPreloadSearcher(envPackage)
|
||||
return function(name)
|
||||
if not envPackage.preload[name] then
|
||||
return string.format("no field package.preload['%s']", name)
|
||||
end
|
||||
return envPackage.preload[name], ":preload:"
|
||||
end
|
||||
end
|
||||
|
||||
local function createLoaderSearcher(envPackage)
|
||||
return function(name)
|
||||
local path, err = envPackage.searchpath(name, envPackage.path)
|
||||
|
||||
if not path then
|
||||
return err
|
||||
end
|
||||
|
||||
local func, err = loadfile(path)
|
||||
if not func then
|
||||
return string.format("error loading module '%s' from file '%s':\t%s", name, path, err)
|
||||
end
|
||||
|
||||
return func, path
|
||||
end
|
||||
end
|
||||
|
||||
local function createEnvironment(filePath)
|
||||
local envPackage = {
|
||||
cpath = nativePackage.cpath,
|
||||
searchpath = nativePackage.searchpath,
|
||||
config = nativePackage.config,
|
||||
searchers = {},
|
||||
loaded = {},
|
||||
preload = {},
|
||||
}
|
||||
|
||||
local dirName = fs.getDir(filePath)
|
||||
--envPackage.path = string.format("%s/?.lua;%s/?/init.lua;", dirName, dirName) .. nativePackage.path
|
||||
envPackage.path = nativePackage.path
|
||||
|
||||
envPackage.searchers[1] = createPreloadSearcher(envPackage)
|
||||
envPackage.searchers[2] = createLoaderSearcher(envPackage)
|
||||
|
||||
local function envRequire(modname)
|
||||
expect(1, modname, "string", "number")
|
||||
modname = tostring(modname)
|
||||
|
||||
if envPackage.loaded[modname] then
|
||||
return envPackage.loaded[modname]
|
||||
end
|
||||
|
||||
local errorOutput = ""
|
||||
local libFunction, libPath
|
||||
for i = 1, #envPackage.searchers do
|
||||
local par, path = envPackage.searchers[i](modname)
|
||||
if type(par) == "function" then
|
||||
libFunction, libPath = par, path
|
||||
break
|
||||
else
|
||||
errorOutput = errorOutput .. "\n\t" .. par
|
||||
end
|
||||
end
|
||||
|
||||
if not libFunction then
|
||||
error(string.format("module '%s' not found:%s", modname, errorOutput), 2)
|
||||
end
|
||||
|
||||
local ok, par = pcall(libFunction)
|
||||
if not ok then
|
||||
error(par, 0)
|
||||
end
|
||||
|
||||
if par == nil then
|
||||
envPackage.loaded[modname] = true
|
||||
return true
|
||||
end
|
||||
|
||||
envPackage.loaded[modname] = par
|
||||
|
||||
return par, libPath
|
||||
end
|
||||
|
||||
copyTable(hostPackages, envPackage.loaded)
|
||||
envPackage.loaded.package = envPackage
|
||||
|
||||
local env_G = copyTable(envPackage.loaded._G or _G)
|
||||
envPackage.loaded._G = env_G
|
||||
env_G._G = env_G
|
||||
|
||||
envPackage.loaded._G.package = envPackage
|
||||
envPackage.loaded._G.require = envRequire
|
||||
|
||||
return envPackage, envRequire
|
||||
end
|
||||
|
||||
return createEnvironment
|
||||
87
Capy64/Assets/Lua/CapyOS/sys/lib/tableutils.lua
Normal file
87
Capy64/Assets/Lua/CapyOS/sys/lib/tableutils.lua
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
local expect = require("expect").expect
|
||||
local tableutils = {}
|
||||
|
||||
local function serialize(data, circular)
|
||||
expect(1, data, "string", "table", "number", "boolean", "nil")
|
||||
if type(data) == "table" then
|
||||
if not circular then
|
||||
circular = {}
|
||||
end
|
||||
local output = "{"
|
||||
for k, v in pairs(data) do
|
||||
if type(v) == "table" then
|
||||
local name = tostring(v)
|
||||
if circular[name] then
|
||||
error("circular reference in table", 2)
|
||||
end
|
||||
circular[name] = true
|
||||
end
|
||||
output = output .. string.format("[%q] = %s,", k, serialize(v, circular))
|
||||
end
|
||||
output = output .. "}"
|
||||
return output
|
||||
else
|
||||
return string.format("%q", data)
|
||||
end
|
||||
end
|
||||
|
||||
function tableutils.serialize(data)
|
||||
expect(1, data, "string", "table", "number", "boolean", "nil")
|
||||
return serialize(data)
|
||||
end
|
||||
|
||||
function tableutils.deserialize(data)
|
||||
local func, err = load("return " .. data, "=tableutils", "t", {})
|
||||
if not func then
|
||||
error(err, 2)
|
||||
end
|
||||
return func()
|
||||
end
|
||||
|
||||
local function prettyvalue(value)
|
||||
if type(value) == "table" or type(value) == "function" or type(value) == "thread" or type(value) == "userdata" or type(value) == "number" then
|
||||
return tostring(value)
|
||||
else
|
||||
return string.format("%q", value)
|
||||
end
|
||||
end
|
||||
|
||||
function tableutils.pretty(data)
|
||||
if type(data) == "table" then
|
||||
local output = "{"
|
||||
|
||||
local index = 0
|
||||
for k, v in pairs(data) do
|
||||
local value = prettyvalue(v)
|
||||
|
||||
if type(k) == "number" and k - 1 == index then
|
||||
index = index + 1
|
||||
output = output .. string.format("\n %s,", value)
|
||||
elseif type(k) == "string" and k:match("^[%a_][%w_]*$") then
|
||||
output = output .. string.format("\n %s = %s,", k, value)
|
||||
else
|
||||
output = output .. string.format("\n [%s] = %s,", prettyvalue(k), value)
|
||||
end
|
||||
end
|
||||
if output == "{" then
|
||||
return "{}"
|
||||
end
|
||||
output = output .. "\n}"
|
||||
|
||||
return output
|
||||
else
|
||||
return prettyvalue(data)
|
||||
end
|
||||
end
|
||||
|
||||
function tableutils.find(tbl, element)
|
||||
for i = 1, #tbl do
|
||||
if tbl[i] == element then
|
||||
return i
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
return tableutils
|
||||
2
Capy64/Assets/Lua/CapyOS/sys/share/help/index
Normal file
2
Capy64/Assets/Lua/CapyOS/sys/share/help/index
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Welcome to CapyOS!
|
||||
Run "programs" to get a list of available programs.
|
||||
12
Capy64/Assets/Lua/CapyOS/sys/share/help/license
Normal file
12
Capy64/Assets/Lua/CapyOS/sys/share/help/license
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
CapyOS and Capy64 are licensed under the Apache 2.0 Public License.
|
||||
https://capy64.alexdevs.me/
|
||||
https://github.com/Ale32bit/Capy64
|
||||
|
||||
Some CapyOS components include code from the CC Recrafted project.
|
||||
https://github.com/Ocawesome101/recrafted
|
||||
|
||||
Some CapyOS components may include code from CC: Tweaked.
|
||||
https://github.com/CC-Tweaked/CC-Tweaked
|
||||
|
||||
json.lua by rxi is licensed under the MIT License.
|
||||
https://github.com/rxi/json.lua
|
||||
3
Capy64/Assets/Lua/CapyOS/sys/share/motd.txt
Normal file
3
Capy64/Assets/Lua/CapyOS/sys/share/motd.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
You can get started with Lua by following this manual: https://www.lua.org/manual/
|
||||
Learn more about Capy64 by following the documentation: https://capy64.alexdevs.me/
|
||||
Found a bug or would like to suggest a feature? https://github.com/Ale32bit/Capy64/issues
|
||||
BIN
Capy64/Assets/Lua/CapyOS/sys/vendor.bmp
Normal file
BIN
Capy64/Assets/Lua/CapyOS/sys/vendor.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
5
Capy64/Assets/Lua/README.txt
Normal file
5
Capy64/Assets/Lua/README.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
If you are looking for your data directory look in:
|
||||
|
||||
Windows: %APPDATA%\Capy64\data
|
||||
Mac: $HOME/.local/share/Capy64/data
|
||||
Linux: $HOME/.local/share/Capy64/data
|
||||
|
|
@ -1 +0,0 @@
|
|||
shell.exit()
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
local args = { ... }
|
||||
local fs = require("fs")
|
||||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
local dir = shell.getDir()
|
||||
|
||||
if args[1] then
|
||||
dir = shell.resolve(args[1])
|
||||
end
|
||||
|
||||
if not fs.isDir(dir) then
|
||||
error("No such directory: " .. dir, 0)
|
||||
return false
|
||||
end
|
||||
|
||||
local files = fs.list(dir)
|
||||
for k, v in ipairs(files) do
|
||||
if fs.isDir(fs.combine(dir, v)) then
|
||||
term.setForeground(colors.lightBlue)
|
||||
print(v .. "/")
|
||||
else
|
||||
term.setForeground(colors.white)
|
||||
print(v)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
local timer = require("timer")
|
||||
|
||||
print("Goodbye!")
|
||||
|
||||
timer.sleep(1000)
|
||||
|
||||
os.shutdown(true)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
local fs = require("fs")
|
||||
|
||||
local args = { ... }
|
||||
if #args == 0 then
|
||||
print("Usage: rm <file>")
|
||||
return
|
||||
end
|
||||
|
||||
local file = shell.resolve(args[1])
|
||||
fs.delete(file, true)
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
local timer = require("timer")
|
||||
|
||||
print("Goodbye!")
|
||||
|
||||
timer.sleep(1000)
|
||||
|
||||
os.shutdown(false)
|
||||
|
|
@ -1 +0,0 @@
|
|||
print(string.format("%s @ %s - %s", os.version(), _HOST, _VERSION))
|
||||
275
Capy64/Assets/Lua/bios.lua
Normal file
275
Capy64/Assets/Lua/bios.lua
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
-- This file is part of Capy64 - https://github.com/Capy64/Capy64
|
||||
-- Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License").
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
local term = require("term")
|
||||
local timer = require("timer")
|
||||
local gpu = require("gpu")
|
||||
local fs = require("fs")
|
||||
local machine = require("machine")
|
||||
local audio = require("audio")
|
||||
local event = require("event")
|
||||
|
||||
local bootSleep = 2
|
||||
local bg = 0x0
|
||||
local fg = 0xffffff
|
||||
local setupbg = 0x0608a6
|
||||
local setupfg = 0xffffff
|
||||
local accent = 0xffea00
|
||||
|
||||
term.setForeground(fg)
|
||||
term.setBackground(bg)
|
||||
term.clear()
|
||||
|
||||
if term.isResizable() then
|
||||
term.setSize(53, 20)
|
||||
end
|
||||
|
||||
local w, h = term.getSize()
|
||||
|
||||
local function sleep(n)
|
||||
local timerId = timer.start(n)
|
||||
repeat
|
||||
local ev, par = coroutine.yield("timer")
|
||||
until par == timerId
|
||||
end
|
||||
|
||||
local function writeCenter(text)
|
||||
local w, h = term.getSize()
|
||||
local _, y = term.getPos()
|
||||
term.setPos(
|
||||
(1 + w / 2) - (#text / 2),
|
||||
y
|
||||
)
|
||||
term.write(text)
|
||||
end
|
||||
|
||||
local function drawVendorImage()
|
||||
if not fs.exists("/sys/vendor.bmp") then
|
||||
return
|
||||
end
|
||||
|
||||
local w, h = gpu.getSize()
|
||||
local ok, err = pcall(function()
|
||||
local task<close> = gpu.loadImageAsync("/sys/vendor.bmp")
|
||||
local buffer<close> = task:await()
|
||||
|
||||
local x, y =
|
||||
math.ceil((w / 2) - (buffer.width / 2)),
|
||||
math.ceil((h / 2) - (buffer.height / 2))
|
||||
|
||||
gpu.drawBuffer(buffer, x, y)
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
print("Warning: Could not draw vendor.bmp", err)
|
||||
end
|
||||
end
|
||||
|
||||
local nPrint = print
|
||||
local function print( text )
|
||||
local x, y = term.getPos()
|
||||
term.write(tostring(text))
|
||||
if y == h then
|
||||
term.scroll(1)
|
||||
term.setPos(1, y)
|
||||
else
|
||||
term.setPos(1, y + 1)
|
||||
end
|
||||
end
|
||||
|
||||
local function printError( text )
|
||||
term.setForeground(0xff0000)
|
||||
print(text)
|
||||
term.setForeground(0xffffff)
|
||||
end
|
||||
|
||||
local function promptKey()
|
||||
print("Press any key to continue")
|
||||
event.pull("key_down")
|
||||
end
|
||||
|
||||
local function alert(...)
|
||||
local args = {...}
|
||||
table.insert(args, "[ OK ]")
|
||||
local lines = {}
|
||||
local width = 0
|
||||
local padding = 1
|
||||
for k, v in ipairs(args) do
|
||||
lines[k] = tostring(v)
|
||||
width = math.max(width, #tostring(v))
|
||||
end
|
||||
|
||||
lines[#lines] = nil
|
||||
|
||||
local okPad = string.rep(" ", (width - #args[#args]) // 2)
|
||||
local okLine = string.format("%s%s", okPad, args[#args])
|
||||
|
||||
local w, h = term.getSize()
|
||||
local cx, cy = w//2, h//2
|
||||
local dx, dy = cx - (width + padding * 2) // 2, cy - #lines//2 - 1
|
||||
|
||||
local pad = string.rep(" ", padding)
|
||||
local emptyLine = string.format("\u{258C}%s%s%s\u{2590}", pad, string.rep(" ", width), pad)
|
||||
|
||||
term.setPos(dx, dy)
|
||||
print("\u{259B}" .. string.rep("\u{2580}", width + padding * 2) .. "\u{259C}")
|
||||
for k, v in ipairs(lines) do
|
||||
term.setPos(dx, dy + k)
|
||||
local space = string.rep(" ", width - #v)
|
||||
print(string.format("\u{258C}%s%s%s%s\u{2590}", pad, v, space, pad))
|
||||
end
|
||||
term.setPos(dx, dy + #lines + 1)
|
||||
print(emptyLine)
|
||||
term.setPos(dx, dy + #lines + 2)
|
||||
local space = string.rep(" ", width - #okLine)
|
||||
term.write(string.format("\u{258C}%s", pad))
|
||||
term.setForeground(accent)
|
||||
term.write(okLine)
|
||||
term.setForeground(setupfg)
|
||||
print(string.format("%s%s\u{2590}", space, pad))
|
||||
term.setPos(dx, dy + #lines + 3)
|
||||
print("\u{2599}" .. string.rep("\u{2584}", width + padding * 2) .. "\u{259F}")
|
||||
|
||||
local _, key, keyname
|
||||
repeat
|
||||
_, key, keyname = event.pull("key_down")
|
||||
until keyname == "enter" or keyname == "escape"
|
||||
end
|
||||
|
||||
local function installDefaultOS()
|
||||
if fs.exists("/sys") then
|
||||
fs.delete("/sys", true)
|
||||
end
|
||||
installOS()
|
||||
alert("Default OS installed!")
|
||||
end
|
||||
|
||||
term.setBlink(false)
|
||||
|
||||
local function setupScreen()
|
||||
local options = {
|
||||
{
|
||||
"Open data folder",
|
||||
openDataFolder,
|
||||
},
|
||||
{
|
||||
"Install default OS",
|
||||
installDefaultOS,
|
||||
},
|
||||
{},
|
||||
{
|
||||
"Exit setup",
|
||||
exit,
|
||||
},
|
||||
{
|
||||
"Shutdown",
|
||||
machine.shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
local selection = 1
|
||||
local function redraw(noDrawSel)
|
||||
local w, h = term.getSize()
|
||||
term.setForeground(setupfg)
|
||||
term.setBackground(setupbg)
|
||||
term.clear()
|
||||
term.setPos(1,2)
|
||||
writeCenter("Capy64 Setup")
|
||||
|
||||
term.setPos(1,3)
|
||||
|
||||
term.setForeground(accent)
|
||||
|
||||
for k, v in ipairs(options) do
|
||||
local _, y = term.getPos()
|
||||
term.setPos(1, y + 1)
|
||||
term.clearLine()
|
||||
|
||||
if #v > 0 then
|
||||
if selection == k and not noDrawSel then
|
||||
writeCenter("[ " .. v[1] .. " ]")
|
||||
else
|
||||
writeCenter(v[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
term.setForeground(setupfg)
|
||||
|
||||
term.setPos(1, h - 2)
|
||||
term.write("\u{2190}\u{2191}\u{2192}\u{2193}: Move")
|
||||
term.setPos(1, h - 1)
|
||||
term.write("Escape: Exit")
|
||||
term.setPos(1, h)
|
||||
term.write("Enter: Select")
|
||||
end
|
||||
|
||||
while true do
|
||||
redraw()
|
||||
local ev = { coroutine.yield("key_down") }
|
||||
if ev[3] == "up" then
|
||||
selection = selection - 1
|
||||
if options[selection] and #options[selection] == 0 then
|
||||
selection = selection - 1
|
||||
end
|
||||
elseif ev[3] == "down" then
|
||||
selection = selection + 1
|
||||
if options[selection] and #options[selection] == 0 then
|
||||
selection = selection + 1
|
||||
end
|
||||
elseif ev[3] == "enter" then
|
||||
redraw(true)
|
||||
options[selection][2]()
|
||||
elseif ev[3] == "escape" then
|
||||
exit()
|
||||
end
|
||||
|
||||
if selection > #options then
|
||||
selection = 1
|
||||
elseif selection < 1 then
|
||||
selection = #options
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local function bootScreen()
|
||||
drawVendorImage()
|
||||
|
||||
term.setPos(1,2)
|
||||
writeCenter("Capy64")
|
||||
term.setPos(1,4)
|
||||
writeCenter("(c) 2023 AlexDevs")
|
||||
|
||||
term.setPos(1, h - 1)
|
||||
writeCenter("Press F2 to open setup")
|
||||
|
||||
local timerId = timer.start(bootSleep)
|
||||
while true do
|
||||
local ev = {coroutine.yield("timer", "key_down")}
|
||||
if ev[1] == "timer" and ev[2] == timerId then
|
||||
exit()
|
||||
elseif ev[1] == "key_down" and ev[3] == "f2" then
|
||||
setupScreen()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
audio.beep(1000, 0.2, 0.2, "square")
|
||||
|
||||
bootScreen()
|
||||
|
||||
term.clear()
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
package.path = "/lib/?.lua;/lib/?/init.lua;" .. package.path
|
||||
|
||||
_G.io = require("io")
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
|
||||
term.setForeground(0x59c9ff)
|
||||
term.setBackground(colors.black)
|
||||
term.clear()
|
||||
term.setPos(1, 1)
|
||||
|
||||
term.write(os.version())
|
||||
term.setPos(1, 2)
|
||||
|
||||
dofile("/bin/shell.lua")
|
||||
|
||||
os.shutdown(false)
|
||||
51
Capy64/Assets/Lua/firmware.lua
Normal file
51
Capy64/Assets/Lua/firmware.lua
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
-- This file is part of Capy64 - https://github.com/Capy64/Capy64
|
||||
-- Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License").
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
local event = require("event")
|
||||
local coroutine = coroutine
|
||||
|
||||
-- Declare event functions
|
||||
function event.pull(...)
|
||||
local ev = table.pack(coroutine.yield(...))
|
||||
if ev[1] == "interrupt" then
|
||||
error("Interrupted", 2)
|
||||
end
|
||||
return table.unpack(ev)
|
||||
end
|
||||
|
||||
function event.pullRaw(...)
|
||||
return coroutine.yield(...)
|
||||
end
|
||||
|
||||
-- Set task awaiter
|
||||
|
||||
local function awaiter(task)
|
||||
local status = task:getStatus()
|
||||
local uuid = task:getID()
|
||||
if status == "running" then
|
||||
local _, taskId, result, err
|
||||
repeat
|
||||
_, taskId, result, err = event.pull("task_finish")
|
||||
until taskId == uuid
|
||||
return result, err
|
||||
elseif status == "succeeded" then
|
||||
return task:getResult(), nil
|
||||
elseif status == "failed" then
|
||||
return nil, task:getError()
|
||||
end
|
||||
end
|
||||
|
||||
-- Second argument freezes the awaiter function, so it cannot be modified
|
||||
event.setAwaiter(awaiter, true)
|
||||
|
|
@ -1,282 +0,0 @@
|
|||
local expect = require("expect").expect
|
||||
local event = require("event")
|
||||
local term = require("term")
|
||||
|
||||
local io = {}
|
||||
|
||||
function io.write(text)
|
||||
text = tostring(text)
|
||||
|
||||
local lines = 0
|
||||
local w, h = term.getSize()
|
||||
|
||||
local function inc_cy(cy)
|
||||
lines = lines + 1
|
||||
|
||||
if cy > h - 1 then
|
||||
term.scroll(1)
|
||||
return cy
|
||||
else
|
||||
return cy + 1
|
||||
end
|
||||
end
|
||||
|
||||
while #text > 0 do
|
||||
local nl = text:find("\n") or #text
|
||||
local chunk = text:sub(1, nl)
|
||||
text = text:sub(#chunk + 1)
|
||||
|
||||
local has_nl = chunk:sub(-1) == "\n"
|
||||
if has_nl then chunk = chunk:sub(1, -2) end
|
||||
|
||||
local cx, cy = term.getPos()
|
||||
while #chunk > 0 do
|
||||
if cx > w then
|
||||
term.setPos(1, inc_cy(cy))
|
||||
cx, cy = term.getPos()
|
||||
end
|
||||
|
||||
local to_write = chunk:sub(1, w - cx + 1)
|
||||
term.write(to_write)
|
||||
|
||||
chunk = chunk:sub(#to_write + 1)
|
||||
cx, cy = term.getPos()
|
||||
end
|
||||
|
||||
if has_nl then
|
||||
term.setPos(1, inc_cy(cy))
|
||||
end
|
||||
end
|
||||
|
||||
return lines
|
||||
end
|
||||
|
||||
local empty = {}
|
||||
function io.read(replace, history, complete, default)
|
||||
expect(1, replace, "string", "nil")
|
||||
expect(2, history, "table", "nil")
|
||||
expect(3, complete, "function", "nil")
|
||||
expect(4, default, "string", "nil")
|
||||
|
||||
if replace then replace = replace:sub(1, 1) end
|
||||
local hist = history or {}
|
||||
history = {}
|
||||
for i = 1, #hist, 1 do
|
||||
history[i] = hist[i]
|
||||
end
|
||||
|
||||
local buffer = default or ""
|
||||
local prev_buf = buffer
|
||||
history[#history + 1] = buffer
|
||||
|
||||
local hist_pos = #history
|
||||
local cursor_pos = 0
|
||||
|
||||
local stx, sty = term.getPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
local dirty = false
|
||||
local completions = {}
|
||||
local comp_id = 0
|
||||
|
||||
local function clearCompletion()
|
||||
if completions[comp_id] then
|
||||
write((" "):rep(#completions[comp_id]))
|
||||
end
|
||||
end
|
||||
|
||||
local function full_redraw(force)
|
||||
if force or dirty then
|
||||
if complete and buffer ~= prev_buf then
|
||||
completions = complete(buffer) or empty
|
||||
comp_id = math.min(1, #completions)
|
||||
end
|
||||
prev_buf = buffer
|
||||
|
||||
term.setPos(stx, sty)
|
||||
local text = buffer
|
||||
if replace then text = replace:rep(#text) end
|
||||
local ln = io.write(text)
|
||||
|
||||
if completions[comp_id] then
|
||||
local oldfg = term.getForeground()
|
||||
local oldbg = term.getBackground()
|
||||
term.setForeground(colors.white)
|
||||
term.setBackground(colors.gray)
|
||||
ln = ln + write(completions[comp_id])
|
||||
term.setForeground(oldfg)
|
||||
term.setBackground(oldbg)
|
||||
else
|
||||
ln = ln + io.write(" ")
|
||||
end
|
||||
|
||||
if sty + ln > h then
|
||||
sty = sty - (sty + ln - h)
|
||||
end
|
||||
end
|
||||
|
||||
-- set cursor to the appropriate spot
|
||||
local cx, cy = stx, sty
|
||||
cx = cx + #buffer - cursor_pos -- + #(completions[comp_id] or "")
|
||||
while cx > w do
|
||||
cx = cx - w
|
||||
cy = cy + 1
|
||||
end
|
||||
term.setPos(cx, cy)
|
||||
end
|
||||
|
||||
term.setBlink(true)
|
||||
|
||||
while true do
|
||||
full_redraw()
|
||||
-- get input
|
||||
local evt, par1, par2 = event.pull()
|
||||
|
||||
if evt == "char" then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer .. par1
|
||||
elseif cursor_pos == #buffer then
|
||||
buffer = par1 .. buffer
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub(-cursor_pos)
|
||||
end
|
||||
|
||||
elseif evt == "paste" then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer .. par1
|
||||
elseif cursor_pos == #buffer then
|
||||
buffer = par1 .. buffer
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 ..
|
||||
buffer:sub(-cursor_pos + (#par1 - 1))
|
||||
end
|
||||
|
||||
elseif evt == "key_down" then
|
||||
if par2 == "back" and #buffer > 0 then
|
||||
dirty = true
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer:sub(1, -2)
|
||||
clearCompletion()
|
||||
elseif cursor_pos < #buffer then
|
||||
buffer = buffer:sub(0, -cursor_pos - 2) .. buffer:sub(-cursor_pos)
|
||||
end
|
||||
|
||||
elseif par2 == "delete" and cursor_pos > 0 then
|
||||
dirty = true
|
||||
|
||||
if cursor_pos == #buffer then
|
||||
buffer = buffer:sub(2)
|
||||
elseif cursor_pos == 1 then
|
||||
buffer = buffer:sub(1, -2)
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1)
|
||||
end
|
||||
cursor_pos = cursor_pos - 1
|
||||
|
||||
elseif par2 == "up" then
|
||||
if #completions > 1 then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if comp_id > 1 then
|
||||
comp_id = comp_id - 1
|
||||
else
|
||||
comp_id = #completions
|
||||
end
|
||||
|
||||
elseif hist_pos > 1 then
|
||||
cursor_pos = 0
|
||||
|
||||
history[hist_pos] = buffer
|
||||
hist_pos = hist_pos - 1
|
||||
|
||||
buffer = (" "):rep(#buffer)
|
||||
full_redraw(true)
|
||||
|
||||
buffer = history[hist_pos]
|
||||
dirty = true
|
||||
end
|
||||
|
||||
elseif par2 == "down" then
|
||||
if #completions > 1 then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if comp_id < #completions then
|
||||
comp_id = comp_id + 1
|
||||
else
|
||||
comp_id = 1
|
||||
end
|
||||
|
||||
elseif hist_pos < #history then
|
||||
cursor_pos = 0
|
||||
|
||||
history[hist_pos] = buffer
|
||||
hist_pos = hist_pos + 1
|
||||
|
||||
buffer = (" "):rep(#buffer)
|
||||
full_redraw(true)
|
||||
|
||||
buffer = history[hist_pos]
|
||||
dirty = true
|
||||
end
|
||||
|
||||
elseif par2 == "left" then
|
||||
if cursor_pos < #buffer then
|
||||
clearCompletion()
|
||||
cursor_pos = cursor_pos + 1
|
||||
end
|
||||
|
||||
elseif par2 == "right" then
|
||||
if cursor_pos > 0 then
|
||||
cursor_pos = cursor_pos - 1
|
||||
|
||||
elseif comp_id > 0 then
|
||||
dirty = true
|
||||
buffer = buffer .. completions[comp_id]
|
||||
end
|
||||
|
||||
elseif par2 == "tab" then
|
||||
if comp_id > 0 then
|
||||
dirty = true
|
||||
buffer = buffer .. completions[comp_id]
|
||||
end
|
||||
|
||||
elseif par2 == "home" then
|
||||
cursor_pos = #buffer
|
||||
|
||||
elseif par2 == "end" then
|
||||
cursor_pos = 0
|
||||
|
||||
elseif par2 == "enter" then
|
||||
clearCompletion()
|
||||
print()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
term.setBlink(false)
|
||||
|
||||
return buffer
|
||||
end
|
||||
|
||||
io.stderr = {}
|
||||
|
||||
function io.stderr.write(text)
|
||||
local fg = term.getForeground()
|
||||
term.setForeground(0xff0000)
|
||||
io.write(text)
|
||||
term.setForeground(fg)
|
||||
end
|
||||
|
||||
function io.stderr.print(...)
|
||||
local fg = term.getForeground()
|
||||
term.setForeground(0xff0000)
|
||||
print(...)
|
||||
term.setForeground(fg)
|
||||
end
|
||||
|
||||
return io
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
local term = require("term")
|
||||
local timer = require("timer")
|
||||
local gpu = require("gpu")
|
||||
|
||||
local bootSleep = 2000
|
||||
local bg = 0x0
|
||||
local fg = 0xffffff
|
||||
|
||||
term.setForeground(fg)
|
||||
term.setBackground(bg)
|
||||
term.clear()
|
||||
|
||||
term.setSize(51, 19)
|
||||
gpu.setScale(2)
|
||||
|
||||
local function sleep(n)
|
||||
local timerId = timer.start(n)
|
||||
repeat
|
||||
local ev, par = coroutine.yield("timer")
|
||||
until par == timerId
|
||||
end
|
||||
|
||||
local function writeCenter(text)
|
||||
local w, h = term.getSize()
|
||||
local _, y = term.getPos()
|
||||
term.setPos(
|
||||
(1 + w / 2) - (#text / 2),
|
||||
y
|
||||
)
|
||||
term.write(text)
|
||||
end
|
||||
|
||||
local w, h = term.getSize()
|
||||
|
||||
term.setBlink(false)
|
||||
|
||||
local function setupScreen()
|
||||
local options = {
|
||||
{
|
||||
"Open data folder",
|
||||
openDataFolder,
|
||||
},
|
||||
{
|
||||
"Install default OS",
|
||||
installOS,
|
||||
},
|
||||
{
|
||||
"Exit setup",
|
||||
exit,
|
||||
},
|
||||
{
|
||||
"Shutdown",
|
||||
os.shutdown,
|
||||
}
|
||||
}
|
||||
|
||||
local selection = 1
|
||||
local function redraw()
|
||||
term.setForeground(fg)
|
||||
term.setBackground(bg)
|
||||
term.clear()
|
||||
term.setPos(1,2)
|
||||
writeCenter("Capy64 Setup")
|
||||
|
||||
term.setPos(1,3)
|
||||
|
||||
for k, v in ipairs(options) do
|
||||
local _, y = term.getPos()
|
||||
term.setPos(1, y + 1)
|
||||
term.clearLine()
|
||||
|
||||
if selection == k then
|
||||
writeCenter("[ " .. v[1] .. " ]")
|
||||
else
|
||||
writeCenter(v[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
redraw()
|
||||
local ev = { coroutine.yield("key_down") }
|
||||
if ev[3] == "up" then
|
||||
selection = selection - 1
|
||||
elseif ev[3] == "down" then
|
||||
selection = selection + 1
|
||||
elseif ev[3] == "enter" then
|
||||
options[selection][2]()
|
||||
elseif ev[3] == "escape" then
|
||||
exit()
|
||||
end
|
||||
|
||||
if selection > #options then
|
||||
selection = 1
|
||||
elseif selection < 1 then
|
||||
selection = #options
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local function bootScreen()
|
||||
term.setPos(1,2)
|
||||
writeCenter("Capy64")
|
||||
term.setPos(1,4)
|
||||
writeCenter("Powered by Capybaras")
|
||||
|
||||
term.setPos(1, h - 1)
|
||||
writeCenter("Press F2 to open setup")
|
||||
|
||||
local timerId = timer.start(bootSleep)
|
||||
while true do
|
||||
local ev = {coroutine.yield("timer", "key_down")}
|
||||
if ev[1] == "timer" and ev[2] == timerId then
|
||||
exit()
|
||||
elseif ev[1] == "key_down" and ev[3] == "f2" then
|
||||
setupScreen()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
bootScreen()
|
||||
|
||||
term.clear()
|
||||
|
|
@ -1,4 +1,9 @@
|
|||
{
|
||||
"EngineMode": 0,
|
||||
"SafeMode": false,
|
||||
"Window": {
|
||||
"Scale": 2
|
||||
},
|
||||
"HTTP": {
|
||||
"Enable": true,
|
||||
"Blacklist": [],
|
||||
|
|
@ -6,5 +11,11 @@
|
|||
"Enable": true,
|
||||
"MaxActiveConnections": 5
|
||||
}
|
||||
},
|
||||
"Integrations": {
|
||||
"Discord": {
|
||||
"Enable": true,
|
||||
"ApplicationId": "1068654606357377074"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
234
Capy64/Capy64.cs
234
Capy64/Capy64.cs
|
|
@ -1,38 +1,103 @@
|
|||
using Capy64.API;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Capy64.API;
|
||||
using Capy64.Core;
|
||||
using Capy64.Eventing;
|
||||
using Capy64.Extensions;
|
||||
using Capy64.Runtime;
|
||||
using Capy64.Integrations;
|
||||
using Capy64.PluginManager;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Capy64.Runtime;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using MonoGame.Extended;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Capy64.Utils;
|
||||
|
||||
namespace Capy64;
|
||||
|
||||
public class Capy64 : Game, IGame
|
||||
public enum EngineMode
|
||||
{
|
||||
public const string Version = "0.0.6-alpha";
|
||||
public static string AppDataPath = Path.Combine(
|
||||
Environment.GetFolderPath(
|
||||
Environment.SpecialFolder.ApplicationData,
|
||||
Environment.SpecialFolderOption.Create),
|
||||
"Capy64");
|
||||
public static Capy64 Instance;
|
||||
Classic,
|
||||
Free
|
||||
}
|
||||
|
||||
public class Capy64 : Game
|
||||
{
|
||||
public const string Version = "1.1.2-beta";
|
||||
|
||||
public static class DefaultParameters
|
||||
{
|
||||
public const int Width = 318;
|
||||
public const int Height = 240;
|
||||
public const float Scale = 2f;
|
||||
public const float BorderMultiplier = 1.5f;
|
||||
public static readonly EngineMode EngineMode = EngineMode.Classic;
|
||||
|
||||
public const int ClassicTickrate = 30;
|
||||
public const int FreeTickrate = 60;
|
||||
}
|
||||
|
||||
public static readonly string AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
public static readonly string AssetsPath = Path.Combine(AssemblyPath, "Assets");
|
||||
|
||||
public static string AppDataPath
|
||||
{
|
||||
get
|
||||
{
|
||||
string baseDir =
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData,
|
||||
Environment.SpecialFolderOption.Create) :
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
|
||||
Environment.SpecialFolderOption.Create) :
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
|
||||
Environment.SpecialFolderOption.Create) :
|
||||
"./";
|
||||
|
||||
return Path.Combine(baseDir, "Capy64");
|
||||
}
|
||||
}
|
||||
|
||||
public static Capy64 Instance { get; private set; }
|
||||
public Capy64 Game => this;
|
||||
public IList<IPlugin> NativePlugins { get; private set; }
|
||||
public IList<IPlugin> Plugins { get; private set; }
|
||||
public int Width { get; set; } = 400;
|
||||
public int Height { get; set; } = 300;
|
||||
public float Scale { get; set; } = 2f;
|
||||
public EngineMode EngineMode { get; private set; } = EngineMode.Classic;
|
||||
public IList<IComponent> NativePlugins { get; private set; }
|
||||
public IList<IComponent> Plugins { get; private set; }
|
||||
public int Width { get; set; } = DefaultParameters.Width;
|
||||
public int Height { get; set; } = DefaultParameters.Height;
|
||||
public float Scale { get; set; } = DefaultParameters.Scale;
|
||||
public Drawing Drawing { get; private set; }
|
||||
public Audio Audio { get; private set; }
|
||||
public LuaState LuaRuntime { get; set; }
|
||||
public EventEmitter EventEmitter { get; private set; }
|
||||
public Eventing.EventEmitter EventEmitter { get; private set; }
|
||||
public DiscordIntegration Discord { get; set; }
|
||||
public int TickRate => tickrate;
|
||||
public IConfiguration Configuration { get; private set; }
|
||||
|
||||
public Color BorderColor { get; set; } = Color.Black;
|
||||
|
||||
public Borders Borders = new()
|
||||
{
|
||||
Top = 0,
|
||||
|
|
@ -40,21 +105,23 @@ public class Capy64 : Game, IGame
|
|||
Left = 0,
|
||||
Right = 0,
|
||||
};
|
||||
|
||||
public SpriteBatch SpriteBatch;
|
||||
|
||||
|
||||
private readonly InputManager _inputManager;
|
||||
private RenderTarget2D renderTarget;
|
||||
private readonly GraphicsDeviceManager _graphics;
|
||||
private IServiceProvider _serviceProvider;
|
||||
private ulong _totalTicks = 0;
|
||||
private int tickrate = 0;
|
||||
private int everyTick => 60 / tickrate;
|
||||
|
||||
public Capy64()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
_graphics = new GraphicsDeviceManager(this);
|
||||
Content.RootDirectory = "Content";
|
||||
//Content.RootDirectory = "Content";
|
||||
IsMouseVisible = true;
|
||||
|
||||
EventEmitter = new();
|
||||
|
|
@ -63,16 +130,38 @@ public class Capy64 : Game, IGame
|
|||
Drawing = new();
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceProvider serviceProvider)
|
||||
public void SetEngineMode(EngineMode mode)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
switch (mode)
|
||||
{
|
||||
case EngineMode.Classic:
|
||||
tickrate = DefaultParameters.ClassicTickrate;
|
||||
Width = DefaultParameters.Width;
|
||||
Height = DefaultParameters.Height;
|
||||
Window.AllowUserResizing = false;
|
||||
ResetBorder();
|
||||
|
||||
break;
|
||||
|
||||
case EngineMode.Free:
|
||||
tickrate = DefaultParameters.FreeTickrate;
|
||||
Window.AllowUserResizing = true;
|
||||
break;
|
||||
}
|
||||
|
||||
EngineMode = mode;
|
||||
|
||||
UpdateSize(true);
|
||||
}
|
||||
|
||||
public void UpdateSize()
|
||||
public void UpdateSize(bool resize = true)
|
||||
{
|
||||
_graphics.PreferredBackBufferWidth = (int)(Width * Scale) + Borders.Left + Borders.Right;
|
||||
_graphics.PreferredBackBufferHeight = (int)(Height * Scale) + Borders.Top + Borders.Right;
|
||||
_graphics.ApplyChanges();
|
||||
if (resize)
|
||||
{
|
||||
_graphics.PreferredBackBufferWidth = (int)(Width * Scale) + Borders.Left + Borders.Right;
|
||||
_graphics.PreferredBackBufferHeight = (int)(Height * Scale) + Borders.Top + Borders.Bottom;
|
||||
_graphics.ApplyChanges();
|
||||
}
|
||||
|
||||
renderTarget = new RenderTarget2D(
|
||||
GraphicsDevice,
|
||||
|
|
@ -85,58 +174,115 @@ public class Capy64 : Game, IGame
|
|||
Drawing.Canvas = renderTarget;
|
||||
|
||||
_inputManager.Texture = renderTarget;
|
||||
_inputManager.WindowScale = Scale;
|
||||
|
||||
EventEmitter.RaiseScreenSizeChange();
|
||||
}
|
||||
|
||||
private void OnWindowSizeChange(object sender, EventArgs e)
|
||||
{
|
||||
var bounds = Window.ClientBounds;
|
||||
Console.WriteLine(bounds);
|
||||
|
||||
if (Window.IsMaximized())
|
||||
if (EngineMode == EngineMode.Classic)
|
||||
{
|
||||
|
||||
UpdateSize(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var bounds = Window.ClientBounds;
|
||||
|
||||
Width = (int)(bounds.Width / Scale);
|
||||
Height = (int)(bounds.Height / Scale);
|
||||
|
||||
UpdateSize();
|
||||
if (Window.IsMaximized())
|
||||
{
|
||||
var vertical = bounds.Height - (Height * Scale);
|
||||
var horizontal = bounds.Width - (Width * Scale);
|
||||
|
||||
Borders.Top = (int)Math.Floor(vertical / 2d);
|
||||
Borders.Bottom = (int)Math.Ceiling(vertical / 2d);
|
||||
|
||||
Borders.Left = (int)Math.Floor(horizontal / 2d);
|
||||
Borders.Right = (int)Math.Ceiling(horizontal / 2d);
|
||||
|
||||
UpdateSize(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetBorder();
|
||||
UpdateSize();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetBorder()
|
||||
{
|
||||
var size = (int)(Scale * DefaultParameters.BorderMultiplier);
|
||||
Borders = new Borders
|
||||
{
|
||||
Top = size,
|
||||
Bottom = size,
|
||||
Left = size,
|
||||
Right = size
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
var configBuilder = new ConfigurationBuilder();
|
||||
|
||||
var settingsPath = Path.Combine(AppDataPath, "settings.json");
|
||||
if (!Directory.Exists(AppDataPath))
|
||||
{
|
||||
Directory.CreateDirectory(AppDataPath);
|
||||
}
|
||||
if (!File.Exists(settingsPath))
|
||||
{
|
||||
File.Copy(Path.Combine(AssetsPath, "default.json"), settingsPath);
|
||||
}
|
||||
|
||||
configBuilder.AddJsonFile(Path.Combine(AssetsPath, "default.json"), false);
|
||||
configBuilder.AddJsonFile(settingsPath, false);
|
||||
|
||||
Configuration = configBuilder.Build();
|
||||
|
||||
Window.Title = "Capy64 " + Version;
|
||||
|
||||
Scale = Configuration.GetValue("Window:Scale", DefaultParameters.Scale);
|
||||
|
||||
ResetBorder();
|
||||
UpdateSize();
|
||||
|
||||
Window.AllowUserResizing = false;
|
||||
Window.AllowUserResizing = true;
|
||||
Window.ClientSizeChanged += OnWindowSizeChange;
|
||||
|
||||
InactiveSleepTime = new TimeSpan(0);
|
||||
|
||||
SetEngineMode(Configuration.GetValue<EngineMode>("EngineMode", DefaultParameters.EngineMode));
|
||||
|
||||
Audio = new Audio();
|
||||
|
||||
NativePlugins = GetNativePlugins();
|
||||
Plugins = PluginLoader.LoadAllPlugins("plugins", _serviceProvider);
|
||||
var safeMode = Configuration.GetValue("SafeMode", false);
|
||||
if (!safeMode)
|
||||
Plugins = PluginLoader.LoadAllPlugins(Path.Combine(AppDataPath, "plugins"));
|
||||
|
||||
EventEmitter.RaiseInit();
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
private List<IPlugin> GetNativePlugins()
|
||||
private List<IComponent> GetNativePlugins()
|
||||
{
|
||||
var iType = typeof(IPlugin);
|
||||
var iType = typeof(IComponent);
|
||||
var types = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(s => s.GetTypes())
|
||||
.Where(p => iType.IsAssignableFrom(p) && !p.IsInterface);
|
||||
|
||||
var plugins = new List<IPlugin>();
|
||||
var plugins = new List<IComponent>();
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
var instance = (IPlugin)ActivatorUtilities.CreateInstance(_serviceProvider, type)!;
|
||||
var instance = (IComponent)Activator.CreateInstance(type, this);
|
||||
plugins.Add(instance);
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +302,8 @@ public class Capy64 : Game, IGame
|
|||
EventEmitter.RaiseTick(new()
|
||||
{
|
||||
GameTime = gameTime,
|
||||
TotalTicks = _totalTicks
|
||||
TotalTicks = _totalTicks,
|
||||
IsActiveTick = (int)_totalTicks % everyTick == 0,
|
||||
});
|
||||
|
||||
Drawing.End();
|
||||
|
|
@ -169,7 +316,14 @@ public class Capy64 : Game, IGame
|
|||
protected override void Draw(GameTime gameTime)
|
||||
{
|
||||
SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp);
|
||||
SpriteBatch.Draw(renderTarget, new(Borders.Left, Borders.Top), null, Color.White, 0f, Vector2.Zero, Scale, SpriteEffects.None, 0);
|
||||
GraphicsDevice.Clear(BorderColor);
|
||||
|
||||
SpriteBatch.DrawRectangle(renderTarget.Bounds.Location.ToVector2() + new Vector2(Borders.Left, Borders.Top),
|
||||
new Size2(renderTarget.Bounds.Width * Scale, renderTarget.Bounds.Height * Scale), Color.Black,
|
||||
Math.Min(renderTarget.Bounds.Width, renderTarget.Bounds.Height), 0);
|
||||
|
||||
SpriteBatch.Draw(renderTarget, new(Borders.Left, Borders.Top), null, Color.White, 0f, Vector2.Zero, Scale,
|
||||
SpriteEffects.None, 0);
|
||||
|
||||
EventEmitter.RaiseOverlay(new()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RollForward>Major</RollForward>
|
||||
<PublishReadyToRun>false</PublishReadyToRun>
|
||||
<TieredCompilation>false</TieredCompilation>
|
||||
|
|
@ -10,43 +10,60 @@
|
|||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>Icon.ico</ApplicationIcon>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\default.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\default.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Assets\font.ttf">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<EmbeddedResource Include="Icon.ico" />
|
||||
<EmbeddedResource Include="Icon.bmp">
|
||||
<LogicalName>Icon.bmp</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\Lua\**">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Assets\Lua/**">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<None Include="..\LICENSE">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Icon.ico" />
|
||||
<EmbeddedResource Include="Icon.bmp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FontStashSharp.MonoGame" Version="1.2.8" />
|
||||
<PackageReference Include="KeraLua" Version="1.3.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageReference Include="FontStashSharp.MonoGame" Version="1.3.6" />
|
||||
<PackageReference Include="KeraLua" Version="1.4.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||
<PackageReference Include="MonoGame.Extended.Graphics" Version="3.8.0" />
|
||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
||||
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
|
||||
<PackageReference Include="System.ComponentModel.Composition" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Assets\bios.lua">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<EditorConfigFiles Remove="C:\Users\Alex\source\repos\Capy64\Capy64\Capy64\.editorconfig" />
|
||||
</ItemGroup>
|
||||
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
|
||||
<Message Text="Restoring dotnet tools" Importance="High" />
|
||||
<Exec Command="dotnet tool restore" />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<Folder Include="Assets\Lua\" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
|
||||
<DefineConstants>_WINDOWS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' ">
|
||||
<DefineConstants>_LINUX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' ">
|
||||
<DefineConstants>_OSX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
#----------------------------- Global Properties ----------------------------#
|
||||
|
||||
/outputDir:bin/$(Platform)
|
||||
/intermediateDir:obj/$(Platform)
|
||||
/platform:DesktopGL
|
||||
/config:
|
||||
/profile:Reach
|
||||
/compress:False
|
||||
|
||||
#-------------------------------- References --------------------------------#
|
||||
|
||||
|
||||
#---------------------------------- Content ---------------------------------#
|
||||
|
||||
|
|
@ -1,73 +1,211 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Xna.Framework.Audio;
|
||||
using System;
|
||||
|
||||
namespace Capy64.Core;
|
||||
|
||||
public class Audio
|
||||
public class Audio : IDisposable
|
||||
{
|
||||
public const int SampleRate = 48000;
|
||||
public const int SamplesPerBuffer = 3000;
|
||||
const int bytesPerSample = 2;
|
||||
public enum Waveform
|
||||
{
|
||||
Sine,
|
||||
Square,
|
||||
Triangle,
|
||||
Sawtooth,
|
||||
Noise
|
||||
}
|
||||
|
||||
private readonly DynamicSoundEffectInstance _instance;
|
||||
private double _time = 0.0;
|
||||
public const int SampleRate = 24000;
|
||||
public const int HQSampleRate = 48000;
|
||||
public const AudioChannels AudioChannel = AudioChannels.Mono;
|
||||
public const int ChannelsCount = 8;
|
||||
public readonly DynamicSoundEffectInstance[] Channels = new DynamicSoundEffectInstance[ChannelsCount];
|
||||
private readonly bool[] freeChannels = new bool[ChannelsCount];
|
||||
|
||||
public readonly DynamicSoundEffectInstance HQChannel = new(HQSampleRate, AudioChannel);
|
||||
|
||||
private static readonly Random rng = new();
|
||||
public Audio()
|
||||
{
|
||||
_instance = new(SampleRate, AudioChannels.Mono);
|
||||
}
|
||||
|
||||
public void test()
|
||||
{
|
||||
var workingBuffer = new float[SamplesPerBuffer];
|
||||
FillWorkingBuffer(workingBuffer);
|
||||
var buffer = DivideBuffer(workingBuffer);
|
||||
_instance.SubmitBuffer(buffer);
|
||||
}
|
||||
|
||||
private byte[] DivideBuffer(float[] from)
|
||||
{
|
||||
var outBuffer = new byte[SamplesPerBuffer * bytesPerSample];
|
||||
|
||||
for (int i = 0; i < from.Length; i++)
|
||||
for (int i = 0; i < ChannelsCount; i++)
|
||||
{
|
||||
var floatSample = MathHelper.Clamp(from[i], -1.0f, 1.0f);
|
||||
var shortSample = (short)(floatSample * short.MaxValue);
|
||||
Channels[i] = new DynamicSoundEffectInstance(SampleRate, AudioChannel);
|
||||
freeChannels[i] = true;
|
||||
Channels[i].BufferNeeded += Audio_BufferNeeded;
|
||||
}
|
||||
|
||||
int index = (i * bytesPerSample) + bytesPerSample;
|
||||
HQChannel.BufferNeeded += HQChannel_BufferNeeded;
|
||||
}
|
||||
|
||||
private static void EnqueueAudioNeedEvent(int i, int pending)
|
||||
{
|
||||
Capy64.Instance.LuaRuntime.QueueEvent("audio_need", LK =>
|
||||
{
|
||||
LK.PushInteger(i);
|
||||
LK.PushInteger(pending);
|
||||
return 2;
|
||||
});
|
||||
}
|
||||
|
||||
private void HQChannel_BufferNeeded(object sender, EventArgs e)
|
||||
{
|
||||
var pending = HQChannel.PendingBufferCount;
|
||||
EnqueueAudioNeedEvent(-1, pending);
|
||||
}
|
||||
|
||||
private void Audio_BufferNeeded(object sender, EventArgs e)
|
||||
{
|
||||
for (int i = 0; i < ChannelsCount; i++)
|
||||
{
|
||||
if (Channels[i] == sender)
|
||||
{
|
||||
freeChannels[i] = true;
|
||||
var pending = Channels[i].PendingBufferCount;
|
||||
EnqueueAudioNeedEvent(i, pending);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int GetChannelId(int inp)
|
||||
{
|
||||
if (inp >= 0)
|
||||
return inp;
|
||||
|
||||
if (inp == -1)
|
||||
return -1;
|
||||
|
||||
if (inp == -2)
|
||||
{
|
||||
for (int i = 0; i < ChannelsCount; i++)
|
||||
{
|
||||
if (freeChannels[i])
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -3;
|
||||
}
|
||||
|
||||
public bool TryGetChannel(int id, out DynamicSoundEffectInstance channel, out int resolvedId)
|
||||
{
|
||||
resolvedId = GetChannelId(id);
|
||||
|
||||
if (resolvedId >= 0)
|
||||
channel = Channels[resolvedId];
|
||||
else if (resolvedId == -1)
|
||||
channel = HQChannel;
|
||||
else
|
||||
channel = null;
|
||||
|
||||
return channel != null;
|
||||
}
|
||||
|
||||
public TimeSpan Submit(int id, byte[] buffer)
|
||||
{
|
||||
if (!TryGetChannel(id, out var channel, out var rId))
|
||||
return TimeSpan.Zero;
|
||||
|
||||
channel.SubmitBuffer(buffer);
|
||||
freeChannels[rId] = false;
|
||||
return channel.GetSampleDuration(buffer.Length);
|
||||
}
|
||||
|
||||
public TimeSpan SubmitHQ(byte[] buffer)
|
||||
{
|
||||
|
||||
HQChannel.SubmitBuffer(buffer);
|
||||
return HQChannel.GetSampleDuration(buffer.Length);
|
||||
}
|
||||
|
||||
public static byte[] GenerateWave(DynamicSoundEffectInstance channel, Waveform form, double frequency, TimeSpan time, float volume = 1f)
|
||||
{
|
||||
var size = channel.GetSampleSizeInBytes(time);
|
||||
var buffer = new byte[size];
|
||||
|
||||
var step = 1d / SampleRate;
|
||||
double x = 0;
|
||||
for (int i = 0; i < size; i += 2)
|
||||
{
|
||||
var value = form switch
|
||||
{
|
||||
Waveform.Sine => GetSinePoint(frequency, x),
|
||||
Waveform.Square => GetSquarePoint(frequency, x),
|
||||
Waveform.Triangle => GetTrianglePoint(frequency, x),
|
||||
Waveform.Sawtooth => GetSawtoothPoint(frequency, x),
|
||||
Waveform.Noise => (rng.NextDouble() * 2) - 1,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
value = Math.Clamp(value, -1, 1);
|
||||
var sample = (short)((value >= 0.0f ? value * short.MaxValue : value * short.MinValue * -1) * volume);
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
outBuffer[index] = (byte)(shortSample >> 8);
|
||||
outBuffer[index + 1] = (byte)shortSample;
|
||||
buffer[i] = (byte)(sample >> 8);
|
||||
buffer[i + 1] = (byte)sample;
|
||||
}
|
||||
else
|
||||
{
|
||||
outBuffer[index] = (byte)shortSample;
|
||||
outBuffer[index + 1] = (byte)(shortSample >> 8);
|
||||
buffer[i] = (byte)sample;
|
||||
buffer[i + 1] = (byte)(sample >> 8);
|
||||
}
|
||||
x += step;
|
||||
}
|
||||
|
||||
return outBuffer;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static double SineWave(double time, double frequency)
|
||||
public static double GetSinePoint(double frequency, double x)
|
||||
{
|
||||
return Math.Sin(time * 2 * Math.PI * frequency);
|
||||
return Math.Sin(x * 2 * Math.PI * frequency);
|
||||
}
|
||||
|
||||
private void FillWorkingBuffer(float[] buffer)
|
||||
private static double GetSquarePoint(double frequency, double x)
|
||||
{
|
||||
for (int i = 0; i < SamplesPerBuffer; i++)
|
||||
double v = GetSinePoint(frequency, x);
|
||||
return v > 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
private static double GetTrianglePoint(double frequency, double x)
|
||||
{
|
||||
double v = 0;
|
||||
for (int k = 1; k <= 25; k++)
|
||||
{
|
||||
// Here is where you sample your wave function
|
||||
buffer[i] = (float)SineWave(_time, 440); // Left Channel
|
||||
|
||||
// Advance time passed since beginning
|
||||
// Since the amount of samples in a second equals the chosen SampleRate
|
||||
// Then each sample should advance the time by 1 / SampleRate
|
||||
_time += 1.0 / SampleRate;
|
||||
v += Math.Pow(-1, k) / Math.Pow((2 * k) - 1, 2)
|
||||
* Math.Sin(frequency * 2 * Math.PI * ((2 * k) - 1) * x);
|
||||
}
|
||||
return -(8 / Math.Pow(Math.PI, 2)) * v;
|
||||
}
|
||||
|
||||
private static double GetSawtoothPoint(double frequency, double x)
|
||||
{
|
||||
double v = 0;
|
||||
for (int k = 1; k <= 50; k++)
|
||||
{
|
||||
v += Math.Pow(-1, k) / k * Math.Sin(frequency * 2 * Math.PI * k * x);
|
||||
}
|
||||
return -(2 / Math.PI) * v;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
for (int i = 0; i < ChannelsCount; i++)
|
||||
{
|
||||
Channels[i]?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
using FontStashSharp;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using FontStashSharp;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MonoGame.Extended;
|
||||
|
|
@ -16,6 +31,7 @@ public class Drawing : IDisposable
|
|||
private Texture2D _whitePixel;
|
||||
private RenderTarget2D _canvas;
|
||||
private bool _isDrawing;
|
||||
private readonly HashSet<Texture2D> _disposeTextures = new();
|
||||
public RenderTarget2D Canvas
|
||||
{
|
||||
get => _canvas;
|
||||
|
|
@ -34,11 +50,11 @@ public class Drawing : IDisposable
|
|||
Begin();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Drawing()
|
||||
{
|
||||
_fontSystem = new FontSystem();
|
||||
_fontSystem.AddFont(File.ReadAllBytes(@"Assets/font.ttf"));
|
||||
_fontSystem.AddFont(File.ReadAllBytes(Path.Combine(Capy64.AssetsPath, "font.ttf")));
|
||||
}
|
||||
|
||||
public void Begin()
|
||||
|
|
@ -59,6 +75,11 @@ public class Drawing : IDisposable
|
|||
|
||||
_spriteBatch.End();
|
||||
_graphicsDevice.SetRenderTarget(null);
|
||||
|
||||
foreach (var t in _disposeTextures)
|
||||
t.Dispose();
|
||||
_disposeTextures.Clear();
|
||||
|
||||
_isDrawing = false;
|
||||
}
|
||||
|
||||
|
|
@ -78,10 +99,10 @@ public class Drawing : IDisposable
|
|||
{
|
||||
if (point.X < 0 || point.Y < 0) return;
|
||||
if (point.X >= _canvas.Width || point.Y >= _canvas.Height) return;
|
||||
|
||||
|
||||
var grid = new Color[_canvas.Width * _canvas.Height];
|
||||
_canvas.GetData(grid);
|
||||
|
||||
|
||||
grid[point.X + (point.Y * _canvas.Width)] = color;
|
||||
|
||||
_canvas.SetData(grid);
|
||||
|
|
@ -133,9 +154,10 @@ public class Drawing : IDisposable
|
|||
_spriteBatch.DrawPoint(point, color, size);
|
||||
}
|
||||
|
||||
public void DrawCircle(Vector2 pos, int radius, Color color, int thickness = 1)
|
||||
public void DrawCircle(Vector2 pos, int radius, Color color, int thickness = 1, int sides = -1)
|
||||
{
|
||||
_spriteBatch.DrawCircle(pos, radius, radius * 4, color, thickness);
|
||||
sides = sides < 0 ? radius * 4 : sides;
|
||||
_spriteBatch.DrawCircle(pos, radius, sides, color, thickness);
|
||||
}
|
||||
|
||||
public void DrawLine(Vector2 start, Vector2 end, Color color, float thickness = 1)
|
||||
|
|
@ -162,6 +184,7 @@ public class Drawing : IDisposable
|
|||
public void Clear(Color? color = default)
|
||||
{
|
||||
Color finalColor = color ?? Color.Black;
|
||||
Capy64.Instance.BorderColor = finalColor;
|
||||
_graphicsDevice.Clear(finalColor);
|
||||
}
|
||||
|
||||
|
|
@ -178,8 +201,28 @@ public class Drawing : IDisposable
|
|||
_spriteBatch.Draw(_whitePixel, position3, null, color, rotation, Vector2.Zero, scale, SpriteEffects.None, layerDepth);
|
||||
}
|
||||
|
||||
public void DrawBuffer(uint[] buffer, Rectangle rect, Rectangle? source = null, Color? color = null, float rotation = 0f, Vector2? origin = null, Vector2? scale = null, SpriteEffects spriteEffects = 0)
|
||||
{
|
||||
var texture = new Texture2D(_graphicsDevice, rect.Width, rect.Height, false, SurfaceFormat.Color);
|
||||
texture.SetData(buffer);
|
||||
|
||||
_spriteBatch.Draw(
|
||||
texture, // Texture
|
||||
rect.Location.ToVector2(), // Position
|
||||
source, // source
|
||||
color ?? Color.White, // Color
|
||||
rotation, // Rotation
|
||||
origin ?? Vector2.Zero, // Origin
|
||||
scale ?? Vector2.One, // Scale
|
||||
spriteEffects, // Flip effects
|
||||
0f // layer depth
|
||||
);
|
||||
_disposeTextures.Add(texture);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
_spriteBatch.Dispose();
|
||||
_whitePixel.Dispose();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,19 @@
|
|||
using Capy64.Extensions.Bindings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Capy64.Extensions.Bindings;
|
||||
|
||||
namespace Capy64.Core;
|
||||
|
||||
|
|
@ -14,6 +24,11 @@ public class SDL
|
|||
return SDL2.UTF8_ToManaged(SDL2.Native_SDL_GetClipboardText(), true);
|
||||
}
|
||||
|
||||
public static void SetClipboardText(string contents)
|
||||
{
|
||||
SDL2.Native_SDL_SetClipboardText(contents);
|
||||
}
|
||||
|
||||
public static bool HasClipboardText()
|
||||
{
|
||||
return SDL2.SDL_HasClipboardText() == 1;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
using Capy64.Eventing.Events;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Capy64.Eventing.Events;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
|
||||
|
|
@ -18,9 +33,15 @@ public class EventEmitter
|
|||
public event EventHandler<KeyEvent> OnKeyUp;
|
||||
public event EventHandler<CharEvent> OnChar;
|
||||
|
||||
// GamePad events
|
||||
public event EventHandler<GamePadButtonEvent> OnGamePadButton;
|
||||
public event EventHandler<GamePadTriggerEvent> OnGamePadTrigger;
|
||||
public event EventHandler<GamePadThumbstickEvent> OnGamePadThumbstick;
|
||||
|
||||
// Functional events
|
||||
public event EventHandler<TickEvent> OnTick;
|
||||
public event EventHandler OnInit;
|
||||
public event EventHandler OnClose;
|
||||
public event EventHandler OnScreenSizeChange;
|
||||
public event EventHandler<OverlayEvent> OnOverlay;
|
||||
|
||||
|
|
@ -84,6 +105,30 @@ public class EventEmitter
|
|||
}
|
||||
}
|
||||
|
||||
public void RaiseGamePadButton(GamePadButtonEvent ev)
|
||||
{
|
||||
if (OnGamePadButton is not null)
|
||||
{
|
||||
OnGamePadButton(this, ev);
|
||||
}
|
||||
}
|
||||
|
||||
public void RaiseGamePadTrigger(GamePadTriggerEvent ev)
|
||||
{
|
||||
if (OnGamePadTrigger is not null)
|
||||
{
|
||||
OnGamePadTrigger(this, ev);
|
||||
}
|
||||
}
|
||||
|
||||
public void RaiseGamePadThumbstick(GamePadThumbstickEvent ev)
|
||||
{
|
||||
if (OnGamePadThumbstick is not null)
|
||||
{
|
||||
OnGamePadThumbstick(this, ev);
|
||||
}
|
||||
}
|
||||
|
||||
public void RaiseTick(TickEvent ev)
|
||||
{
|
||||
if (OnTick is not null)
|
||||
|
|
@ -100,6 +145,14 @@ public class EventEmitter
|
|||
}
|
||||
}
|
||||
|
||||
public void RaiseClose()
|
||||
{
|
||||
if (OnClose is not null)
|
||||
{
|
||||
OnClose(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public void RaiseScreenSizeChange()
|
||||
{
|
||||
if (OnScreenSizeChange is not null)
|
||||
|
|
@ -110,7 +163,8 @@ public class EventEmitter
|
|||
|
||||
public void RaiseOverlay(OverlayEvent ev)
|
||||
{
|
||||
if(OnOverlay is not null) {
|
||||
if (OnOverlay is not null)
|
||||
{
|
||||
OnOverlay(this, ev);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
using System;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Capy64.Eventing.Events;
|
||||
|
||||
|
|
|
|||
25
Capy64/Eventing/Events/GamePadButtonEvent.cs
Normal file
25
Capy64/Eventing/Events/GamePadButtonEvent.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
|
||||
namespace Capy64.Eventing.Events;
|
||||
|
||||
public class GamePadButtonEvent : EventArgs
|
||||
{
|
||||
public Buttons Button { get; set; }
|
||||
public ButtonState State { get; set; }
|
||||
}
|
||||
25
Capy64/Eventing/Events/GamePadThumbstickEvent.cs
Normal file
25
Capy64/Eventing/Events/GamePadThumbstickEvent.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Capy64.Eventing.Events;
|
||||
|
||||
public class GamePadThumbstickEvent : EventArgs
|
||||
{
|
||||
public int Stick { get; set; }
|
||||
public Vector2 Value { get; set; }
|
||||
}
|
||||
24
Capy64/Eventing/Events/GamePadTriggerEvent.cs
Normal file
24
Capy64/Eventing/Events/GamePadTriggerEvent.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Capy64.Eventing.Events;
|
||||
|
||||
public class GamePadTriggerEvent : EventArgs
|
||||
{
|
||||
public int Trigger { get; set; }
|
||||
public float Value { get; set; }
|
||||
}
|
||||
|
|
@ -1,4 +1,19 @@
|
|||
using Microsoft.Xna.Framework.Input;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using static Capy64.Eventing.InputManager;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using static Capy64.Eventing.InputManager;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Capy64.Eventing.Events;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Capy64.Eventing.Events;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,19 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Capy64.Eventing.Events;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Capy64.Eventing.Events;
|
||||
|
|
@ -7,4 +22,5 @@ public class TickEvent : EventArgs
|
|||
{
|
||||
public GameTime GameTime { get; set; }
|
||||
public ulong TotalTicks { get; set; }
|
||||
public bool IsActiveTick { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
|
|
@ -37,7 +52,7 @@ public class InputManager
|
|||
Ctrl = LCtrl | RCtrl,
|
||||
}
|
||||
|
||||
private static Keys[] IgnoredTextInputKeys =
|
||||
private static readonly Keys[] IgnoredTextInputKeys =
|
||||
{
|
||||
Keys.Enter,
|
||||
Keys.Back,
|
||||
|
|
@ -59,7 +74,7 @@ public class InputManager
|
|||
};
|
||||
|
||||
public Texture2D Texture { get; set; }
|
||||
public float WindowScale { get; set; }
|
||||
public static float WindowScale => Capy64.Instance.Scale;
|
||||
public const int MouseScrollDelta = 120;
|
||||
|
||||
private Point mousePosition;
|
||||
|
|
@ -67,7 +82,7 @@ public class InputManager
|
|||
private int hMouseScroll;
|
||||
|
||||
private Modifiers keyboardMods = 0;
|
||||
private HashSet<Keys> pressedKeys = new();
|
||||
private readonly HashSet<Keys> pressedKeys = new();
|
||||
|
||||
private readonly Game _game;
|
||||
private readonly EventEmitter _eventEmitter;
|
||||
|
|
@ -90,6 +105,7 @@ public class InputManager
|
|||
{
|
||||
UpdateMouse(Mouse.GetState(), IsActive);
|
||||
UpdateKeyboard(Keyboard.GetState(), IsActive);
|
||||
UpdateGamePad(GamePad.GetState(PlayerIndex.One), IsActive);
|
||||
}
|
||||
|
||||
private void UpdateMouse(MouseState state, bool isActive)
|
||||
|
|
@ -97,7 +113,7 @@ public class InputManager
|
|||
if (!isActive)
|
||||
return;
|
||||
|
||||
var rawPosition = state.Position;
|
||||
var rawPosition = state.Position - new Point(Capy64.Instance.Borders.Left, Capy64.Instance.Borders.Top);
|
||||
var pos = new Point((int)(rawPosition.X / WindowScale), (int)(rawPosition.Y / WindowScale)) + new Point(1, 1);
|
||||
|
||||
if (pos.X < 1 || pos.Y < 1 || pos.X > Texture.Width || pos.Y > Texture.Height)
|
||||
|
|
@ -288,4 +304,210 @@ public class InputManager
|
|||
{
|
||||
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
|
||||
}
|
||||
|
||||
private GamePadState oldGamePadState = new();
|
||||
private void UpdateGamePad(GamePadState state, bool isActive)
|
||||
{
|
||||
if (!isActive)
|
||||
return;
|
||||
|
||||
if (!state.IsConnected)
|
||||
return;
|
||||
|
||||
var ob = oldGamePadState.Buttons;
|
||||
var b = state.Buttons;
|
||||
// ABXY
|
||||
if (ob.A != b.A)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.A,
|
||||
State = b.A,
|
||||
});
|
||||
}
|
||||
|
||||
if (ob.B != b.B)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.B,
|
||||
State = b.B,
|
||||
});
|
||||
}
|
||||
|
||||
if (ob.X != b.X)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.X,
|
||||
State = b.X,
|
||||
});
|
||||
}
|
||||
|
||||
if (ob.Y != b.Y)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.Y,
|
||||
State = b.Y,
|
||||
});
|
||||
}
|
||||
|
||||
// Back & Start
|
||||
if (ob.Back != b.Back)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.Back,
|
||||
State = b.Back,
|
||||
});
|
||||
}
|
||||
|
||||
if (ob.Start != b.Start)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.Start,
|
||||
State = b.Start,
|
||||
});
|
||||
}
|
||||
|
||||
// Shoulders
|
||||
if (ob.LeftShoulder != b.LeftShoulder)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.LeftShoulder,
|
||||
State = b.LeftShoulder,
|
||||
});
|
||||
}
|
||||
|
||||
if (ob.RightShoulder != b.RightShoulder)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.RightShoulder,
|
||||
State = b.RightShoulder,
|
||||
});
|
||||
}
|
||||
|
||||
// Sticks
|
||||
if (ob.LeftStick != b.LeftStick)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.LeftStick,
|
||||
State = b.LeftStick,
|
||||
});
|
||||
}
|
||||
|
||||
if (ob.RightStick != b.RightStick)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.RightStick,
|
||||
State = b.RightStick,
|
||||
});
|
||||
}
|
||||
|
||||
// BIG BUTTON
|
||||
if (ob.BigButton != b.BigButton)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.BigButton,
|
||||
State = b.BigButton,
|
||||
});
|
||||
}
|
||||
|
||||
// D-PAD
|
||||
var od = oldGamePadState.DPad;
|
||||
var d = state.DPad;
|
||||
|
||||
if (od.Left != d.Left)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.DPadLeft,
|
||||
State = d.Left,
|
||||
});
|
||||
}
|
||||
|
||||
if (od.Right != d.Right)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.DPadRight,
|
||||
State = d.Right,
|
||||
});
|
||||
}
|
||||
|
||||
if (od.Up != d.Up)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.DPadUp,
|
||||
State = d.Up,
|
||||
});
|
||||
}
|
||||
|
||||
if (od.Down != d.Down)
|
||||
{
|
||||
_eventEmitter.RaiseGamePadButton(new()
|
||||
{
|
||||
Button = Buttons.DPadDown,
|
||||
State = d.Down,
|
||||
});
|
||||
}
|
||||
|
||||
// triggers
|
||||
var ot = oldGamePadState.Triggers;
|
||||
var t = state.Triggers;
|
||||
|
||||
if (ot.Left != t.Left)
|
||||
{
|
||||
|
||||
_eventEmitter.RaiseGamePadTrigger(new()
|
||||
{
|
||||
Trigger = 1, // left
|
||||
Value = t.Left,
|
||||
});
|
||||
}
|
||||
|
||||
if (ot.Right != t.Right)
|
||||
{
|
||||
|
||||
_eventEmitter.RaiseGamePadTrigger(new()
|
||||
{
|
||||
Trigger = 2, // right
|
||||
Value = t.Right,
|
||||
});
|
||||
}
|
||||
|
||||
// thumbsticks
|
||||
var os = oldGamePadState.ThumbSticks;
|
||||
var s = state.ThumbSticks;
|
||||
|
||||
if (os.Left != s.Left)
|
||||
{
|
||||
|
||||
_eventEmitter.RaiseGamePadThumbstick(new()
|
||||
{
|
||||
Stick = 1, // left
|
||||
Value = s.Left,
|
||||
});
|
||||
}
|
||||
|
||||
if (os.Right != s.Right)
|
||||
{
|
||||
|
||||
_eventEmitter.RaiseGamePadThumbstick(new()
|
||||
{
|
||||
Stick = 2, // right
|
||||
Value = s.Right,
|
||||
});
|
||||
}
|
||||
|
||||
oldGamePadState = state;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
36
Capy64/Extensions/Bindings/Common.cs
Normal file
36
Capy64/Extensions/Bindings/Common.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Capy64.Extensions.Bindings;
|
||||
|
||||
public partial class Common
|
||||
{
|
||||
public const int SW_HIDE = 0;
|
||||
public const int SW_SHOW = 5;
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
public static partial IntPtr GetConsoleWindow();
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool IsWindowVisible(IntPtr hWnd);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
}
|
||||
|
|
@ -1,32 +1,59 @@
|
|||
using System;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Capy64.Extensions.Bindings;
|
||||
|
||||
public partial class SDL2
|
||||
{
|
||||
private const string SDL = "SDL2.dll";
|
||||
#if _WINDOWS
|
||||
private const string LibraryName = "SDL2.dll";
|
||||
#elif _LINUX
|
||||
private const string LibraryName = "libSDL2-2.0.so.0";
|
||||
#elif _OSX
|
||||
private const string LibraryName = "libSDL2.dylib";
|
||||
#else
|
||||
private const string LibraryName = "SDL2";
|
||||
#endif
|
||||
|
||||
[LibraryImport(SDL)]
|
||||
[LibraryImport(LibraryName)]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
internal static partial void SDL_MaximizeWindow(IntPtr window);
|
||||
|
||||
[LibraryImport(SDL)]
|
||||
[LibraryImport(LibraryName)]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
internal static partial uint SDL_GetWindowFlags(IntPtr window);
|
||||
|
||||
[LibraryImport(SDL, EntryPoint = "SDL_GetClipboardText")]
|
||||
[LibraryImport(LibraryName, EntryPoint = "SDL_GetClipboardText")]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
internal static partial IntPtr Native_SDL_GetClipboardText();
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <returns>0 is false; 1 is true</returns>
|
||||
[LibraryImport(SDL)]
|
||||
[LibraryImport(LibraryName)]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
internal static partial int SDL_HasClipboardText();
|
||||
|
||||
[LibraryImport(SDL)]
|
||||
[LibraryImport(LibraryName, EntryPoint = "SDL_SetClipboardText", StringMarshalling = StringMarshalling.Utf8)]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
internal static partial int Native_SDL_SetClipboardText(string contents);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
internal static partial void SDL_free(IntPtr memblock);
|
||||
|
||||
|
|
@ -78,7 +105,7 @@ public partial class SDL2
|
|||
}
|
||||
char* chars = stackalloc char[len];
|
||||
int strLen = System.Text.Encoding.UTF8.GetChars((byte*)s, len, chars, len);
|
||||
string result = new string(chars, 0, strLen);
|
||||
string result = new(chars, 0, strLen);
|
||||
#endif
|
||||
|
||||
/* Some SDL functions will malloc, we have to free! */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
using Capy64.Extensions.Bindings;
|
||||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Capy64.Extensions.Bindings;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
|
|
@ -54,4 +69,19 @@ public static class GameWindowExtensions
|
|||
{
|
||||
return window.GetWindowFlags().HasFlag(WindowFlags.SDL_WINDOW_MAXIMIZED);
|
||||
}
|
||||
|
||||
public static void ToggleConsole(this GameWindow _, bool show)
|
||||
{
|
||||
var console = Common.GetConsoleWindow();
|
||||
if (console != IntPtr.Zero)
|
||||
Common.ShowWindow(console, show ? Common.SW_SHOW : Common.SW_HIDE);
|
||||
}
|
||||
|
||||
public static bool IsConsoleVisible(this GameWindow _)
|
||||
{
|
||||
var console = Common.GetConsoleWindow();
|
||||
if (console != IntPtr.Zero)
|
||||
return false;
|
||||
return Common.IsWindowVisible(console);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
using Capy64.API;
|
||||
using Capy64.Core;
|
||||
using Capy64.Eventing;
|
||||
using Capy64.Runtime;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Capy64;
|
||||
|
||||
public interface IGame
|
||||
{
|
||||
Capy64 Game { get; }
|
||||
IList<IPlugin> NativePlugins { get; }
|
||||
IList<IPlugin> Plugins { get; }
|
||||
GameWindow Window { get; }
|
||||
Drawing Drawing { get; }
|
||||
LuaState LuaRuntime { get; set; }
|
||||
EventEmitter EventEmitter { get; }
|
||||
void ConfigureServices(IServiceProvider serviceProvider);
|
||||
|
||||
int Width { get; set; }
|
||||
int Height { get; set; }
|
||||
float Scale { get; set; }
|
||||
void UpdateSize();
|
||||
|
||||
event EventHandler<EventArgs> Exiting;
|
||||
void Run();
|
||||
void Exit();
|
||||
}
|
||||
BIN
Capy64/Icon.bmp
BIN
Capy64/Icon.bmp
Binary file not shown.
|
Before Width: | Height: | Size: 256 KiB After Width: | Height: | Size: 1.3 MiB |
BIN
Capy64/Icon.ico
BIN
Capy64/Icon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 894 B |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue