├── .gitignore ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── aarch64-none-elf.json ├── build.rs ├── src ├── lang_items.rs └── lib.rs └── wrapper.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/rust,linux,macos 2 | 3 | ### Linux ### 4 | *~ 5 | 6 | # temporary files which can be created if a process still has a handle open of a deleted file 7 | .fuse_hidden* 8 | 9 | # KDE directory preferences 10 | .directory 11 | 12 | # Linux trash folder which might appear on any partition or disk 13 | .Trash-* 14 | 15 | # .nfs files are created when an open file is removed but is still being accessed 16 | .nfs* 17 | 18 | ### macOS ### 19 | *.DS_Store 20 | .AppleDouble 21 | .LSOverride 22 | 23 | # Icon must end with two \r 24 | Icon 25 | 26 | # Thumbnails 27 | ._* 28 | 29 | # Files that might appear in the root of a volume 30 | .DocumentRevisions-V100 31 | .fseventsd 32 | .Spotlight-V100 33 | .TemporaryItems 34 | .Trashes 35 | .VolumeIcon.icns 36 | .com.apple.timemachine.donotpresent 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | ### Rust ### 46 | # Generated by Cargo 47 | # will have compiled files and executables 48 | /target/ 49 | 50 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 51 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 52 | Cargo.lock 53 | 54 | # These are backup files generated by rustfmt 55 | **/*.rs.bk 56 | 57 | 58 | # End of https://www.gitignore.io/api/rust,linux,macos 59 | 60 | # Make 61 | build 62 | out 63 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rusted-switch" 3 | version = "0.0.6" 4 | authors = ["igor@borges.me"] 5 | 6 | [lib] 7 | crate-type = ["staticlib"] 8 | 9 | [profile.dev] 10 | panic = "abort" 11 | lto = true 12 | debug = true 13 | 14 | [profile.test] 15 | lto = true 16 | debug = true 17 | 18 | [profile.release] 19 | panic = "abort" 20 | lto = true 21 | 22 | [dependencies] 23 | rlibc = "*" 24 | panic-abort = "0.3.1" 25 | 26 | [build-dependencies] 27 | bindgen = "0.37.0" 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Igor Borges 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITPRO)/libnx/switch_rules 11 | 12 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # DATA is a list of directories containing data files 17 | # INCLUDES is a list of directories containing header files 18 | # EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". 19 | # 20 | # NO_ICON: if set to anything, do not use icon. 21 | # NO_NACP: if set to anything, no .nacp file is generated. 22 | # APP_TITLE is the name of the app stored in the .nacp file (Optional) 23 | # APP_AUTHOR is the author of the app stored in the .nacp file (Optional) 24 | # APP_VERSION is the version of the app stored in the .nacp file (Optional) 25 | # APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) 26 | # ICON is the filename of the icon (.jpg), relative to the project folder. 27 | # If not set, it attempts to use one of the following (in this order): 28 | # - .jpg 29 | # - icon.jpg 30 | # - /default_icon.jpg 31 | #--------------------------------------------------------------------------------- 32 | TARGET := $(notdir $(CURDIR)) 33 | OUTDIR := out 34 | BUILD := build 35 | SOURCES := src 36 | DATA := data 37 | INCLUDES := include 38 | EXEFS_SRC := exefs_src 39 | 40 | APP_TITLE := Rusted Switch 41 | APP_AUTHOR := Igor Borges 42 | APP_VERSION := 0.0.6 43 | 44 | #--------------------------------------------------------------------------------- 45 | # rust variables 46 | #--------------------------------------------------------------------------------- 47 | TARGET_TRIPLE ?= aarch64-none-elf 48 | XARGO ?= RUST_TARGET_PATH="$(TOPDIR)" xargo 49 | 50 | RUST_BINARY := $(shell cat $(TOPDIR)/Cargo.toml | grep name | cut -d\" -f 2 | tr - _) 51 | RUST_BUILD_DIR := $(TOPDIR)/target/$(TARGET_TRIPLE) 52 | RUST_RELEASE_LIB := $(RUST_BUILD_DIR)/release/lib$(RUST_BINARY).a 53 | RUST_DEPS = $(TOPDIR)/Cargo.toml $(TOPDIR)/build.rs $(TOPDIR)/src/*.rs 54 | RUST_LIB := $(TOPDIR)/$(BUILD)/lib$(RUST_BINARY).a 55 | 56 | #--------------------------------------------------------------------------------- 57 | # options for code generation 58 | #--------------------------------------------------------------------------------- 59 | ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE 60 | 61 | CFLAGS := -g -Wall -O2 -ffunction-sections \ 62 | $(ARCH) $(DEFINES) 63 | 64 | CFLAGS += $(INCLUDE) -DSWITCH 65 | 66 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 67 | 68 | ASFLAGS := -g $(ARCH) 69 | LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 70 | 71 | LIBS := -lnx -L. -l$(RUST_BINARY) 72 | 73 | #--------------------------------------------------------------------------------- 74 | # list of directories containing libraries, this must be the top level containing 75 | # include and lib 76 | #--------------------------------------------------------------------------------- 77 | LIBDIRS := $(PORTLIBS) $(LIBNX) 78 | 79 | 80 | #--------------------------------------------------------------------------------- 81 | # no real need to edit anything past this point unless you need to add additional 82 | # rules for different file extensions 83 | #--------------------------------------------------------------------------------- 84 | ifneq ($(BUILD),$(notdir $(CURDIR))) 85 | #--------------------------------------------------------------------------------- 86 | 87 | export OUTPUT := $(CURDIR)/$(OUTDIR)/$(TARGET) 88 | export TOPDIR := $(CURDIR) 89 | 90 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 91 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 92 | 93 | export DEPSDIR := $(CURDIR)/$(BUILD) 94 | 95 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 96 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 97 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 98 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 99 | 100 | #--------------------------------------------------------------------------------- 101 | # use CXX for linking C++ projects, CC for standard C 102 | #--------------------------------------------------------------------------------- 103 | ifeq ($(strip $(CPPFILES)),) 104 | #--------------------------------------------------------------------------------- 105 | export LD := $(CC) 106 | #--------------------------------------------------------------------------------- 107 | else 108 | #--------------------------------------------------------------------------------- 109 | export LD := $(CXX) 110 | #--------------------------------------------------------------------------------- 111 | endif 112 | #--------------------------------------------------------------------------------- 113 | 114 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 115 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 116 | 117 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 118 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 119 | -I$(CURDIR)/$(BUILD) 120 | 121 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 122 | 123 | export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) 124 | 125 | ifeq ($(strip $(ICON)),) 126 | icons := $(wildcard *.jpg) 127 | ifneq (,$(findstring $(TARGET).jpg,$(icons))) 128 | export APP_ICON := $(TOPDIR)/$(TARGET).jpg 129 | else 130 | ifneq (,$(findstring icon.jpg,$(icons))) 131 | export APP_ICON := $(TOPDIR)/icon.jpg 132 | endif 133 | endif 134 | else 135 | export APP_ICON := $(TOPDIR)/$(ICON) 136 | endif 137 | 138 | ifeq ($(strip $(NO_ICON)),) 139 | export NROFLAGS += --icon=$(APP_ICON) 140 | endif 141 | 142 | ifeq ($(strip $(NO_NACP)),) 143 | export NROFLAGS += --nacp=$(CURDIR)/$(OUTDIR)/$(TARGET).nacp 144 | endif 145 | 146 | ifneq ($(APP_TITLEID),) 147 | export NACPFLAGS += --titleid=$(APP_TITLEID) 148 | endif 149 | 150 | .PHONY: $(BUILD) clean all 151 | 152 | #--------------------------------------------------------------------------------- 153 | all: $(RUST_RELEASE_LIB) $(BUILD) 154 | 155 | $(BUILD): 156 | mkdir -p $@ $(BUILD) $(OUTDIR) 157 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 158 | 159 | #--------------------------------------------------------------------------------- 160 | clean: 161 | @echo clean ... 162 | @$(XARGO) clean 163 | @rm -fr $(OUTDIR) $(BUILD)/* target 164 | 165 | #--------------------------------------------------------------------------------- 166 | else 167 | .PHONY: all 168 | 169 | DEPENDS := $(OFILES:.o=.d) 170 | 171 | #--------------------------------------------------------------------------------- 172 | # main targets 173 | #--------------------------------------------------------------------------------- 174 | all : $(OUTPUT).pfs0 $(OUTPUT).nro 175 | 176 | $(OUTPUT).pfs0 : $(OUTPUT).nso 177 | 178 | $(OUTPUT).nso : $(OUTPUT).elf 179 | 180 | ifeq ($(strip $(NO_NACP)),) 181 | $(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp 182 | else 183 | $(OUTPUT).nro : $(OUTPUT).elf 184 | endif 185 | 186 | $(OUTPUT).elf : $(OFILES) $(RUST_LIB) 187 | 188 | #--------------------------------------------------------------------------------- 189 | # you need a rule like this for each extension you use as binary data 190 | #--------------------------------------------------------------------------------- 191 | %.bin.o : %.bin 192 | #--------------------------------------------------------------------------------- 193 | @echo $(notdir $<) 194 | @$(bin2o) 195 | 196 | -include $(DEPENDS) 197 | 198 | #--------------------------------------------------------------------------------------- 199 | endif 200 | #--------------------------------------------------------------------------------------- 201 | 202 | #--------------------------------------------------------------------------------- 203 | # rust rules 204 | #--------------------------------------------------------------------------------- 205 | check: 206 | @$(XARGO) check --target=$(TARGET_TRIPLE) 207 | 208 | $(RUST_RELEASE_LIB): $(RUST_DEPS) 209 | @echo "+ Building $@ [xargo --release] (via shell out)" 210 | @echo "+ Command: $(XARGO) build --release --target=$(TARGET_TRIPLE)" 211 | #@$(XARGO) build --release --target=$(TARGET_TRIPLE) 212 | @$(shell bash -c '$(XARGO) build --release --target=$(TARGET_TRIPLE)') 213 | 214 | $(RUST_LIB): $(RUST_RELEASE_LIB) | $(BUILD_DIR) 215 | @cp $< $@ 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | Rusted Switch 5 |

6 | 7 | ## How 8 | This project uses [rust-bindgen](https://github.com/rust-lang-nursery/rust-bindgen) to create bindings for [libnx](https://github.com/switchbrew/libnx). 9 | 10 | If you have [devkitPro](https://devkitpro.org/wiki/Getting_Started) installed, it's just a matter of running `make`. 11 | 12 | ## Credits 13 | - [rpi3-rust-template](https://github.com/cs140e/rpi3-rust-template) on building for aarch64-none-elf. 14 | -------------------------------------------------------------------------------- /aarch64-none-elf.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi-blacklist": [ 3 | "stdcall", 4 | "fastcall", 5 | "vectorcall", 6 | "thiscall", 7 | "win64", 8 | "sysv64" 9 | ], 10 | "arch": "aarch64", 11 | "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", 12 | "executables": true, 13 | "linker": "aarch64-none-elf-ld", 14 | "linker-flavor": "ld", 15 | "linker-is-gnu": true, 16 | "llvm-target": "aarch64-unknown-none", 17 | "no-compiler-rt": true, 18 | "features": "+a53,+strict-align", 19 | "max-atomic-width": 128, 20 | "os": "none", 21 | "panic": "abort", 22 | "panic-strategy": "abort", 23 | "position-independent-executables": true, 24 | "relocation-model": "pic", 25 | "target-c-int-width": "32", 26 | "target-endian": "little", 27 | "target-pointer-width": "64", 28 | "disable-redzone": true 29 | } 30 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | 3 | use std::env; 4 | use std::path::PathBuf; 5 | 6 | pub fn main() { 7 | let devkitpro_path = env::var("DEVKITPRO").unwrap(); 8 | 9 | println!("cargo:rustc-link-lib=static=nx"); 10 | println!("cargo:rustc-link-search=native={}/libnx/lib", devkitpro_path); 11 | 12 | // The bindgen::Builder is the main entry point 13 | // to bindgen, and lets you build up options for 14 | // the resulting bindings. 15 | let bindings = bindgen::Builder::default() 16 | // The input header we would like to generate bindings for. 17 | .trust_clang_mangling(false) 18 | .use_core() 19 | .ctypes_prefix("lang_items") 20 | 21 | .header("wrapper.h") 22 | 23 | .clang_arg(format!("-I{}/libnx/include", devkitpro_path)) 24 | .clang_arg(format!("-I{}/devkitA64/aarch64-none-elf/include", devkitpro_path)) 25 | 26 | .bitfield_enum("HidMouseButton") 27 | .bitfield_enum("HidKeyboardModifier") 28 | .rustified_enum("HidKeyboardScancode") 29 | .bitfield_enum("HidControllerType") 30 | .rustified_enum("HidControllerLayoutType") 31 | .bitfield_enum("HidControllerColorDescription") 32 | .bitfield_enum("HidControllerKeys") 33 | .rustified_enum("HidControllerJoystick") 34 | .bitfield_enum("HidControllerConnectionState") 35 | .rustified_enum("HidControllerID") 36 | 37 | .generate_inline_functions(true) 38 | 39 | .blacklist_type("u8") 40 | .blacklist_type("u16") 41 | .blacklist_type("u32") 42 | .blacklist_type("u64") 43 | 44 | // Finish the builder and generate the bindings. 45 | .generate() 46 | // Unwrap the Result and panic on failure. 47 | .expect("Unable to generate bindings"); 48 | 49 | // Write the bindings to the $OUT_DIR/bindings.rs file. 50 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 51 | bindings 52 | .write_to_file(out_path.join("bindings.rs")) 53 | .expect("Couldn't write bindings!"); 54 | } 55 | -------------------------------------------------------------------------------- /src/lang_items.rs: -------------------------------------------------------------------------------- 1 | pub enum c_void {} 2 | pub type c_char = i8; 3 | pub type c_int = i32; 4 | pub type c_long = i64; 5 | pub type c_longlong = i64; 6 | pub type c_schar = i8; 7 | pub type c_short = i16; 8 | pub type c_uchar = u8; 9 | pub type c_uint = u32; 10 | pub type c_ulong = u64; 11 | pub type c_ulonglong = u64; 12 | pub type c_ushort = u16; 13 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_upper_case_globals)] 4 | #![allow(non_snake_case)] 5 | #![feature(core_intrinsics)] 6 | 7 | extern crate panic_abort; 8 | 9 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 10 | 11 | pub const fn null() -> *mut T { 0 as *mut T } 12 | 13 | #[no_mangle] 14 | pub extern "C" fn main() { 15 | unsafe { 16 | consoleInit(null()); 17 | 18 | let mut k_held_old = HidControllerKeys(0); 19 | 20 | printf("\x1b[1;1HPress PLUS to exit.\n".as_ptr() as *const i8); 21 | printf("\x1b[2;1HOr any other key to see its value.\n".as_ptr() as *const i8); 22 | 23 | while appletMainLoop() { 24 | hidScanInput(); 25 | let k_held = HidControllerKeys(hidKeysHeld(HidControllerID::CONTROLLER_P1_AUTO) as u32); 26 | 27 | if k_held == HidControllerKeys::KEY_PLUS { 28 | break; 29 | } 30 | 31 | if k_held != k_held_old { 32 | consoleClear(); 33 | 34 | printf("\x1b[1;1HPress PLUS to exit.\n".as_ptr() as *const i8); 35 | printf("\x1b[2;1HOr any other key to see its value.\n".as_ptr() as *const i8); 36 | printf("\x1b[3;1HThis key is currently pressed: %d\n".as_ptr() as *const i8, k_held); 37 | } 38 | 39 | k_held_old = k_held; 40 | 41 | consoleUpdate(null()); 42 | } 43 | 44 | consoleExit(null()); 45 | } 46 | } 47 | 48 | pub mod lang_items; 49 | -------------------------------------------------------------------------------- /wrapper.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | --------------------------------------------------------------------------------