Initial commit

This commit is contained in:
Niklas080208
2026-02-11 20:33:01 +01:00
commit 617265f004
145 changed files with 45252 additions and 0 deletions

224
Makefile Normal file
View File

@@ -0,0 +1,224 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#---------------------------------------------------------------------------------
TARGET := swr-ini-tool
BUILD := build.nx
SOURCES := src
RESOURCES := resources
DATA := data
INCLUDES :=
APP_TITLE := SWR INI Tool
APP_AUTHOR := NiklasCFW
ROMFS := $(BUILD)/romfs
BOREALIS_PATH := lib/borealis
BOREALIS_RESOURCES := romfs:/borealis/
APP_RESOURCES := romfs:/
#---------------------------------------------------------------------------------
# version control constants
#---------------------------------------------------------------------------------
TARGET_VERSION := 0.1.0
APP_VERSION := $(TARGET_VERSION)
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__ \
-DAPP_TITLE="\"$(APP_TITLE)\"" \
-DBOREALIS_RESOURCES="\"$(BOREALIS_RESOURCES)\"" \
-DAPP_RESOURCES="\"$(APP_RESOURCES)\""
CXXFLAGS := $(CFLAGS) -std=c++1z
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx -lm
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
include $(TOPDIR)/$(BOREALIS_PATH)/library/borealis.mk
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
else
ifneq (,$(findstring icon.jpg,$(icons)))
export APP_ICON := $(TOPDIR)/icon.jpg
endif
endif
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
ifeq ($(strip $(NO_ICON)),)
export NROFLAGS += --icon=$(APP_ICON)
endif
ifeq ($(strip $(NO_NACP)),)
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
endif
ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID)
endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) $(ROMFS) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(ROMFS):
@[ -d $@ ] || mkdir -p $@
@echo Merging ROMFS...
@cp -ruf $(CURDIR)/$(BOREALIS_PATH)/resources/. $(CURDIR)/$(ROMFS)/borealis/
@cp -ruf $(CURDIR)/$(RESOURCES)/. $(CURDIR)/$(ROMFS)/
$(BUILD): $(ROMFS)
@[ -d $@ ] || mkdir -p $@
@MSYS2_ARG_CONV_EXCL="-D;$(MSYS2_ARG_CONV_EXCL)" $(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).nso $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

BIN
icon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,21 @@
---
AccessModifierOffset: -2
AlignConsecutiveAssignments: 'true'
BasedOnStyle: WebKit
BreakBeforeBraces: Allman
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<borealis/.*\.hpp>'
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IndentCaseLabels: 'true'
PointerAlignment: Left
SortIncludes: 'true'
Standard: Cpp11
...

1
lib/borealis/.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.glsl text eol=lf

10
lib/borealis/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
.*/
build/
build.nx/
*~
*.bak
*.nro
*.nacp
*.elf
.vscode
builddir

12
lib/borealis/.gitrepo Normal file
View File

@@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = https://github.com/natinusala/borealis
branch = master
commit = 2ec4957301f702635feac64111dcd212d2a37ec6
parent = ec9981362c84ff6d44bb574f311f1d994dc80d9c
method = rebase
cmdver = 0.4.0

17
lib/borealis/COPYING Normal file
View File

@@ -0,0 +1,17 @@
Copyright (c) 2019 fincs
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

674
lib/borealis/LICENSE Normal file
View File

@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

231
lib/borealis/Makefile Normal file
View File

@@ -0,0 +1,231 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated.
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
# ICON is the filename of the icon (.jpg), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
TARGET := borealis_example
BUILD := build.nx
SOURCES := example
DATA := data
ICON := resources/icon/borealis.jpg
INCLUDES := example
APP_TITLE := borealis example
APP_AUTHOR := natinusala
APP_VERSION := 1.0
ROMFS := resources
BOREALIS_PATH := .
BOREALIS_RESOURCES := romfs:/
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__ \
-DBOREALIS_RESOURCES="\"$(BOREALIS_RESOURCES)\""
CXXFLAGS := $(CFLAGS) -fno-exceptions -std=c++1z -O2
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx -lm
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
include $(TOPDIR)/library/borealis.mk
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
else
ifneq (,$(findstring icon.jpg,$(icons)))
export APP_ICON := $(TOPDIR)/icon.jpg
endif
endif
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
ifeq ($(strip $(NO_ICON)),)
export NROFLAGS += --icon=$(APP_ICON)
endif
ifeq ($(strip $(NO_NACP)),)
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
endif
ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID)
endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

65
lib/borealis/README.md Normal file
View File

@@ -0,0 +1,65 @@
# borealis
Hardware accelerated, Nintendo Switch inspired UI library for PC and Nintendo Switch. Powered by nanovg. Skeleton based on the [hybrid glfw app](https://github.com/fincs/hybrid_app) by fincs.
The library is still in early development and WILL lack features and contain bugs. Feel free to report them in the issues, or make a pull request if you happen to fix some.
You can see the planned features in the Projects tab.
Documentation is available [right here](https://github.com/natinusala/borealis/wiki). Warning: may not be up to date.
## Building the example for Switch
To build for Switch, a standard development environment must first be set up. In order to do so, [refer to the Getting Started guide](https://devkitpro.org/wiki/Getting_Started).
```bash
(sudo) (dkp-)pacman -S switch-glfw switch-mesa switch-glm
make -j
nxlink -s borealis_example.nro
```
## Building the example for PC
To build for PC, the following components are required:
- meson/ninja build system
- A C++ compiler supporting the C++17 standard
- GLFW version 3.3 or higher (as a static library)
- GLM version 0.9.8 or higher
Please refer to the usual sources of information for your particular operating system. Usually the commands needed to build this project will look like this:
```bash
meson build
ninja -C build
./build/borealis_example
```
Also, please note that the `resources` folder must be available in the working directory, otherwise the program will fail to find the shaders.
### Building the example for Windows using msys2
msys2 provides all packages needed to build this project:
```bash
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-meson mingw-w64-x86_64-ninja mingw-w64-x86_64-pkg-config mingw-w64-x86_64-glfw mingw-w64-x86_64-glm
meson build
ninja -C build
./build/borealis_example
```
### Including in your project (TL;DR: see the example makefile in this repo)
0. Your project must be built as C++17 (`-std=c++1z`). You also need to remove `-fno-rtti` and `-fno-exceptions` if you have them
1. Use a submodule (or even better, a [subrepo](https://github.com/ingydotnet/git-subrepo)) to clone this repository in your project
2. Copy the `resources` folder to the root of your project
3. For PC (meson):
1. take a standard meson file
2. use `subdir` to import the library folder
3. use the `borealis_files`, `borealis_dependencies` and `borealis_include` variables for respectively objects to build, dependencies (glfw...) and includes directory
4. add a `BOREALIS_RESOURCES` define pointing to the resources folder at runtime (so `resources`)
4. For Switch:
1. take a standard homebrew makefile
2. add a `BOREALIS_PATH` variable containing the subfolder you put the library in
3. use `include` to load `borealis.mk` (after `LIBDIRS` and `BOREALIS_PATH`)
4. set `ROMFS` to the resources folder
5. add a `BOREALIS_RESOURCES` define pointing to the resources folder at runtime (so `romfs:/`)

View File

@@ -0,0 +1,171 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <borealis.hpp>
#include <string>
#include "sample_installer_page.hpp"
#include "sample_loading_page.hpp"
std::vector<std::string> NOTIFICATIONS = {
"You have cool hair",
"I like your shoes",
"borealis is powered by nanovg",
"The Triforce is an inside job",
"Pozznx will trigger in one day and twelve hours",
"Aurora Borealis? At this time of day, at this time of year, in this part of the gaming market, located entirely within your Switch?!",
"May I see it?",
"Hmm, Steamed Hams!"
};
int main(int argc, char* argv[])
{
// Init the app
brls::Logger::setLogLevel(brls::LogLevel::DEBUG);
if (!brls::Application::init("Borealis example"))
{
brls::Logger::error("Unable to init Borealis application");
return EXIT_FAILURE;
}
// Create a sample view
brls::TabFrame* rootFrame = new brls::TabFrame();
rootFrame->setTitle("Borealis Example App");
rootFrame->setIcon(BOREALIS_ASSET("icon/borealis.jpg"));
brls::List* testList = new brls::List();
brls::ListItem* dialogItem = new brls::ListItem("Open a dialog");
dialogItem->getClickEvent()->subscribe([](brls::View* view) {
brls::Dialog* dialog = new brls::Dialog("Warning: PozzNX will wipe all data on your Switch and render it inoperable, do you want to proceed?");
brls::GenericEvent::Callback closeCallback = [dialog](brls::View* view) {
dialog->close();
brls::Application::notify("Running PozzNX...");
};
dialog->addButton("Continue", closeCallback);
dialog->addButton("Continue", closeCallback);
dialog->addButton("Continue", closeCallback);
dialog->setCancelable(false);
dialog->open();
});
brls::ListItem* notificationItem = new brls::ListItem("Post a random notification");
notificationItem->getClickEvent()->subscribe([](brls::View* view) {
std::string notification = NOTIFICATIONS[std::rand() % NOTIFICATIONS.size()];
brls::Application::notify(notification);
});
brls::ListItem* themeItem = new brls::ListItem("TV Resolution");
themeItem->setValue("Automatic");
brls::SelectListItem* jankItem = new brls::SelectListItem(
"User Interface Jank",
{ "Native", "Minimal", "Regular", "Maximum", "SX OS", "Windows Vista", "iOS 14" });
brls::ListItem* crashItem = new brls::ListItem("Divide by 0", "Can the Switch do it?");
crashItem->getClickEvent()->subscribe([](brls::View* view) { brls::Application::crash("The software was closed because an error occured:\nSIGABRT (signal 6)"); });
brls::ListItem* popupItem = new brls::ListItem("Open popup");
popupItem->getClickEvent()->subscribe([](brls::View* view) {
brls::TabFrame* popupTabFrame = new brls::TabFrame();
popupTabFrame->addTab("Red", new brls::Rectangle(nvgRGB(255, 0, 0)));
popupTabFrame->addTab("Green", new brls::Rectangle(nvgRGB(0, 255, 0)));
popupTabFrame->addTab("Blue", new brls::Rectangle(nvgRGB(0, 0, 255)));
brls::PopupFrame::open("Popup title", BOREALIS_ASSET("icon/borealis.jpg"), popupTabFrame, "Subtitle left", "Subtitle right");
});
brls::ListItem* installerItem = new brls::ListItem("Open example installer");
installerItem->getClickEvent()->subscribe([](brls::View* view) {
brls::StagedAppletFrame* stagedFrame = new brls::StagedAppletFrame();
stagedFrame->setTitle("My great installer");
stagedFrame->addStage(new SampleInstallerPage(stagedFrame, "Go to step 2"));
stagedFrame->addStage(new SampleLoadingPage(stagedFrame));
stagedFrame->addStage(new SampleInstallerPage(stagedFrame, "Finish"));
brls::Application::pushView(stagedFrame);
});
brls::SelectListItem* layerSelectItem = new brls::SelectListItem("Select Layer", { "Layer 1", "Layer 2" });
testList->addView(dialogItem);
testList->addView(notificationItem);
testList->addView(themeItem);
testList->addView(jankItem);
testList->addView(crashItem);
testList->addView(installerItem);
testList->addView(popupItem);
brls::Label* testLabel = new brls::Label(brls::LabelStyle::REGULAR, "For more information about how to use Nintendo Switch and its features, please refer to the Nintendo Support Website on your smart device or PC.", true);
testList->addView(testLabel);
brls::ListItem* actionTestItem = new brls::ListItem("Custom Actions");
actionTestItem->registerAction("Show notification", brls::Key::L, [] {
brls::Application::notify("Custom Action triggered");
return true;
});
testList->addView(actionTestItem);
brls::LayerView* testLayers = new brls::LayerView();
brls::List* layerList1 = new brls::List();
brls::List* layerList2 = new brls::List();
layerList1->addView(new brls::Header("Layer 1", false));
layerList1->addView(new brls::ListItem("Item 1"));
layerList1->addView(new brls::ListItem("Item 2"));
layerList1->addView(new brls::ListItem("Item 3"));
layerList2->addView(new brls::Header("Layer 2", false));
layerList2->addView(new brls::ListItem("Item 1"));
layerList2->addView(new brls::ListItem("Item 2"));
layerList2->addView(new brls::ListItem("Item 3"));
testLayers->addLayer(layerList1);
testLayers->addLayer(layerList2);
layerSelectItem->getValueSelectedEvent()->subscribe([=](size_t selection) {
testLayers->changeLayer(selection);
});
testList->addView(layerSelectItem);
rootFrame->addTab("First tab", testList);
rootFrame->addTab("Second tab", testLayers);
rootFrame->addSeparator();
rootFrame->addTab("Third tab", new brls::Rectangle(nvgRGB(255, 0, 0)));
rootFrame->addTab("Fourth tab", new brls::Rectangle(nvgRGB(0, 255, 0)));
// Add the root view to the stack
brls::Application::pushView(rootFrame);
// Run the app
while (brls::Application::mainLoop())
;
// Exit
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,75 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 Billy Laws
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "sample_installer_page.hpp"
#include <math.h>
SampleInstallerPage::SampleInstallerPage(brls::StagedAppletFrame* frame, std::string label)
{
// Label
this->button = (new brls::Button(brls::ButtonStyle::BORDERLESS))->setLabel(label)->setImage(BOREALIS_ASSET("icon/borealis.jpg"));
this->button->setParent(this);
this->button->getClickEvent()->subscribe([frame](View* view) {
if (frame->isLastStage())
brls::Application::popView();
else
frame->nextStage();
});
this->label = new brls::Label(brls::LabelStyle::DIALOG, "Here, you would normally do useful things", true);
this->label->setHorizontalAlign(NVG_ALIGN_CENTER);
this->label->setParent(this);
}
void SampleInstallerPage::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
{
this->label->frame(ctx);
this->button->frame(ctx);
}
brls::View* SampleInstallerPage::getDefaultFocus()
{
return this->button;
}
void SampleInstallerPage::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
{
this->label->setWidth(roundf((float)this->width * style->CrashFrame.labelWidth));
this->label->invalidate(true);
this->label->setBoundaries(
this->x + this->width / 2 - this->label->getWidth() / 2,
this->y + (this->height - style->AppletFrame.footerHeight) / 2,
this->label->getWidth(),
this->label->getHeight());
this->button->setBoundaries(
this->x + this->width / 2 - style->CrashFrame.buttonWidth / 2,
this->y + this->height / 2 + style->CrashFrame.buttonHeight,
style->CrashFrame.buttonWidth,
style->CrashFrame.buttonHeight);
this->button->invalidate();
}
SampleInstallerPage::~SampleInstallerPage()
{
delete this->label;
delete this->button;
}

View File

@@ -0,0 +1,38 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 Billy Laws
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
class SampleInstallerPage : public brls::View
{
private:
brls::Button* button;
brls::Label* label;
public:
SampleInstallerPage(brls::StagedAppletFrame* frame, std::string label);
~SampleInstallerPage();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
brls::View* getDefaultFocus() override;
};

View File

@@ -0,0 +1,80 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 Billy Laws
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "sample_loading_page.hpp"
#include <math.h>
SampleLoadingPage::SampleLoadingPage(brls::StagedAppletFrame* frame)
: frame(frame)
{
// Label
this->progressDisp = new brls::ProgressDisplay();
this->progressDisp->setProgress(this->progressValue, 1000);
this->progressDisp->setParent(this);
this->label = new brls::Label(brls::LabelStyle::DIALOG, "Example loading display", true);
this->label->setHorizontalAlign(NVG_ALIGN_CENTER);
this->label->setParent(this);
}
void SampleLoadingPage::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx)
{
if (progressValue == 500)
this->frame->nextStage();
this->progressValue++;
this->progressDisp->setProgress(this->progressValue, 500);
this->progressDisp->frame(ctx);
this->label->frame(ctx);
}
void SampleLoadingPage::layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash)
{
this->label->setWidth(roundf((float)this->width * style->CrashFrame.labelWidth));
this->label->invalidate(true);
this->label->setBoundaries(
this->x + this->width / 2 - this->label->getWidth() / 2,
this->y + (this->height - style->AppletFrame.footerHeight) / 2,
this->label->getWidth(),
this->label->getHeight());
this->progressDisp->setBoundaries(
this->x + this->width / 2 - style->CrashFrame.buttonWidth,
this->y + this->height / 2,
style->CrashFrame.buttonWidth * 2,
style->CrashFrame.buttonHeight);
}
void SampleLoadingPage::willAppear(bool resetState)
{
this->progressDisp->willAppear(resetState);
}
void SampleLoadingPage::willDisappear(bool resetState)
{
this->progressDisp->willDisappear(resetState);
}
SampleLoadingPage::~SampleLoadingPage()
{
delete this->progressDisp;
delete this->label;
}

View File

@@ -0,0 +1,42 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 Billy Laws
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis.hpp>
class SampleLoadingPage : public brls::View
{
private:
brls::StagedAppletFrame* frame;
brls::ProgressDisplay* progressDisp;
brls::Label* label;
int progressValue = 0;
public:
SampleLoadingPage(brls::StagedAppletFrame* frame);
~SampleLoadingPage();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
};

View File

@@ -0,0 +1,18 @@
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(BOREALIS_PATH)/$(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
LIBS := -lglfw3 -lEGL -lglapi -ldrm_nouveau -lm $(LIBS)
SOURCES := $(SOURCES) \
$(current_dir)/lib \
$(current_dir)/lib/extern/glad \
$(current_dir)/lib/extern/nanovg \
$(current_dir)/lib/extern/libretro-common/compat \
$(current_dir)/lib/extern/libretro-common/encodings \
$(current_dir)/lib/extern/libretro-common/features
INCLUDES := $(INCLUDES) \
$(current_dir)/include \
$(current_dir)/include/borealis/extern/glad \
$(current_dir)/include/borealis/extern/nanovg \
$(current_dir)/include/borealis/extern/libretro-common

View File

@@ -0,0 +1,56 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef BOREALIS_RESOURCES
#error BOREALIS_RESOURCES define missing
#endif
#define BOREALIS_ASSET(_str) BOREALIS_RESOURCES _str
// Library
#include <borealis/applet_frame.hpp>
#include <borealis/application.hpp>
#include <borealis/box_layout.hpp>
#include <borealis/button.hpp>
#include <borealis/crash_frame.hpp>
#include <borealis/dialog.hpp>
#include <borealis/dropdown.hpp>
#include <borealis/event.hpp>
#include <borealis/header.hpp>
#include <borealis/image.hpp>
#include <borealis/label.hpp>
#include <borealis/layer_view.hpp>
#include <borealis/list.hpp>
#include <borealis/logger.hpp>
#include <borealis/material_icon.hpp>
#include <borealis/notification_manager.hpp>
#include <borealis/popup_frame.hpp>
#include <borealis/progress_display.hpp>
#include <borealis/progress_spinner.hpp>
#include <borealis/rectangle.hpp>
#include <borealis/repeating_task.hpp>
#include <borealis/sidebar.hpp>
#include <borealis/staged_applet_frame.hpp>
#include <borealis/style.hpp>
#include <borealis/tab_frame.hpp>
#include <borealis/table.hpp>
#include <borealis/theme.hpp>
#include <borealis/thumbnail_frame.hpp>
#include <borealis/view.hpp>

View File

@@ -0,0 +1,68 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2020 WerWolv
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <functional>
#include <string>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
namespace brls
{
class View;
typedef std::function<bool(void)> ActionListener;
// ZL and ZR do not exist here because GLFW doesn't know them
enum class Key
{
A = GLFW_GAMEPAD_BUTTON_A,
B = GLFW_GAMEPAD_BUTTON_B,
X = GLFW_GAMEPAD_BUTTON_X,
Y = GLFW_GAMEPAD_BUTTON_Y,
LSTICK = GLFW_GAMEPAD_BUTTON_LEFT_THUMB,
RSTICK = GLFW_GAMEPAD_BUTTON_RIGHT_THUMB,
L = GLFW_GAMEPAD_BUTTON_LEFT_BUMPER,
R = GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER,
PLUS = GLFW_GAMEPAD_BUTTON_START,
MINUS = GLFW_GAMEPAD_BUTTON_BACK,
DLEFT = GLFW_GAMEPAD_BUTTON_DPAD_LEFT,
DUP = GLFW_GAMEPAD_BUTTON_DPAD_UP,
DRIGHT = GLFW_GAMEPAD_BUTTON_DPAD_RIGHT,
DDOWN = GLFW_GAMEPAD_BUTTON_DPAD_DOWN,
};
struct Action
{
Key key;
std::string hintText;
bool available;
bool hidden;
ActionListener actionListener;
bool operator==(const Key other)
{
return this->key == other;
}
};
} // namespace brls

View File

@@ -0,0 +1,178 @@
/* RetroArch - A frontend for libretro.
* Borealis, a Nintendo Switch UI Library
* Copyright (C) 2014-2017 - Jean-André Santoni
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2019 - natinusala
Copyright (C) 2019 - p-sam
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <functional>
namespace brls
{
typedef float (*easing_cb)(float, float, float, float);
typedef std::function<void(void*)> tween_cb;
enum menu_animation_ctl_state
{
MENU_ANIMATION_CTL_NONE = 0,
MENU_ANIMATION_CTL_DEINIT,
MENU_ANIMATION_CTL_CLEAR_ACTIVE,
MENU_ANIMATION_CTL_SET_ACTIVE
};
enum menu_animation_easing_type
{
/* Linear */
EASING_LINEAR = 0,
/* Quad */
EASING_IN_QUAD,
EASING_OUT_QUAD,
EASING_IN_OUT_QUAD,
EASING_OUT_IN_QUAD,
/* Cubic */
EASING_IN_CUBIC,
EASING_OUT_CUBIC,
EASING_IN_OUT_CUBIC,
EASING_OUT_IN_CUBIC,
/* Quart */
EASING_IN_QUART,
EASING_OUT_QUART,
EASING_IN_OUT_QUART,
EASING_OUT_IN_QUART,
/* Quint */
EASING_IN_QUINT,
EASING_OUT_QUINT,
EASING_IN_OUT_QUINT,
EASING_OUT_IN_QUINT,
/* Sine */
EASING_IN_SINE,
EASING_OUT_SINE,
EASING_IN_OUT_SINE,
EASING_OUT_IN_SINE,
/* Expo */
EASING_IN_EXPO,
EASING_OUT_EXPO,
EASING_IN_OUT_EXPO,
EASING_OUT_IN_EXPO,
/* Circ */
EASING_IN_CIRC,
EASING_OUT_CIRC,
EASING_IN_OUT_CIRC,
EASING_OUT_IN_CIRC,
/* Bounce */
EASING_IN_BOUNCE,
EASING_OUT_BOUNCE,
EASING_IN_OUT_BOUNCE,
EASING_OUT_IN_BOUNCE,
EASING_LAST
};
/* TODO:
* Add a reverse loop ticker for languages
* that read right to left */
enum menu_animation_ticker_type
{
TICKER_TYPE_BOUNCE = 0,
TICKER_TYPE_LOOP,
TICKER_TYPE_LAST
};
typedef uintptr_t menu_animation_ctx_tag;
typedef struct menu_animation_ctx_subject
{
size_t count;
const void* data;
} menu_animation_ctx_subject_t;
typedef struct menu_animation_ctx_entry
{
enum menu_animation_easing_type easing_enum;
uintptr_t tag;
float duration;
float target_value;
float* subject;
tween_cb cb;
tween_cb tick;
void* userdata;
} menu_animation_ctx_entry_t;
typedef struct menu_animation_ctx_ticker
{
bool selected;
size_t len;
uint64_t idx;
enum menu_animation_ticker_type type_enum;
char* s;
const char* str;
const char* spacer;
} menu_animation_ctx_ticker_t;
typedef float menu_timer_t;
typedef struct menu_timer_ctx_entry
{
float duration;
tween_cb cb;
tween_cb tick;
void* userdata;
} menu_timer_ctx_entry_t;
typedef struct menu_delayed_animation
{
menu_timer_t timer;
menu_animation_ctx_entry_t entry;
} menu_delayed_animation_t;
void menu_timer_start(menu_timer_t* timer, menu_timer_ctx_entry_t* timer_entry);
void menu_timer_kill(menu_timer_t* timer);
void menu_animation_init(void);
void menu_animation_free(void);
bool menu_animation_update(void);
bool menu_animation_ticker(menu_animation_ctx_ticker_t* ticker);
float menu_animation_get_delta_time(void);
bool menu_animation_is_active(void);
bool menu_animation_kill_by_tag(menu_animation_ctx_tag* tag);
void menu_animation_kill_by_subject(menu_animation_ctx_subject_t* subject);
bool menu_animation_push(menu_animation_ctx_entry_t* entry);
void menu_animation_push_delayed(unsigned delay, menu_animation_ctx_entry_t* entry);
bool menu_animation_ctl(enum menu_animation_ctl_state state, void* data);
uint64_t menu_animation_get_ticker_idx(void);
uint64_t menu_animation_get_ticker_slow_idx(void);
void menu_animation_get_highlight(float* gradient_x, float* gradient_y, float* color);
} // namespace brls

View File

@@ -0,0 +1,94 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/frame_context.hpp>
#include <borealis/hint.hpp>
#include <borealis/image.hpp>
#include <borealis/view.hpp>
#include <string>
namespace brls
{
enum class HeaderStyle
{
REGULAR,
POPUP // Only meant for PopupFrames. Using it in other contexts might cause weird behaviour
};
// A Horizon settings-like frame, with header and footer (no sidebar)
class AppletFrame : public View
{
private:
std::string title = "";
std::string footerText = "";
std::string subTitleLeft = "", subTitleRight = "";
View* icon = nullptr;
Hint* hint = nullptr;
View* contentView = nullptr;
bool slideOut = false;
bool slideIn = false;
ViewAnimation animation;
protected:
HeaderStyle headerStyle = HeaderStyle::REGULAR;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
unsigned leftPadding = 0;
unsigned rightPadding = 0;
public:
AppletFrame(bool padLeft, bool padRight);
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
View* getDefaultFocus() override;
virtual bool onCancel();
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
void show(std::function<void(void)> cb, bool animate = true, ViewAnimation animation = ViewAnimation::FADE) override;
void hide(std::function<void(void)> cb, bool animated = true, ViewAnimation animation = ViewAnimation::FADE) override;
void onWindowSizeChanged() override;
void setTitle(std::string title);
void setFooterText(std::string footerText);
void setSubtitle(std::string left, std::string right);
void setIcon(unsigned char* buffer, size_t bufferSize);
void setIcon(std::string imagePath);
void setIcon(View* view);
virtual void setContentView(View* view);
bool hasContentView();
void setHeaderStyle(HeaderStyle headerStyle);
void setAnimateHint(bool animate)
{
this->hint->setAnimate(animate);
}
~AppletFrame();
};
} // namespace brls

View File

@@ -0,0 +1,204 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <nanovg.h>
#include <borealis/animations.hpp>
#include <borealis/frame_context.hpp>
#include <borealis/hint.hpp>
#include <borealis/label.hpp>
#include <borealis/logger.hpp>
#include <borealis/notification_manager.hpp>
#include <borealis/style.hpp>
#include <borealis/task_manager.hpp>
#include <borealis/theme.hpp>
#include <borealis/view.hpp>
#include <map>
#include <vector>
namespace brls
{
// The top-right framerate counter
class FramerateCounter : public Label
{
private:
retro_time_t lastSecond = 0;
unsigned frames = 0;
public:
FramerateCounter();
void frame(FrameContext* ctx) override;
};
class Application
{
public:
//Init with default style and theme (as close to HOS as possible)
static bool init(std::string title);
// Init with given style and theme
static bool init(std::string title, Style style, Theme theme);
static bool mainLoop();
/**
* Pushes a view on this applications's view stack
*
* The view will automatically be resized to take
* the whole screen and layout() will be called
*
* The view will gain focus if applicable
*/
static void pushView(View* view, ViewAnimation animation = ViewAnimation::FADE);
/**
* Pops the last pushed view from the stack
* and gives focus back where it was before
*/
static void popView(
ViewAnimation animation = ViewAnimation::FADE, std::function<void(void)> cb = []() {});
/**
* Gives the focus to the given view
* or clears the focus if given nullptr
*/
static void giveFocus(View* view);
static Style* getStyle();
static void setTheme(Theme theme);
static ThemeValues* getThemeValues();
static ThemeValues* getThemeValuesForVariant(ThemeVariant variant);
static ThemeVariant getThemeVariant();
static int loadFont(const char* fontName, const char* filePath);
static int loadFontFromMemory(const char* fontName, void* data, size_t size, bool freeData);
static int findFont(const char* fontName);
static FontStash* getFontStash();
static void notify(std::string text);
static void onGamepadButtonPressed(char button, bool repeating);
/**
* "Crashes" the app (displays a fullscreen CrashFrame)
*/
static void crash(std::string text);
static void quit();
/**
* Blocks any and all user inputs
*/
static void blockInputs();
/**
* Unblocks inputs after a call to
* blockInputs()
*/
static void unblockInputs();
static NVGcontext* getNVGContext();
static TaskManager* getTaskManager();
static NotificationManager* getNotificationManager();
static void setCommonFooter(std::string footer);
static std::string* getCommonFooter();
static void setDisplayFramerate(bool enabled);
static void toggleFramerateDisplay();
static void setMaximumFPS(unsigned fps);
// public so that the glfw callback can access it
inline static unsigned contentWidth, contentHeight;
inline static float windowScale;
static void resizeFramerateCounter();
static void resizeNotificationManager();
static GenericEvent* getGlobalFocusChangeEvent();
static VoidEvent* getGlobalHintsUpdateEvent();
static View* getCurrentFocus();
static std::string getTitle();
private:
inline static GLFWwindow* window;
inline static NVGcontext* vg;
inline static std::string title;
inline static TaskManager* taskManager;
inline static NotificationManager* notificationManager;
inline static FontStash fontStash;
inline static std::vector<View*> viewStack;
inline static std::vector<View*> focusStack;
inline static unsigned windowWidth, windowHeight;
inline static View* currentFocus;
inline static Theme currentTheme;
inline static ThemeVariant currentThemeVariant;
inline static GLFWgamepadstate oldGamepad;
inline static GLFWgamepadstate gamepad;
inline static Style currentStyle;
inline static unsigned blockInputsTokens = 0; // any value > 0 means inputs are blocked
inline static std::string commonFooter = "";
inline static FramerateCounter* framerateCounter = nullptr;
inline static float frameTime = 0.0f;
inline static View* repetitionOldFocus = nullptr;
inline static GenericEvent globalFocusChangeEvent;
inline static VoidEvent globalHintsUpdateEvent;
static void navigate(FocusDirection direction);
static void onWindowSizeChanged();
static void frame();
static void clear();
static void exit();
/**
* Handles actions for the currently focused view and
* the given button
* Returns true if at least one action has been fired
*/
static bool handleAction(char button);
};
} // namespace brls

View File

@@ -0,0 +1,173 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 WerWolv
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/view.hpp>
#include <vector>
namespace brls
{
enum class BoxLayoutOrientation
{
VERTICAL,
HORIZONTAL
};
// TODO: Implement all gravity options for both orientations
enum class BoxLayoutGravity
{
DEFAULT, // left for horizontal, top for vertical
LEFT,
RIGHT,
TOP,
BOTTOM,
CENTER
};
class BoxLayoutChild
{
public:
View* view;
bool fill; // should the child fill the remaining space?
};
// A basic horizontal or vertical box layout :
// - Children can currently only be stretched to full width (vertical) or height (horizontal)
// - Only works with children with fixed width (horizontal) or height (vertical)
// TODO: More complex alignment and/or stretching parameters to children
class BoxLayout : public View
{
private:
BoxLayoutOrientation orientation;
unsigned spacing = 0;
bool resize = false; // should the view be resized according to children size after a layout?
BoxLayoutGravity gravity = BoxLayoutGravity::DEFAULT;
protected:
std::vector<BoxLayoutChild*> children;
size_t originalDefaultFocus = 0;
size_t defaultFocusedIndex = 0;
bool childFocused = false;
bool rememberFocus = false;
unsigned marginTop = 0;
unsigned marginRight = 0;
unsigned marginBottom = 0;
unsigned marginLeft = 0;
/**
* Should the BoxLayout apply spacing after
* this view?
*/
virtual void customSpacing(View* current, View* next, int* spacing) {}
public:
BoxLayout(BoxLayoutOrientation orientation, size_t defaultFocus = 0);
~BoxLayout();
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
View* getNextFocus(FocusDirection direction, void* parentUserdata) override;
View* getDefaultFocus() override;
void onChildFocusGained(View* child) override;
void onChildFocusLost(View* child) override;
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
void onWindowSizeChanged() override;
/**
* Sets gravity
*/
void setGravity(BoxLayoutGravity gravity);
/**
* Sets spacing between views
*/
void setSpacing(unsigned spacing);
unsigned getSpacing();
/**
* Sets margins around views
* Bottom (vertical) or right (horizontal) are
* only effective if the last child is set to fill
*/
void setMargins(unsigned top, unsigned right, unsigned bottom, unsigned left);
void setMarginBottom(unsigned bottom);
/**
* Adds a view to this box layout
* If fill is set to true, the child will
* fill the remaining space
*/
void addView(View* view, bool fill = false, bool resetState = false);
/**
* Removes the view at specified
* The view will be freed if free
* is set to true (defaults to true)
*
* Warning: this method isn't correctly
* implemented - currently removing a view will
* most likely result in memory corruption
*/
void removeView(int index, bool free = true);
/**
* Removes all views
* from this layout
*/
void clear(bool free = true);
/**
* Returns true if this layout
* doesn't contain any views
*/
bool isEmpty();
bool isChildFocused();
void setFocusedIndex(unsigned index);
size_t getViewsCount();
View* getChild(size_t i);
/**
* If enabled, will force the layout to resize itself
* to match the children size
* Mandatory for using in a ScrollView
*/
void setResize(bool resize);
/**
* Should the default focus be set to the originally focused
* view (until the layout disappears)?
*/
void setRememberFocus(bool rememberFocus);
};
} // namespace brls

View File

@@ -0,0 +1,99 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/image.hpp>
#include <borealis/label.hpp>
#include <borealis/view.hpp>
namespace brls
{
enum class ButtonStyle
{
PLAIN = 0, // regular, plain button
BORDERED, // text and a border
BORDERLESS, // only text
CRASH, // same as borderless but with a different text color
DIALOG // same as borderless but with a different text color
};
enum class ButtonState
{
ENABLED = 0,
DISABLED
};
// A button
class Button : public View
{
private:
ButtonStyle style;
Label* label = nullptr;
Image* image = nullptr;
GenericEvent clickEvent;
LabelStyle getLabelStyle();
ButtonState state = ButtonState::ENABLED;
float cornerRadiusOverride = 0;
public:
Button(ButtonStyle style);
~Button();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
virtual bool onClick();
void layout(NVGcontext* vg, Style* style, FontStash* stash);
void getHighlightInsets(unsigned* top, unsigned* right, unsigned* bottom, unsigned* left) override;
ButtonState getState();
void setState(ButtonState state);
Button* setLabel(std::string label);
Button* setImage(std::string path);
Button* setImage(unsigned char* buffer, size_t bufferSize);
GenericEvent* getClickEvent();
View* getDefaultFocus() override
{
return this;
}
void setCornerRadius(float cornerRadius);
void getHighlightMetrics(Style* style, float* cornerRadius) override
{
if (cornerRadiusOverride)
*cornerRadius = cornerRadiusOverride;
else
*cornerRadius = style->Button.cornerRadius;
}
bool isHighlightBackgroundEnabled() override
{
return false;
}
};
} // namespace brls

View File

@@ -0,0 +1,55 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/button.hpp>
#include <borealis/hint.hpp>
#include <borealis/label.hpp>
#include <borealis/view.hpp>
namespace brls
{
// A screen similar to the "The software has closed" dialog
// pressing OK will exit the app
class CrashFrame : public View
{
private:
Label* label;
Button* button;
Hint* hint;
public:
CrashFrame(std::string text);
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
void onShowAnimationEnd() override;
View* getDefaultFocus() override;
bool isTranslucent() override
{
return true; // have it always translucent to disable fade out animation
}
~CrashFrame();
};
} // namespace brls

View File

@@ -0,0 +1,95 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/box_layout.hpp>
#include <borealis/view.hpp>
namespace brls
{
// TODO: Add a "can be cancelled with B" flag
// TODO: Add buttons at creation time
// TODO: Add the blurred dialog type once the blur is finished
class DialogButton
{
public:
std::string label;
GenericEvent::Callback cb;
};
// A modal dialog with zero to three buttons
// and anything as content
// Create the dialog then use open() and close()
class Dialog : public View
{
private:
View* contentView = nullptr;
unsigned frameX, frameY, frameWidth, frameHeight;
std::vector<DialogButton*> buttons;
BoxLayout* verticalButtonsLayout = nullptr;
BoxLayout* horizontalButtonsLayout = nullptr;
void rebuildButtons();
unsigned getButtonsHeight();
bool cancelable = true;
public:
Dialog(std::string text);
Dialog(View* contentView);
~Dialog();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
View* getDefaultFocus() override;
virtual bool onCancel();
/**
* Adds a button to this dialog, with a maximum of three
* The position depends on the add order
*
* Adding a button after the dialog has been opened is
* NOT SUPPORTED
*/
void addButton(std::string label, GenericEvent::Callback cb);
/**
* A cancelable dialog is closed when
* the user presses B (defaults to true)
*
* A dialog without any buttons cannot
* be cancelable
*/
void setCancelable(bool cancelable);
void open();
void close(std::function<void(void)> cb = []() {});
bool isTranslucent() override
{
return true;
}
};
} // namespace brls

View File

@@ -0,0 +1,80 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019-2020 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/event.hpp>
#include <borealis/list.hpp>
#include <borealis/view.hpp>
#include <string>
namespace brls
{
// Fired when the user has selected a value
//
// Parameter is either the selected value index
// or -1 if the user cancelled
//
// Assume that the Dropdown is deleted
// as soon as this function is called
typedef Event<int> ValueSelectedEvent;
// Allows the user to select between multiple
// values
// Use Dropdown::open()
class Dropdown : public View
{
private:
Dropdown(std::string title, std::vector<std::string> values, ValueSelectedEvent::Callback cb, size_t selected = 0);
std::string title;
int valuesCount;
ValueSelectedEvent valueEvent;
List* list;
Hint* hint;
float topOffset; // for slide in animation
protected:
unsigned getShowAnimationDuration(ViewAnimation animation) override;
public:
~Dropdown();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
View* getDefaultFocus() override;
virtual bool onCancel();
void show(std::function<void(void)> cb, bool animate = true, ViewAnimation animation = ViewAnimation::FADE) override;
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
static void open(std::string title, std::vector<std::string> values, ValueSelectedEvent::Callback cb, int selected = -1);
bool isTranslucent() override
{
return true || View::isTranslucent();
}
};
} // namespace brls

View File

@@ -0,0 +1,75 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2020 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <algorithm>
#include <functional>
#include <list>
namespace brls
{
// Simple observer pattern implementation
//
// Usage:
// 1. typedef your event type
// 2. create as many events as you want using that type
// 3. call subscribe on the events with your observers
// 4. call fire when you want to fire the events
// it wil return true if at least one subscriber exists
// for that event
template <typename... Ts>
class Event
{
public:
typedef std::function<void(Ts...)> Callback;
typedef std::list<Callback> CallbacksList;
typedef typename CallbacksList::iterator Subscription;
Subscription subscribe(Callback cb);
void unsubscribe(Subscription subscription);
bool fire(Ts... args);
private:
CallbacksList callbacks;
};
template <typename... Ts>
typename Event<Ts...>::Subscription Event<Ts...>::subscribe(Event<Ts...>::Callback cb)
{
this->callbacks.push_back(cb);
return --this->callbacks.end();
}
template <typename... Ts>
void Event<Ts...>::unsubscribe(Event<Ts...>::Subscription subscription)
{
this->callbacks.erase(subscription);
}
template <typename... Ts>
bool Event<Ts...>::fire(Ts... args)
{
for (Callback cb : this->callbacks)
cb(args...);
return !this->callbacks.empty();
}
}; // namespace brls

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (boolean.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef __LIBRETRO_SDK_BOOLEAN_H
#define __LIBRETRO_SDK_BOOLEAN_H
#ifndef __cplusplus
#if defined(_MSC_VER) && _MSC_VER < 1800 && !defined(SN_TARGET_PS3)
/* Hack applied for MSVC when compiling in C89 mode as it isn't C99 compliant. */
#define bool unsigned char
#define true 1
#define false 0
#else
#include <stdbool.h>
#endif
#endif
#endif

View File

@@ -0,0 +1,59 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (strl.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef __LIBRETRO_SDK_COMPAT_STRL_H
#define __LIBRETRO_SDK_COMPAT_STRL_H
#include <string.h>
#include <stddef.h>
#if defined(RARCH_INTERNAL) && defined(HAVE_CONFIG_H)
#include "../../../config.h"
#endif
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
#ifdef __MACH__
#ifndef HAVE_STRL
#define HAVE_STRL
#endif
#endif
#ifndef HAVE_STRL
/* Avoid possible naming collisions during link since
* we prefer to use the actual name. */
#define strlcpy(dst, src, size) strlcpy_retro__(dst, src, size)
#define strlcat(dst, src, size) strlcat_retro__(dst, src, size)
size_t strlcpy(char *dest, const char *source, size_t size);
size_t strlcat(char *dest, const char *source, size_t size);
#endif
char *strldup(const char *s, size_t n);
RETRO_END_DECLS
#endif

View File

@@ -0,0 +1,67 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (utf.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef _LIBRETRO_ENCODINGS_UTF_H
#define _LIBRETRO_ENCODINGS_UTF_H
#include <stdint.h>
#include <stddef.h>
#include <boolean.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
enum CodePage
{
CODEPAGE_LOCAL = 0, /* CP_ACP */
CODEPAGE_UTF8 = 65001 /* CP_UTF8 */
};
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
const char *in, size_t in_size);
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
const uint16_t *in, size_t in_size);
size_t utf8len(const char *string);
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars);
const char *utf8skip(const char *str, size_t chars);
uint32_t utf8_walk(const char **string);
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len);
char* utf8_to_local_string_alloc(const char *str);
char* local_to_utf8_string_alloc(const char *str);
wchar_t* utf8_to_utf16_string_alloc(const char *str);
char* utf16_to_utf8_string_alloc(const wchar_t *str);
RETRO_END_DECLS
#endif

View File

@@ -0,0 +1,75 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (features_cpu.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef _LIBRETRO_SDK_CPU_INFO_H
#define _LIBRETRO_SDK_CPU_INFO_H
#include <retro_common_api.h>
#include <stdint.h>
#include <libretro.h>
RETRO_BEGIN_DECLS
/**
* cpu_features_get_perf_counter:
*
* Gets performance counter.
*
* Returns: performance counter.
**/
retro_perf_tick_t cpu_features_get_perf_counter(void);
/**
* cpu_features_get_time_usec:
*
* Gets time in microseconds, from an undefined epoch.
* The epoch may change between computers or across reboots.
*
* Returns: time in microseconds
**/
retro_time_t cpu_features_get_time_usec(void);
/**
* cpu_features_get:
*
* Gets CPU features.
*
* Returns: bitmask of all CPU features available.
**/
uint64_t cpu_features_get(void);
/**
* cpu_features_get_core_amount:
*
* Gets the amount of available CPU cores.
*
* Returns: amount of CPU cores available.
**/
unsigned cpu_features_get_core_amount(void);
void cpu_features_get_model_name(char *name, int len);
RETRO_END_DECLS
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_assert.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef __RETRO_ASSERT_H
#define __RETRO_ASSERT_H
#include <assert.h>
#ifdef RARCH_INTERNAL
#include <stdio.h>
#define retro_assert(cond) do { \
if (!(cond)) { printf("Assertion failed at %s:%d.\n", __FILE__, __LINE__); abort(); } \
} while(0)
#else
#define retro_assert(cond) assert(cond)
#endif
#endif

View File

@@ -0,0 +1,117 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_common_api.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef _LIBRETRO_COMMON_RETRO_COMMON_API_H
#define _LIBRETRO_COMMON_RETRO_COMMON_API_H
/*
This file is designed to normalize the libretro-common compiling environment
for public API headers. This should be leaner than a normal compiling environment,
since it gets #included into other project's sources.
*/
/* ------------------------------------ */
/*
Ordinarily we want to put #ifdef __cplusplus extern "C" in C library
headers to enable them to get used by c++ sources.
However, we want to support building this library as C++ as well, so a
special technique is called for.
*/
#define RETRO_BEGIN_DECLS
#define RETRO_END_DECLS
#ifdef __cplusplus
#ifdef CXX_BUILD
/* build wants everything to be built as c++, so no extern "C" */
#else
#undef RETRO_BEGIN_DECLS
#undef RETRO_END_DECLS
#define RETRO_BEGIN_DECLS extern "C" {
#define RETRO_END_DECLS }
#endif
#else
/* header is included by a C source file, so no extern "C" */
#endif
/*
IMO, this non-standard ssize_t should not be used.
However, it's a good example of how to handle something like this.
*/
#ifdef _MSC_VER
#ifndef HAVE_SSIZE_T
#define HAVE_SSIZE_T
#if defined(_WIN64)
typedef __int64 ssize_t;
#elif defined(_WIN32)
typedef int ssize_t;
#endif
#endif
#elif defined(__MACH__)
#include <sys/types.h>
#endif
#ifdef _MSC_VER
#if _MSC_VER >= 1800
#include <inttypes.h>
#else
#ifndef PRId64
#define PRId64 "I64d"
#define PRIu64 "I64u"
#define PRIuPTR "Iu"
#endif
#endif
#else
/* C++11 says this one isn't needed, but apparently (some versions of) mingw require it anyways */
/* https://stackoverflow.com/questions/8132399/how-to-printf-uint64-t-fails-with-spurious-trailing-in-format */
/* https://github.com/libretro/RetroArch/issues/6009 */
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#endif
#ifndef PRId64
#error "inttypes.h is being screwy"
#endif
#define STRING_REP_INT64 "%" PRId64
#define STRING_REP_UINT64 "%" PRIu64
#define STRING_REP_USIZE "%" PRIuPTR
/*
I would like to see retro_inline.h moved in here; possibly boolean too.
rationale: these are used in public APIs, and it is easier to find problems
and write code that works the first time portably when theyre included uniformly
than to do the analysis from scratch each time you think you need it, for each feature.
Moreover it helps force you to make hard decisions: if you EVER bring in boolean.h,
then you should pay the price everywhere, so you can see how much grief it will cause.
Of course, another school of thought is that you should do as little damage as possible
in as few places as possible...
*/
/* _LIBRETRO_COMMON_RETRO_COMMON_API_H */
#endif

View File

@@ -0,0 +1,39 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_inline.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef __LIBRETRO_SDK_INLINE_H
#define __LIBRETRO_SDK_INLINE_H
#ifndef INLINE
#if defined(_WIN32) || defined(__INTEL_COMPILER)
#define INLINE __inline
#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L
#define INLINE inline
#elif defined(__GNUC__)
#define INLINE __inline__
#else
#define INLINE
#endif
#endif
#endif

View File

@@ -0,0 +1,94 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_math.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef _LIBRETRO_COMMON_MATH_H
#define _LIBRETRO_COMMON_MATH_H
#include <stdint.h>
#if defined(_WIN32) && !defined(_XBOX)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#elif defined(_WIN32) && defined(_XBOX)
#include <Xtl.h>
#endif
#include <limits.h>
#ifdef _MSC_VER
#include <compat/msvc.h>
#endif
#include <retro_inline.h>
#ifndef M_PI
#if !defined(USE_MATH_DEFINES)
#define M_PI 3.14159265358979323846264338327
#endif
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/**
* next_pow2:
* @v : initial value
*
* Get next power of 2 value based on initial value.
*
* Returns: next power of 2 value (derived from @v).
**/
static INLINE uint32_t next_pow2(uint32_t v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
/**
* prev_pow2:
* @v : initial value
*
* Get previous power of 2 value based on initial value.
*
* Returns: previous power of 2 value (derived from @v).
**/
static INLINE uint32_t prev_pow2(uint32_t v)
{
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
return v - (v >> 1);
}
#endif

View File

@@ -0,0 +1,182 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_miscellaneous.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef __RARCH_MISCELLANEOUS_H
#define __RARCH_MISCELLANEOUS_H
#define RARCH_MAX_SUBSYSTEMS 10
#define RARCH_MAX_SUBSYSTEM_ROMS 10
#include <stdint.h>
#include <boolean.h>
#include <retro_inline.h>
#if defined(_WIN32) && !defined(_XBOX)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#elif defined(_WIN32) && defined(_XBOX)
#include <Xtl.h>
#endif
#if defined(__CELLOS_LV2__)
#include <sys/fs_external.h>
#endif
#include <limits.h>
#ifdef _MSC_VER
#include <compat/msvc.h>
#endif
static INLINE void bits_or_bits(uint32_t *a, uint32_t *b, uint32_t count)
{
uint32_t i;
for (i = 0; i < count;i++)
a[i] |= b[i];
}
static INLINE void bits_clear_bits(uint32_t *a, uint32_t *b, uint32_t count)
{
uint32_t i;
for (i = 0; i < count;i++)
a[i] &= ~b[i];
}
static INLINE bool bits_any_set(uint32_t* ptr, uint32_t count)
{
uint32_t i;
for (i = 0; i < count; i++)
{
if (ptr[i] != 0)
return true;
}
return false;
}
#ifndef PATH_MAX_LENGTH
#if defined(__CELLOS_LV2__)
#define PATH_MAX_LENGTH CELL_FS_MAX_FS_PATH_LENGTH
#elif defined(_XBOX1) || defined(_3DS) || defined(PSP) || defined(PS2) || defined(GEKKO)|| defined(WIIU) || defined(ORBIS)
#define PATH_MAX_LENGTH 512
#else
#define PATH_MAX_LENGTH 4096
#endif
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define BITS_GET_ELEM(a, i) ((a).data[i])
#define BITS_GET_ELEM_PTR(a, i) ((a)->data[i])
#define BIT_SET(a, bit) ((a)[(bit) >> 3] |= (1 << ((bit) & 7)))
#define BIT_CLEAR(a, bit) ((a)[(bit) >> 3] &= ~(1 << ((bit) & 7)))
#define BIT_GET(a, bit) (((a)[(bit) >> 3] >> ((bit) & 7)) & 1)
#define BIT16_SET(a, bit) ((a) |= (1 << ((bit) & 15)))
#define BIT16_CLEAR(a, bit) ((a) &= ~(1 << ((bit) & 15)))
#define BIT16_GET(a, bit) (((a) >> ((bit) & 15)) & 1)
#define BIT16_CLEAR_ALL(a) ((a) = 0)
#define BIT32_SET(a, bit) ((a) |= (1 << ((bit) & 31)))
#define BIT32_CLEAR(a, bit) ((a) &= ~(1 << ((bit) & 31)))
#define BIT32_GET(a, bit) (((a) >> ((bit) & 31)) & 1)
#define BIT32_CLEAR_ALL(a) ((a) = 0)
#define BIT64_SET(a, bit) ((a) |= (UINT64_C(1) << ((bit) & 63)))
#define BIT64_CLEAR(a, bit) ((a) &= ~(UINT64_C(1) << ((bit) & 63)))
#define BIT64_GET(a, bit) (((a) >> ((bit) & 63)) & 1)
#define BIT64_CLEAR_ALL(a) ((a) = 0)
#define BIT128_SET(a, bit) ((a).data[(bit) >> 5] |= (1 << ((bit) & 31)))
#define BIT128_CLEAR(a, bit) ((a).data[(bit) >> 5] &= ~(1 << ((bit) & 31)))
#define BIT128_GET(a, bit) (((a).data[(bit) >> 5] >> ((bit) & 31)) & 1)
#define BIT128_CLEAR_ALL(a) memset(&(a), 0, sizeof(a))
#define BIT128_SET_PTR(a, bit) BIT128_SET(*a, bit)
#define BIT128_CLEAR_PTR(a, bit) BIT128_CLEAR(*a, bit)
#define BIT128_GET_PTR(a, bit) BIT128_GET(*a, bit)
#define BIT128_CLEAR_ALL_PTR(a) BIT128_CLEAR_ALL(*a)
#define BIT256_SET(a, bit) BIT128_SET(a, bit)
#define BIT256_CLEAR(a, bit) BIT128_CLEAR(a, bit)
#define BIT256_GET(a, bit) BIT128_GET(a, bit)
#define BIT256_CLEAR_ALL(a) BIT128_CLEAR_ALL(a)
#define BIT256_SET_PTR(a, bit) BIT256_SET(*a, bit)
#define BIT256_CLEAR_PTR(a, bit) BIT256_CLEAR(*a, bit)
#define BIT256_GET_PTR(a, bit) BIT256_GET(*a, bit)
#define BIT256_CLEAR_ALL_PTR(a) BIT256_CLEAR_ALL(*a)
#define BITS_COPY16_PTR(a,bits) \
{ \
BIT128_CLEAR_ALL_PTR(a); \
BITS_GET_ELEM_PTR(a, 0) = (bits) & 0xffff; \
}
#define BITS_COPY32_PTR(a,bits) \
{ \
BIT128_CLEAR_ALL_PTR(a); \
BITS_GET_ELEM_PTR(a, 0) = (bits); \
}
/* Helper macros and struct to keep track of many booleans. */
/* This struct has 256 bits. */
typedef struct
{
uint32_t data[8];
} retro_bits_t;
#ifdef _WIN32
# ifdef _WIN64
# define PRI_SIZET PRIu64
# else
# if _MSC_VER == 1800
# define PRI_SIZET PRIu32
# else
# define PRI_SIZET "u"
# endif
# endif
#elif PS2
# define PRI_SIZET "u"
#else
# if (SIZE_MAX == 0xFFFF)
# define PRI_SIZET "hu"
# elif (SIZE_MAX == 0xFFFFFFFF)
# define PRI_SIZET "u"
# elif (SIZE_MAX == 0xFFFFFFFFFFFFFFFF)
# define PRI_SIZET "lu"
# else
# error PRI_SIZET: unknown SIZE_MAX
# endif
#endif
#endif

View File

@@ -0,0 +1,116 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_timers.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef __LIBRETRO_COMMON_TIMERS_H
#define __LIBRETRO_COMMON_TIMERS_H
#include <stdint.h>
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
#include <sys/timer.h>
#elif defined(XENON)
#include <time/time.h>
#elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__)
#include <unistd.h>
#elif defined(WIIU)
#include <wiiu/os/thread.h>
#elif defined(PSP)
#include <pspthreadman.h>
#elif defined(VITA)
#include <psp2/kernel/threadmgr.h>
#elif defined(PS2)
#include <SDL/SDL_timer.h>
#elif defined(_3DS)
#include <3ds.h>
#else
#include <time.h>
#endif
#if defined(_WIN32) && !defined(_XBOX)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#elif defined(_WIN32) && defined(_XBOX)
#include <Xtl.h>
#endif
#include <limits.h>
#ifdef _MSC_VER
#include <compat/msvc.h>
#endif
#include <retro_inline.h>
#ifdef DJGPP
#define timespec timeval
#define tv_nsec tv_usec
#include <unistd.h>
extern int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
static int nanosleepDOS(const struct timespec *rqtp, struct timespec *rmtp)
{
usleep(1000000 * rqtp->tv_sec + rqtp->tv_nsec / 1000);
if (rmtp)
rmtp->tv_sec = rmtp->tv_nsec=0;
return 0;
}
#define nanosleep nanosleepDOS
#endif
/**
* retro_sleep:
* @msec : amount in milliseconds to sleep
*
* Sleeps for a specified amount of milliseconds (@msec).
**/
static INLINE void retro_sleep(unsigned msec)
{
#if defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)
sys_timer_usleep(1000 * msec);
#elif defined(PSP) || defined(VITA)
sceKernelDelayThread(1000 * msec);
#elif defined(PS2)
SDL_Delay(msec);
#elif defined(_3DS)
svcSleepThread(1000000 * (s64)msec);
#elif defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
SleepEx(msec, FALSE);
#elif defined(_WIN32)
Sleep(msec);
#elif defined(XENON)
udelay(1000 * msec);
#elif defined(GEKKO) || defined(__PSL1GHT__) || defined(__QNX__)
usleep(1000 * msec);
#elif defined(WIIU)
OSSleepTicks(ms_to_ticks(msec));
#else
struct timespec tv = {0};
tv.tv_sec = msec / 1000;
tv.tv_nsec = (msec % 1000) * 1000000;
nanosleep(&tv, NULL);
#endif
}
#endif

View File

@@ -0,0 +1,111 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (file_stream.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef __LIBRETRO_SDK_FILE_STREAM_H
#define __LIBRETRO_SDK_FILE_STREAM_H
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/types.h>
#include <libretro.h>
#include <retro_common_api.h>
#include <retro_inline.h>
#include <boolean.h>
#include <stdarg.h>
#define FILESTREAM_REQUIRED_VFS_VERSION 2
RETRO_BEGIN_DECLS
typedef struct RFILE RFILE;
#define FILESTREAM_REQUIRED_VFS_VERSION 2
void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info);
int64_t filestream_get_size(RFILE *stream);
int64_t filestream_truncate(RFILE *stream, int64_t length);
/**
* filestream_open:
* @path : path to file
* @mode : file mode to use when opening (read/write)
* @bufsize : optional buffer size (-1 or 0 to use default)
*
* Opens a file for reading or writing, depending on the requested mode.
* Returns a pointer to an RFILE if opened successfully, otherwise NULL.
**/
RFILE *filestream_open(const char *path, unsigned mode, unsigned hints);
int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position);
int64_t filestream_read(RFILE *stream, void *data, int64_t len);
int64_t filestream_write(RFILE *stream, const void *data, int64_t len);
int64_t filestream_tell(RFILE *stream);
void filestream_rewind(RFILE *stream);
int filestream_close(RFILE *stream);
int64_t filestream_read_file(const char *path, void **buf, int64_t *len);
char *filestream_gets(RFILE *stream, char *s, size_t len);
int filestream_getc(RFILE *stream);
int filestream_scanf(RFILE *stream, const char* format, ...);
int filestream_eof(RFILE *stream);
bool filestream_write_file(const char *path, const void *data, int64_t size);
int filestream_putc(RFILE *stream, int c);
int filestream_vprintf(RFILE *stream, const char* format, va_list args);
int filestream_printf(RFILE *stream, const char* format, ...);
int filestream_error(RFILE *stream);
int filestream_flush(RFILE *stream);
int filestream_delete(const char *path);
int filestream_rename(const char *old_path, const char *new_path);
const char *filestream_get_path(RFILE *stream);
bool filestream_exists(const char *path);
char *filestream_getline(RFILE *stream);
RETRO_END_DECLS
#endif

View File

@@ -0,0 +1,134 @@
/* Copyright (C) 2010-2019 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (stdstring.h).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#ifndef __LIBRETRO_SDK_STDSTRING_H
#define __LIBRETRO_SDK_STDSTRING_H
#include <stdlib.h>
#include <stddef.h>
#include <ctype.h>
#include <string.h>
#include <boolean.h>
#include <retro_common_api.h>
#include <retro_inline.h>
#include <compat/strl.h>
RETRO_BEGIN_DECLS
static INLINE bool string_is_empty(const char *data)
{
return !data || (*data == '\0');
}
static INLINE bool string_is_equal(const char *a, const char *b)
{
return (a && b) ? !strcmp(a, b) : false;
}
#define STRLEN_CONST(x) ((sizeof((x))-1))
#define string_is_not_equal(a, b) !string_is_equal((a), (b))
#define string_add_pair_open(s, size) strlcat((s), " (", (size))
#define string_add_pair_close(s, size) strlcat((s), ")", (size))
#define string_add_bracket_open(s, size) strlcat((s), "{", (size))
#define string_add_bracket_close(s, size) strlcat((s), "}", (size))
#define string_add_single_quote(s, size) strlcat((s), "'", (size))
#define string_add_quote(s, size) strlcat((s), "\"", (size))
#define string_add_colon(s, size) strlcat((s), ":", (size))
#define string_add_glob_open(s, size) strlcat((s), "glob('*", (size))
#define string_add_glob_close(s, size) strlcat((s), "*')", (size))
#define string_is_not_equal_fast(a, b, size) (memcmp(a, b, size) != 0)
#define string_is_equal_fast(a, b, size) (memcmp(a, b, size) == 0)
static INLINE void string_add_between_pairs(char *s, const char *str,
size_t size)
{
string_add_pair_open(s, size);
strlcat(s, str, size);
string_add_pair_close(s, size);
}
static INLINE bool string_is_equal_case_insensitive(const char *a,
const char *b)
{
int result = 0;
const unsigned char *p1 = (const unsigned char*)a;
const unsigned char *p2 = (const unsigned char*)b;
if (!a || !b)
return false;
if (p1 == p2)
return true;
while ((result = tolower (*p1) - tolower (*p2++)) == 0)
if (*p1++ == '\0')
break;
return (result == 0);
}
static INLINE bool string_is_equal_noncase(const char *a, const char *b)
{
int result = 0;
const unsigned char *p1 = (const unsigned char*)a;
const unsigned char *p2 = (const unsigned char*)b;
if (!a || !b)
return false;
if (p1 == p2)
return false;
while ((result = tolower (*p1) - tolower (*p2++)) == 0)
if (*p1++ == '\0')
break;
return (result == 0);
}
char *string_to_upper(char *s);
char *string_to_lower(char *s);
char *string_ucwords(char *s);
char *string_replace_substring(const char *in, const char *pattern,
const char *by);
/* Remove leading whitespaces */
char *string_trim_whitespace_left(char *const s);
/* Remove trailing whitespaces */
char *string_trim_whitespace_right(char *const s);
/* Remove leading and trailing whitespaces */
char *string_trim_whitespace(char *const s);
/* max_lines == 0 means no limit */
char *word_wrap(char *buffer, const char *string,
int line_width, bool unicode, unsigned max_lines);
RETRO_END_DECLS
#endif

View File

@@ -0,0 +1,18 @@
Copyright (c) 2013 Mikko Mononen memon@inside.org
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,685 @@
//
// Copyright (c) 2013 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef NANOVG_H
#define NANOVG_H
#ifdef __cplusplus
extern "C" {
#endif
#define NVG_PI 3.14159265358979323846264338327f
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union
#endif
typedef struct NVGcontext NVGcontext;
struct NVGcolor {
union {
float rgba[4];
struct {
float r,g,b,a;
};
};
};
typedef struct NVGcolor NVGcolor;
struct NVGpaint {
float xform[6];
float extent[2];
float radius;
float feather;
NVGcolor innerColor;
NVGcolor outerColor;
int image;
};
typedef struct NVGpaint NVGpaint;
enum NVGwinding {
NVG_CCW = 1, // Winding for solid shapes
NVG_CW = 2, // Winding for holes
};
enum NVGsolidity {
NVG_SOLID = 1, // CCW
NVG_HOLE = 2, // CW
};
enum NVGlineCap {
NVG_BUTT,
NVG_ROUND,
NVG_SQUARE,
NVG_BEVEL,
NVG_MITER,
};
enum NVGalign {
// Horizontal align
NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left.
NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center.
NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right.
// Vertical align
NVG_ALIGN_TOP = 1<<3, // Align text vertically to top.
NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle.
NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom.
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline.
};
enum NVGblendFactor {
NVG_ZERO = 1<<0,
NVG_ONE = 1<<1,
NVG_SRC_COLOR = 1<<2,
NVG_ONE_MINUS_SRC_COLOR = 1<<3,
NVG_DST_COLOR = 1<<4,
NVG_ONE_MINUS_DST_COLOR = 1<<5,
NVG_SRC_ALPHA = 1<<6,
NVG_ONE_MINUS_SRC_ALPHA = 1<<7,
NVG_DST_ALPHA = 1<<8,
NVG_ONE_MINUS_DST_ALPHA = 1<<9,
NVG_SRC_ALPHA_SATURATE = 1<<10,
};
enum NVGcompositeOperation {
NVG_SOURCE_OVER,
NVG_SOURCE_IN,
NVG_SOURCE_OUT,
NVG_ATOP,
NVG_DESTINATION_OVER,
NVG_DESTINATION_IN,
NVG_DESTINATION_OUT,
NVG_DESTINATION_ATOP,
NVG_LIGHTER,
NVG_COPY,
NVG_XOR,
};
struct NVGcompositeOperationState {
int srcRGB;
int dstRGB;
int srcAlpha;
int dstAlpha;
};
typedef struct NVGcompositeOperationState NVGcompositeOperationState;
struct NVGglyphPosition {
const char* str; // Position of the glyph in the input string.
float x; // The x-coordinate of the logical glyph position.
float minx, maxx; // The bounds of the glyph shape.
};
typedef struct NVGglyphPosition NVGglyphPosition;
struct NVGtextRow {
const char* start; // Pointer to the input text where the row starts.
const char* end; // Pointer to the input text where the row ends (one past the last character).
const char* next; // Pointer to the beginning of the next row.
float width; // Logical width of the row.
float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending.
};
typedef struct NVGtextRow NVGtextRow;
enum NVGimageFlags {
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image.
NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction.
NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction.
NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered.
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha.
NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear
};
// Begin drawing a new frame
// Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame()
// nvgBeginFrame() defines the size of the window to render to in relation currently
// set viewport (i.e. glViewport on GL backends). Device pixel ration allows to
// control the rendering on Hi-DPI devices.
// For example, GLFW returns two dimension for an opened window: window size and
// frame buffer size. In that case you would set windowWidth/Height to the window size
// devicePixelRatio to: frameBufferWidth / windowWidth.
void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio);
// Cancels drawing the current frame.
void nvgCancelFrame(NVGcontext* ctx);
// Ends drawing flushing remaining render state.
void nvgEndFrame(NVGcontext* ctx);
//
// Composite operation
//
// The composite operations in NanoVG are modeled after HTML Canvas API, and
// the blend func is based on OpenGL (see corresponding manuals for more info).
// The colors in the blending state have premultiplied alpha.
// Sets the composite operation. The op parameter should be one of NVGcompositeOperation.
void nvgGlobalCompositeOperation(NVGcontext* ctx, int op);
// Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor.
void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor);
// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor.
void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha);
//
// Color utils
//
// Colors in NanoVG are stored as unsigned ints in ABGR format.
// Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f).
NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b);
// Returns a color value from red, green, blue values. Alpha will be set to 1.0f.
NVGcolor nvgRGBf(float r, float g, float b);
// Returns a color value from red, green, blue and alpha values.
NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
// Returns a color value from red, green, blue and alpha values.
NVGcolor nvgRGBAf(float r, float g, float b, float a);
// Linearly interpolates from color c0 to c1, and returns resulting color value.
NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u);
// Sets transparency of a color value.
NVGcolor nvgTransRGBA(NVGcolor c0, unsigned char a);
// Sets transparency of a color value.
NVGcolor nvgTransRGBAf(NVGcolor c0, float a);
// Returns color value specified by hue, saturation and lightness.
// HSL values are all in range [0..1], alpha will be set to 255.
NVGcolor nvgHSL(float h, float s, float l);
// Returns color value specified by hue, saturation and lightness and alpha.
// HSL values are all in range [0..1], alpha in range [0..255]
NVGcolor nvgHSLA(float h, float s, float l, unsigned char a);
//
// State Handling
//
// NanoVG contains state which represents how paths will be rendered.
// The state contains transform, fill and stroke styles, text and font styles,
// and scissor clipping.
// Pushes and saves the current render state into a state stack.
// A matching nvgRestore() must be used to restore the state.
void nvgSave(NVGcontext* ctx);
// Pops and restores current render state.
void nvgRestore(NVGcontext* ctx);
// Resets current render state to default values. Does not affect the render state stack.
void nvgReset(NVGcontext* ctx);
//
// Render styles
//
// Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern.
// Solid color is simply defined as a color value, different kinds of paints can be created
// using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern().
//
// Current render style can be saved and restored using nvgSave() and nvgRestore().
// Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default.
void nvgShapeAntiAlias(NVGcontext* ctx, int enabled);
// Sets current stroke style to a solid color.
void nvgStrokeColor(NVGcontext* ctx, NVGcolor color);
// Sets current stroke style to a paint, which can be a one of the gradients or a pattern.
void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint);
// Sets current fill style to a solid color.
void nvgFillColor(NVGcontext* ctx, NVGcolor color);
// Sets current fill style to a paint, which can be a one of the gradients or a pattern.
void nvgFillPaint(NVGcontext* ctx, NVGpaint paint);
// Sets the miter limit of the stroke style.
// Miter limit controls when a sharp corner is beveled.
void nvgMiterLimit(NVGcontext* ctx, float limit);
// Sets the stroke width of the stroke style.
void nvgStrokeWidth(NVGcontext* ctx, float size);
// Sets how the end of the line (cap) is drawn,
// Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE.
void nvgLineCap(NVGcontext* ctx, int cap);
// Sets how sharp path corners are drawn.
// Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL.
void nvgLineJoin(NVGcontext* ctx, int join);
// Sets the transparency applied to all rendered shapes.
// Already transparent paths will get proportionally more transparent as well.
void nvgGlobalAlpha(NVGcontext* ctx, float alpha);
//
// Transforms
//
// The paths, gradients, patterns and scissor region are transformed by an transformation
// matrix at the time when they are passed to the API.
// The current transformation matrix is a affine matrix:
// [sx kx tx]
// [ky sy ty]
// [ 0 0 1]
// Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation.
// The last row is assumed to be 0,0,1 and is not stored.
//
// Apart from nvgResetTransform(), each transformation function first creates
// specific transformation matrix and pre-multiplies the current transformation by it.
//
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore().
// Resets current transform to a identity matrix.
void nvgResetTransform(NVGcontext* ctx);
// Premultiplies current coordinate system by specified matrix.
// The parameters are interpreted as matrix as follows:
// [a c e]
// [b d f]
// [0 0 1]
void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f);
// Translates current coordinate system.
void nvgTranslate(NVGcontext* ctx, float x, float y);
// Rotates current coordinate system. Angle is specified in radians.
void nvgRotate(NVGcontext* ctx, float angle);
// Skews the current coordinate system along X axis. Angle is specified in radians.
void nvgSkewX(NVGcontext* ctx, float angle);
// Skews the current coordinate system along Y axis. Angle is specified in radians.
void nvgSkewY(NVGcontext* ctx, float angle);
// Scales the current coordinate system.
void nvgScale(NVGcontext* ctx, float x, float y);
// Stores the top part (a-f) of the current transformation matrix in to the specified buffer.
// [a c e]
// [b d f]
// [0 0 1]
// There should be space for 6 floats in the return buffer for the values a-f.
void nvgCurrentTransform(NVGcontext* ctx, float* xform);
// The following functions can be used to make calculations on 2x3 transformation matrices.
// A 2x3 matrix is represented as float[6].
// Sets the transform to identity matrix.
void nvgTransformIdentity(float* dst);
// Sets the transform to translation matrix matrix.
void nvgTransformTranslate(float* dst, float tx, float ty);
// Sets the transform to scale matrix.
void nvgTransformScale(float* dst, float sx, float sy);
// Sets the transform to rotate matrix. Angle is specified in radians.
void nvgTransformRotate(float* dst, float a);
// Sets the transform to skew-x matrix. Angle is specified in radians.
void nvgTransformSkewX(float* dst, float a);
// Sets the transform to skew-y matrix. Angle is specified in radians.
void nvgTransformSkewY(float* dst, float a);
// Sets the transform to the result of multiplication of two transforms, of A = A*B.
void nvgTransformMultiply(float* dst, const float* src);
// Sets the transform to the result of multiplication of two transforms, of A = B*A.
void nvgTransformPremultiply(float* dst, const float* src);
// Sets the destination to inverse of specified transform.
// Returns 1 if the inverse could be calculated, else 0.
int nvgTransformInverse(float* dst, const float* src);
// Transform a point by given transform.
void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy);
// Converts degrees to radians and vice versa.
float nvgDegToRad(float deg);
float nvgRadToDeg(float rad);
//
// Images
//
// NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering.
// In addition you can upload your own image. The image loading is provided by stb_image.
// The parameter imageFlags is combination of flags defined in NVGimageFlags.
// Creates image by loading it from the disk from specified file name.
// Returns handle to the image.
int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags);
// Creates image by loading it from the specified chunk of memory.
// Returns handle to the image.
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata);
// Creates image from specified image data.
// Returns handle to the image.
int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data);
// Updates image data specified by image handle.
void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data);
// Returns the dimensions of a created image.
void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h);
// Deletes created image.
void nvgDeleteImage(NVGcontext* ctx, int image);
//
// Paints
//
// NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern.
// These can be used as paints for strokes and fills.
// Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates
// of the linear gradient, icol specifies the start color and ocol the end color.
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
NVGpaint nvgLinearGradient(NVGcontext* ctx, float sx, float sy, float ex, float ey,
NVGcolor icol, NVGcolor ocol);
// Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering
// drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle,
// (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry
// the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient.
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h,
float r, float f, NVGcolor icol, NVGcolor ocol);
// Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify
// the inner and outer radius of the gradient, icol specifies the start color and ocol the end color.
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr,
NVGcolor icol, NVGcolor ocol);
// Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern,
// (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render.
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey,
float angle, int image, float alpha);
//
// Scissoring
//
// Scissoring allows you to clip the rendering into a rectangle. This is useful for various
// user interface cases like rendering a text edit or a timeline.
// Sets the current scissor rectangle.
// The scissor rectangle is transformed by the current transform.
void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h);
// Intersects current scissor rectangle with the specified rectangle.
// The scissor rectangle is transformed by the current transform.
// Note: in case the rotation of previous scissor rect differs from
// the current one, the intersection will be done between the specified
// rectangle and the previous scissor rectangle transformed in the current
// transform space. The resulting shape is always rectangle.
void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h);
// Reset and disables scissoring.
void nvgResetScissor(NVGcontext* ctx);
//
// Paths
//
// Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths.
// Then you define one or more paths and sub-paths which describe the shape. The are functions
// to draw common shapes like rectangles and circles, and lower level step-by-step functions,
// which allow to define a path curve by curve.
//
// NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise
// winding and holes should have counter clockwise order. To specify winding of a path you can
// call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW.
//
// Finally you can fill the path using current fill style by calling nvgFill(), and stroke it
// with current stroke style by calling nvgStroke().
//
// The curve segments and sub-paths are transformed by the current transform.
// Clears the current path and sub-paths.
void nvgBeginPath(NVGcontext* ctx);
// Starts new sub-path with specified point as first point.
void nvgMoveTo(NVGcontext* ctx, float x, float y);
// Adds line segment from the last point in the path to the specified point.
void nvgLineTo(NVGcontext* ctx, float x, float y);
// Adds cubic bezier segment from last point in the path via two control points to the specified point.
void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y);
// Adds quadratic bezier segment from last point in the path via a control point to the specified point.
void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y);
// Adds an arc segment at the corner defined by the last path point, and two specified points.
void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius);
// Closes current sub-path with a line segment.
void nvgClosePath(NVGcontext* ctx);
// Sets the current sub-path winding, see NVGwinding and NVGsolidity.
void nvgPathWinding(NVGcontext* ctx, int dir);
// Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r,
// and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW).
// Angles are specified in radians.
void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir);
// Creates new rectangle shaped sub-path.
void nvgRect(NVGcontext* ctx, float x, float y, float w, float h);
// Creates new rounded rectangle shaped sub-path.
void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r);
// Creates new rounded rectangle shaped sub-path with varying radii for each corner.
void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft);
// Creates new ellipse shaped sub-path.
void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry);
// Creates new circle shaped sub-path.
void nvgCircle(NVGcontext* ctx, float cx, float cy, float r);
// Fills the current path with current fill style.
void nvgFill(NVGcontext* ctx);
// Fills the current path with current stroke style.
void nvgStroke(NVGcontext* ctx);
//
// Text
//
// NanoVG allows you to load .ttf files and use the font to render text.
//
// The appearance of the text can be defined by setting the current text style
// and by specifying the fill color. Common text and font settings such as
// font size, letter spacing and text align are supported. Font blur allows you
// to create simple text effects such as drop shadows.
//
// At render time the font face can be set based on the font handles or name.
//
// Font measure functions return values in local space, the calculations are
// carried in the same resolution as the final rendering. This is done because
// the text glyph positions are snapped to the nearest pixels sharp rendering.
//
// The local space means that values are not rotated or scale as per the current
// transformation. For example if you set font size to 12, which would mean that
// line height is 16, then regardless of the current scaling and rotation, the
// returned line height is always 16. Some measures may vary because of the scaling
// since aforementioned pixel snapping.
//
// While this may sound a little odd, the setup allows you to always render the
// same way regardless of scaling. I.e. following works regardless of scaling:
//
// const char* txt = "Text me up.";
// nvgTextBounds(vg, x,y, txt, NULL, bounds);
// nvgBeginPath(vg);
// nvgRoundedRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]);
// nvgFill(vg);
//
// Note: currently only solid color fill is supported for text.
// Creates font by loading it from the disk from specified file name.
// Returns handle to the font.
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename);
// Creates font by loading it from the specified memory chunk.
// Returns handle to the font.
int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData);
// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found.
int nvgFindFont(NVGcontext* ctx, const char* name);
// Adds a fallback font by handle.
int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont);
// Adds a fallback font by name.
int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont);
// Sets the font size of current text style.
void nvgFontSize(NVGcontext* ctx, float size);
// Sets the blur of current text style.
void nvgFontBlur(NVGcontext* ctx, float blur);
// Sets the letter spacing of current text style.
void nvgTextLetterSpacing(NVGcontext* ctx, float spacing);
// Sets the proportional line height of current text style. The line height is specified as multiple of font size.
void nvgTextLineHeight(NVGcontext* ctx, float lineHeight);
// Sets the text align of current text style, see NVGalign for options.
void nvgTextAlign(NVGcontext* ctx, int align);
// Sets the font face based on specified id of current text style.
void nvgFontFaceId(NVGcontext* ctx, int font);
// Sets the font face based on specified name of current text style.
void nvgFontFace(NVGcontext* ctx, const char* font);
// Draws text string at specified location. If end is specified only the sub-string up to the end is drawn.
float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end);
// Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn.
// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
// Words longer than the max width are slit at nearest character (i.e. no hyphenation).
void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end);
// Measures the specified text string. Parameter bounds should be a pointer to float[4],
// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax]
// Returns the horizontal advance of the measured text (i.e. where the next character should drawn).
// Measured values are returned in local coordinate space.
float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds);
// Measures the specified multi-text string. Parameter bounds should be a pointer to float[4],
// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax]
// Measured values are returned in local coordinate space.
void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds);
// Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used.
// Measured values are returned in local coordinate space.
int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions);
// Returns the vertical metrics based on the current text style.
// Measured values are returned in local coordinate space.
void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh);
// Breaks the specified text into lines. If end is specified only the sub-string will be used.
// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
// Words longer than the max width are slit at nearest character (i.e. no hyphenation).
int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows);
//
// Internal Render API
//
enum NVGtexture {
NVG_TEXTURE_ALPHA = 0x01,
NVG_TEXTURE_RGBA = 0x02,
};
struct NVGscissor {
float xform[6];
float extent[2];
};
typedef struct NVGscissor NVGscissor;
struct NVGvertex {
float x,y,u,v;
};
typedef struct NVGvertex NVGvertex;
struct NVGpath {
int first;
int count;
unsigned char closed;
int nbevel;
NVGvertex* fill;
int nfill;
NVGvertex* stroke;
int nstroke;
int winding;
int convex;
};
typedef struct NVGpath NVGpath;
struct NVGparams {
void* userPtr;
int edgeAntiAlias;
int (*renderCreate)(void* uptr);
int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data);
int (*renderDeleteTexture)(void* uptr, int image);
int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data);
int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h);
void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio);
void (*renderCancel)(void* uptr);
void (*renderFlush)(void* uptr);
void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths);
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths);
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts);
void (*renderDelete)(void* uptr);
};
typedef struct NVGparams NVGparams;
// Constructor and destructor, called by the render back-end.
NVGcontext* nvgCreateInternal(NVGparams* params);
void nvgDeleteInternal(NVGcontext* ctx);
NVGparams* nvgInternalParams(NVGcontext* ctx);
// Debug function to dump cached path data.
void nvgDebugDumpPathCache(NVGcontext* ctx);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#define NVG_NOTUSED(v) for (;;) { (void)(1 ? (void)0 : ( (void)(v) ) ); break; }
#ifdef __cplusplus
}
#endif
#endif // NANOVG_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,154 @@
//
// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef NANOVG_GL_UTILS_H
#define NANOVG_GL_UTILS_H
struct NVGLUframebuffer {
NVGcontext* ctx;
GLuint fbo;
GLuint rbo;
GLuint texture;
int image;
};
typedef struct NVGLUframebuffer NVGLUframebuffer;
// Helper function to create GL frame buffer to render to.
void nvgluBindFramebuffer(NVGLUframebuffer* fb);
NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags);
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb);
#endif // NANOVG_GL_UTILS_H
#ifdef NANOVG_GL_IMPLEMENTATION
#if defined(NANOVG_GL3) || defined(NANOVG_GLES2) || defined(NANOVG_GLES3)
// FBO is core in OpenGL 3>.
# define NANOVG_FBO_VALID 1
#elif defined(NANOVG_GL2)
// On OS X including glext defines FBO on GL2 too.
# ifdef __APPLE__
# include <OpenGL/glext.h>
# define NANOVG_FBO_VALID 1
# endif
#endif
static GLint defaultFBO = -1;
NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags)
{
#ifdef NANOVG_FBO_VALID
GLint defaultFBO;
GLint defaultRBO;
NVGLUframebuffer* fb = NULL;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
glGetIntegerv(GL_RENDERBUFFER_BINDING, &defaultRBO);
fb = (NVGLUframebuffer*)malloc(sizeof(NVGLUframebuffer));
if (fb == NULL) goto error;
memset(fb, 0, sizeof(NVGLUframebuffer));
fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL);
#if defined NANOVG_GL2
fb->texture = nvglImageHandleGL2(ctx, fb->image);
#elif defined NANOVG_GL3
fb->texture = nvglImageHandleGL3(ctx, fb->image);
#elif defined NANOVG_GLES2
fb->texture = nvglImageHandleGLES2(ctx, fb->image);
#elif defined NANOVG_GLES3
fb->texture = nvglImageHandleGLES3(ctx, fb->image);
#endif
fb->ctx = ctx;
// frame buffer object
glGenFramebuffers(1, &fb->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo);
// render buffer object
glGenRenderbuffers(1, &fb->rbo);
glBindRenderbuffer(GL_RENDERBUFFER, fb->rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h);
// combine all
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
#ifdef GL_DEPTH24_STENCIL8
// If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback.
// Some graphics cards require a depth buffer along with a stencil.
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
#endif // GL_DEPTH24_STENCIL8
goto error;
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
return fb;
error:
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
nvgluDeleteFramebuffer(fb);
return NULL;
#else
NVG_NOTUSED(ctx);
NVG_NOTUSED(w);
NVG_NOTUSED(h);
NVG_NOTUSED(imageFlags);
return NULL;
#endif
}
void nvgluBindFramebuffer(NVGLUframebuffer* fb)
{
#ifdef NANOVG_FBO_VALID
if (defaultFBO == -1) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
glBindFramebuffer(GL_FRAMEBUFFER, fb != NULL ? fb->fbo : defaultFBO);
#else
NVG_NOTUSED(fb);
#endif
}
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb)
{
#ifdef NANOVG_FBO_VALID
if (fb == NULL) return;
if (fb->fbo != 0)
glDeleteFramebuffers(1, &fb->fbo);
if (fb->rbo != 0)
glDeleteRenderbuffers(1, &fb->rbo);
if (fb->image >= 0)
nvgDeleteImage(fb->ctx, fb->image);
fb->ctx = NULL;
fb->fbo = 0;
fb->rbo = 0;
fb->texture = 0;
fb->image = -1;
free(fb);
#else
NVG_NOTUSED(fb);
#endif
}
#endif // NANOVG_GL_IMPLEMENTATION

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <nanovg.h>
#include <borealis/style.hpp>
#include <borealis/theme.hpp>
namespace brls
{
class FontStash
{
public:
int regular = 0;
int korean = 0;
int material = 0;
int sharedSymbols = 0;
};
class FrameContext
{
public:
NVGcontext* vg = nullptr;
float pixelRatio = 0.0;
FontStash* fontStash = nullptr;
ThemeValues* theme = nullptr;
};
} // namespace brls

View File

@@ -0,0 +1,42 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/view.hpp>
namespace brls
{
// A simple header with text, a rectangle on the left
// and a separator
class Header : public View
{
private:
std::string label;
std::string sublabel;
bool separator;
public:
Header(std::string label, bool separator = true, std::string sublabel = "");
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
};
} // namespace brls

View File

@@ -0,0 +1,63 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2020 WerWolv
Copyright (C) 2020 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/box_layout.hpp>
#include <borealis/label.hpp>
#include <borealis/view.hpp>
namespace brls
{
// Displays button hints for the currently focused view
// Depending on the view's available actions
// there can only be one Hint visible at any time
class Hint : public BoxLayout
{
private:
bool animate;
GenericEvent::Subscription globalFocusEventSubscriptor;
VoidEvent::Subscription globalHintsUpdateEventSubscriptor;
static inline std::vector<Hint*> globalHintStack;
static void pushHint(Hint* hint);
static void popHint(Hint* hint);
static void animateHints();
static std::string getKeyIcon(Key key);
void rebuildHints();
public:
Hint(bool animate = true);
~Hint();
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
void setAnimate(bool animate)
{
this->animate = animate;
}
};
} // namespace brls

View File

@@ -0,0 +1,78 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 WerWolv
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/frame_context.hpp>
#include <borealis/view.hpp>
namespace brls
{
enum class ImageScaleType
{
NO_RESIZE = 0, // Nothing is resized
FIT, // The image is shrinked to fit the view boundaries
CROP, // The image is not resized but is cropped if bigger than the view
SCALE, // The image is stretched to match the view boundaries
VIEW_RESIZE // The view is resized to match the image
};
// An image
class Image : public View
{
public:
Image(std::string imagePath);
Image(unsigned char* buffer, size_t bufferSize);
~Image();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
void setImage(unsigned char* buffer, size_t bufferSize);
void setImage(std::string imagePath);
void setScaleType(ImageScaleType imageScaleType);
void setOpacity(float opacity);
void setCornerRadius(float radius)
{
this->cornerRadius = radius;
}
private:
std::string imagePath;
unsigned char* imageBuffer = nullptr;
size_t imageBufferSize = 0;
int texture = -1;
NVGpaint imgPaint;
ImageScaleType imageScaleType = ImageScaleType::FIT;
float cornerRadius = 0;
int imageX = 0, imageY = 0;
int imageWidth = 0, imageHeight = 0;
int origViewWidth = 0, origViewHeight = 0;
void reloadTexture();
};
} // namespace brls

View File

@@ -0,0 +1,112 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/view.hpp>
namespace brls
{
enum class LabelStyle
{
REGULAR = 0,
MEDIUM,
SMALL,
DESCRIPTION,
CRASH,
BUTTON_PLAIN,
BUTTON_PLAIN_DISABLED,
BUTTON_BORDERLESS,
LIST_ITEM,
NOTIFICATION,
DIALOG,
BUTTON_DIALOG,
HINT
};
// A Label, multiline or with a ticker
class Label : public View
{
private:
std::string text;
bool multiline;
unsigned fontSize;
float lineHeight;
LabelStyle labelStyle;
NVGalign horizontalAlign = NVG_ALIGN_LEFT;
NVGalign verticalAlign = NVG_ALIGN_MIDDLE;
NVGcolor customColor;
bool useCustomColor = false;
int customFont;
bool useCustomFont = false;
public:
Label(LabelStyle labelStyle, std::string text, bool multiline = false);
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
void setVerticalAlign(NVGalign align);
void setHorizontalAlign(NVGalign align);
void setText(std::string text);
void setStyle(LabelStyle style);
void setFontSize(unsigned size);
/**
* Sets the label color
*/
void setColor(NVGcolor color);
/**
* Unsets the label color - it
* will now use the default one
* for the label style
*/
void unsetColor();
/**
* Returns the effective label color
* = custom or the style default
*/
NVGcolor getColor(ThemeValues* theme);
/**
* Sets the font id
*/
void setFont(int fontId);
/**
* Unsets the font id - it
* will now use the regular one
*/
void unsetFont();
/**
* Returns the font used
* = custom or the regular font
*/
int getFont(FontStash* stash);
};
} // namespace brls

View File

@@ -0,0 +1,51 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 WerWolv
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/view.hpp>
#include <vector>
namespace brls
{
// A view containing multiple children views with the ability to freely switch between these layers
class LayerView : public View
{
public:
LayerView();
~LayerView();
void addLayer(View* view);
void changeLayer(int index, bool focus = false);
int getLayerIndex();
View* getDefaultFocus() override;
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
private:
std::vector<View*> layers;
int selectedIndex = 0;
};
}

View File

@@ -0,0 +1,218 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/box_layout.hpp>
#include <borealis/image.hpp>
#include <borealis/label.hpp>
#include <borealis/rectangle.hpp>
#include <borealis/scroll_view.hpp>
#include <string>
namespace brls
{
// A list item
// TODO: Use a Label with integrated ticker
class ListItem : public View
{
private:
std::string label;
std::string subLabel;
std::string value;
bool valueFaint;
std::string oldValue;
bool oldValueFaint;
float valueAnimation = 0.0f;
bool checked = false; // check mark on the right
unsigned textSize;
bool drawTopSeparator = true;
Label* descriptionView = nullptr;
Image* thumbnailView = nullptr;
bool reduceDescriptionSpacing = false;
GenericEvent clickEvent;
bool indented = false;
void resetValueAnimation();
public:
ListItem(std::string label, std::string description = "", std::string subLabel = "");
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
void getHighlightInsets(unsigned* top, unsigned* right, unsigned* bottom, unsigned* left) override;
virtual bool onClick();
View* getDefaultFocus() override;
void setThumbnail(Image* image);
void setThumbnail(std::string imagePath);
void setThumbnail(unsigned char* buffer, size_t bufferSize);
bool hasDescription();
void setDrawTopSeparator(bool draw);
bool getReduceDescriptionSpacing();
void setReduceDescriptionSpacing(bool value);
void setIndented(bool indented);
void setTextSize(unsigned textSize);
void setChecked(bool checked);
std::string getLabel();
/**
* Sets the value of this list item
* (the text on the right)
* Set faint to true to have the new value
* use a darker color (typically "OFF" labels)
*/
void setValue(std::string value, bool faint = false, bool animate = true);
std::string getValue();
GenericEvent* getClickEvent();
~ListItem();
};
// Some spacing (to make groups of ListItems)
class ListItemGroupSpacing : public Rectangle
{
public:
ListItemGroupSpacing(bool separator = false);
};
// A list item with mutliple choices for its value
// (will open a Dropdown)
// Fired when the user has selected a value
//
// Parameter is either the selected value index
// or -1 if the user cancelled
typedef Event<int> ValueSelectedEvent;
class SelectListItem : public ListItem
{
public:
SelectListItem(std::string label, std::vector<std::string> values, unsigned selectedValue = 0);
void setSelectedValue(unsigned value);
ValueSelectedEvent* getValueSelectedEvent();
private:
std::vector<std::string> values;
unsigned selectedValue;
ValueSelectedEvent valueEvent;
};
// A list item with a ON/OFF value
// that can be toggled
// Use the click event to detect when the value
// changes
class ToggleListItem : public ListItem
{
private:
bool toggleState;
std::string onValue, offValue;
void updateValue();
public:
ToggleListItem(std::string label, bool initialValue, std::string description = "", std::string onValue = "On", std::string offValue = "Off");
virtual bool onClick() override;
bool getToggleState();
};
// A list item which spawns the swkbd
// to input its value (string)
class InputListItem : public ListItem
{
protected:
std::string helpText;
int maxInputLength;
public:
InputListItem(std::string label, std::string initialValue, std::string helpText, std::string description = "", int maxInputLength = 32);
virtual bool onClick() override;
};
// A list item which spawns the swkbd
// to input its value (integer)
class IntegerInputListItem : public InputListItem
{
public:
IntegerInputListItem(std::string label, int initialValue, std::string helpText, std::string description = "", int maxInputLength = 32);
virtual bool onClick() override;
};
class List; // forward declaration for ListContentView::list
// The content view of lists (used internally)
class ListContentView : public BoxLayout
{
public:
ListContentView(List* list, size_t defaultFocus = 0);
protected:
void customSpacing(View* current, View* next, int* spacing) override;
private:
List* list;
};
// A vertical list of various widgets, with proper margins and spacing
// and a scroll bar
// In practice it's a ScrollView which content view is
// a ListContentView (BoxLayout)
class List : public ScrollView
{
private:
ListContentView* layout;
public:
List(size_t defaultFocus = 0);
~List();
// Wrapped BoxLayout methods
void addView(View* view, bool fill = false);
void clear(bool free = true);
void setMargins(unsigned top, unsigned right, unsigned bottom, unsigned left);
void setMarginBottom(unsigned bottom);
void setSpacing(unsigned spacing);
unsigned getSpacing();
virtual void customSpacing(View* current, View* next, int* spacing);
};
} // namespace brls

View File

@@ -0,0 +1,49 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdarg.h>
#include <string>
namespace brls
{
enum class LogLevel
{
ERROR = 0,
INFO,
DEBUG
};
class Logger
{
public:
static void setLogLevel(LogLevel logLevel);
static void error(const char* format, ...);
static void info(const char* format, ...);
static void debug(const char* format, ...);
protected:
static void log(LogLevel logLevel, const char* prefix, const char* color, const char* format, va_list ap);
};
} // namespace brls

View File

@@ -0,0 +1,40 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/view.hpp>
namespace brls
{
// A Material icon (from the Material font)
class MaterialIcon : public View
{
private:
std::string icon;
unsigned middleX, middleY;
public:
MaterialIcon(std::string icon);
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
};
} // namespace brls

View File

@@ -0,0 +1,64 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/animations.hpp>
#include <borealis/label.hpp>
#include <borealis/view.hpp>
#define BRLS_NOTIFICATIONS_MAX 8
// TODO: check in HOS that the animation duration + notification timeout are correct
namespace brls
{
class Notification : public View
{
public:
Notification(std::string text);
~Notification();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
menu_timer_t timeoutTimer;
private:
Label* label;
};
class NotificationManager : public View
{
private:
Notification* notifications[BRLS_NOTIFICATIONS_MAX];
void layoutNotification(size_t i);
public:
NotificationManager();
~NotificationManager();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
void notify(std::string text);
};
}; // namespace brls

View File

@@ -0,0 +1,63 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 WerWolv
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/applet_frame.hpp>
#include <borealis/list.hpp>
#include <borealis/view.hpp>
#include <string>
namespace brls
{
class PopupFrame : public View
{
private:
PopupFrame(std::string title, unsigned char* imageBuffer, size_t imageBufferSize, AppletFrame* contentView, std::string subTitleLeft = "", std::string subTitleRight = "");
PopupFrame(std::string title, std::string imagePath, AppletFrame* contentView, std::string subTitleLeft = "", std::string subTitleRight = "");
PopupFrame(std::string title, AppletFrame* contentView, std::string subTitleLeft = "", std::string subTitleRight = "");
AppletFrame* contentView = nullptr;
protected:
unsigned getShowAnimationDuration(ViewAnimation animation) override;
public:
~PopupFrame();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
View* getDefaultFocus() override;
virtual bool onCancel();
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
static void open(std::string title, unsigned char* imageBuffer, size_t imageBufferSize, AppletFrame* contentView, std::string subTitleLeft = "", std::string subTitleRight = "");
static void open(std::string title, std::string imagePath, AppletFrame* contentView, std::string subTitleLeft = "", std::string subTitleRight = "");
static void open(std::string title, AppletFrame* contentView, std::string subTitleLeft = "", std::string subTitleRight = "");
bool isTranslucent() override
{
return true;
}
};
} // namespace brls

View File

@@ -0,0 +1,58 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 Billy Laws
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/label.hpp>
#include <borealis/progress_spinner.hpp>
#include <borealis/view.hpp>
namespace brls
{
// TODO: Add the "ProgressDisplayFlags_" prefix to the members
enum ProgressDisplayFlags
{
SPINNER = 1u << 0,
PERCENTAGE = 1u << 1
};
inline constexpr ProgressDisplayFlags DEFAULT_PROGRESS_DISPLAY_FLAGS = (ProgressDisplayFlags)(ProgressDisplayFlags::SPINNER | ProgressDisplayFlags::PERCENTAGE);
// A progress bar with an optional spinner and percentage text.
class ProgressDisplay : public View
{
public:
ProgressDisplay(ProgressDisplayFlags progressFlags = DEFAULT_PROGRESS_DISPLAY_FLAGS);
~ProgressDisplay();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
void setProgress(int current, int max);
private:
float progressPercentage = 0.0f;
Label* label;
ProgressSpinner* spinner;
};
} // namespace brls

View File

@@ -0,0 +1,44 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 Billy Laws
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/view.hpp>
namespace brls
{
// A progress spinner
class ProgressSpinner : public View
{
public:
ProgressSpinner();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
private:
float animationValue = 0.0f;
void restartAnimation();
};
} // namespace brls

View File

@@ -0,0 +1,47 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/frame_context.hpp>
#include <borealis/view.hpp>
namespace brls
{
// A solid color rectangle
class Rectangle : public View
{
protected:
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
public:
Rectangle(NVGcolor color);
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void setColor(NVGcolor color);
~Rectangle() {}
private:
NVGcolor color = nvgRGB(0, 0, 255);
};
} // namespace brls

View File

@@ -0,0 +1,87 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <features/features_cpu.h>
namespace brls
{
// A task that is repeated at a given interval
// by the UI thread
class RepeatingTask
{
private:
retro_time_t interval;
retro_time_t lastRun = 0;
bool running = false;
bool stopRequested = false;
public:
RepeatingTask(retro_time_t interval);
virtual ~RepeatingTask();
/**
* Actual code to run by the task
* Must call RepeatingTask::run() !
*/
virtual void run(retro_time_t currentTime);
/**
* Fired when the task starts
*/
virtual void onStart() {};
/**
* Fired when the task stops
*/
virtual void onStop() {};
/**
* Starts the task
*/
void start();
/**
* Fires the task immediately and delays the
* next run
*/
void fireNow();
/**
* Pauses the task without deleting it
*/
void pause();
/**
* Stops and deletes the task
*/
void stop();
retro_time_t getInterval();
retro_time_t getLastRun();
bool isRunning();
bool isStopRequested();
};
} // namespace brls

View File

@@ -0,0 +1,65 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2020 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/view.hpp>
namespace brls
{
// TODO: horizontal scrolling, either in ScrollView or in a separate class (like Android has)
// A view that automatically scrolls vertically
// when one of its children gains focus
class ScrollView : public View
{
private:
View* contentView = nullptr;
bool ready = false; // has layout been called at least once?
unsigned middleY = 0; // y + height/2
unsigned bottomY = 0; // y + height
float scrollY = 0.0f; // from 0.0f to 1.0f, in % of content view height
bool updateScrollingOnNextLayout = false;
bool updateScrollingOnNextFrame = false;
unsigned getYCenter(View* view);
void prebakeScrolling();
bool updateScrolling(bool animated);
void startScrolling(bool animated, float newScroll);
void scrollAnimationTick();
public:
~ScrollView();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
View* getDefaultFocus() override;
void onChildFocusGained(View* child) override;
void onWindowSizeChanged() override;
void setContentView(View* view);
View* getContentView();
};
} // namespace brls

View File

@@ -0,0 +1,94 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/box_layout.hpp>
#include <string>
#include <vector>
namespace brls
{
// A sidebar with multiple tabs
class SidebarSeparator : public View
{
public:
SidebarSeparator();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
};
class Sidebar;
// TODO: Use a Label view with integrated ticker for label and sublabel
// TODO: Have the label always tick when active
class SidebarItem : public View
{
private:
std::string label;
bool active = false;
Sidebar* sidebar = nullptr;
View* associatedView = nullptr;
public:
SidebarItem(std::string label, Sidebar* sidebar);
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
View* getDefaultFocus() override
{
return this;
}
virtual bool onClick();
void setActive(bool active);
bool isActive();
void onFocusGained() override;
void setAssociatedView(View* view);
View* getAssociatedView();
~SidebarItem();
};
// TODO: Add a style with icons, make it collapsible?
class Sidebar : public BoxLayout
{
private:
SidebarItem* currentActive = nullptr;
public:
Sidebar();
SidebarItem* addItem(std::string label, View* view);
void addSeparator();
void setActive(SidebarItem* item);
View* getDefaultFocus() override;
void onChildFocusGained(View* child) override;
size_t lastFocus = 0;
};
} // namespace brls

View File

@@ -0,0 +1,54 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 Billy Laws
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/applet_frame.hpp>
#include <borealis/sidebar.hpp>
#include <string>
#include <vector>
namespace brls
{
// An applet frame for implementing a stage based app with a progress display in top right
class StagedAppletFrame : public AppletFrame
{
public:
StagedAppletFrame();
~StagedAppletFrame();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void addStage(View* view);
void nextStage();
void previousStage();
unsigned getCurrentStage();
unsigned getStagesCount();
unsigned isLastStage();
private:
size_t currentStage = 0;
std::vector<View*> stageViews;
void enterStage(int index, bool requestFocus);
};
} // namespace brls

View File

@@ -0,0 +1,323 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 WerWolv
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
namespace brls
{
class Style
{
public:
// AppletFrame
struct
{
unsigned headerHeightRegular;
unsigned headerHeightPopup; // PopupFrame
unsigned footerHeight;
unsigned imageLeftPadding;
unsigned imageTopPadding;
unsigned imageSize;
unsigned separatorSpacing;
unsigned titleSize;
unsigned titleStart;
unsigned titleOffset;
unsigned footerTextSize;
unsigned footerTextSpacing;
unsigned slideAnimation;
} AppletFrame;
// Highlight
struct
{
unsigned strokeWidth;
float cornerRadius;
unsigned shadowWidth;
unsigned shadowOffset;
unsigned shadowFeather;
unsigned shadowOpacity;
unsigned animationDuration;
} Highlight;
// Background
struct
{
unsigned sidebarBorderHeight;
} Background;
// Sidebar
struct
{
unsigned width;
unsigned spacing;
unsigned marginLeft;
unsigned marginRight;
unsigned marginTop;
unsigned marginBottom;
struct
{
unsigned height;
unsigned textSize;
unsigned padding;
unsigned textOffsetX;
unsigned activeMarkerWidth;
unsigned highlight;
} Item;
struct
{
unsigned height;
} Separator;
} Sidebar;
// List
struct
{
unsigned marginLeftRight;
unsigned marginTopBottom;
unsigned spacing;
// Item
struct
{
unsigned height;
unsigned heightWithSubLabel;
unsigned valueSize;
unsigned padding;
unsigned thumbnailPadding;
unsigned descriptionIndent;
unsigned descriptionSpacing;
unsigned indent;
unsigned selectRadius;
} Item;
} List;
// Label
struct
{
unsigned regularFontSize;
unsigned mediumFontSize;
unsigned smallFontSize;
unsigned descriptionFontSize;
unsigned crashFontSize;
unsigned buttonFontSize;
unsigned listItemFontSize;
unsigned notificationFontSize;
unsigned dialogFontSize;
unsigned hintFontSize;
float lineHeight;
float notificationLineHeight;
} Label;
// CrashFrame
struct
{
float labelWidth; // proportional to frame width, from 0 to 1
unsigned boxStrokeWidth;
unsigned boxSize;
unsigned boxSpacing;
unsigned buttonWidth;
unsigned buttonHeight;
unsigned buttonSpacing;
} CrashFrame;
// Button
struct
{
float cornerRadius;
unsigned highlightInset;
float shadowWidth;
float shadowFeather;
float shadowOpacity;
float shadowOffset;
} Button;
// TableRow
struct
{
unsigned headerHeight;
unsigned headerTextSize;
unsigned bodyHeight;
unsigned bodyIndent;
unsigned bodyTextSize;
unsigned padding;
} TableRow;
// Dropdown
struct
{
unsigned listWidth;
unsigned listPadding;
unsigned listItemHeight;
unsigned listItemTextSize;
unsigned headerHeight;
unsigned headerFontSize;
unsigned headerPadding;
} Dropdown;
struct
{
unsigned edgePadding;
unsigned separatorSpacing;
unsigned footerHeight;
unsigned imageLeftPadding;
unsigned imageTopPadding;
unsigned imageSize;
unsigned contentWidth;
unsigned contentHeight;
unsigned headerTextLeftPadding;
unsigned headerTextTopPadding;
unsigned subTitleLeftPadding;
unsigned subTitleTopPadding;
unsigned subTitleSpacing;
unsigned subTitleSeparatorLeftPadding;
unsigned subTitleSeparatorTopPadding;
unsigned subTitleSeparatorHeight;
unsigned headerFontSize;
unsigned subTitleFontSize;
} PopupFrame;
// StagedAppletFrame
struct
{
unsigned progressIndicatorSpacing;
unsigned progressIndicatorRadiusUnselected;
unsigned progressIndicatorRadiusSelected;
unsigned progressIndicatorBorderWidth;
} StagedAppletFrame;
// ProgressSpinner
struct
{
float centerGapMultiplier;
float barWidthMultiplier;
unsigned animationDuration;
} ProgressSpinner;
// ProgressDisplay
struct
{
unsigned percentageLabelWidth;
} ProgressDisplay;
// Header
struct
{
unsigned height;
unsigned padding;
unsigned rectangleWidth;
unsigned fontSize;
} Header;
// FramerateCounter
struct
{
unsigned width;
unsigned height;
} FramerateCounter;
// ThumbnailSidebar
struct
{
unsigned marginLeftRight;
unsigned marginTopBottom;
unsigned buttonHeight;
unsigned buttonMargin;
} ThumbnailSidebar;
// AnimationDuration
struct
{
unsigned show;
unsigned showSlide;
unsigned highlight;
unsigned shake;
unsigned collapse;
unsigned progress;
unsigned notificationTimeout;
} AnimationDuration;
// Notification
struct
{
unsigned width;
unsigned padding;
unsigned slideAnimation;
} Notification;
// Dialog
struct
{
unsigned width;
unsigned height;
unsigned paddingTopBottom;
unsigned paddingLeftRight;
float cornerRadius;
unsigned buttonHeight;
unsigned buttonSeparatorHeight;
float shadowWidth;
float shadowFeather;
float shadowOpacity;
float shadowOffset;
} Dialog;
// As close to HOS as possible
static Style horizon();
// TODO: Make a condensed style
};
} // namespace brls

View File

@@ -0,0 +1,35 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 WerWolv
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <functional>
#include <string>
namespace brls
{
class Swkbd
{
public:
static bool openForText(std::function<void(std::string)> f, std::string headerText = "", std::string subText = "", int maxStringLength = 32, std::string initialText = "");
static bool openForNumber(std::function<void(int)> f, std::string headerText = "", std::string subText = "", int maxStringLength = 32, std::string initialText = "", std::string leftButton = "", std::string rightButton = "");
};
} // namespace brls

View File

@@ -0,0 +1,59 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/applet_frame.hpp>
#include <borealis/sidebar.hpp>
#include <string>
#include <vector>
namespace brls
{
// An applet frame containing a sidebar on the left with multiple tabs
class TabFrame : public AppletFrame
{
public:
TabFrame();
/**
* Adds a tab with given label and view
* All tabs and separators must be added
* before the TabFrame is itself added to
* the view hierarchy
*/
void addTab(std::string label, View* view);
void addSeparator();
View* getDefaultFocus() override;
virtual bool onCancel() override;
~TabFrame();
private:
Sidebar* sidebar;
BoxLayout* layout;
View* rightPane = nullptr;
void switchToView(View* view);
};
} // namespace brls

View File

@@ -0,0 +1,73 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/view.hpp>
#include <string>
#include <vector>
namespace brls
{
// The type of a Table Row
enum class TableRowType
{
HEADER = 0,
BODY
};
// A Table row
class TableRow
{
private:
TableRowType type;
std::string label;
std::string value;
public:
TableRow(TableRowType type, std::string label, std::string value = "");
std::string* getLabel();
std::string* getValue();
TableRowType getType();
void setValue(std::string value);
};
// A simple, static two-columns table, as seen in
// the Settings app (Internet connection details)
// All rows must be added before adding the view
// to a layout (it's static)
class Table : public View
{
private:
std::vector<TableRow*> rows;
public:
~Table();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
TableRow* addRow(TableRowType type, std::string label, std::string value = "");
};
} // namespace brls

View File

@@ -0,0 +1,43 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/repeating_task.hpp>
#include <vector>
namespace brls
{
class TaskManager
{
private:
std::vector<RepeatingTask*> repeatingTasks;
void stopRepeatingTask(RepeatingTask* task);
public:
void frame();
void registerRepeatingTask(RepeatingTask* task);
~TaskManager();
};
} // namespace brls

View File

@@ -0,0 +1,96 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <nanovg.h>
namespace brls
{
// Theme variants
// (used in Application)
//
// Not an enum class because it's used
// as an array index in Theme
enum ThemeVariant
{
ThemeVariant_LIGHT = 0,
ThemeVariant_DARK,
ThemeVariant_NUMBER_OF_VARIANTS
};
typedef struct
{
float backgroundColor[3]; // gl color
NVGcolor backgroundColorRGB;
NVGcolor textColor;
NVGcolor descriptionColor;
NVGcolor notificationTextColor;
NVGcolor backdropColor;
NVGcolor separatorColor;
NVGcolor sidebarColor;
NVGcolor activeTabColor;
NVGcolor sidebarSeparatorColor;
NVGcolor highlightBackgroundColor;
NVGcolor highlightColor1;
NVGcolor highlightColor2;
NVGcolor listItemSeparatorColor;
NVGcolor listItemValueColor;
NVGcolor listItemFaintValueColor;
NVGcolor tableEvenBackgroundColor;
NVGcolor tableBodyTextColor;
NVGcolor dropdownBackgroundColor;
NVGcolor nextStageBulletColor;
NVGcolor spinnerBarColor;
NVGcolor headerRectangleColor;
NVGcolor buttonPlainEnabledBackgroundColor;
NVGcolor buttonPlainDisabledBackgroundColor;
NVGcolor buttonPlainEnabledTextColor;
NVGcolor buttonPlainDisabledTextColor;
NVGcolor dialogColor;
NVGcolor dialogBackdrop;
NVGcolor dialogButtonColor;
NVGcolor dialogButtonSeparatorColor;
} ThemeValues;
// A theme contains colors for all variants
class Theme
{
public:
ThemeValues colors[ThemeVariant_NUMBER_OF_VARIANTS];
// As close to HOS as possible
static Theme horizon();
};
} // namespace brls

View File

@@ -0,0 +1,78 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <borealis/applet_frame.hpp>
#include <borealis/box_layout.hpp>
#include <borealis/button.hpp>
#include <borealis/image.hpp>
namespace brls
{
// The sidebar used in ThumbnailFrame
class ThumbnailSidebar : public View
{
private:
Image* image = nullptr;
Button* button = nullptr;
Label* title = nullptr;
Label* subTitle = nullptr;
public:
ThumbnailSidebar();
~ThumbnailSidebar();
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) override;
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
View* getDefaultFocus() override;
void setThumbnail(std::string imagePath);
void setThumbnail(unsigned char* buffer, size_t bufferSize);
void setTitle(std::string title);
void setSubtitle(std::string subTitle);
Button* getButton();
};
// An applet frame with a sidebar on the right, containing a thumbnail
// and a button (similar to the Wi-Fi settings in HOS)
class ThumbnailFrame : public AppletFrame
{
private:
ThumbnailSidebar* sidebar = nullptr;
BoxLayout* boxLayout = nullptr;
View* thumbnailContentView = nullptr;
public:
ThumbnailFrame();
~ThumbnailFrame();
void setContentView(View* view) override;
protected:
void layout(NVGcontext* vg, Style* style, FontStash* stash) override;
ThumbnailSidebar* getSidebar();
};
} // namespace brls

View File

@@ -0,0 +1,406 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <features/features_cpu.h>
#include <stdio.h>
#include <borealis/actions.hpp>
#include <borealis/event.hpp>
#include <borealis/frame_context.hpp>
#include <functional>
#include <map>
#include <string>
#include <vector>
namespace brls
{
// The animation to play when
// pushing or popping a view
// (implemented in Application)
enum class ViewAnimation
{
FADE, // the old view fades away and the new one fades in
SLIDE_LEFT, // the old view slides out to the left and the new one slides in from the right
SLIDE_RIGHT // inverted SLIDE_LEFT
};
// Focus direction when navigating
enum class FocusDirection
{
UP,
DOWN,
LEFT,
RIGHT
};
// View background
enum class Background
{
NONE,
SIDEBAR,
DEBUG,
BACKDROP
};
extern NVGcolor transparent;
class View;
typedef Event<View*> GenericEvent;
typedef Event<> VoidEvent;
// Superclass for all the other views
// Lifecycle of a view is :
// new -> [willAppear -> willDisappear] -> delete
//
// Users have do to the new, the rest of the lifecycle is taken
// care of by the library
//
// willAppear and willDisappear can be called zero or multiple times
// before deletion (in case of a TabLayout for instance)
class View
{
private:
Background background = Background::NONE;
void drawBackground(NVGcontext* vg, FrameContext* ctx, Style* style);
void drawHighlight(NVGcontext* vg, ThemeValues* theme, float alpha, Style* style, bool background);
float highlightAlpha = 0.0f;
bool dirty = true;
bool highlightShaking = false;
retro_time_t highlightShakeStart;
FocusDirection highlightShakeDirection;
float highlightShakeAmplitude;
bool fadeIn = false; // is the fade in animation running?
bool forceTranslucent = false;
ThemeValues* themeOverride = nullptr;
bool hidden = false;
std::vector<Action> actions;
/**
* Parent user data, typically the index of the view
* in the internal layout structure
*/
void* parentUserdata = nullptr;
protected:
int x = 0;
int y = 0;
unsigned width = 0;
unsigned height = 0;
float collapseState = 1.0f;
bool focused = false;
View* parent = nullptr;
GenericEvent focusEvent;
virtual unsigned getShowAnimationDuration(ViewAnimation animation);
virtual void getHighlightInsets(unsigned* top, unsigned* right, unsigned* bottom, unsigned* left)
{
*top = 0;
*right = 0;
*bottom = 0;
*left = 0;
}
virtual void getHighlightMetrics(Style* style, float* cornerRadius)
{
*cornerRadius = style->Highlight.cornerRadius;
}
virtual bool isHighlightBackgroundEnabled()
{
return true;
}
// Helper functions to apply this view's alpha to a color
NVGcolor a(NVGcolor color);
NVGpaint a(NVGpaint paint);
NVGcolor RGB(unsigned r, unsigned g, unsigned b)
{
return this->a(nvgRGB(r, g, b));
}
NVGcolor RGBA(unsigned r, unsigned g, unsigned b, unsigned a)
{
return this->a(nvgRGBA(r, g, b, a));
}
NVGcolor RGBf(float r, float g, float b)
{
return this->a(nvgRGBf(r, g, b));
}
NVGcolor RGBAf(float r, float g, float b, float a)
{
return this->a(nvgRGBAf(r, g, b, a));
}
/**
* Should the hint alpha be animated when
* pushing the view?
*/
virtual bool animateHint()
{
return false;
}
public:
void setBoundaries(int x, int y, unsigned width, unsigned height);
void setBackground(Background background);
void setWidth(unsigned width);
void setHeight(unsigned height);
void shakeHighlight(FocusDirection direction);
int getX();
int getY();
unsigned getWidth();
unsigned getHeight(bool includeCollapse = true);
void setForceTranslucent(bool translucent);
void setParent(View* parent, void* parentUserdata = nullptr);
View* getParent();
bool hasParent();
void* getParentUserData();
void registerAction(std::string hintText, Key key, ActionListener actionListener, bool hidden = false);
void updateActionHint(Key key, std::string hintText);
void setActionAvailable(Key key, bool available);
std::string describe() const { return typeid(*this).name(); }
const std::vector<Action>& getActions()
{
return this->actions;
}
/**
* Called each frame
* Do not override it to draw your view,
* override draw() instead
*/
virtual void frame(FrameContext* ctx);
/**
* Called by frame() to draw
* the view onscreen
*/
virtual void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx) = 0;
/**
* Triggered when the view has been
* resized and needs to layout its
* children
*/
virtual void layout(NVGcontext* vg, Style* style, FontStash* stash)
{
// Nothing to do
}
/**
* Called when the view will appear
* on screen, before or after layout()
*
* Can be called if the view has
* already appeared, so be careful
*/
virtual void willAppear(bool resetState = false)
{
// Nothing to do
}
/**
* Called when the view will disappear
* from the screen
*
* Can be called if the view has
* already disappeared, so be careful
*/
virtual void willDisappear(bool resetState = false)
{
// Nothing to do
}
/**
* Called when the show() animation (fade in)
* ends
*/
virtual void onShowAnimationEnd() {};
/**
* Shows the view (fade in animation)
*
* Called once when the view is
* pushed to the view stack
*
* Not recursive
*/
virtual void show(std::function<void(void)> cb, bool animate = true, ViewAnimation animation = ViewAnimation::FADE);
/**
* Hides the view in a collapse animation
*/
void collapse(bool animated = true);
bool isCollapsed();
/**
* Shows the view in a expand animation (opposite
* of collapse)
*/
void expand(bool animated = true);
/**
* Hides the view (fade out animation)
*
* Called if another view is pushed
* on top of this one
*
* Not recursive
*/
virtual void hide(std::function<void(void)> cb, bool animated = true, ViewAnimation animation = ViewAnimation::FADE);
bool isHidden();
/**
* Calls layout() on next frame
* unless immediate is true in which case
* it's called immediately
*/
void invalidate(bool immediate = false);
/**
* Is this view translucent?
*
* If you override it please return
* <value> || View::isTranslucent()
* to keep the fadeIn transition
*/
virtual bool isTranslucent()
{
return fadeIn || forceTranslucent;
}
bool isFocused();
/**
* Returns the default view to focus when focusing this view
* Typically the view itself or one of its children
*
* Returning nullptr means that the view is not focusable
* (and neither are its children)
*
* When pressing a key, the flow is :
* 1. starting from the currently focused view's parent, traverse the tree upwards and
* repeatidly call getNextFocus() on every view until we find a next view to focus or meet the end of the tree
* 2. if a view is found, getNextFocus() will internally call getDefaultFocus() for the selected child
* 3. give focus to the result, if it exists
*/
virtual View* getDefaultFocus()
{
return nullptr;
}
/**
* Returns the next view to focus given the requested direction
* and the currently focused view (as parent user data)
*
* Returning nullptr means that there is no next view to focus
* in that direction - getNextFocus will then be called on our
* parent if any
*/
virtual View* getNextFocus(FocusDirection direction, void* parentUserdata)
{
return nullptr;
}
/**
* Fired when focus is gained
*/
virtual void onFocusGained();
/**
* Fired when focus is lost
*/
virtual void onFocusLost();
/**
* Fired when focus is gained on one of this view's children
*/
virtual void onChildFocusGained(View* child)
{
if (this->hasParent())
this->getParent()->onChildFocusGained(this);
}
/**
* Fired when focus is gained on one of this view's children
*/
virtual void onChildFocusLost(View* child)
{
if (this->hasParent())
this->getParent()->onChildFocusLost(this);
}
/**
* Fired when the window size changes
* Not guaranteed to be called before or after layout()
*/
virtual void onWindowSizeChanged()
{
// Nothing by default
}
GenericEvent* getFocusEvent();
float alpha = 1.0f;
virtual float getAlpha(bool child = false);
/**
* Forces this view and its children to use
* the specified theme variant
*/
void overrideThemeVariant(ThemeValues* newTheme);
virtual ~View();
};
} // namespace brls

View File

@@ -0,0 +1,933 @@
/* RetroArch - A frontend for libretro.
* Borealis, a Nintendo Switch UI Library
* Copyright (C) 2014-2018 - Jean-André Santoni
* Copyright (C) 2011-2018 - Daniel De Matteis
* Copyright (C) 2019 - natinusala
* Copyright (C) 2019 - p-sam
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <compat/strl.h>
#include <encodings/utf.h>
#include <features/features_cpu.h>
#include <math.h>
#include <retro_assert.h>
#include <retro_math.h>
#include <retro_miscellaneous.h>
#include <stdio.h>
#include <string.h>
#include <string/stdstring.h>
#include <borealis/animations.hpp>
#include <vector>
namespace brls
{
struct tween
{
float duration;
float running_since;
float initial_value;
float target_value;
float* subject;
uintptr_t tag;
easing_cb easing;
tween_cb cb;
tween_cb tick;
void* userdata;
bool deleted;
};
struct menu_animation
{
std::vector<tween> list;
std::vector<tween> pending;
bool pending_deletes;
bool in_update;
};
typedef struct menu_animation menu_animation_t;
#define TICKER_SPEED 333
#define TICKER_SLOW_SPEED 1600
static const char ticker_spacer_default[] = " | ";
static menu_animation_t anim;
static retro_time_t cur_time = 0;
static retro_time_t old_time = 0;
static uint64_t ticker_idx = 0; /* updated every TICKER_SPEED ms */
static uint64_t ticker_slow_idx = 0; /* updated every TICKER_SLOW_SPEED ms */
static float delta_time = 0.0f;
static bool animation_is_active = false;
static bool ticker_is_active = false;
static double highlight_gradient_x = 0.0f;
static double highlight_gradient_y = 0.0f;
static double highlight_color = 0.0f;
/* from https://github.com/kikito/tween.lua/blob/master/tween.lua */
static float easing_linear(float t, float b, float c, float d)
{
return c * t / d + b;
}
static float easing_in_out_quad(float t, float b, float c, float d)
{
t = t / d * 2;
if (t < 1)
return c / 2 * pow(t, 2) + b;
return -c / 2 * ((t - 1) * (t - 3) - 1) + b;
}
static float easing_in_quad(float t, float b, float c, float d)
{
return c * pow(t / d, 2) + b;
}
static float easing_out_quad(float t, float b, float c, float d)
{
t = t / d;
return -c * t * (t - 2) + b;
}
static float easing_out_in_quad(float t, float b, float c, float d)
{
if (t < d / 2)
return easing_out_quad(t * 2, b, c / 2, d);
return easing_in_quad((t * 2) - d, b + c / 2, c / 2, d);
}
static float easing_in_cubic(float t, float b, float c, float d)
{
return c * pow(t / d, 3) + b;
}
static float easing_out_cubic(float t, float b, float c, float d)
{
return c * (pow(t / d - 1, 3) + 1) + b;
}
static float easing_in_out_cubic(float t, float b, float c, float d)
{
t = t / d * 2;
if (t < 1)
return c / 2 * t * t * t + b;
t = t - 2;
return c / 2 * (t * t * t + 2) + b;
}
static float easing_out_in_cubic(float t, float b, float c, float d)
{
if (t < d / 2)
return easing_out_cubic(t * 2, b, c / 2, d);
return easing_in_cubic((t * 2) - d, b + c / 2, c / 2, d);
}
static float easing_in_quart(float t, float b, float c, float d)
{
return c * pow(t / d, 4) + b;
}
static float easing_out_quart(float t, float b, float c, float d)
{
return -c * (pow(t / d - 1, 4) - 1) + b;
}
static float easing_in_out_quart(float t, float b, float c, float d)
{
t = t / d * 2;
if (t < 1)
return c / 2 * pow(t, 4) + b;
return -c / 2 * (pow(t - 2, 4) - 2) + b;
}
static float easing_out_in_quart(float t, float b, float c, float d)
{
if (t < d / 2)
return easing_out_quart(t * 2, b, c / 2, d);
return easing_in_quart((t * 2) - d, b + c / 2, c / 2, d);
}
static float easing_in_quint(float t, float b, float c, float d)
{
return c * pow(t / d, 5) + b;
}
static float easing_out_quint(float t, float b, float c, float d)
{
return c * (pow(t / d - 1, 5) + 1) + b;
}
static float easing_in_out_quint(float t, float b, float c, float d)
{
t = t / d * 2;
if (t < 1)
return c / 2 * pow(t, 5) + b;
return c / 2 * (pow(t - 2, 5) + 2) + b;
}
static float easing_out_in_quint(float t, float b, float c, float d)
{
if (t < d / 2)
return easing_out_quint(t * 2, b, c / 2, d);
return easing_in_quint((t * 2) - d, b + c / 2, c / 2, d);
}
static float easing_in_sine(float t, float b, float c, float d)
{
return -c * cos(t / d * (M_PI / 2)) + c + b;
}
static float easing_out_sine(float t, float b, float c, float d)
{
return c * sin(t / d * (M_PI / 2)) + b;
}
static float easing_in_out_sine(float t, float b, float c, float d)
{
return -c / 2 * (cos(M_PI * t / d) - 1) + b;
}
static float easing_out_in_sine(float t, float b, float c, float d)
{
if (t < d / 2)
return easing_out_sine(t * 2, b, c / 2, d);
return easing_in_sine((t * 2) - d, b + c / 2, c / 2, d);
}
static float easing_in_expo(float t, float b, float c, float d)
{
if (t == 0)
return b;
return c * powf(2, 10 * (t / d - 1)) + b - c * 0.001;
}
static float easing_out_expo(float t, float b, float c, float d)
{
if (t == d)
return b + c;
return c * 1.001 * (-powf(2, -10 * t / d) + 1) + b;
}
static float easing_in_out_expo(float t, float b, float c, float d)
{
if (t == 0)
return b;
if (t == d)
return b + c;
t = t / d * 2;
if (t < 1)
return c / 2 * powf(2, 10 * (t - 1)) + b - c * 0.0005;
return c / 2 * 1.0005 * (-powf(2, -10 * (t - 1)) + 2) + b;
}
static float easing_out_in_expo(float t, float b, float c, float d)
{
if (t < d / 2)
return easing_out_expo(t * 2, b, c / 2, d);
return easing_in_expo((t * 2) - d, b + c / 2, c / 2, d);
}
static float easing_in_circ(float t, float b, float c, float d)
{
return (-c * (sqrt(1 - powf(t / d, 2)) - 1) + b);
}
static float easing_out_circ(float t, float b, float c, float d)
{
return (c * sqrt(1 - powf(t / d - 1, 2)) + b);
}
static float easing_in_out_circ(float t, float b, float c, float d)
{
t = t / d * 2;
if (t < 1)
return -c / 2 * (sqrt(1 - t * t) - 1) + b;
t = t - 2;
return c / 2 * (sqrt(1 - t * t) + 1) + b;
}
static float easing_out_in_circ(float t, float b, float c, float d)
{
if (t < d / 2)
return easing_out_circ(t * 2, b, c / 2, d);
return easing_in_circ((t * 2) - d, b + c / 2, c / 2, d);
}
static float easing_out_bounce(float t, float b, float c, float d)
{
t = t / d;
if (t < 1 / 2.75)
return c * (7.5625 * t * t) + b;
if (t < 2 / 2.75)
{
t = t - (1.5 / 2.75);
return c * (7.5625 * t * t + 0.75) + b;
}
else if (t < 2.5 / 2.75)
{
t = t - (2.25 / 2.75);
return c * (7.5625 * t * t + 0.9375) + b;
}
t = t - (2.625 / 2.75);
return c * (7.5625 * t * t + 0.984375) + b;
}
static float easing_in_bounce(float t, float b, float c, float d)
{
return c - easing_out_bounce(d - t, 0, c, d) + b;
}
static float easing_in_out_bounce(float t, float b, float c, float d)
{
if (t < d / 2)
return easing_in_bounce(t * 2, 0, c, d) * 0.5 + b;
return easing_out_bounce(t * 2 - d, 0, c, d) * 0.5 + c * .5 + b;
}
static float easing_out_in_bounce(float t, float b, float c, float d)
{
if (t < d / 2)
return easing_out_bounce(t * 2, b, c / 2, d);
return easing_in_bounce((t * 2) - d, b + c / 2, c / 2, d);
}
static void menu_animation_ticker_generic(uint64_t idx,
size_t max_width, size_t* offset, size_t* width)
{
int ticker_period = (int)(2 * (*width - max_width) + 4);
int phase = idx % ticker_period;
int phase_left_stop = 2;
int phase_left_moving = (int)(phase_left_stop + (*width - max_width));
int phase_right_stop = phase_left_moving + 2;
int left_offset = phase - phase_left_stop;
int right_offset = (int)((*width - max_width) - (phase - phase_right_stop));
if (phase < phase_left_stop)
*offset = 0;
else if (phase < phase_left_moving)
*offset = left_offset;
else if (phase < phase_right_stop)
*offset = *width - max_width;
else
*offset = right_offset;
*width = max_width;
}
static void menu_animation_ticker_loop(uint64_t idx,
size_t max_width, size_t str_width, size_t spacer_width,
size_t* offset1, size_t* width1,
size_t* offset2, size_t* width2,
size_t* offset3, size_t* width3)
{
int ticker_period = (int)(str_width + spacer_width);
int phase = idx % ticker_period;
/* Output offsets/widths are unsigned size_t, but it's
* easier to perform the required calculations with ints,
* so create some temporary variables... */
int offset;
int width;
/* Looping text is composed of up to three strings,
* where std::string 1 and 2 are different regions of the
* source text and std::string 2 is a spacer:
*
* |-----max_width-----|
* [std::string 1][std::string 2][std::string 3]
*
* The following implementation could probably be optimised,
* but any performance gains would be trivial compared with
* all the std::string manipulation that has to happen afterwards...
*/
/* String 1 */
offset = (phase < (int)str_width) ? phase : 0;
width = (int)(str_width - phase);
width = (width < 0) ? 0 : width;
width = (width > (int)max_width) ? max_width : width;
*offset1 = offset;
*width1 = width;
/* String 2 */
offset = (int)(phase - str_width);
offset = offset < 0 ? 0 : offset;
width = (int)(max_width - *width1);
width = (width > (int)spacer_width) ? spacer_width : width;
width = width - offset;
*offset2 = offset;
*width2 = width;
/* String 3 */
width = max_width - (*width1 + *width2);
width = width < 0 ? 0 : width;
/* Note: offset is always zero here so offset3 is
* unnecessary - but include it anyway to preserve
* symmetry... */
*offset3 = 0;
*width3 = width;
}
void menu_animation_init(void)
{
// Nothing to do
}
void menu_animation_free(void)
{
anim.list.clear();
anim.pending.clear();
anim.in_update = false;
anim.pending_deletes = false;
}
static void menu_delayed_animation_cb(void* userdata)
{
menu_delayed_animation_t* delayed_animation = (menu_delayed_animation_t*)userdata;
menu_animation_push(&delayed_animation->entry);
free(delayed_animation);
}
void menu_animation_push_delayed(unsigned delay, menu_animation_ctx_entry_t* entry)
{
menu_timer_ctx_entry_t timer_entry;
menu_delayed_animation_t* delayed_animation = (menu_delayed_animation_t*)malloc(sizeof(menu_delayed_animation_t));
delayed_animation->entry.cb = entry->cb;
delayed_animation->entry.duration = entry->duration;
delayed_animation->entry.easing_enum = entry->easing_enum;
delayed_animation->entry.subject = entry->subject;
delayed_animation->entry.tag = entry->tag;
delayed_animation->entry.target_value = entry->target_value;
delayed_animation->entry.tick = entry->tick;
delayed_animation->entry.userdata = entry->userdata;
timer_entry.cb = menu_delayed_animation_cb;
timer_entry.tick = NULL;
timer_entry.duration = delay;
timer_entry.userdata = delayed_animation;
menu_timer_start(&delayed_animation->timer, &timer_entry);
}
bool menu_animation_push(menu_animation_ctx_entry_t* entry)
{
struct tween t;
t.duration = entry->duration;
t.running_since = 0;
t.initial_value = *entry->subject;
t.target_value = entry->target_value;
t.subject = entry->subject;
t.tag = entry->tag;
t.cb = entry->cb;
t.tick = entry->tick;
t.userdata = entry->userdata;
t.easing = NULL;
t.deleted = false;
switch (entry->easing_enum)
{
case EASING_LINEAR:
t.easing = &easing_linear;
break;
/* Quad */
case EASING_IN_QUAD:
t.easing = &easing_in_quad;
break;
case EASING_OUT_QUAD:
t.easing = &easing_out_quad;
break;
case EASING_IN_OUT_QUAD:
t.easing = &easing_in_out_quad;
break;
case EASING_OUT_IN_QUAD:
t.easing = &easing_out_in_quad;
break;
/* Cubic */
case EASING_IN_CUBIC:
t.easing = &easing_in_cubic;
break;
case EASING_OUT_CUBIC:
t.easing = &easing_out_cubic;
break;
case EASING_IN_OUT_CUBIC:
t.easing = &easing_in_out_cubic;
break;
case EASING_OUT_IN_CUBIC:
t.easing = &easing_out_in_cubic;
break;
/* Quart */
case EASING_IN_QUART:
t.easing = &easing_in_quart;
break;
case EASING_OUT_QUART:
t.easing = &easing_out_quart;
break;
case EASING_IN_OUT_QUART:
t.easing = &easing_in_out_quart;
break;
case EASING_OUT_IN_QUART:
t.easing = &easing_out_in_quart;
break;
/* Quint */
case EASING_IN_QUINT:
t.easing = &easing_in_quint;
break;
case EASING_OUT_QUINT:
t.easing = &easing_out_quint;
break;
case EASING_IN_OUT_QUINT:
t.easing = &easing_in_out_quint;
break;
case EASING_OUT_IN_QUINT:
t.easing = &easing_out_in_quint;
break;
/* Sine */
case EASING_IN_SINE:
t.easing = &easing_in_sine;
break;
case EASING_OUT_SINE:
t.easing = &easing_out_sine;
break;
case EASING_IN_OUT_SINE:
t.easing = &easing_in_out_sine;
break;
case EASING_OUT_IN_SINE:
t.easing = &easing_out_in_sine;
break;
/* Expo */
case EASING_IN_EXPO:
t.easing = &easing_in_expo;
break;
case EASING_OUT_EXPO:
t.easing = &easing_out_expo;
break;
case EASING_IN_OUT_EXPO:
t.easing = &easing_in_out_expo;
break;
case EASING_OUT_IN_EXPO:
t.easing = &easing_out_in_expo;
break;
/* Circ */
case EASING_IN_CIRC:
t.easing = &easing_in_circ;
break;
case EASING_OUT_CIRC:
t.easing = &easing_out_circ;
break;
case EASING_IN_OUT_CIRC:
t.easing = &easing_in_out_circ;
break;
case EASING_OUT_IN_CIRC:
t.easing = &easing_out_in_circ;
break;
/* Bounce */
case EASING_IN_BOUNCE:
t.easing = &easing_in_bounce;
break;
case EASING_OUT_BOUNCE:
t.easing = &easing_out_bounce;
break;
case EASING_IN_OUT_BOUNCE:
t.easing = &easing_in_out_bounce;
break;
case EASING_OUT_IN_BOUNCE:
t.easing = &easing_out_in_bounce;
break;
default:
break;
}
/* ignore born dead tweens */
if (!t.easing || t.duration == 0 || t.initial_value == t.target_value)
return false;
if (anim.in_update)
anim.pending.push_back(t);
else
anim.list.push_back(t);
return true;
}
void menu_animation_get_highlight(float* gradient_x, float* gradient_y, float* color)
{
if (gradient_x)
*gradient_x = (float)highlight_gradient_x;
if (gradient_y)
*gradient_y = (float)highlight_gradient_y;
if (color)
*color = (float)highlight_color;
}
#define HIGHLIGHT_SPEED 350.0
static void menu_animation_update_time(bool timedate_enable)
{
static retro_time_t
last_clock_update
= 0;
static retro_time_t
last_ticker_update
= 0;
static retro_time_t
last_ticker_slow_update
= 0;
/* Adjust ticker speed */
float speed_factor = 1.0f;
unsigned ticker_speed = (unsigned)(((float)TICKER_SPEED / speed_factor) + 0.5);
unsigned ticker_slow_speed = (unsigned)(((float)TICKER_SLOW_SPEED / speed_factor) + 0.5);
cur_time = cpu_features_get_time_usec() / 1000;
delta_time = old_time == 0 ? 0 : cur_time - old_time;
old_time = cur_time;
highlight_gradient_x = (cos((double)cur_time / HIGHLIGHT_SPEED / 3.0) + 1.0) / 2.0;
highlight_gradient_y = (sin((double)cur_time / HIGHLIGHT_SPEED / 3.0) + 1.0) / 2.0;
highlight_color = (sin((double)cur_time / HIGHLIGHT_SPEED * 2.0) + 1.0) / 2.0;
if (((cur_time - last_clock_update) > 1000)
&& timedate_enable)
{
animation_is_active = true;
last_clock_update = cur_time;
}
if (ticker_is_active
&& cur_time - last_ticker_update >= ticker_speed)
{
ticker_idx++;
last_ticker_update = cur_time;
}
if (ticker_is_active
&& cur_time - last_ticker_slow_update >= ticker_slow_speed)
{
ticker_slow_idx++;
last_ticker_slow_update = cur_time;
}
}
bool menu_animation_update(void)
{
unsigned i;
menu_animation_update_time(false);
anim.in_update = true;
anim.pending_deletes = false;
for (i = 0; i < anim.list.size(); i++)
{
struct tween* tween = &anim.list[i];
tween->running_since += delta_time;
*tween->subject = tween->easing(
tween->running_since,
tween->initial_value,
tween->target_value - tween->initial_value,
tween->duration);
if (tween->tick)
tween->tick(tween->userdata);
if (tween->running_since >= tween->duration)
{
*tween->subject = tween->target_value;
if (tween->cb)
tween->cb(tween->userdata);
anim.list.erase(anim.list.begin() + i);
i--;
}
}
if (anim.pending_deletes)
{
for (i = 0; i < anim.list.size(); i++)
{
struct tween* tween = &anim.list[i];
if (tween->deleted)
{
anim.list.erase(anim.list.begin() + i);
i--;
}
}
anim.pending_deletes = false;
}
if (anim.pending.size() > 0)
{
anim.list.insert(anim.list.begin(), anim.pending.begin(), anim.pending.end());
anim.pending.clear();
}
anim.in_update = false;
animation_is_active = anim.list.size() > 0;
return animation_is_active;
}
bool menu_animation_ticker(menu_animation_ctx_ticker_t* ticker)
{
size_t str_len = utf8len(ticker->str);
if (!ticker->spacer)
ticker->spacer = ticker_spacer_default;
if ((size_t)str_len <= ticker->len)
{
utf8cpy(ticker->s,
PATH_MAX_LENGTH,
ticker->str,
ticker->len);
return false;
}
if (!ticker->selected)
{
utf8cpy(ticker->s, PATH_MAX_LENGTH, ticker->str, ticker->len - 3);
strlcat(ticker->s, "...", PATH_MAX_LENGTH);
return false;
}
/* Note: If we reach this point then str_len > ticker->len
* (previously had an unecessary 'if (str_len > ticker->len)'
* check here...) */
switch (ticker->type_enum)
{
case TICKER_TYPE_LOOP:
{
size_t offset1, offset2, offset3;
size_t width1, width2, width3;
/* Horribly oversized temporary buffer
* > utf8 support makes this whole thing incredibly
* ugly/inefficient. Not much we can do about it... */
char tmp[PATH_MAX_LENGTH];
tmp[0] = '\0';
ticker->s[0] = '\0';
menu_animation_ticker_loop(
ticker->idx,
ticker->len,
str_len, utf8len(ticker->spacer),
&offset1, &width1,
&offset2, &width2,
&offset3, &width3);
if (width1 > 0)
{
utf8cpy(
ticker->s,
PATH_MAX_LENGTH,
utf8skip(ticker->str, offset1),
width1);
}
if (width2 > 0)
{
utf8cpy(
tmp,
PATH_MAX_LENGTH,
utf8skip(ticker->spacer, offset2),
width2);
strlcat(ticker->s, tmp, PATH_MAX_LENGTH);
}
if (width3 > 0)
{
utf8cpy(
tmp,
PATH_MAX_LENGTH,
utf8skip(ticker->str, offset3),
width3);
strlcat(ticker->s, tmp, PATH_MAX_LENGTH);
}
break;
}
case TICKER_TYPE_BOUNCE:
default:
{
size_t offset = 0;
menu_animation_ticker_generic(
ticker->idx,
ticker->len,
&offset,
&str_len);
utf8cpy(
ticker->s,
PATH_MAX_LENGTH,
utf8skip(ticker->str, offset),
str_len);
break;
}
}
ticker_is_active = true;
return true;
}
bool menu_animation_is_active(void)
{
return animation_is_active || ticker_is_active;
}
bool menu_animation_kill_by_tag(menu_animation_ctx_tag* tag)
{
unsigned i;
if (!tag || *tag == (uintptr_t)-1)
return false;
for (i = 0; i < anim.list.size(); ++i)
{
struct tween* t = &anim.list[i];
if (t->tag != *tag)
continue;
if (anim.in_update)
{
t->deleted = true;
anim.pending_deletes = true;
}
else
{
anim.list.erase(anim.list.begin() + i);
--i;
}
}
return true;
}
void menu_animation_kill_by_subject(menu_animation_ctx_subject_t* subject)
{
unsigned i, j, killed = 0;
float** sub = (float**)subject->data;
for (i = 0; i < anim.list.size() && killed < subject->count; ++i)
{
struct tween* t = &anim.list[i];
for (j = 0; j < subject->count; ++j)
{
if (t->subject != sub[j])
continue;
if (anim.in_update)
{
t->deleted = true;
anim.pending_deletes = true;
}
else
{
anim.list.erase(anim.list.begin() + i);
--i;
}
killed++;
break;
}
}
}
float menu_animation_get_delta_time(void)
{
return delta_time;
}
bool menu_animation_ctl(enum menu_animation_ctl_state state, void* data)
{
switch (state)
{
case MENU_ANIMATION_CTL_CLEAR_ACTIVE:
animation_is_active = false;
ticker_is_active = false;
break;
case MENU_ANIMATION_CTL_SET_ACTIVE:
animation_is_active = true;
ticker_is_active = true;
break;
case MENU_ANIMATION_CTL_NONE:
default:
break;
}
return true;
}
void menu_timer_start(menu_timer_t* timer, menu_timer_ctx_entry_t* timer_entry)
{
menu_animation_ctx_entry_t entry;
menu_animation_ctx_tag tag = (uintptr_t)timer;
menu_timer_kill(timer);
*timer = 0.0f;
entry.easing_enum = EASING_LINEAR;
entry.tag = tag;
entry.duration = timer_entry->duration;
entry.target_value = 1.0f;
entry.subject = timer;
entry.cb = timer_entry->cb;
entry.tick = timer_entry->tick;
entry.userdata = timer_entry->userdata;
menu_animation_push(&entry);
}
void menu_timer_kill(menu_timer_t* timer)
{
menu_animation_ctx_tag tag = (uintptr_t)timer;
menu_animation_kill_by_tag(&tag);
}
uint64_t menu_animation_get_ticker_idx(void)
{
return ticker_idx;
}
uint64_t menu_animation_get_ticker_slow_idx(void)
{
return ticker_slow_idx;
}
} // namespace brls

View File

@@ -0,0 +1,414 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/applet_frame.hpp>
#include <borealis/application.hpp>
namespace brls
{
AppletFrame::AppletFrame(bool padLeft, bool padRight)
{
Style* style = Application::getStyle();
if (padLeft)
this->leftPadding = style->AppletFrame.separatorSpacing;
if (padRight)
this->rightPadding = style->AppletFrame.separatorSpacing;
this->hint = new Hint();
this->hint->setParent(this);
this->registerAction("Back", Key::B, [this] { return this->onCancel(); });
}
void AppletFrame::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
// Text
if (this->headerStyle == HeaderStyle::REGULAR)
{
// Title
NVGcolor titleColor = a(ctx->theme->textColor);
if (this->contentView)
titleColor.a *= this->contentView->getAlpha();
nvgFillColor(vg, titleColor);
nvgFontSize(vg, style->AppletFrame.titleSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgFontFaceId(vg, ctx->fontStash->regular);
nvgBeginPath(vg);
nvgText(vg, x + style->AppletFrame.titleStart, y + style->AppletFrame.headerHeightRegular / 2 + style->AppletFrame.titleOffset, this->title.c_str(), nullptr);
// Header
nvgBeginPath(vg);
nvgFillColor(vg, a(ctx->theme->textColor));
nvgRect(vg, x + style->AppletFrame.separatorSpacing, y + style->AppletFrame.headerHeightRegular - 1, width - style->AppletFrame.separatorSpacing * 2, 1);
nvgFill(vg);
}
else if (this->headerStyle == HeaderStyle::POPUP)
{
// Header Text
nvgBeginPath(vg);
nvgFillColor(vg, a(ctx->theme->textColor));
nvgFontFaceId(vg, ctx->fontStash->regular);
nvgFontSize(vg, style->PopupFrame.headerFontSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgText(vg, x + style->PopupFrame.headerTextLeftPadding,
y + style->PopupFrame.headerTextTopPadding,
this->title.c_str(),
nullptr);
// Sub title text 1
nvgBeginPath(vg);
nvgFillColor(vg, a(ctx->theme->descriptionColor));
nvgFontFaceId(vg, ctx->fontStash->regular);
nvgFontSize(vg, style->PopupFrame.subTitleFontSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
nvgText(vg, x + style->PopupFrame.subTitleLeftPadding,
y + style->PopupFrame.subTitleTopPadding,
this->subTitleLeft.c_str(),
nullptr);
float bounds[4];
nvgTextBounds(vg, x, y, this->subTitleLeft.c_str(), nullptr, bounds);
// Sub title separator
nvgFillColor(vg, a(ctx->theme->descriptionColor)); // we purposely don't apply opacity
nvgBeginPath(vg);
nvgRect(vg, x + style->PopupFrame.subTitleLeftPadding + (bounds[2] - bounds[0]) + style->PopupFrame.subTitleSpacing,
y + style->PopupFrame.subTitleSeparatorTopPadding,
1,
style->PopupFrame.subTitleSeparatorHeight);
nvgFill(vg);
// Sub title text 2
nvgBeginPath(vg);
nvgFillColor(vg, a(ctx->theme->descriptionColor));
nvgFontFaceId(vg, ctx->fontStash->regular);
nvgFontSize(vg, style->PopupFrame.subTitleFontSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
nvgText(vg, x + style->PopupFrame.subTitleLeftPadding + (bounds[2] - bounds[0]) + (style->PopupFrame.subTitleSpacing * 2),
y + style->PopupFrame.subTitleTopPadding,
this->subTitleRight.c_str(),
nullptr);
// Header
nvgBeginPath(vg);
nvgRect(vg, x + style->AppletFrame.separatorSpacing, y + style->AppletFrame.headerHeightPopup - 1, width - style->AppletFrame.separatorSpacing * 2, 1);
nvgFill(vg);
}
// Footer
NVGcolor footerColor = a(ctx->theme->textColor);
if (this->slideIn)
footerColor.a = 0.0f;
else if (this->slideOut)
footerColor.a = 1.0f;
nvgFillColor(vg, footerColor);
std::string* text = &this->footerText;
if (*text == "")
text = Application::getCommonFooter();
nvgFontSize(vg, style->AppletFrame.footerTextSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgBeginPath(vg);
nvgText(vg, x + style->AppletFrame.separatorSpacing + style->AppletFrame.footerTextSpacing, y + height - style->AppletFrame.footerHeight / 2, text->c_str(), nullptr);
// Hint
this->hint->frame(ctx);
// Icon
if (this->icon)
this->icon->frame(ctx);
// Separators
nvgFillColor(vg, a(ctx->theme->separatorColor));
// Footer
nvgBeginPath(vg);
nvgRect(vg, x + style->AppletFrame.separatorSpacing, y + height - style->AppletFrame.footerHeight, width - style->AppletFrame.separatorSpacing * 2, 1);
nvgFill(vg);
// Content view
if (contentView)
{
float slideAlpha = 1.0f - this->contentView->alpha;
if ((this->slideIn && this->animation == ViewAnimation::SLIDE_LEFT) || (this->slideOut && this->animation == ViewAnimation::SLIDE_RIGHT))
slideAlpha = 1.0f - slideAlpha;
int translation = (int)((float)style->AppletFrame.slideAnimation * slideAlpha);
if ((this->slideIn && this->animation == ViewAnimation::SLIDE_LEFT) || (this->slideOut && this->animation == ViewAnimation::SLIDE_RIGHT))
translation -= style->AppletFrame.slideAnimation;
if (this->slideOut || this->slideIn)
nvgTranslate(vg, -translation, 0);
contentView->frame(ctx);
if (this->slideOut || this->slideIn)
nvgTranslate(vg, translation, 0);
}
}
View* AppletFrame::getDefaultFocus()
{
if (this->contentView)
return this->contentView->getDefaultFocus();
return nullptr;
}
void AppletFrame::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
// Icon
if (this->icon)
{
if (this->headerStyle == HeaderStyle::REGULAR)
{
this->icon->setBoundaries(style->AppletFrame.imageLeftPadding, style->AppletFrame.imageTopPadding, style->AppletFrame.imageSize, style->AppletFrame.imageSize);
this->icon->invalidate();
}
else if (this->headerStyle == HeaderStyle::POPUP)
{
this->icon->setBoundaries(style->PopupFrame.edgePadding + style->PopupFrame.imageLeftPadding, style->PopupFrame.imageTopPadding, style->PopupFrame.imageSize, style->PopupFrame.imageSize);
this->icon->invalidate();
}
}
// Content
if (this->contentView)
{
if (this->headerStyle == HeaderStyle::REGULAR)
this->contentView->setBoundaries(this->x + leftPadding, this->y + style->AppletFrame.headerHeightRegular, this->width - this->leftPadding - this->rightPadding, this->height - style->AppletFrame.footerHeight - style->AppletFrame.headerHeightRegular);
else if (this->headerStyle == HeaderStyle::POPUP)
this->contentView->setBoundaries(this->x + leftPadding, this->y + style->AppletFrame.headerHeightPopup, this->width - this->leftPadding - this->rightPadding, this->height - style->AppletFrame.footerHeight - style->AppletFrame.headerHeightPopup);
this->contentView->invalidate();
}
// Hint
// TODO: convert the bottom-left footer into a Label to get its width and avoid clipping with the hint
unsigned hintWidth = this->width - style->AppletFrame.separatorSpacing * 2 - style->AppletFrame.footerTextSpacing * 2;
this->hint->setBoundaries(
this->x + this->width - hintWidth - style->AppletFrame.separatorSpacing - style->AppletFrame.footerTextSpacing,
this->y + this->height - style->AppletFrame.footerHeight,
hintWidth,
style->AppletFrame.footerHeight);
this->hint->invalidate();
}
void AppletFrame::setContentView(View* view)
{
this->contentView = view;
if (this->contentView)
{
this->contentView->setParent(this);
this->contentView->willAppear();
}
this->invalidate();
}
bool AppletFrame::hasContentView()
{
return this->contentView;
}
void AppletFrame::setTitle(std::string title)
{
this->title = title;
}
void AppletFrame::setFooterText(std::string footerText)
{
this->footerText = footerText;
}
void AppletFrame::setSubtitle(std::string left, std::string right)
{
this->subTitleLeft = left;
this->subTitleRight = right;
}
void AppletFrame::setIcon(unsigned char* buffer, size_t bufferSize)
{
if (!this->icon)
{
Image* icon = new Image(buffer, bufferSize);
icon->setScaleType(ImageScaleType::SCALE);
icon->setParent(this);
this->icon = icon;
}
else if (Image* icon = dynamic_cast<Image*>(this->icon))
{
icon->setImage(buffer, bufferSize);
}
this->icon->invalidate();
}
void AppletFrame::setIcon(std::string imagePath)
{
if (!this->icon)
{
Image* icon = new Image(imagePath);
icon->setScaleType(ImageScaleType::SCALE);
icon->setParent(this);
this->icon = icon;
}
else if (Image* icon = dynamic_cast<Image*>(this->icon))
{
icon->setImage(imagePath);
}
this->icon->invalidate();
}
void AppletFrame::setIcon(View* view)
{
if (this->icon)
delete this->icon;
if (view != nullptr)
view->setParent(this);
this->icon = view;
}
void AppletFrame::setHeaderStyle(HeaderStyle headerStyle)
{
this->headerStyle = headerStyle;
this->invalidate();
}
AppletFrame::~AppletFrame()
{
if (this->contentView)
{
this->contentView->willDisappear(true);
delete this->contentView;
}
if (this->icon)
delete this->icon;
delete this->hint;
}
void AppletFrame::willAppear(bool resetState)
{
if (this->icon)
this->icon->willAppear(resetState);
if (this->contentView)
this->contentView->willAppear(resetState);
this->hint->willAppear(resetState);
}
void AppletFrame::willDisappear(bool resetState)
{
if (this->icon)
this->icon->willDisappear(resetState);
if (this->contentView)
this->contentView->willDisappear(resetState);
this->hint->willDisappear(resetState);
}
void AppletFrame::show(std::function<void(void)> cb, bool animated, ViewAnimation animation)
{
this->animation = animation;
if (animated && (animation == ViewAnimation::SLIDE_LEFT || animation == ViewAnimation::SLIDE_RIGHT) && this->contentView)
{
this->slideIn = true;
this->contentView->show([this]() {
this->slideIn = false;
},
true, animation);
}
else if (this->contentView && this->contentView->isHidden())
{
this->contentView->show([]() {}, animated, animation);
}
View::show(cb, animated, animation);
}
void AppletFrame::hide(std::function<void(void)> cb, bool animated, ViewAnimation animation)
{
this->animation = animation;
if (animated && (animation == ViewAnimation::SLIDE_LEFT || animation == ViewAnimation::SLIDE_RIGHT) && this->contentView)
{
this->slideOut = true;
this->contentView->hide([this, cb]() {
this->slideOut = false;
},
true, animation);
}
else if (this->contentView && !this->contentView->isHidden())
{
this->contentView->hide([]() {}, animated, animation);
}
View::hide(cb, animated, animation);
}
bool AppletFrame::onCancel()
{
/*
* TODO: this assumes AppletFrames are used as "activities" in the app, which may be wrong
* so we should instead change the view stack to an "activity" stack and have them popped when
* the user presses B on the root view of an "activity"
*/
Application::popView();
return true;
}
void AppletFrame::onWindowSizeChanged()
{
if (this->contentView)
this->contentView->onWindowSizeChanged();
if (this->icon)
this->icon->onWindowSizeChanged();
if (this->hint)
this->hint->onWindowSizeChanged();
}
} // namespace brls

View File

@@ -0,0 +1,991 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
Copyright (C) 2020 WerWolv
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <algorithm>
#include <borealis.hpp>
#include <string>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <glad.h>
#define GLM_FORCE_PURE
#define GLM_ENABLE_EXPERIMENTAL
#include <nanovg.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#define NANOVG_GL3_IMPLEMENTATION
#include <nanovg_gl.h>
#ifdef __SWITCH__
#include <switch.h>
#endif
#include <chrono>
#include <set>
#include <thread>
// Constants used for scaling as well as
// creating a window of the right size on PC
constexpr uint32_t WINDOW_WIDTH = 1280;
constexpr uint32_t WINDOW_HEIGHT = 720;
#define DEFAULT_FPS 60
#define BUTTON_REPEAT_DELAY 15
#define BUTTON_REPEAT_CADENCY 5
// glfw code from the glfw hybrid app by fincs
// https://github.com/fincs/hybrid_app
namespace brls
{
// TODO: Use this instead of a glViewport each frame
static void windowFramebufferSizeCallback(GLFWwindow* window, int width, int height)
{
if (!width || !height)
return;
glViewport(0, 0, width, height);
Application::windowScale = (float)width / (float)WINDOW_WIDTH;
float contentHeight = ((float)height / (Application::windowScale * (float)WINDOW_HEIGHT)) * (float)WINDOW_HEIGHT;
Application::contentWidth = WINDOW_WIDTH;
Application::contentHeight = (unsigned)roundf(contentHeight);
Application::resizeNotificationManager();
Logger::info("Window size changed to %dx%d", width, height);
Logger::info("New scale factor is %f", Application::windowScale);
}
static void joystickCallback(int jid, int event)
{
if (event == GLFW_CONNECTED)
{
Logger::info("Joystick %d connected", jid);
if (glfwJoystickIsGamepad(jid))
Logger::info("Joystick %d is gamepad: \"%s\"", jid, glfwGetGamepadName(jid));
}
else if (event == GLFW_DISCONNECTED)
Logger::info("Joystick %d disconnected", jid);
}
static void errorCallback(int errorCode, const char* description)
{
Logger::error("[GLFW:%d] %s", errorCode, description);
}
static void windowKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (action == GLFW_PRESS)
{
// Check for toggle-fullscreen combo
if (key == GLFW_KEY_ENTER && mods == GLFW_MOD_ALT)
{
static int saved_x, saved_y, saved_width, saved_height;
if (!glfwGetWindowMonitor(window))
{
// Back up window position/size
glfwGetWindowPos(window, &saved_x, &saved_y);
glfwGetWindowSize(window, &saved_width, &saved_height);
// Switch to fullscreen mode
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
}
else
{
// Switch back to windowed mode
glfwSetWindowMonitor(window, nullptr, saved_x, saved_y, saved_width, saved_height, GLFW_DONT_CARE);
}
}
}
}
bool Application::init(std::string title)
{
return Application::init(title, Style::horizon(), Theme::horizon());
}
bool Application::init(std::string title, Style style, Theme theme)
{
// Init rng
std::srand(std::time(nullptr));
// Init managers
Application::taskManager = new TaskManager();
Application::notificationManager = new NotificationManager();
// Init static variables
Application::currentStyle = style;
Application::currentFocus = nullptr;
Application::oldGamepad = {};
Application::gamepad = {};
Application::title = title;
// Init theme to defaults
Application::setTheme(theme);
// Init glfw
glfwSetErrorCallback(errorCallback);
glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE);
if (!glfwInit())
{
Logger::error("Failed to initialize glfw");
return false;
}
// Create window
#ifdef __APPLE__
// Explicitly ask for a 3.2 context on OS X
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Force scaling off to keep desired framebuffer size
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
#else
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#endif
Application::window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, title.c_str(), nullptr, nullptr);
if (!window)
{
Logger::error("glfw: failed to create window\n");
glfwTerminate();
return false;
}
// Configure window
glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE);
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, windowFramebufferSizeCallback);
glfwSetKeyCallback(window, windowKeyCallback);
glfwSetJoystickCallback(joystickCallback);
// Load OpenGL routines using glad
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
glfwSwapInterval(1);
Logger::info("GL Vendor: %s", glGetString(GL_VENDOR));
Logger::info("GL Renderer: %s", glGetString(GL_RENDERER));
Logger::info("GL Version: %s", glGetString(GL_VERSION));
if (glfwJoystickIsGamepad(GLFW_JOYSTICK_1))
{
GLFWgamepadstate state;
Logger::info("Gamepad detected: %s", glfwGetGamepadName(GLFW_JOYSTICK_1));
glfwGetGamepadState(GLFW_JOYSTICK_1, &state);
}
// Initialize the scene
Application::vg = nvgCreateGL3(NVG_STENCIL_STROKES | NVG_ANTIALIAS);
if (!vg)
{
Logger::error("Unable to init nanovg");
glfwTerminate();
return false;
}
windowFramebufferSizeCallback(window, WINDOW_WIDTH, WINDOW_HEIGHT);
glfwSetTime(0.0);
// Load fonts
#ifdef __SWITCH__
{
PlFontData font;
// Standard font
Result rc = plGetSharedFontByType(&font, PlSharedFontType_Standard);
if (R_SUCCEEDED(rc))
{
Logger::info("Using Switch shared font");
Application::fontStash.regular = Application::loadFontFromMemory("regular", font.address, font.size, false);
}
// Korean font
rc = plGetSharedFontByType(&font, PlSharedFontType_KO);
if (R_SUCCEEDED(rc))
{
Logger::info("Adding Switch shared Korean font");
Application::fontStash.korean = Application::loadFontFromMemory("korean", font.address, font.size, false);
nvgAddFallbackFontId(Application::vg, Application::fontStash.regular, Application::fontStash.korean);
}
// Extented font
rc = plGetSharedFontByType(&font, PlSharedFontType_NintendoExt);
if (R_SUCCEEDED(rc))
{
Logger::info("Using Switch shared symbols font");
Application::fontStash.sharedSymbols = Application::loadFontFromMemory("symbols", font.address, font.size, false);
}
}
#else
// Use illegal font if available
if (access(BOREALIS_ASSET("Illegal-Font.ttf"), F_OK) != -1)
Application::fontStash.regular = Application::loadFont("regular", BOREALIS_ASSET("Illegal-Font.ttf"));
else
Application::fontStash.regular = Application::loadFont("regular", BOREALIS_ASSET("inter/Inter-Switch.ttf"));
if (access(BOREALIS_ASSET("Wingdings.ttf"), F_OK) != -1)
Application::fontStash.sharedSymbols = Application::loadFont("sharedSymbols", BOREALIS_ASSET("Wingdings.ttf"));
#endif
// Material font
if (access(BOREALIS_ASSET("material/MaterialIcons-Regular.ttf"), F_OK) != -1)
Application::fontStash.material = Application::loadFont("material", BOREALIS_ASSET("material/MaterialIcons-Regular.ttf"));
// Set symbols font as fallback
if (Application::fontStash.sharedSymbols)
{
Logger::info("Using shared symbols font");
nvgAddFallbackFontId(Application::vg, Application::fontStash.regular, Application::fontStash.sharedSymbols);
}
else
{
Logger::error("Shared symbols font not found");
}
// Set Material as fallback
if (Application::fontStash.material)
{
Logger::info("Using Material font");
nvgAddFallbackFontId(Application::vg, Application::fontStash.regular, Application::fontStash.material);
}
else
{
Logger::error("Material font not found");
}
// Load theme
#ifdef __SWITCH__
ColorSetId nxTheme;
setsysGetColorSetId(&nxTheme);
if (nxTheme == ColorSetId_Dark)
Application::currentThemeVariant = ThemeVariant_DARK;
else
Application::currentThemeVariant = ThemeVariant_LIGHT;
#else
char* themeEnv = getenv("BOREALIS_THEME");
if (themeEnv != nullptr && !strcasecmp(themeEnv, "DARK"))
Application::currentThemeVariant = ThemeVariant_DARK;
else
Application::currentThemeVariant = ThemeVariant_LIGHT;
#endif
// Init window size
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
Application::windowWidth = viewport[2];
Application::windowHeight = viewport[3];
// Init animations engine
menu_animation_init();
// Default FPS cap
Application::setMaximumFPS(DEFAULT_FPS);
return true;
}
bool Application::mainLoop()
{
// Frame start
retro_time_t frameStart = 0;
if (Application::frameTime > 0.0f)
frameStart = cpu_features_get_time_usec();
// glfw events
bool is_active;
do
{
is_active = !glfwGetWindowAttrib(Application::window, GLFW_ICONIFIED);
if (is_active)
glfwPollEvents();
else
glfwWaitEvents();
if (glfwWindowShouldClose(Application::window))
{
Application::exit();
return false;
}
} while (!is_active);
// libnx applet main loop
#ifdef __SWITCH__
if (!appletMainLoop())
{
Application::exit();
return false;
}
#endif
// Gamepad
if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &Application::gamepad))
{
// Keyboard -> DPAD Mapping
Application::gamepad.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT] = glfwGetKey(window, GLFW_KEY_LEFT);
Application::gamepad.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] = glfwGetKey(window, GLFW_KEY_RIGHT);
Application::gamepad.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP] = glfwGetKey(window, GLFW_KEY_UP);
Application::gamepad.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN] = glfwGetKey(window, GLFW_KEY_DOWN);
Application::gamepad.buttons[GLFW_GAMEPAD_BUTTON_START] = glfwGetKey(window, GLFW_KEY_ESCAPE);
Application::gamepad.buttons[GLFW_GAMEPAD_BUTTON_BACK] = glfwGetKey(window, GLFW_KEY_F1);
Application::gamepad.buttons[GLFW_GAMEPAD_BUTTON_A] = glfwGetKey(window, GLFW_KEY_ENTER);
Application::gamepad.buttons[GLFW_GAMEPAD_BUTTON_B] = glfwGetKey(window, GLFW_KEY_BACKSPACE);
Application::gamepad.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] = glfwGetKey(window, GLFW_KEY_L);
Application::gamepad.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] = glfwGetKey(window, GLFW_KEY_R);
}
// Trigger gamepad events
// TODO: Translate axis events to dpad events here
bool anyButtonPressed = false;
bool repeating = false;
static retro_time_t buttonPressTime = 0;
static int repeatingButtonTimer = 0;
for (int i = GLFW_GAMEPAD_BUTTON_A; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
{
if (Application::gamepad.buttons[i] == GLFW_PRESS)
{
anyButtonPressed = true;
repeating = (repeatingButtonTimer > BUTTON_REPEAT_DELAY && repeatingButtonTimer % BUTTON_REPEAT_CADENCY == 0);
if (Application::oldGamepad.buttons[i] != GLFW_PRESS || repeating)
Application::onGamepadButtonPressed(i, repeating);
}
if (Application::gamepad.buttons[i] != Application::oldGamepad.buttons[i])
buttonPressTime = repeatingButtonTimer = 0;
}
if (anyButtonPressed && cpu_features_get_time_usec() - buttonPressTime > 1000)
{
buttonPressTime = cpu_features_get_time_usec();
repeatingButtonTimer++; // Increased once every ~1ms
}
Application::oldGamepad = Application::gamepad;
// Handle window size changes
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
unsigned newWidth = viewport[2];
unsigned newHeight = viewport[3];
if (Application::windowWidth != newWidth || Application::windowHeight != newHeight)
{
Application::windowWidth = newWidth;
Application::windowHeight = newHeight;
Application::onWindowSizeChanged();
}
// Animations
menu_animation_update();
// Tasks
Application::taskManager->frame();
// Render
Application::frame();
glfwSwapBuffers(window);
// Sleep if necessary
if (Application::frameTime > 0.0f)
{
retro_time_t currentFrameTime = cpu_features_get_time_usec() - frameStart;
retro_time_t frameTime = (retro_time_t)(Application::frameTime * 1000);
if (frameTime > currentFrameTime)
{
retro_time_t toSleep = frameTime - currentFrameTime;
std::this_thread::sleep_for(std::chrono::microseconds(toSleep));
}
}
return true;
}
void Application::quit()
{
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
void Application::navigate(FocusDirection direction)
{
View* currentFocus = Application::currentFocus;
// Do nothing if there is no current focus or if it doesn't have a parent
// (in which case there is nothing to traverse)
if (!currentFocus || !currentFocus->hasParent())
return;
// Get next view to focus by traversing the views tree upwards
View* nextFocus = currentFocus->getParent()->getNextFocus(direction, currentFocus->getParentUserData());
while (!nextFocus) // stop when we find a view to focus
{
if (!currentFocus->hasParent() || !currentFocus->getParent()->hasParent()) // stop when we reach the root of the tree
break;
currentFocus = currentFocus->getParent();
nextFocus = currentFocus->getParent()->getNextFocus(direction, currentFocus->getParentUserData());
}
// No view to focus at the end of the traversal: wiggle and return
if (!nextFocus)
{
Application::currentFocus->shakeHighlight(direction);
return;
}
// Otherwise give it focus
Application::giveFocus(nextFocus);
}
void Application::onGamepadButtonPressed(char button, bool repeating)
{
if (Application::blockInputsTokens != 0)
return;
if (repeating && Application::repetitionOldFocus == Application::currentFocus)
return;
Application::repetitionOldFocus = Application::currentFocus;
// Actions
if (Application::handleAction(button))
return;
// Navigation
// Only navigate if the button hasn't been consumed by an action
// (allows overriding DPAD buttons using actions)
switch (button)
{
case GLFW_GAMEPAD_BUTTON_DPAD_DOWN:
Application::navigate(FocusDirection::DOWN);
break;
case GLFW_GAMEPAD_BUTTON_DPAD_UP:
Application::navigate(FocusDirection::UP);
break;
case GLFW_GAMEPAD_BUTTON_DPAD_LEFT:
Application::navigate(FocusDirection::LEFT);
break;
case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT:
Application::navigate(FocusDirection::RIGHT);
break;
default:
break;
}
}
View* Application::getCurrentFocus()
{
return Application::currentFocus;
}
bool Application::handleAction(char button)
{
View* hintParent = Application::currentFocus;
std::set<Key> consumedKeys;
while (hintParent != nullptr)
{
for (auto& action : hintParent->getActions())
{
if (action.key != static_cast<Key>(button))
continue;
if (consumedKeys.find(action.key) != consumedKeys.end())
continue;
if (action.available)
if (action.actionListener())
consumedKeys.insert(action.key);
}
hintParent = hintParent->getParent();
}
return !consumedKeys.empty();
}
void Application::frame()
{
// Frame context
FrameContext frameContext = FrameContext();
frameContext.pixelRatio = (float)Application::windowWidth / (float)Application::windowHeight;
frameContext.vg = Application::vg;
frameContext.fontStash = &Application::fontStash;
frameContext.theme = Application::getThemeValues();
nvgBeginFrame(Application::vg, Application::windowWidth, Application::windowHeight, frameContext.pixelRatio);
nvgScale(Application::vg, Application::windowScale, Application::windowScale);
// GL Clear
glClearColor(
frameContext.theme->backgroundColor[0],
frameContext.theme->backgroundColor[1],
frameContext.theme->backgroundColor[2],
1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
std::vector<View*> viewsToDraw;
// Draw all views in the stack
// until we find one that's not translucent
for (size_t i = 0; i < Application::viewStack.size(); i++)
{
View* view = Application::viewStack[Application::viewStack.size() - 1 - i];
viewsToDraw.push_back(view);
if (!view->isTranslucent())
break;
}
for (size_t i = 0; i < viewsToDraw.size(); i++)
{
View* view = viewsToDraw[viewsToDraw.size() - 1 - i];
view->frame(&frameContext);
}
// Framerate counter
if (Application::framerateCounter)
Application::framerateCounter->frame(&frameContext);
// Notifications
Application::notificationManager->frame(&frameContext);
// End frame
nvgResetTransform(Application::vg); // scale
nvgEndFrame(Application::vg);
}
void Application::exit()
{
Application::clear();
if (Application::vg)
nvgDeleteGL3(Application::vg);
glfwTerminate();
menu_animation_free();
if (Application::framerateCounter)
delete Application::framerateCounter;
delete Application::taskManager;
delete Application::notificationManager;
}
void Application::setDisplayFramerate(bool enabled)
{
if (!Application::framerateCounter && enabled)
{
Logger::info("Enabling framerate counter");
Application::framerateCounter = new FramerateCounter();
Application::resizeFramerateCounter();
}
else if (Application::framerateCounter && !enabled)
{
Logger::info("Disabling framerate counter");
delete Application::framerateCounter;
Application::framerateCounter = nullptr;
}
}
void Application::toggleFramerateDisplay()
{
Application::setDisplayFramerate(!Application::framerateCounter);
}
void Application::resizeFramerateCounter()
{
if (!Application::framerateCounter)
return;
Style* style = Application::getStyle();
unsigned framerateCounterWidth = style->FramerateCounter.width;
unsigned width = WINDOW_WIDTH;
Application::framerateCounter->setBoundaries(
width - framerateCounterWidth,
0,
framerateCounterWidth,
style->FramerateCounter.height);
Application::framerateCounter->invalidate();
}
void Application::resizeNotificationManager()
{
Application::notificationManager->setBoundaries(0, 0, Application::contentWidth, Application::contentHeight);
Application::notificationManager->invalidate();
}
void Application::notify(std::string text)
{
Application::notificationManager->notify(text);
}
NotificationManager* Application::getNotificationManager()
{
return Application::notificationManager;
}
void Application::giveFocus(View* view)
{
View* oldFocus = Application::currentFocus;
View* newFocus = view ? view->getDefaultFocus() : nullptr;
if (oldFocus != newFocus)
{
if (oldFocus)
oldFocus->onFocusLost();
Application::currentFocus = newFocus;
Application::globalFocusChangeEvent.fire(newFocus);
if (newFocus)
{
newFocus->onFocusGained();
Logger::debug("Giving focus to %s", newFocus->describe().c_str());
}
}
}
void Application::popView(ViewAnimation animation, std::function<void(void)> cb)
{
if (Application::viewStack.size() <= 1) // never pop the root view
return;
Application::blockInputs();
View* last = Application::viewStack[Application::viewStack.size() - 1];
last->willDisappear(true);
last->setForceTranslucent(true);
bool wait = animation == ViewAnimation::FADE; // wait for the new view animation to be done before showing the old one?
// Hide animation (and show previous view, if any)
last->hide([last, animation, wait, cb]() {
last->setForceTranslucent(false);
Application::viewStack.pop_back();
delete last;
// Animate the old view once the new one
// has ended its animation
if (Application::viewStack.size() > 0 && wait)
{
View* newLast = Application::viewStack[Application::viewStack.size() - 1];
if (newLast->isHidden())
{
newLast->willAppear(false);
newLast->show(cb, true, animation);
}
else
{
cb();
}
}
Application::unblockInputs();
},
true, animation);
// Animate the old view immediately
if (!wait && Application::viewStack.size() > 1)
{
View* toShow = Application::viewStack[Application::viewStack.size() - 2];
toShow->willAppear(false);
toShow->show(cb, true, animation);
}
// Focus
if (Application::focusStack.size() > 0)
{
View* newFocus = Application::focusStack[Application::focusStack.size() - 1];
Logger::debug("Giving focus to %s, and removing it from the focus stack", newFocus->describe().c_str());
Application::giveFocus(newFocus);
Application::focusStack.pop_back();
}
}
void Application::pushView(View* view, ViewAnimation animation)
{
Application::blockInputs();
// Call hide() on the previous view in the stack if no
// views are translucent, then call show() once the animation ends
View* last = nullptr;
if (Application::viewStack.size() > 0)
last = Application::viewStack[Application::viewStack.size() - 1];
bool fadeOut = last && !last->isTranslucent() && !view->isTranslucent(); // play the fade out animation?
bool wait = animation == ViewAnimation::FADE; // wait for the old view animation to be done before showing the new one?
view->registerAction("Exit", Key::PLUS, [] { Application::quit(); return true; });
view->registerAction(
"FPS", Key::MINUS, [] { Application::toggleFramerateDisplay(); return true; }, true);
// Fade out animation
if (fadeOut)
{
view->setForceTranslucent(true); // set the new view translucent until the fade out animation is done playing
// Animate the new view directly
if (!wait)
{
view->show([]() {
Application::unblockInputs();
},
true, animation);
}
last->hide([animation, wait]() {
View* newLast = Application::viewStack[Application::viewStack.size() - 1];
newLast->setForceTranslucent(false);
// Animate the new view once the old one
// has ended its animation
if (wait)
newLast->show([]() { Application::unblockInputs(); }, true, animation);
},
true, animation);
}
view->setBoundaries(0, 0, Application::contentWidth, Application::contentHeight);
if (!fadeOut)
view->show([]() { Application::unblockInputs(); }, true, animation);
else
view->alpha = 0.0f;
// Focus
if (Application::viewStack.size() > 0)
{
Logger::debug("Pushing %s to the focus stack", Application::currentFocus->describe().c_str());
Application::focusStack.push_back(Application::currentFocus);
}
// Layout and prepare view
view->invalidate(true);
view->willAppear(true);
Application::giveFocus(view->getDefaultFocus());
// And push it
Application::viewStack.push_back(view);
}
void Application::onWindowSizeChanged()
{
Logger::debug("Layout triggered");
for (View* view : Application::viewStack)
{
view->setBoundaries(0, 0, Application::contentWidth, Application::contentHeight);
view->invalidate();
view->onWindowSizeChanged();
}
Application::resizeNotificationManager();
Application::resizeFramerateCounter();
}
void Application::clear()
{
for (View* view : Application::viewStack)
{
view->willDisappear(true);
delete view;
}
Application::viewStack.clear();
}
Style* Application::getStyle()
{
return &Application::currentStyle;
}
void Application::setTheme(Theme theme)
{
Application::currentTheme = theme;
}
ThemeValues* Application::getThemeValues()
{
return &Application::currentTheme.colors[Application::currentThemeVariant];
}
ThemeValues* Application::getThemeValuesForVariant(ThemeVariant variant)
{
return &Application::currentTheme.colors[variant];
}
ThemeVariant Application::getThemeVariant()
{
return Application::currentThemeVariant;
}
int Application::loadFont(const char* fontName, const char* filePath)
{
return nvgCreateFont(Application::vg, fontName, filePath);
}
int Application::loadFontFromMemory(const char* fontName, void* address, size_t size, bool freeData)
{
return nvgCreateFontMem(Application::vg, fontName, (unsigned char*)address, size, freeData);
}
int Application::findFont(const char* fontName)
{
return nvgFindFont(Application::vg, fontName);
}
void Application::crash(std::string text)
{
CrashFrame* crashFrame = new CrashFrame(text);
Application::pushView(crashFrame);
}
void Application::blockInputs()
{
Application::blockInputsTokens += 1;
}
void Application::unblockInputs()
{
if (Application::blockInputsTokens > 0)
Application::blockInputsTokens -= 1;
}
NVGcontext* Application::getNVGContext()
{
return Application::vg;
}
TaskManager* Application::getTaskManager()
{
return Application::taskManager;
}
void Application::setCommonFooter(std::string footer)
{
Application::commonFooter = footer;
}
std::string* Application::getCommonFooter()
{
return &Application::commonFooter;
}
FramerateCounter::FramerateCounter()
: Label(LabelStyle::LIST_ITEM, "FPS: ---")
{
this->setColor(nvgRGB(255, 255, 255));
this->setVerticalAlign(NVG_ALIGN_MIDDLE);
this->setHorizontalAlign(NVG_ALIGN_RIGHT);
this->setBackground(Background::BACKDROP);
this->lastSecond = cpu_features_get_time_usec() / 1000;
}
void FramerateCounter::frame(FrameContext* ctx)
{
// Update counter
retro_time_t current = cpu_features_get_time_usec() / 1000;
if (current - this->lastSecond >= 1000)
{
char fps[10];
snprintf(fps, sizeof(fps), "FPS: %03d", this->frames);
this->setText(std::string(fps));
this->invalidate(); // update width for background
this->frames = 0;
this->lastSecond = current;
}
this->frames++;
// Regular frame
Label::frame(ctx);
}
void Application::setMaximumFPS(unsigned fps)
{
if (fps == 0)
Application::frameTime = 0.0f;
else
{
Application::frameTime = 1000 / (float)fps;
}
Logger::info("Maximum FPS set to %d - using a frame time of %.2f ms", fps, Application::frameTime);
}
std::string Application::getTitle()
{
return Application::title;
}
GenericEvent* Application::getGlobalFocusChangeEvent()
{
return &Application::globalFocusChangeEvent;
}
VoidEvent* Application::getGlobalHintsUpdateEvent()
{
return &Application::globalHintsUpdateEvent;
}
FontStash* Application::getFontStash()
{
return &Application::fontStash;
}
} // namespace brls

View File

@@ -0,0 +1,376 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 WerWolv
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <stdio.h>
#include <borealis/animations.hpp>
#include <borealis/application.hpp>
#include <borealis/box_layout.hpp>
#include <borealis/logger.hpp>
#include <iterator>
namespace brls
{
BoxLayout::BoxLayout(BoxLayoutOrientation orientation, size_t defaultFocus)
: orientation(orientation)
, originalDefaultFocus(defaultFocus)
, defaultFocusedIndex(defaultFocus)
{
}
void BoxLayout::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
// Draw children
for (BoxLayoutChild* child : this->children)
child->view->frame(ctx);
}
void BoxLayout::setGravity(BoxLayoutGravity gravity)
{
this->gravity = gravity;
this->invalidate();
}
void BoxLayout::setSpacing(unsigned spacing)
{
this->spacing = spacing;
this->invalidate();
}
unsigned BoxLayout::getSpacing()
{
return this->spacing;
}
void BoxLayout::setMargins(unsigned top, unsigned right, unsigned bottom, unsigned left)
{
this->marginBottom = bottom;
this->marginLeft = left;
this->marginRight = right;
this->marginTop = top;
this->invalidate();
}
void BoxLayout::setMarginBottom(unsigned bottom)
{
this->marginBottom = bottom;
this->invalidate();
}
size_t BoxLayout::getViewsCount()
{
return this->children.size();
}
View* BoxLayout::getDefaultFocus()
{
// Focus default focus first
if (this->defaultFocusedIndex < this->children.size())
{
View* newFocus = this->children[this->defaultFocusedIndex]->view->getDefaultFocus();
if (newFocus)
return newFocus;
}
// Fallback to finding the first focusable view
for (size_t i = 0; i < this->children.size(); i++)
{
View* newFocus = this->children[i]->view->getDefaultFocus();
if (newFocus)
return newFocus;
}
return nullptr;
}
View* BoxLayout::getNextFocus(FocusDirection direction, void* parentUserData)
{
// Return nullptr immediately if focus direction mismatches the layout direction
if ((this->orientation == BoxLayoutOrientation::HORIZONTAL && direction != FocusDirection::LEFT && direction != FocusDirection::RIGHT) || (this->orientation == BoxLayoutOrientation::VERTICAL && direction != FocusDirection::UP && direction != FocusDirection::DOWN))
{
return nullptr;
}
// Traverse the children
size_t offset = 1; // which way are we going in the children list
if ((this->orientation == BoxLayoutOrientation::HORIZONTAL && direction == FocusDirection::LEFT) || (this->orientation == BoxLayoutOrientation::VERTICAL && direction == FocusDirection::UP))
{
offset = -1;
}
size_t currentFocusIndex = *((size_t*)parentUserData) + offset;
View* currentFocus = nullptr;
while (!currentFocus && currentFocusIndex >= 0 && currentFocusIndex < this->children.size())
{
currentFocus = this->children[currentFocusIndex]->view->getDefaultFocus();
currentFocusIndex += offset;
}
return currentFocus;
}
void BoxLayout::removeView(int index, bool free)
{
BoxLayoutChild* toRemove = this->children[index];
toRemove->view->willDisappear(true);
if (free)
delete toRemove->view;
delete toRemove;
this->children.erase(this->children.begin() + index);
}
void BoxLayout::clear(bool free)
{
while (!this->children.empty())
this->removeView(0, free);
}
void BoxLayout::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
// Vertical orientation
if (this->orientation == BoxLayoutOrientation::VERTICAL)
{
unsigned entriesHeight = 0;
int yAdvance = this->y + this->marginTop;
for (size_t i = 0; i < this->children.size(); i++)
{
BoxLayoutChild* child = this->children[i];
unsigned childHeight = child->view->getHeight();
if (child->fill)
child->view->setBoundaries(this->x + this->marginLeft,
yAdvance,
this->width - this->marginLeft - this->marginRight,
this->y + this->height - yAdvance - this->marginBottom);
else
child->view->setBoundaries(this->x + this->marginLeft,
yAdvance,
this->width - this->marginLeft - this->marginRight,
child->view->getHeight(false));
child->view->invalidate(true); // call layout directly in case height is updated
childHeight = child->view->getHeight();
int spacing = (int)this->spacing;
View* next = (this->children.size() > 1 && i <= this->children.size() - 2) ? this->children[i + 1]->view : nullptr;
this->customSpacing(child->view, next, &spacing);
if (child->view->isCollapsed())
spacing = 0;
if (!child->view->isHidden())
entriesHeight += spacing + childHeight;
yAdvance += spacing + childHeight;
}
// TODO: apply gravity
// Update height if needed
if (this->resize)
this->setHeight(entriesHeight - spacing + this->marginTop + this->marginBottom);
}
// Horizontal orientation
else if (this->orientation == BoxLayoutOrientation::HORIZONTAL)
{
// Layout
int xAdvance = this->x + this->marginLeft;
for (size_t i = 0; i < this->children.size(); i++)
{
BoxLayoutChild* child = this->children[i];
unsigned childWidth = child->view->getWidth();
if (child->fill)
child->view->setBoundaries(xAdvance,
this->y + this->marginTop,
this->x + this->width - xAdvance - this->marginRight,
this->height - this->marginTop - this->marginBottom);
else
child->view->setBoundaries(xAdvance,
this->y + this->marginTop,
childWidth,
this->height - this->marginTop - this->marginBottom);
child->view->invalidate(true); // call layout directly in case width is updated
childWidth = child->view->getWidth();
int spacing = (int)this->spacing;
View* next = (this->children.size() > 1 && i <= this->children.size() - 2) ? this->children[i + 1]->view : nullptr;
this->customSpacing(child->view, next, &spacing);
if (child->view->isCollapsed())
spacing = 0;
xAdvance += spacing + childWidth;
}
// Apply gravity
// TODO: more efficient gravity implementation?
if (!this->children.empty())
{
switch (this->gravity)
{
case BoxLayoutGravity::RIGHT:
{
// Take the remaining empty space between the last view's
// right boundary and ours and push all views by this amount
View* lastView = this->children[this->children.size() - 1]->view;
unsigned lastViewRight = lastView->getX() + lastView->getWidth();
unsigned ourRight = this->getX() + this->getWidth();
if (lastViewRight <= ourRight)
{
unsigned difference = ourRight - lastViewRight;
for (BoxLayoutChild* child : this->children)
{
View* view = child->view;
view->setBoundaries(
view->getX() + difference,
view->getY(),
view->getWidth(),
view->getHeight());
view->invalidate();
}
}
break;
}
default:
break;
}
}
// TODO: update width if needed (introduce entriesWidth)
}
}
void BoxLayout::setResize(bool resize)
{
this->resize = resize;
this->invalidate();
}
void BoxLayout::addView(View* view, bool fill, bool resetState)
{
BoxLayoutChild* child = new BoxLayoutChild();
child->view = view;
child->fill = fill;
this->children.push_back(child);
size_t position = this->children.size() - 1;
size_t* userdata = (size_t*)malloc(sizeof(size_t));
*userdata = position;
view->setParent(this, userdata);
view->willAppear(resetState);
this->invalidate();
}
View* BoxLayout::getChild(size_t index)
{
return this->children[index]->view;
}
bool BoxLayout::isEmpty()
{
return this->children.size() == 0;
}
bool BoxLayout::isChildFocused()
{
return this->childFocused;
}
void BoxLayout::onChildFocusGained(View* child)
{
this->childFocused = true;
// Remember focus if needed
if (this->rememberFocus)
{
size_t index = *((size_t*)child->getParentUserData());
this->defaultFocusedIndex = index;
}
View::onChildFocusGained(child);
}
void BoxLayout::onChildFocusLost(View* child)
{
this->childFocused = false;
View::onChildFocusLost(child);
}
BoxLayout::~BoxLayout()
{
for (BoxLayoutChild* child : this->children)
{
child->view->willDisappear(true);
delete child->view;
delete child;
}
this->children.clear();
}
void BoxLayout::willAppear(bool resetState)
{
for (BoxLayoutChild* child : this->children)
child->view->willAppear(resetState);
}
void BoxLayout::willDisappear(bool resetState)
{
for (BoxLayoutChild* child : this->children)
child->view->willDisappear(resetState);
// Reset default focus to original one if needed
if (this->rememberFocus)
this->defaultFocusedIndex = this->originalDefaultFocus;
}
void BoxLayout::onWindowSizeChanged()
{
for (BoxLayoutChild* child : this->children)
child->view->onWindowSizeChanged();
}
void BoxLayout::setRememberFocus(bool remember)
{
this->rememberFocus = remember;
}
} // namespace brls

View File

@@ -0,0 +1,213 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <borealis/application.hpp>
#include <borealis/button.hpp>
namespace brls
{
Button::Button(ButtonStyle style)
: style(style)
{
this->registerAction("OK", Key::A, [this] { return this->onClick(); });
}
LabelStyle Button::getLabelStyle()
{
if (this->style == ButtonStyle::BORDERLESS)
return LabelStyle::BUTTON_BORDERLESS;
else if (this->style == ButtonStyle::DIALOG)
return LabelStyle::BUTTON_DIALOG;
else if (this->style == ButtonStyle::CRASH)
return LabelStyle::CRASH;
if (this->state == ButtonState::DISABLED)
return LabelStyle::BUTTON_PLAIN_DISABLED;
else
return LabelStyle::BUTTON_PLAIN;
}
Button::~Button()
{
if (this->label != nullptr)
delete this->label;
if (this->image != nullptr)
delete this->image;
}
void Button::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
unsigned imageWidth = this->label ? this->getHeight() : this->getWidth();
unsigned imageHeight = this->getHeight();
if (!this->image)
imageWidth = 0;
if (this->label != nullptr)
{
this->label->setWidth(this->getWidth() - imageWidth);
this->label->invalidate(true);
this->label->setBoundaries(
this->x + imageWidth,
this->y + this->getHeight() / 2 - this->label->getHeight() / 2,
this->label->getWidth(),
this->label->getHeight());
this->label->invalidate();
}
if (this->image != nullptr)
{
this->image->setHeight(imageHeight);
this->image->setWidth(imageWidth);
this->image->invalidate(true);
this->image->setBoundaries(
this->x,
this->y + this->getHeight() / 2 - this->image->getHeight() / 2,
this->image->getWidth(),
this->image->getHeight());
}
}
Button* Button::setLabel(std::string label)
{
if (this->label != nullptr)
delete this->label;
this->label = new Label(this->getLabelStyle(), label, true);
this->label->setHorizontalAlign(NVG_ALIGN_CENTER);
this->label->setParent(this);
return this;
}
Button* Button::setImage(std::string path)
{
this->image = new Image(path);
this->image->setParent(this);
return this;
}
Button* Button::setImage(unsigned char* buffer, size_t bufferSize)
{
this->image = new Image(buffer, bufferSize);
this->image->setParent(this);
return this;
}
void Button::setState(ButtonState state)
{
this->state = state;
if (this->label != nullptr)
this->label->setStyle(this->getLabelStyle());
}
ButtonState Button::getState()
{
return this->state;
}
void Button::getHighlightInsets(unsigned* top, unsigned* right, unsigned* bottom, unsigned* left)
{
if (this->style == ButtonStyle::DIALOG)
{
View::getHighlightInsets(top, right, bottom, left);
*right -= 1;
return;
}
Style* style = Application::getStyle();
*top = style->Button.highlightInset;
*right = style->Button.highlightInset;
*bottom = style->Button.highlightInset;
*left = style->Button.highlightInset;
}
void Button::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
float cornerRadius = this->cornerRadiusOverride ? this->cornerRadiusOverride : (float)style->Button.cornerRadius;
// Background
switch (this->style)
{
case ButtonStyle::PLAIN:
{
nvgFillColor(vg, a(this->state == ButtonState::DISABLED ? ctx->theme->buttonPlainDisabledBackgroundColor : ctx->theme->buttonPlainEnabledBackgroundColor));
nvgBeginPath(vg);
nvgRoundedRect(vg, x, y, width, height, cornerRadius);
nvgFill(vg);
break;
}
default:
break;
}
// Shadow
if (this->state == ButtonState::ENABLED && this->style == ButtonStyle::PLAIN)
{
float shadowWidth = style->Button.shadowWidth;
float shadowFeather = style->Button.shadowFeather;
float shadowOpacity = style->Button.shadowOpacity;
float shadowOffset = style->Button.shadowOffset;
NVGpaint shadowPaint = nvgBoxGradient(vg,
x, y + shadowWidth,
width, height,
cornerRadius * 2, shadowFeather,
RGBA(0, 0, 0, shadowOpacity * alpha), transparent);
nvgBeginPath(vg);
nvgRect(vg, x - shadowOffset, y - shadowOffset,
width + shadowOffset * 2, height + shadowOffset * 3);
nvgRoundedRect(vg, x, y, width, height, cornerRadius);
nvgPathWinding(vg, NVG_HOLE);
nvgFillPaint(vg, shadowPaint);
nvgFill(vg);
}
// Label
if (this->label != nullptr)
this->label->frame(ctx);
if (this->image != nullptr)
this->image->frame(ctx);
}
bool Button::onClick()
{
if (this->state == ButtonState::DISABLED)
return false;
return this->clickEvent.fire(this);
}
GenericEvent* Button::getClickEvent()
{
return &this->clickEvent;
}
void Button::setCornerRadius(float cornerRadius)
{
this->cornerRadiusOverride = cornerRadius;
if (this->image != nullptr)
this->image->setCornerRadius(cornerRadius);
}
} // namespace brls

View File

@@ -0,0 +1,151 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <borealis/animations.hpp>
#include <borealis/application.hpp>
#include <borealis/crash_frame.hpp>
namespace brls
{
CrashFrame::CrashFrame(std::string text)
{
// Label
this->label = new Label(LabelStyle::CRASH, text, true);
this->label->setHorizontalAlign(NVG_ALIGN_CENTER);
this->label->setParent(this);
// Button
this->button = (new Button(ButtonStyle::CRASH))->setLabel("OK");
this->button->setParent(this);
this->button->alpha = 0.0f;
this->button->getClickEvent()->subscribe([](View* view) { Application::quit(); });
this->button->overrideThemeVariant(Application::getThemeValuesForVariant(ThemeVariant_DARK));
// Hint
this->hint = new Hint();
this->hint->setParent(this);
}
void CrashFrame::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
nvgSave(vg);
// Background
nvgFillColor(vg, RGB(0, 0, 0));
nvgBeginPath(vg);
nvgRect(vg, x, y, width, height);
nvgFill(vg);
// Scale
float scale = (this->alpha + 2.0f) / 3.0f;
nvgTranslate(vg, (1.0f - scale) * width * 0.5f, (1.0f - scale) * height * 0.5f);
nvgScale(vg, scale, scale);
// Label
this->label->frame(ctx);
// [!] box
unsigned boxSize = style->CrashFrame.boxSize;
nvgStrokeColor(vg, RGB(255, 255, 255));
nvgStrokeWidth(vg, style->CrashFrame.boxStrokeWidth);
nvgBeginPath(vg);
nvgRect(vg, x + width / 2 - boxSize / 2, y + style->CrashFrame.boxSpacing, boxSize, boxSize);
nvgStroke(vg);
nvgFillColor(vg, RGB(255, 255, 255));
nvgFontSize(vg, (float)style->CrashFrame.boxSize / 1.25f);
nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
nvgBeginPath(vg);
nvgText(vg, x + width / 2, y + style->CrashFrame.boxSpacing + boxSize / 2, "!", nullptr);
nvgFill(vg);
// End scale
nvgResetTransform(vg);
nvgRestore(vg);
// Footer
nvgBeginPath(vg);
nvgRect(vg, x + style->AppletFrame.separatorSpacing, y + height - style->AppletFrame.footerHeight, width - style->AppletFrame.separatorSpacing * 2, 1);
nvgFill(vg);
nvgFontSize(vg, style->AppletFrame.footerTextSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgBeginPath(vg);
nvgText(vg, x + style->AppletFrame.separatorSpacing + style->AppletFrame.footerTextSpacing, y + height - style->AppletFrame.footerHeight / 2, Application::getTitle().c_str(), nullptr);
// Button
this->button->frame(ctx);
// Hint
this->hint->frame(ctx);
}
void CrashFrame::onShowAnimationEnd()
{
this->button->show([]() {});
}
View* CrashFrame::getDefaultFocus()
{
return this->button->getDefaultFocus();
}
void CrashFrame::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
// Label
this->label->setWidth(roundf((float)this->width * style->CrashFrame.labelWidth));
this->label->invalidate(true);
this->label->setBoundaries(
this->x + this->width / 2 - this->label->getWidth() / 2,
this->y + (this->height - style->AppletFrame.footerHeight) / 2,
this->label->getWidth(),
this->label->getHeight());
// Button
this->button->setBoundaries(
this->x + this->width / 2 - style->CrashFrame.buttonWidth / 2,
this->y + this->height - style->AppletFrame.footerHeight - style->CrashFrame.boxSpacing - style->CrashFrame.buttonHeight,
style->CrashFrame.buttonWidth,
style->CrashFrame.buttonHeight);
this->button->invalidate();
// Hint
// TODO: convert the bottom-left footer into a Label to get its width and avoid clipping with the hint
unsigned hintWidth = this->width - style->AppletFrame.separatorSpacing * 2 - style->AppletFrame.footerTextSpacing * 2;
this->hint->setBoundaries(
this->x + this->width - hintWidth - style->AppletFrame.separatorSpacing - style->AppletFrame.footerTextSpacing,
this->y + this->height - style->AppletFrame.footerHeight,
hintWidth,
style->AppletFrame.footerHeight);
this->hint->invalidate();
}
CrashFrame::~CrashFrame()
{
delete this->label;
delete this->hint;
}
} // namespace brls

View File

@@ -0,0 +1,342 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/application.hpp>
#include <borealis/button.hpp>
#include <borealis/dialog.hpp>
// TODO: different open animation?
namespace brls
{
Dialog::Dialog(View* contentView)
: contentView(contentView)
{
if (contentView)
contentView->setParent(this);
this->registerAction("Back", Key::B, [this] { return this->onCancel(); });
}
Dialog::Dialog(std::string text)
: Dialog(new Label(LabelStyle::DIALOG, text, true))
{
}
void Dialog::addButton(std::string label, GenericEvent::Callback cb)
{
if (this->buttons.size() >= 3)
return;
DialogButton* button = new DialogButton();
button->label = label;
button->cb = cb;
this->buttons.push_back(button);
this->rebuildButtons();
this->invalidate();
}
void Dialog::open()
{
Application::pushView(this);
if (this->buttons.size() == 0)
Application::blockInputs();
}
// TODO: do something better in case another view was pushed in the meantime
void Dialog::close(std::function<void(void)> cb)
{
Application::popView(ViewAnimation::FADE, cb);
if (this->buttons.size() == 0)
Application::unblockInputs();
}
void Dialog::setCancelable(bool cancelable)
{
this->cancelable = cancelable;
}
void Dialog::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
// Backdrop
nvgFillColor(vg, a(ctx->theme->dialogBackdrop));
nvgBeginPath(vg);
nvgRect(vg, x, y, width, height);
nvgFill(vg);
// Shadow
float shadowWidth = style->Dialog.shadowWidth;
float shadowFeather = style->Dialog.shadowFeather;
float shadowOpacity = style->Dialog.shadowOpacity;
float shadowOffset = style->Dialog.shadowOffset;
NVGpaint shadowPaint = nvgBoxGradient(vg,
this->frameX, this->frameY + shadowWidth,
this->frameWidth, this->frameHeight,
style->Dialog.cornerRadius * 2, shadowFeather,
RGBA(0, 0, 0, shadowOpacity * alpha), transparent);
nvgBeginPath(vg);
nvgRect(vg, this->frameX - shadowOffset, this->frameY - shadowOffset,
this->frameWidth + shadowOffset * 2, this->frameHeight + shadowOffset * 3);
nvgRoundedRect(vg, this->frameX, this->frameY, this->frameWidth, this->frameHeight, style->Dialog.cornerRadius);
nvgPathWinding(vg, NVG_HOLE);
nvgFillPaint(vg, shadowPaint);
nvgFill(vg);
// Frame
nvgFillColor(vg, a(ctx->theme->dialogColor));
nvgBeginPath(vg);
nvgRoundedRect(vg, this->frameX, this->frameY, this->frameWidth, this->frameHeight, style->Dialog.cornerRadius);
nvgFill(vg);
// Content view
if (this->contentView)
this->contentView->frame(ctx);
// Buttons separator
if (this->buttons.size() > 0)
{
unsigned buttonsHeight = this->getButtonsHeight();
nvgFillColor(vg, a(ctx->theme->dialogButtonSeparatorColor));
// First vertical separator
nvgBeginPath(vg);
nvgRect(vg, this->frameX, this->frameY + this->frameHeight - buttonsHeight, this->frameWidth, style->Dialog.buttonSeparatorHeight);
nvgFill(vg);
// Second vertical separator
if (this->buttons.size() == 3)
{
nvgBeginPath(vg);
nvgRect(vg, this->frameX, this->frameY + this->frameHeight - style->Dialog.buttonHeight, this->frameWidth, style->Dialog.buttonSeparatorHeight);
nvgFill(vg);
}
// Horizontal separator
if (this->buttons.size() >= 2)
{
nvgBeginPath(vg);
nvgRect(
vg,
this->frameX + this->frameWidth / 2 + style->Dialog.buttonSeparatorHeight / 2,
this->frameY + this->frameHeight - style->Dialog.buttonHeight + 1, // offset by 1 to fix aliasing artifact
style->Dialog.buttonSeparatorHeight,
style->Dialog.buttonHeight - 1);
nvgFill(vg);
}
}
// Buttons
if (this->verticalButtonsLayout)
this->verticalButtonsLayout->frame(ctx);
}
View* Dialog::getDefaultFocus()
{
if (this->buttons.size() > 0 && this->verticalButtonsLayout)
return this->verticalButtonsLayout->getDefaultFocus();
return nullptr;
}
bool Dialog::onCancel()
{
if (this->cancelable)
this->close();
return this->cancelable;
}
unsigned Dialog::getButtonsHeight()
{
Style* style = Application::getStyle();
if (this->buttons.size() == 3)
return style->Dialog.buttonHeight * 2;
else if (this->buttons.size() > 0) // 1 or 2
return style->Dialog.buttonHeight;
else
return 0;
}
void Dialog::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
this->frameWidth = style->Dialog.width;
this->frameHeight = style->Dialog.height;
unsigned buttonsHeight = this->getButtonsHeight();
this->frameHeight += buttonsHeight;
this->frameX = getWidth() / 2 - this->frameWidth / 2;
this->frameY = getHeight() / 2 - this->frameHeight / 2;
unsigned contentX = this->frameX + style->Dialog.paddingLeftRight;
unsigned contentY = this->frameY + style->Dialog.paddingTopBottom;
unsigned contentWidth = this->frameWidth - style->Dialog.paddingLeftRight * 2;
unsigned contentHeight = this->frameHeight - style->Dialog.paddingTopBottom * 2 - buttonsHeight;
if (this->contentView)
{
// First layout to get height
this->contentView->setBoundaries(
contentX,
contentY,
contentWidth,
contentHeight);
this->contentView->invalidate(true); // layout directly to get height
// Center the content view in the dialog
// or resize it if needed
unsigned newContentHeight = this->contentView->getHeight();
int difference = contentHeight - newContentHeight;
if (difference < 0)
{
this->frameHeight += -difference;
}
else
{
contentY += difference / 2;
this->contentView->setBoundaries(
contentX,
contentY,
contentWidth,
contentHeight);
this->contentView->invalidate();
}
}
// Buttons
if (this->verticalButtonsLayout)
{
this->verticalButtonsLayout->setBoundaries(
this->frameX,
this->frameY + this->frameHeight - buttonsHeight,
this->frameWidth,
style->Dialog.buttonHeight);
// Only one big button
if (this->buttons.size() == 1)
{
this->verticalButtonsLayout->getChild(0)->setHeight(style->Dialog.buttonHeight);
}
// Two buttons on one row
else if (this->buttons.size() == 2)
{
this->horizontalButtonsLayout->setHeight(style->Dialog.buttonHeight);
this->horizontalButtonsLayout->getChild(0)->setWidth(this->frameWidth / 2);
this->horizontalButtonsLayout->getChild(1)->setWidth(this->frameWidth / 2);
}
// Two rows: one with one button and one with two
else if (this->buttons.size() == 3)
{
this->verticalButtonsLayout->getChild(0)->setHeight(style->Dialog.buttonHeight);
this->horizontalButtonsLayout->setHeight(style->Dialog.buttonHeight);
this->horizontalButtonsLayout->getChild(0)->setWidth(this->frameWidth / 2);
this->horizontalButtonsLayout->getChild(1)->setWidth(this->frameWidth / 2);
}
this->verticalButtonsLayout->invalidate();
if (this->horizontalButtonsLayout)
this->horizontalButtonsLayout->invalidate();
}
}
void Dialog::rebuildButtons()
{
if (this->verticalButtonsLayout)
delete this->verticalButtonsLayout;
this->verticalButtonsLayout = nullptr;
// horizontal box layout will be deleted by
// the vertical layout destructor
if (this->buttons.size() > 0)
{
this->verticalButtonsLayout = new BoxLayout(BoxLayoutOrientation::VERTICAL);
this->verticalButtonsLayout->setParent(this);
// Only one big button
if (this->buttons.size() == 1)
{
Button* button = (new Button(ButtonStyle::DIALOG))->setLabel(this->buttons[0]->label);
button->getClickEvent()->subscribe(this->buttons[0]->cb);
this->verticalButtonsLayout->addView(button);
}
// Two buttons on one row
else if (this->buttons.size() == 2)
{
this->horizontalButtonsLayout = new BoxLayout(BoxLayoutOrientation::HORIZONTAL);
this->verticalButtonsLayout->addView(this->horizontalButtonsLayout);
for (DialogButton* dialogButton : this->buttons)
{
Button* button = (new Button(ButtonStyle::DIALOG))->setLabel(dialogButton->label);
button->getClickEvent()->subscribe(dialogButton->cb);
this->horizontalButtonsLayout->addView(button);
}
}
// Two rows: one with one button and one with two
else if (this->buttons.size() == 3)
{
Button* button = (new Button(ButtonStyle::DIALOG))->setLabel(this->buttons[0]->label);
button->getClickEvent()->subscribe(this->buttons[0]->cb);
this->verticalButtonsLayout->addView(button);
this->horizontalButtonsLayout = new BoxLayout(BoxLayoutOrientation::HORIZONTAL);
this->verticalButtonsLayout->addView(this->horizontalButtonsLayout);
for (size_t i = 1; i < this->buttons.size(); i++)
{
DialogButton* dialogButton = this->buttons[i];
Button* button = (new Button(ButtonStyle::DIALOG))->setLabel(dialogButton->label);
button->getClickEvent()->subscribe(dialogButton->cb);
this->horizontalButtonsLayout->addView(button);
}
}
}
}
Dialog::~Dialog()
{
if (this->contentView)
delete this->contentView;
if (this->verticalButtonsLayout)
delete this->verticalButtonsLayout;
for (DialogButton* dialogButton : this->buttons)
delete dialogButton;
// horizontal box layout will be deleted by
// the vertical layout destructor
}
} // namespace brls

View File

@@ -0,0 +1,214 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/animations.hpp>
#include <borealis/application.hpp>
#include <borealis/dropdown.hpp>
#include <borealis/logger.hpp>
#define SELECT_VIEW_MAX_ITEMS 6 // for max height
#define min(a, b) ((a < b) ? a : b)
// TODO: Turns out the fade out animation is the same as the fade in (top -> bottom)
namespace brls
{
Dropdown::Dropdown(std::string title, std::vector<std::string> values, ValueSelectedEvent::Callback cb, size_t selected)
: title(title)
{
Style* style = Application::getStyle();
this->valueEvent.subscribe(cb);
this->topOffset = (float)style->Dropdown.listPadding / 8.0f;
this->valuesCount = values.size();
this->list = new List(selected);
this->list->setParent(this);
this->list->setMargins(1, 0, 1, 0);
for (size_t i = 0; i < values.size(); i++)
{
std::string value = values[i];
ListItem* item = new ListItem(value);
if (i == selected)
item->setChecked(true);
item->setHeight(style->Dropdown.listItemHeight);
item->setTextSize(style->Dropdown.listItemTextSize);
item->getClickEvent()->subscribe([this, i](View* view) {
this->valueEvent.fire(i);
Application::popView();
});
this->list->addView(item);
}
this->hint = new Hint();
this->hint->setParent(this);
this->registerAction("Back", Key::B, [this] { return this->onCancel(); });
}
void Dropdown::show(std::function<void(void)> cb, bool animate, ViewAnimation animation)
{
View::show(cb);
menu_animation_ctx_entry_t entry;
entry.duration = this->getShowAnimationDuration(animation);
entry.easing_enum = EASING_OUT_QUAD;
entry.subject = &this->topOffset;
entry.tag = (uintptr_t) nullptr;
entry.target_value = 0.0f;
entry.tick = [this](void* userdata) { this->invalidate(); };
entry.userdata = nullptr;
menu_animation_push(&entry);
}
void Dropdown::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
unsigned top = this->list->getY() - style->Dropdown.headerHeight - style->Dropdown.listPadding;
// Backdrop
nvgFillColor(vg, a(ctx->theme->dropdownBackgroundColor));
nvgBeginPath(vg);
nvgRect(vg, x, y, width, top);
nvgFill(vg);
// TODO: Shadow
// Background
nvgFillColor(vg, a(ctx->theme->sidebarColor));
nvgBeginPath(vg);
nvgRect(vg, x, top, width, height - top);
nvgFill(vg);
// List
this->list->frame(ctx);
// Footer
this->hint->frame(ctx);
nvgFillColor(vg, ctx->theme->separatorColor); // we purposely don't apply opacity
nvgBeginPath(vg);
nvgRect(vg, x + style->AppletFrame.separatorSpacing, y + height - style->AppletFrame.footerHeight, width - style->AppletFrame.separatorSpacing * 2, 1);
nvgFill(vg);
nvgFillColor(vg, ctx->theme->textColor); // we purposely don't apply opacity
nvgFontSize(vg, style->AppletFrame.footerTextSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgBeginPath(vg);
nvgText(vg, x + style->AppletFrame.separatorSpacing + style->AppletFrame.footerTextSpacing, y + height - style->AppletFrame.footerHeight / 2, Application::getCommonFooter()->c_str(), nullptr);
// Header
nvgFillColor(vg, a(ctx->theme->separatorColor));
nvgBeginPath(vg);
nvgRect(vg, x + style->AppletFrame.separatorSpacing, top + style->Dropdown.headerHeight - 1, width - style->AppletFrame.separatorSpacing * 2, 1);
nvgFill(vg);
nvgBeginPath(vg);
nvgFillColor(vg, a(ctx->theme->textColor));
nvgFontFaceId(vg, ctx->fontStash->regular);
nvgFontSize(vg, style->Dropdown.headerFontSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgText(vg, x + style->Dropdown.headerPadding, top + style->Dropdown.headerHeight / 2, this->title.c_str(), nullptr);
}
bool Dropdown::onCancel()
{
this->valueEvent.fire(-1);
Application::popView();
return true;
}
unsigned Dropdown::getShowAnimationDuration(ViewAnimation animation)
{
return View::getShowAnimationDuration(animation) / 2;
}
void Dropdown::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
// Layout and move the list
unsigned listHeight = min(SELECT_VIEW_MAX_ITEMS, this->valuesCount) * style->Dropdown.listItemHeight - (unsigned)this->topOffset;
unsigned listWidth = style->Dropdown.listWidth + style->List.marginLeftRight * 2;
this->list->setBoundaries(
this->width / 2 - listWidth / 2,
this->height - style->AppletFrame.footerHeight - listHeight - style->Dropdown.listPadding + (unsigned)this->topOffset,
listWidth,
listHeight);
this->list->invalidate(true); // call layout directly to update scrolling
// Hint
// TODO: convert the bottom-left footer into a Label to get its width and avoid clipping with the hint
unsigned hintWidth = this->width - style->AppletFrame.separatorSpacing * 2 - style->AppletFrame.footerTextSpacing * 2;
this->hint->setBoundaries(
this->x + this->width - hintWidth - style->AppletFrame.separatorSpacing - style->AppletFrame.footerTextSpacing,
this->y + this->height - style->AppletFrame.footerHeight,
hintWidth,
style->AppletFrame.footerHeight);
this->hint->invalidate();
}
View* Dropdown::getDefaultFocus()
{
return this->list->getDefaultFocus();
}
void Dropdown::open(std::string title, std::vector<std::string> values, ValueSelectedEvent::Callback cb, int selected)
{
Dropdown* dropdown = new Dropdown(title, values, cb, selected);
Application::pushView(dropdown);
}
void Dropdown::willAppear(bool resetState)
{
if (this->list)
this->list->willAppear(resetState);
if (this->hint)
this->hint->willAppear(resetState);
}
void Dropdown::willDisappear(bool resetState)
{
if (this->list)
this->list->willDisappear(resetState);
if (this->hint)
this->hint->willDisappear(resetState);
}
Dropdown::~Dropdown()
{
delete this->list;
delete this->hint;
}
} // namespace brls

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
*.dll
*.exe
*.c~
*.un~
*.o

View File

@@ -0,0 +1,69 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (compat_strl.c).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#include <stdlib.h>
#include <ctype.h>
#include <compat/strl.h>
/* Implementation of strlcpy()/strlcat() based on OpenBSD. */
#ifndef __MACH__
size_t strlcpy(char *dest, const char *source, size_t size)
{
size_t src_size = 0;
size_t n = size;
if (n)
while (--n && (*dest++ = *source++)) src_size++;
if (!n)
{
if (size) *dest = '\0';
while (*source++) src_size++;
}
return src_size;
}
size_t strlcat(char *dest, const char *source, size_t size)
{
size_t len = strlen(dest);
dest += len;
if (len > size)
size = 0;
else
size -= len;
return len + strlcpy(dest, source, size);
}
#endif
char *strldup(const char *s, size_t n)
{
char *dst = (char*)malloc(sizeof(char) * (n + 1));
strlcpy(dst, s, n);
return dst;
}

View File

@@ -0,0 +1,510 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (encoding_utf.c).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <boolean.h>
#include <compat/strl.h>
#include <retro_inline.h>
#include <encodings/utf.h>
#if defined(_WIN32) && !defined(_XBOX)
#include <windows.h>
#elif defined(_XBOX)
#include <xtl.h>
#endif
static unsigned leading_ones(uint8_t c)
{
unsigned ones = 0;
while (c & 0x80)
{
ones++;
c <<= 1;
}
return ones;
}
/* Simple implementation. Assumes the sequence is
* properly synchronized and terminated. */
size_t utf8_conv_utf32(uint32_t *out, size_t out_chars,
const char *in, size_t in_size)
{
unsigned i;
size_t ret = 0;
while (in_size && out_chars)
{
unsigned extra, shift;
uint32_t c;
uint8_t first = *in++;
unsigned ones = leading_ones(first);
if (ones > 6 || ones == 1) /* Invalid or desync. */
break;
extra = ones ? ones - 1 : ones;
if (1 + extra > in_size) /* Overflow. */
break;
shift = (extra - 1) * 6;
c = (first & ((1 << (7 - ones)) - 1)) << (6 * extra);
for (i = 0; i < extra; i++, in++, shift -= 6)
c |= (*in & 0x3f) << shift;
*out++ = c;
in_size -= 1 + extra;
out_chars--;
ret++;
}
return ret;
}
bool utf16_conv_utf8(uint8_t *out, size_t *out_chars,
const uint16_t *in, size_t in_size)
{
static uint8_t kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
size_t out_pos = 0;
size_t in_pos = 0;
for (;;)
{
unsigned numAdds;
uint32_t value;
if (in_pos == in_size)
{
*out_chars = out_pos;
return true;
}
value = in[in_pos++];
if (value < 0x80)
{
if (out)
out[out_pos] = (char)value;
out_pos++;
continue;
}
if (value >= 0xD800 && value < 0xE000)
{
uint32_t c2;
if (value >= 0xDC00 || in_pos == in_size)
break;
c2 = in[in_pos++];
if (c2 < 0xDC00 || c2 >= 0xE000)
break;
value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000;
}
for (numAdds = 1; numAdds < 5; numAdds++)
if (value < (((uint32_t)1) << (numAdds * 5 + 6)))
break;
if (out)
out[out_pos] = (char)(kUtf8Limits[numAdds - 1]
+ (value >> (6 * numAdds)));
out_pos++;
do
{
numAdds--;
if (out)
out[out_pos] = (char)(0x80
+ ((value >> (6 * numAdds)) & 0x3F));
out_pos++;
}while (numAdds != 0);
}
*out_chars = out_pos;
return false;
}
/* Acts mostly like strlcpy.
*
* Copies the given number of UTF-8 characters,
* but at most d_len bytes.
*
* Always NULL terminates.
* Does not copy half a character.
*
* Returns number of bytes. 's' is assumed valid UTF-8.
* Use only if 'chars' is considerably less than 'd_len'. */
size_t utf8cpy(char *d, size_t d_len, const char *s, size_t chars)
{
const uint8_t *sb = (const uint8_t*)s;
const uint8_t *sb_org = sb;
if (!s)
return 0;
while (*sb && chars-- > 0)
{
sb++;
while ((*sb & 0xC0) == 0x80) sb++;
}
if ((size_t)(sb - sb_org) > d_len-1 /* NUL */)
{
sb = sb_org + d_len-1;
while ((*sb & 0xC0) == 0x80) sb--;
}
memcpy(d, sb_org, sb-sb_org);
d[sb-sb_org] = '\0';
return sb-sb_org;
}
const char *utf8skip(const char *str, size_t chars)
{
const uint8_t *strb = (const uint8_t*)str;
if (!chars)
return str;
do
{
strb++;
while ((*strb & 0xC0)==0x80) strb++;
chars--;
} while(chars);
return (const char*)strb;
}
size_t utf8len(const char *string)
{
size_t ret = 0;
if (!string)
return 0;
while (*string)
{
if ((*string & 0xC0) != 0x80)
ret++;
string++;
}
return ret;
}
#define utf8_walkbyte(string) (*((*(string))++))
/* Does not validate the input, returns garbage if it's not UTF-8. */
uint32_t utf8_walk(const char **string)
{
uint8_t first = utf8_walkbyte(string);
uint32_t ret = 0;
if (first < 128)
return first;
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xE0)
{
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
if (first >= 0xF0)
{
ret = (ret << 6) | (utf8_walkbyte(string) & 0x3F);
return ret | (first & 7) << 18;
}
return ret | (first & 15) << 12;
}
return ret | (first & 31) << 6;
}
static bool utf16_to_char(uint8_t **utf_data,
size_t *dest_len, const uint16_t *in)
{
unsigned len = 0;
while (in[len] != '\0')
len++;
utf16_conv_utf8(NULL, dest_len, in, len);
*dest_len += 1;
*utf_data = (uint8_t*)malloc(*dest_len);
if (*utf_data == 0)
return false;
return utf16_conv_utf8(*utf_data, dest_len, in, len);
}
bool utf16_to_char_string(const uint16_t *in, char *s, size_t len)
{
size_t dest_len = 0;
uint8_t *utf16_data = NULL;
bool ret = utf16_to_char(&utf16_data, &dest_len, in);
if (ret)
{
utf16_data[dest_len] = 0;
strlcpy(s, (const char*)utf16_data, len);
}
free(utf16_data);
utf16_data = NULL;
return ret;
}
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
/* Returned pointer MUST be freed by the caller if non-NULL. */
static char *mb_to_mb_string_alloc(const char *str,
enum CodePage cp_in, enum CodePage cp_out)
{
char *path_buf = NULL;
wchar_t *path_buf_wide = NULL;
int path_buf_len = 0;
int path_buf_wide_len = MultiByteToWideChar(cp_in, 0, str, -1, NULL, 0);
/* Windows 95 will return 0 from these functions with
* a UTF8 codepage set without MSLU.
*
* From an unknown MSDN version (others omit this info):
* - CP_UTF8 Windows 98/Me, Windows NT 4.0 and later:
* Translate using UTF-8. When this is set, dwFlags must be zero.
* - Windows 95: Under the Microsoft Layer for Unicode,
* MultiByteToWideChar also supports CP_UTF7 and CP_UTF8.
*/
if (path_buf_wide_len)
{
path_buf_wide = (wchar_t*)
calloc(path_buf_wide_len + sizeof(wchar_t), sizeof(wchar_t));
if (path_buf_wide)
{
MultiByteToWideChar(cp_in, 0,
str, -1, path_buf_wide, path_buf_wide_len);
if (*path_buf_wide)
{
path_buf_len = WideCharToMultiByte(cp_out, 0,
path_buf_wide, -1, NULL, 0, NULL, NULL);
if (path_buf_len)
{
path_buf = (char*)
calloc(path_buf_len + sizeof(char), sizeof(char));
if (path_buf)
{
WideCharToMultiByte(cp_out, 0,
path_buf_wide, -1, path_buf,
path_buf_len, NULL, NULL);
free(path_buf_wide);
if (*path_buf)
return path_buf;
free(path_buf);
return NULL;
}
}
else
{
free(path_buf_wide);
return strdup(str);
}
}
}
}
else
return strdup(str);
if (path_buf_wide)
free(path_buf_wide);
return NULL;
}
#endif
/* Returned pointer MUST be freed by the caller if non-NULL. */
char* utf8_to_local_string_alloc(const char *str)
{
if (str && *str)
{
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
return mb_to_mb_string_alloc(str, CODEPAGE_UTF8, CODEPAGE_LOCAL);
#else
/* assume string needs no modification if not on Windows */
return strdup(str);
#endif
}
return NULL;
}
/* Returned pointer MUST be freed by the caller if non-NULL. */
char* local_to_utf8_string_alloc(const char *str)
{
if (str && *str)
{
#if defined(_WIN32) && !defined(_XBOX) && !defined(UNICODE)
return mb_to_mb_string_alloc(str, CODEPAGE_LOCAL, CODEPAGE_UTF8);
#else
/* assume string needs no modification if not on Windows */
return strdup(str);
#endif
}
return NULL;
}
/* Returned pointer MUST be freed by the caller if non-NULL. */
wchar_t* utf8_to_utf16_string_alloc(const char *str)
{
#ifdef _WIN32
int len = 0;
int out_len = 0;
#else
size_t len = 0;
size_t out_len = 0;
#endif
wchar_t *buf = NULL;
if (!str || !*str)
return NULL;
#ifdef _WIN32
len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
if (len)
{
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
if (!buf)
return NULL;
out_len = MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, len);
}
else
{
/* fallback to ANSI codepage instead */
len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
if (len)
{
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
if (!buf)
return NULL;
out_len = MultiByteToWideChar(CP_ACP, 0, str, -1, buf, len);
}
}
if (out_len < 0)
{
free(buf);
return NULL;
}
#else
/* NOTE: For now, assume non-Windows platforms' locale is already UTF-8. */
len = mbstowcs(NULL, str, 0) + 1;
if (len)
{
buf = (wchar_t*)calloc(len, sizeof(wchar_t));
if (!buf)
return NULL;
out_len = mbstowcs(buf, str, len);
}
if (out_len == (size_t)-1)
{
free(buf);
return NULL;
}
#endif
return buf;
}
/* Returned pointer MUST be freed by the caller if non-NULL. */
char* utf16_to_utf8_string_alloc(const wchar_t *str)
{
#ifdef _WIN32
int len = 0;
#else
size_t len = 0;
#endif
char *buf = NULL;
if (!str || !*str)
return NULL;
#ifdef _WIN32
{
UINT code_page = CP_UTF8;
len = WideCharToMultiByte(code_page,
0, str, -1, NULL, 0, NULL, NULL);
/* fallback to ANSI codepage instead */
if (!len)
{
code_page = CP_ACP;
len = WideCharToMultiByte(code_page,
0, str, -1, NULL, 0, NULL, NULL);
}
buf = (char*)calloc(len, sizeof(char));
if (!buf)
return NULL;
if (WideCharToMultiByte(code_page,
0, str, -1, buf, len, NULL, NULL) < 0)
{
free(buf);
return NULL;
}
}
#else
/* NOTE: For now, assume non-Windows platforms'
* locale is already UTF-8. */
len = wcstombs(NULL, str, 0) + 1;
if (len)
{
buf = (char*)calloc(len, sizeof(char));
if (!buf)
return NULL;
if (wcstombs(buf, str, len) == (size_t)-1)
{
free(buf);
return NULL;
}
}
#endif
return buf;
}

View File

@@ -0,0 +1,889 @@
/* Copyright (C) 2010-2018 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (features_cpu.c).
* ---------------------------------------------------------------------------------------
*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32)
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <compat/strl.h>
#include <streams/file_stream.h>
#include <libretro.h>
#include <features/features_cpu.h>
#include <retro_timers.h>
#if defined(_WIN32) && !defined(_XBOX)
#include <windows.h>
#endif
#if defined(__CELLOS_LV2__)
#ifndef _PPU_INTRINSICS_H
#include <ppu_intrinsics.h>
#endif
#elif defined(_XBOX360)
#include <PPCIntrinsics.h>
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(ANDROID) || defined(__QNX__) || defined(DJGPP)
/* POSIX_MONOTONIC_CLOCK is not being defined in Android headers despite support being present. */
#include <time.h>
#endif
#if defined(__QNX__) && !defined(CLOCK_MONOTONIC)
#define CLOCK_MONOTONIC 2
#endif
#if defined(PSP)
#include <pspkernel.h>
#include <sys/time.h>
#include <psprtc.h>
#endif
#if defined(VITA)
#include <psp2/kernel/processmgr.h>
#include <psp2/rtc.h>
#endif
#if defined(PS2)
#include <kernel.h>
#include <timer.h>
#include <time.h>
#endif
#if defined(__PSL1GHT__)
#include <sys/time.h>
#elif defined(__CELLOS_LV2__)
#include <sys/sys_time.h>
#endif
#ifdef GEKKO
#include <ogc/lwp_watchdog.h>
#endif
#ifdef WIIU
#include <wiiu/os/time.h>
#endif
#if defined(HAVE_LIBNX)
#include <switch.h>
#elif defined(SWITCH)
#include <libtransistor/types.h>
#include <libtransistor/svc.h>
#endif
#if defined(_3DS)
#include <3ds/svc.h>
#include <3ds/os.h>
#include <3ds/services/cfgu.h>
#endif
/* iOS/OSX specific. Lacks clock_gettime(), so implement it. */
#ifdef __MACH__
#include <sys/time.h>
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 0
#endif
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#endif
/* this function is part of iOS 10 now */
static int ra_clock_gettime(int clk_ik, struct timespec *t)
{
struct timeval now;
int rv = gettimeofday(&now, NULL);
if (rv)
return rv;
t->tv_sec = now.tv_sec;
t->tv_nsec = now.tv_usec * 1000;
return 0;
}
#endif
#if defined(__MACH__) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000
#else
#define ra_clock_gettime clock_gettime
#endif
#ifdef EMSCRIPTEN
#include <emscripten.h>
#endif
#if defined(BSD) || defined(__APPLE__)
#include <sys/sysctl.h>
#endif
#include <string.h>
/**
* cpu_features_get_perf_counter:
*
* Gets performance counter.
*
* Returns: performance counter.
**/
retro_perf_tick_t cpu_features_get_perf_counter(void)
{
retro_perf_tick_t time_ticks = 0;
#if defined(_WIN32)
long tv_sec, tv_usec;
#if defined(_MSC_VER) && _MSC_VER <= 1200
static const unsigned __int64 epoch = 11644473600000000;
#else
static const unsigned __int64 epoch = 11644473600000000ULL;
#endif
FILETIME file_time;
SYSTEMTIME system_time;
ULARGE_INTEGER ularge;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
ularge.LowPart = file_time.dwLowDateTime;
ularge.HighPart = file_time.dwHighDateTime;
tv_sec = (long)((ularge.QuadPart - epoch) / 10000000L);
tv_usec = (long)(system_time.wMilliseconds * 1000);
time_ticks = (1000000 * tv_sec + tv_usec);
#elif defined(__linux__) || defined(__QNX__) || defined(__MACH__)
struct timespec tv = {0};
if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) == 0)
time_ticks = (retro_perf_tick_t)tv.tv_sec * 1000000000 +
(retro_perf_tick_t)tv.tv_nsec;
#elif defined(__GNUC__) && defined(__i386__) || defined(__i486__) || defined(__i686__) || defined(_M_X64) || defined(_M_AMD64)
__asm__ volatile ("rdtsc" : "=A" (time_ticks));
#elif defined(__GNUC__) && defined(__x86_64__) || defined(_M_IX86)
unsigned a, d;
__asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
time_ticks = (retro_perf_tick_t)a | ((retro_perf_tick_t)d << 32);
#elif defined(__ARM_ARCH_6__)
__asm__ volatile( "mrc p15, 0, %0, c9, c13, 0" : "=r"(time_ticks) );
#elif defined(__CELLOS_LV2__) || defined(_XBOX360) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__)
time_ticks = __mftb();
#elif defined(GEKKO)
time_ticks = gettime();
#elif defined(PSP)
sceRtcGetCurrentTick((uint64_t*)&time_ticks);
#elif defined(VITA)
sceRtcGetCurrentTick((SceRtcTick*)&time_ticks);
#elif defined(PS2)
time_ticks = clock()*294912; // 294,912MHZ / 1000 msecs
#elif defined(_3DS)
time_ticks = svcGetSystemTick();
#elif defined(WIIU)
time_ticks = OSGetSystemTime();
#elif defined(__mips__)
struct timeval tv;
gettimeofday(&tv,NULL);
time_ticks = (1000000 * tv.tv_sec + tv.tv_usec);
#elif defined(HAVE_LIBNX)
time_ticks = armGetSystemTick();
#endif
return time_ticks;
}
/**
* cpu_features_get_time_usec:
*
* Gets time in microseconds.
*
* Returns: time in microseconds.
**/
retro_time_t cpu_features_get_time_usec(void)
{
#if defined(_WIN32)
static LARGE_INTEGER freq;
LARGE_INTEGER count;
/* Frequency is guaranteed to not change. */
if (!freq.QuadPart && !QueryPerformanceFrequency(&freq))
return 0;
if (!QueryPerformanceCounter(&count))
return 0;
return count.QuadPart * 1000000 / freq.QuadPart;
#elif defined(__CELLOS_LV2__)
return sys_time_get_system_time();
#elif defined(GEKKO)
return ticks_to_microsecs(gettime());
#elif defined(WIIU)
return ticks_to_us(OSGetSystemTime());
#elif defined(SWITCH) || defined(HAVE_LIBNX)
return (svcGetSystemTick() * 10) / 192;
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(__QNX__) || defined(ANDROID) || defined(__MACH__)
struct timespec tv = {0};
if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) < 0)
return 0;
return tv.tv_sec * INT64_C(1000000) + (tv.tv_nsec + 500) / 1000;
#elif defined(EMSCRIPTEN)
return emscripten_get_now() * 1000;
#elif defined(PS2)
return clock()*1000;
#elif defined(__mips__) || defined(DJGPP)
struct timeval tv;
gettimeofday(&tv,NULL);
return (1000000 * tv.tv_sec + tv.tv_usec);
#elif defined(_3DS)
return osGetTime() * 1000;
#elif defined(VITA)
return sceKernelGetProcessTimeWide();
#else
#error "Your platform does not have a timer function implemented in cpu_features_get_time_usec(). Cannot continue."
#endif
}
#if defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i686__) || (defined(_M_X64) && _MSC_VER > 1310) || (defined(_M_IX86) && _MSC_VER > 1310)
#define CPU_X86
#endif
#if defined(_MSC_VER) && !defined(_XBOX)
#if (_MSC_VER > 1310)
#include <intrin.h>
#endif
#endif
#if defined(CPU_X86) && !defined(__MACH__)
void x86_cpuid(int func, int flags[4])
{
/* On Android, we compile RetroArch with PIC, and we
* are not allowed to clobber the ebx register. */
#ifdef __x86_64__
#define REG_b "rbx"
#define REG_S "rsi"
#else
#define REG_b "ebx"
#define REG_S "esi"
#endif
#if defined(__GNUC__)
__asm__ volatile (
"mov %%" REG_b ", %%" REG_S "\n"
"cpuid\n"
"xchg %%" REG_b ", %%" REG_S "\n"
: "=a"(flags[0]), "=S"(flags[1]), "=c"(flags[2]), "=d"(flags[3])
: "a"(func));
#elif defined(_MSC_VER)
__cpuid(flags, func);
#else
printf("Unknown compiler. Cannot check CPUID with inline assembly.\n");
memset(flags, 0, 4 * sizeof(int));
#endif
}
/* Only runs on i686 and above. Needs to be conditionally run. */
static uint64_t xgetbv_x86(uint32_t idx)
{
#if defined(__GNUC__)
uint32_t eax, edx;
__asm__ volatile (
/* Older GCC versions (Apple's GCC for example) do
* not understand xgetbv instruction.
* Stamp out the machine code directly.
*/
".byte 0x0f, 0x01, 0xd0\n"
: "=a"(eax), "=d"(edx) : "c"(idx));
return ((uint64_t)edx << 32) | eax;
#elif _MSC_FULL_VER >= 160040219
/* Intrinsic only works on 2010 SP1 and above. */
return _xgetbv(idx);
#else
printf("Unknown compiler. Cannot check xgetbv bits.\n");
return 0;
#endif
}
#endif
#if defined(__ARM_NEON__)
static void arm_enable_runfast_mode(void)
{
/* RunFast mode. Enables flush-to-zero and some
* floating point optimizations. */
static const unsigned x = 0x04086060;
static const unsigned y = 0x03000000;
int r;
__asm__ volatile(
"fmrx %0, fpscr \n\t" /* r0 = FPSCR */
"and %0, %0, %1 \n\t" /* r0 = r0 & 0x04086060 */
"orr %0, %0, %2 \n\t" /* r0 = r0 | 0x03000000 */
"fmxr fpscr, %0 \n\t" /* FPSCR = r0 */
: "=r"(r)
: "r"(x), "r"(y)
);
}
#endif
#if defined(__linux__) && !defined(CPU_X86)
static unsigned char check_arm_cpu_feature(const char* feature)
{
char line[1024];
unsigned char status = 0;
RFILE *fp = filestream_open("/proc/cpuinfo",
RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!fp)
return 0;
while (filestream_gets(fp, line, sizeof(line)) != NULL)
{
if (strncmp(line, "Features\t: ", 11))
continue;
if (strstr(line + 11, feature) != NULL)
status = 1;
break;
}
filestream_close(fp);
return status;
}
#if !defined(_SC_NPROCESSORS_ONLN)
/* Parse an decimal integer starting from 'input', but not going further
* than 'limit'. Return the value into '*result'.
*
* NOTE: Does not skip over leading spaces, or deal with sign characters.
* NOTE: Ignores overflows.
*
* The function returns NULL in case of error (bad format), or the new
* position after the decimal number in case of success (which will always
* be <= 'limit').
*/
static const char *parse_decimal(const char* input,
const char* limit, int* result)
{
const char* p = input;
int val = 0;
while (p < limit)
{
int d = (*p - '0');
if ((unsigned)d >= 10U)
break;
val = val*10 + d;
p++;
}
if (p == input)
return NULL;
*result = val;
return p;
}
/* Parse a textual list of cpus and store the result inside a CpuList object.
* Input format is the following:
* - comma-separated list of items (no spaces)
* - each item is either a single decimal number (cpu index), or a range made
* of two numbers separated by a single dash (-). Ranges are inclusive.
*
* Examples: 0
* 2,4-127,128-143
* 0-1
*/
static void cpulist_parse(CpuList* list, char **buf, ssize_t length)
{
const char* p = (const char*)buf;
const char* end = p + length;
/* NOTE: the input line coming from sysfs typically contains a
* trailing newline, so take care of it in the code below
*/
while (p < end && *p != '\n')
{
int val, start_value, end_value;
/* Find the end of current item, and put it into 'q' */
const char *q = (const char*)memchr(p, ',', end-p);
if (!q)
q = end;
/* Get first value */
p = parse_decimal(p, q, &start_value);
if (p == NULL)
return;
end_value = start_value;
/* If we're not at the end of the item, expect a dash and
* and integer; extract end value.
*/
if (p < q && *p == '-')
{
p = parse_decimal(p+1, q, &end_value);
if (p == NULL)
return;
}
/* Set bits CPU list bits */
for (val = start_value; val <= end_value; val++)
{
if ((unsigned)val < 32)
list->mask |= (uint32_t)(1U << val);
}
/* Jump to next item */
p = q;
if (p < end)
p++;
}
}
/* Read a CPU list from one sysfs file */
static void cpulist_read_from(CpuList* list, const char* filename)
{
ssize_t length;
char *buf = NULL;
list->mask = 0;
if (filestream_read_file(filename, (void**)&buf, &length) != 1)
return;
cpulist_parse(list, &buf, length);
if (buf)
free(buf);
buf = NULL;
}
#endif
#endif
/**
* cpu_features_get_core_amount:
*
* Gets the amount of available CPU cores.
*
* Returns: amount of CPU cores available.
**/
unsigned cpu_features_get_core_amount(void)
{
#if defined(_WIN32) && !defined(_XBOX)
/* Win32 */
SYSTEM_INFO sysinfo;
#if defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
GetNativeSystemInfo(&sysinfo);
#else
GetSystemInfo(&sysinfo);
#endif
return sysinfo.dwNumberOfProcessors;
#elif defined(GEKKO)
return 1;
#elif defined(PSP) || defined(PS2)
return 1;
#elif defined(VITA)
return 4;
#elif defined(HAVE_LIBNX) || defined(SWITCH)
return 4;
#elif defined(_3DS)
u8 device_model = 0xFF;
CFGU_GetSystemModel(&device_model);/*(0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL)*/
switch (device_model)
{
case 0:
case 1:
case 3:
/*Old 3/2DS*/
return 2;
case 2:
case 4:
case 5:
/*New 3/2DS*/
return 4;
default:
/*Unknown Device Or Check Failed*/
break;
}
return 1;
#elif defined(WIIU)
return 3;
#elif defined(_SC_NPROCESSORS_ONLN)
/* Linux, most UNIX-likes. */
long ret = sysconf(_SC_NPROCESSORS_ONLN);
if (ret <= 0)
return (unsigned)1;
return (unsigned)ret;
#elif defined(BSD) || defined(__APPLE__)
/* BSD */
/* Copypasta from stackoverflow, dunno if it works. */
int num_cpu = 0;
int mib[4];
size_t len = sizeof(num_cpu);
mib[0] = CTL_HW;
mib[1] = HW_AVAILCPU;
sysctl(mib, 2, &num_cpu, &len, NULL, 0);
if (num_cpu < 1)
{
mib[1] = HW_NCPU;
sysctl(mib, 2, &num_cpu, &len, NULL, 0);
if (num_cpu < 1)
num_cpu = 1;
}
return num_cpu;
#elif defined(__linux__)
CpuList cpus_present[1];
CpuList cpus_possible[1];
int amount = 0;
cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present");
cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible");
/* Compute the intersection of both sets to get the actual number of
* CPU cores that can be used on this device by the kernel.
*/
cpus_present->mask &= cpus_possible->mask;
amount = __builtin_popcount(cpus_present->mask);
if (amount == 0)
return 1;
return amount;
#elif defined(_XBOX360)
return 3;
#else
/* No idea, assume single core. */
return 1;
#endif
}
/* According to http://en.wikipedia.org/wiki/CPUID */
#define VENDOR_INTEL_b 0x756e6547
#define VENDOR_INTEL_c 0x6c65746e
#define VENDOR_INTEL_d 0x49656e69
/**
* cpu_features_get:
*
* Gets CPU features..
*
* Returns: bitmask of all CPU features available.
**/
uint64_t cpu_features_get(void)
{
int flags[4];
int vendor_shuffle[3];
char vendor[13];
size_t len = 0;
uint64_t cpu_flags = 0;
uint64_t cpu = 0;
unsigned max_flag = 0;
#if defined(CPU_X86) && !defined(__MACH__)
int vendor_is_intel = 0;
const int avx_flags = (1 << 27) | (1 << 28);
#endif
char buf[sizeof(" MMX MMXEXT SSE SSE2 SSE3 SSSE3 SS4 SSE4.2 AES AVX AVX2 NEON VMX VMX128 VFPU PS")];
memset(buf, 0, sizeof(buf));
(void)len;
(void)cpu_flags;
(void)flags;
(void)max_flag;
(void)vendor;
(void)vendor_shuffle;
#if defined(__MACH__)
len = sizeof(size_t);
if (sysctlbyname("hw.optional.mmx", NULL, &len, NULL, 0) == 0)
{
cpu |= RETRO_SIMD_MMX;
cpu |= RETRO_SIMD_MMXEXT;
}
len = sizeof(size_t);
if (sysctlbyname("hw.optional.floatingpoint", NULL, &len, NULL, 0) == 0)
{
cpu |= RETRO_SIMD_CMOV;
}
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse2", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE2;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse3", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE3;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.supplementalsse3", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSSE3;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse4_1", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE4;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse4_2", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE42;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.aes", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_AES;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.avx1_0", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_AVX;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.avx2_0", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_AVX2;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.altivec", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_VMX;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.neon", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_NEON;
#elif defined(_XBOX1)
cpu |= RETRO_SIMD_MMX;
cpu |= RETRO_SIMD_SSE;
cpu |= RETRO_SIMD_MMXEXT;
#elif defined(CPU_X86)
(void)avx_flags;
x86_cpuid(0, flags);
vendor_shuffle[0] = flags[1];
vendor_shuffle[1] = flags[3];
vendor_shuffle[2] = flags[2];
vendor[0] = '\0';
memcpy(vendor, vendor_shuffle, sizeof(vendor_shuffle));
/* printf("[CPUID]: Vendor: %s\n", vendor); */
vendor_is_intel = (
flags[1] == VENDOR_INTEL_b &&
flags[2] == VENDOR_INTEL_c &&
flags[3] == VENDOR_INTEL_d);
max_flag = flags[0];
if (max_flag < 1) /* Does CPUID not support func = 1? (unlikely ...) */
return 0;
x86_cpuid(1, flags);
if (flags[3] & (1 << 15))
cpu |= RETRO_SIMD_CMOV;
if (flags[3] & (1 << 23))
cpu |= RETRO_SIMD_MMX;
if (flags[3] & (1 << 25))
{
/* SSE also implies MMXEXT (according to FFmpeg source). */
cpu |= RETRO_SIMD_SSE;
cpu |= RETRO_SIMD_MMXEXT;
}
if (flags[3] & (1 << 26))
cpu |= RETRO_SIMD_SSE2;
if (flags[2] & (1 << 0))
cpu |= RETRO_SIMD_SSE3;
if (flags[2] & (1 << 9))
cpu |= RETRO_SIMD_SSSE3;
if (flags[2] & (1 << 19))
cpu |= RETRO_SIMD_SSE4;
if (flags[2] & (1 << 20))
cpu |= RETRO_SIMD_SSE42;
if ((flags[2] & (1 << 23)))
cpu |= RETRO_SIMD_POPCNT;
if (vendor_is_intel && (flags[2] & (1 << 22)))
cpu |= RETRO_SIMD_MOVBE;
if (flags[2] & (1 << 25))
cpu |= RETRO_SIMD_AES;
/* Must only perform xgetbv check if we have
* AVX CPU support (guaranteed to have at least i686). */
if (((flags[2] & avx_flags) == avx_flags)
&& ((xgetbv_x86(0) & 0x6) == 0x6))
cpu |= RETRO_SIMD_AVX;
if (max_flag >= 7)
{
x86_cpuid(7, flags);
if (flags[1] & (1 << 5))
cpu |= RETRO_SIMD_AVX2;
}
x86_cpuid(0x80000000, flags);
max_flag = flags[0];
if (max_flag >= 0x80000001u)
{
x86_cpuid(0x80000001, flags);
if (flags[3] & (1 << 23))
cpu |= RETRO_SIMD_MMX;
if (flags[3] & (1 << 22))
cpu |= RETRO_SIMD_MMXEXT;
}
#elif defined(__linux__)
if (check_arm_cpu_feature("neon"))
{
cpu |= RETRO_SIMD_NEON;
#ifdef __ARM_NEON__
arm_enable_runfast_mode();
#endif
}
if (check_arm_cpu_feature("vfpv3"))
cpu |= RETRO_SIMD_VFPV3;
if (check_arm_cpu_feature("vfpv4"))
cpu |= RETRO_SIMD_VFPV4;
if (check_arm_cpu_feature("asimd"))
{
cpu |= RETRO_SIMD_ASIMD;
#ifdef __ARM_NEON__
cpu |= RETRO_SIMD_NEON;
arm_enable_runfast_mode();
#endif
}
#if 0
check_arm_cpu_feature("swp");
check_arm_cpu_feature("half");
check_arm_cpu_feature("thumb");
check_arm_cpu_feature("fastmult");
check_arm_cpu_feature("vfp");
check_arm_cpu_feature("edsp");
check_arm_cpu_feature("thumbee");
check_arm_cpu_feature("tls");
check_arm_cpu_feature("idiva");
check_arm_cpu_feature("idivt");
#endif
#elif defined(__ARM_NEON__)
cpu |= RETRO_SIMD_NEON;
arm_enable_runfast_mode();
#elif defined(__ALTIVEC__)
cpu |= RETRO_SIMD_VMX;
#elif defined(XBOX360)
cpu |= RETRO_SIMD_VMX128;
#elif defined(PSP) || defined(PS2)
cpu |= RETRO_SIMD_VFPU;
#elif defined(GEKKO)
cpu |= RETRO_SIMD_PS;
#endif
if (cpu & RETRO_SIMD_MMX) strlcat(buf, " MMX", sizeof(buf));
if (cpu & RETRO_SIMD_MMXEXT) strlcat(buf, " MMXEXT", sizeof(buf));
if (cpu & RETRO_SIMD_SSE) strlcat(buf, " SSE", sizeof(buf));
if (cpu & RETRO_SIMD_SSE2) strlcat(buf, " SSE2", sizeof(buf));
if (cpu & RETRO_SIMD_SSE3) strlcat(buf, " SSE3", sizeof(buf));
if (cpu & RETRO_SIMD_SSSE3) strlcat(buf, " SSSE3", sizeof(buf));
if (cpu & RETRO_SIMD_SSE4) strlcat(buf, " SSE4", sizeof(buf));
if (cpu & RETRO_SIMD_SSE42) strlcat(buf, " SSE4.2", sizeof(buf));
if (cpu & RETRO_SIMD_AES) strlcat(buf, " AES", sizeof(buf));
if (cpu & RETRO_SIMD_AVX) strlcat(buf, " AVX", sizeof(buf));
if (cpu & RETRO_SIMD_AVX2) strlcat(buf, " AVX2", sizeof(buf));
if (cpu & RETRO_SIMD_NEON) strlcat(buf, " NEON", sizeof(buf));
if (cpu & RETRO_SIMD_VFPV3) strlcat(buf, " VFPv3", sizeof(buf));
if (cpu & RETRO_SIMD_VFPV4) strlcat(buf, " VFPv4", sizeof(buf));
if (cpu & RETRO_SIMD_VMX) strlcat(buf, " VMX", sizeof(buf));
if (cpu & RETRO_SIMD_VMX128) strlcat(buf, " VMX128", sizeof(buf));
if (cpu & RETRO_SIMD_VFPU) strlcat(buf, " VFPU", sizeof(buf));
if (cpu & RETRO_SIMD_PS) strlcat(buf, " PS", sizeof(buf));
if (cpu & RETRO_SIMD_ASIMD) strlcat(buf, " ASIMD", sizeof(buf));
return cpu;
}
void cpu_features_get_model_name(char *name, int len)
{
#if defined(CPU_X86) && !defined(__MACH__)
union {
int i[4];
unsigned char s[16];
} flags;
int i, j;
size_t pos = 0;
bool start = false;
if (!name)
return;
x86_cpuid(0x80000000, flags.i);
if (flags.i[0] < 0x80000004)
return;
for (i = 0; i < 3; i++)
{
memset(flags.i, 0, sizeof(flags.i));
x86_cpuid(0x80000002 + i, flags.i);
for (j = 0; j < sizeof(flags.s); j++)
{
if (!start && flags.s[j] == ' ')
continue;
else
start = true;
if (pos == len - 1)
{
/* truncate if we ran out of room */
name[pos] = '\0';
goto end;
}
name[pos++] = flags.s[j];
}
}
end:
/* terminate our string */
if (pos < (size_t)len)
name[pos] = '\0';
#elif defined(__MACH__)
if (!name)
return;
{
size_t len_size = len;
sysctlbyname("machdep.cpu.brand_string", name, &len_size, NULL, 0);
}
#else
if (!name)
return;
return;
#endif
}

View File

@@ -0,0 +1,18 @@
Copyright (c) 2013 Mikko Mononen memon@inside.org
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/application.hpp>
#include <borealis/header.hpp>
namespace brls
{
Header::Header(std::string label, bool separator, std::string sublabel)
: label(label)
, sublabel(sublabel)
, separator(separator)
{
Style* style = Application::getStyle();
this->setHeight(style->Header.height);
}
void Header::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
unsigned padding = style->Header.padding;
// Rectangle
nvgBeginPath(vg);
nvgFillColor(vg, a(ctx->theme->headerRectangleColor));
nvgRect(vg, x, y + padding, style->Header.rectangleWidth, height - padding * 2);
nvgFill(vg);
// Label
nvgBeginPath(vg);
nvgFontFaceId(vg, ctx->fontStash->regular);
nvgFontSize(vg, style->Header.fontSize);
nvgFillColor(vg, a(ctx->theme->textColor));
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgText(vg, x + style->Header.rectangleWidth + padding, y + height / 2, this->label.c_str(), nullptr);
// Sublabel
if (this->sublabel != "")
{
nvgBeginPath(vg);
nvgFontFaceId(vg, ctx->fontStash->regular);
nvgFontSize(vg, style->Header.fontSize);
nvgFillColor(vg, a(ctx->theme->descriptionColor));
nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE);
nvgText(vg, x + width - style->Header.rectangleWidth - padding, y + height / 2, this->sublabel.c_str(), nullptr);
}
// Separator
if (this->separator)
{
nvgBeginPath(vg);
nvgFillColor(vg, a(ctx->theme->listItemSeparatorColor));
nvgRect(vg, x, y + height, width, 1);
nvgFill(vg);
}
}
} // namespace brls

View File

@@ -0,0 +1,224 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2020 WerWolv
Copyright (C) 2020 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/actions.hpp>
#include <borealis/application.hpp>
#include <borealis/hint.hpp>
#include <borealis/label.hpp>
#include <set>
namespace brls
{
Hint::Hint(bool animate)
: BoxLayout(BoxLayoutOrientation::HORIZONTAL)
, animate(animate)
{
Style* style = Application::getStyle();
this->setGravity(BoxLayoutGravity::RIGHT);
this->setHeight(style->AppletFrame.footerHeight);
this->setSpacing(style->AppletFrame.footerTextSpacing);
// Subscribe to all events
this->globalFocusEventSubscriptor = Application::getGlobalFocusChangeEvent()->subscribe([this](View* newFocus) {
this->rebuildHints();
});
this->globalHintsUpdateEventSubscriptor = Application::getGlobalHintsUpdateEvent()->subscribe([this]() {
this->rebuildHints();
});
}
bool actionsSortFunc(Action a, Action b)
{
// From left to right:
// - first +
// - then all hints that are not B and A
// - finally B and A
// + is before all others
if (a.key == Key::PLUS)
return true;
// A is after all others
if (b.key == Key::A)
return true;
// B is after all others but A
if (b.key == Key::B && a.key != Key::A)
return true;
// Keep original order for the rest
return false;
}
void Hint::rebuildHints()
{
// Check if the focused element is still a child of the same parent as the hint view's
{
View* focusParent = Application::getCurrentFocus();
View* hintBaseParent = this;
while (focusParent != nullptr)
{
if (focusParent->getParent() == nullptr)
break;
focusParent = focusParent->getParent();
}
while (hintBaseParent != nullptr)
{
if (hintBaseParent->getParent() == nullptr)
break;
hintBaseParent = hintBaseParent->getParent();
}
if (focusParent != hintBaseParent)
return;
}
// Empty the layout and re-populate it with new Labels
this->clear(true);
std::set<Key> addedKeys; // we only ever want one action per key
View* focusParent = Application::getCurrentFocus();
// Iterate over the view tree to find all the actions to display
std::vector<Action> actions;
while (focusParent != nullptr)
{
for (auto& action : focusParent->getActions())
{
if (action.hidden)
continue;
if (addedKeys.find(action.key) != addedKeys.end())
continue;
addedKeys.insert(action.key);
actions.push_back(action);
}
focusParent = focusParent->getParent();
}
// Sort the actions
std::stable_sort(actions.begin(), actions.end(), actionsSortFunc);
// Populate the layout with labels
for (Action action : actions)
{
std::string hintText = Hint::getKeyIcon(action.key) + " " + action.hintText;
Label* label = new Label(LabelStyle::HINT, hintText);
this->addView(label);
}
}
Hint::~Hint()
{
// Unregister all events
Application::getGlobalFocusChangeEvent()->unsubscribe(this->globalFocusEventSubscriptor);
Application::getGlobalHintsUpdateEvent()->unsubscribe(this->globalHintsUpdateEventSubscriptor);
}
std::string Hint::getKeyIcon(Key key)
{
switch (key)
{
case Key::A:
return "\uE0E0";
case Key::B:
return "\uE0E1";
case Key::X:
return "\uE0E2";
case Key::Y:
return "\uE0E3";
case Key::LSTICK:
return "\uE104";
case Key::RSTICK:
return "\uE105";
case Key::L:
return "\uE0E4";
case Key::R:
return "\uE0E5";
case Key::PLUS:
return "\uE0EF";
case Key::MINUS:
return "\uE0F0";
case Key::DLEFT:
return "\uE0ED";
case Key::DUP:
return "\uE0EB";
case Key::DRIGHT:
return "\uE0EF";
case Key::DDOWN:
return "\uE0EC";
default:
return "\uE152";
}
}
void Hint::willAppear(bool resetState)
{
// Push ourself to hide other hints
// We assume that we are the top-most hint
Hint::pushHint(this);
}
void Hint::willDisappear(bool resetState)
{
// Pop ourself to show other hints
Hint::popHint(this);
}
void Hint::pushHint(Hint* hint)
{
// If the hint is already in the stack, remove it and put it back on top
Hint::globalHintStack.erase(std::remove(Hint::globalHintStack.begin(), Hint::globalHintStack.end(), hint), Hint::globalHintStack.end());
Hint::globalHintStack.push_back(hint);
//Trigger animation
Hint::animateHints();
}
void Hint::popHint(Hint* hint)
{
Hint::globalHintStack.erase(std::remove(Hint::globalHintStack.begin(), Hint::globalHintStack.end(), hint), Hint::globalHintStack.end());
Hint::animateHints();
// animateHints() won't call hide() on the hint since it's been removed from the stack
// but it doesn't matter since it is getting destroyed anyay
}
void Hint::animateHints()
{
for (size_t i = 0; i < Hint::globalHintStack.size(); i++)
{
// Last one = top one: show
if (i == Hint::globalHintStack.size() - 1)
Hint::globalHintStack[i]->show([]() {}, false);
else
Hint::globalHintStack[i]->hide([]() {}, false);
}
}
} // namespace brls

View File

@@ -0,0 +1,183 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 WerWolv
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/application.hpp>
#include <borealis/image.hpp>
#include <cstring>
namespace brls
{
Image::Image(std::string imagePath)
{
this->setImage(imagePath);
this->setOpacity(1.0F);
}
Image::Image(unsigned char* buffer, size_t bufferSize)
{
this->setImage(buffer, bufferSize);
this->setOpacity(1.0F);
}
Image::~Image()
{
if (this->imageBuffer != nullptr)
delete[] this->imageBuffer;
if (this->texture != -1)
nvgDeleteImage(Application::getNVGContext(), this->texture);
}
void Image::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
nvgSave(vg);
if (this->texture != -1)
{
nvgBeginPath(vg);
nvgRoundedRect(vg, x + this->imageX, y + this->imageY, this->imageWidth, this->imageHeight, this->cornerRadius);
nvgFillPaint(vg, a(this->imgPaint));
nvgFill(vg);
}
nvgRestore(vg);
}
void Image::reloadTexture()
{
NVGcontext* vg = Application::getNVGContext();
if (this->texture != -1)
nvgDeleteImage(vg, this->texture);
if (!this->imagePath.empty())
this->texture = nvgCreateImage(vg, this->imagePath.c_str(), 0);
else if (this->imageBuffer != nullptr)
this->texture = nvgCreateImageMem(vg, 0, this->imageBuffer, this->imageBufferSize);
}
void Image::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
if (this->origViewWidth == 0 || this->origViewHeight == 0)
{
this->origViewWidth = this->getWidth();
this->origViewHeight = this->getHeight();
}
nvgImageSize(vg, this->texture, &this->imageWidth, &this->imageHeight);
this->setWidth(this->origViewWidth);
this->setHeight(this->origViewHeight);
this->imageX = 0;
this->imageY = 0;
float viewAspectRatio = static_cast<float>(this->getWidth()) / static_cast<float>(this->getHeight());
float imageAspectRatio = static_cast<float>(this->imageWidth) / static_cast<float>(this->imageHeight);
switch (imageScaleType)
{
case ImageScaleType::NO_RESIZE:
this->imageX = (this->origViewWidth - this->imageWidth) / 2.0F;
this->imageY = (this->origViewHeight - this->imageHeight) / 2.0F;
break;
case ImageScaleType::FIT:
if (viewAspectRatio >= imageAspectRatio)
{
this->imageHeight = this->getHeight();
this->imageWidth = this->imageHeight * imageAspectRatio;
this->imageX = (this->origViewWidth - this->imageWidth) / 2.0F;
}
else
{
this->imageWidth = this->getWidth();
this->imageHeight = this->imageWidth * imageAspectRatio;
this->imageY = (this->origViewHeight - this->imageHeight) / 2.0F;
}
break;
case ImageScaleType::CROP:
if (viewAspectRatio < imageAspectRatio)
{
this->imageHeight = this->getHeight();
this->imageWidth = this->imageHeight * imageAspectRatio;
this->imageX = (this->origViewWidth - this->imageWidth) / 2.0F;
}
else
{
this->imageWidth = this->getWidth();
this->imageHeight = this->imageWidth * imageAspectRatio;
this->imageY = (this->origViewHeight - this->imageHeight) / 2.0F;
}
break;
case ImageScaleType::SCALE:
this->imageWidth = this->getWidth();
this->imageHeight = this->getHeight();
break;
case ImageScaleType::VIEW_RESIZE:
this->setWidth(this->imageWidth);
this->setHeight(this->imageHeight);
break;
}
this->imgPaint = nvgImagePattern(vg, getX() + this->imageX, getY() + this->imageY, this->imageWidth, this->imageHeight, 0, this->texture, this->alpha);
}
void Image::setImage(unsigned char* buffer, size_t bufferSize)
{
if (this->imageBuffer != nullptr)
delete[] this->imageBuffer;
this->imagePath = "";
this->imageBuffer = new unsigned char[bufferSize];
std::memcpy(this->imageBuffer, buffer, bufferSize);
this->imageBufferSize = bufferSize;
this->reloadTexture();
this->invalidate();
}
void Image::setImage(std::string imagePath)
{
this->imagePath = imagePath;
if (this->imageBuffer != nullptr)
delete[] this->imageBuffer;
this->imageBuffer = nullptr;
this->reloadTexture();
this->invalidate();
}
void Image::setOpacity(float opacity)
{
this->alpha = opacity;
this->invalidate();
}
void Image::setScaleType(ImageScaleType imageScaleType)
{
this->imageScaleType = imageScaleType;
this->invalidate();
}
} // namespace brls

View File

@@ -0,0 +1,229 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/application.hpp>
#include <borealis/label.hpp>
namespace brls
{
Label::Label(LabelStyle labelStyle, std::string text, bool multiline)
: text(text)
, multiline(multiline)
, labelStyle(labelStyle)
{
Style* style = Application::getStyle();
this->lineHeight = style->Label.lineHeight;
switch (labelStyle)
{
case LabelStyle::REGULAR:
this->fontSize = style->Label.regularFontSize;
break;
case LabelStyle::MEDIUM:
this->fontSize = style->Label.mediumFontSize;
break;
case LabelStyle::SMALL:
this->fontSize = style->Label.smallFontSize;
break;
case LabelStyle::DESCRIPTION:
this->fontSize = style->Label.descriptionFontSize;
break;
case LabelStyle::CRASH:
this->fontSize = style->Label.crashFontSize;
break;
case LabelStyle::BUTTON_PLAIN_DISABLED:
case LabelStyle::BUTTON_PLAIN:
case LabelStyle::BUTTON_BORDERLESS:
case LabelStyle::BUTTON_DIALOG:
this->fontSize = style->Label.buttonFontSize;
break;
case LabelStyle::LIST_ITEM:
this->fontSize = style->Label.listItemFontSize;
break;
case LabelStyle::NOTIFICATION:
this->fontSize = style->Label.notificationFontSize;
this->lineHeight = style->Label.notificationLineHeight;
break;
case LabelStyle::DIALOG:
this->fontSize = style->Label.dialogFontSize;
break;
case LabelStyle::HINT:
this->fontSize = style->Label.hintFontSize;
}
}
void Label::setHorizontalAlign(NVGalign align)
{
this->horizontalAlign = align;
}
void Label::setVerticalAlign(NVGalign align)
{
this->verticalAlign = align;
}
void Label::setFontSize(unsigned size)
{
this->fontSize = size;
if (this->getParent())
this->getParent()->invalidate();
}
void Label::setText(std::string text)
{
this->text = text;
if (this->hasParent())
this->getParent()->invalidate();
}
void Label::setStyle(LabelStyle style)
{
this->labelStyle = style;
}
void Label::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
nvgSave(vg);
nvgReset(vg);
nvgFontSize(vg, this->fontSize);
nvgTextAlign(vg, this->horizontalAlign | NVG_ALIGN_TOP);
nvgFontFaceId(vg, this->getFont(stash));
nvgTextLineHeight(vg, this->lineHeight);
float bounds[4];
// Update width or height to text bounds
if (this->multiline)
{
nvgTextBoxBounds(vg, this->x, this->y, this->width, this->text.c_str(), nullptr, bounds);
this->height = bounds[3] - bounds[1]; // ymax - ymin
}
else
{
nvgTextBounds(vg, this->x, this->y, this->text.c_str(), nullptr, bounds);
unsigned oldWidth = this->width;
this->width = bounds[2] - bounds[0]; // xmax - xmin
// offset the position to compensate the width change
// and keep right alignment
if (this->horizontalAlign == NVG_ALIGN_RIGHT)
this->x += oldWidth - this->width;
}
nvgRestore(vg);
}
void Label::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
nvgFillColor(vg, this->getColor(ctx->theme));
// Draw
nvgFontSize(vg, this->fontSize);
nvgFontFaceId(vg, this->getFont(ctx->fontStash));
if (this->multiline)
{
nvgTextLineHeight(vg, this->lineHeight);
nvgTextAlign(vg, this->horizontalAlign | NVG_ALIGN_TOP);
nvgBeginPath(vg);
nvgTextBox(vg, x, y, width, this->text.c_str(), nullptr);
}
else
{
nvgTextLineHeight(vg, 1.0f);
nvgTextAlign(vg, this->horizontalAlign | this->verticalAlign);
nvgBeginPath(vg);
if (this->horizontalAlign == NVG_ALIGN_RIGHT)
x += width;
else if (this->horizontalAlign == NVG_ALIGN_CENTER)
x += width / 2;
// TODO: Ticker
if (this->verticalAlign == NVG_ALIGN_BOTTOM)
nvgText(vg, x, y + height, this->text.c_str(), nullptr);
else
nvgText(vg, x, y + height / 2, this->text.c_str(), nullptr);
}
}
void Label::setColor(NVGcolor color)
{
this->customColor = color;
this->useCustomColor = true;
}
void Label::unsetColor()
{
this->useCustomColor = false;
}
NVGcolor Label::getColor(ThemeValues* theme)
{
// Use custom color if any
if (this->useCustomColor)
return a(this->customColor);
switch (this->labelStyle)
{
case LabelStyle::DESCRIPTION:
return a(theme->descriptionColor);
case LabelStyle::CRASH:
return RGB(255, 255, 255);
case LabelStyle::BUTTON_PLAIN:
return a(theme->buttonPlainEnabledTextColor);
case LabelStyle::BUTTON_PLAIN_DISABLED:
return a(theme->buttonPlainDisabledTextColor);
case LabelStyle::NOTIFICATION:
return a(theme->notificationTextColor);
case LabelStyle::BUTTON_DIALOG:
return a(theme->dialogButtonColor);
default:
return a(theme->textColor);
}
}
void Label::setFont(int font)
{
this->customFont = font;
this->useCustomFont = true;
}
void Label::unsetFont()
{
this->useCustomFont = false;
}
int Label::getFont(FontStash* stash)
{
if (this->useCustomFont)
return this->customFont;
return stash->regular;
}
} // namespace brls

View File

@@ -0,0 +1,127 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 WerWolv
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/application.hpp>
#include <borealis/layer_view.hpp>
namespace brls
{
LayerView::LayerView()
{
}
LayerView::~LayerView()
{
for (unsigned int i = 0; i < this->layers.size(); i++)
delete this->layers[i];
this->layers.clear();
}
void LayerView::addLayer(View* view)
{
if (view)
{
view->setParent(this);
this->layers.push_back(view);
}
}
void LayerView::changeLayer(int index, bool focus)
{
if (index >= 0 && index < static_cast<int>(this->layers.size()))
{
Application::blockInputs();
if (this->selectedIndex >= 0)
{
this->layers[this->selectedIndex]->willDisappear(true);
this->layers[this->selectedIndex]->hide([]() {});
}
this->selectedIndex = index;
this->layers[this->selectedIndex]->willAppear(true);
this->layers[this->selectedIndex]->show([=]() {
if (focus)
Application::giveFocus(this->layers[this->selectedIndex]->getDefaultFocus());
Application::unblockInputs();
});
this->layers[index]->invalidate();
}
if (index == -1)
{
if (this->selectedIndex > 0)
{
this->layers[this->selectedIndex]->willDisappear(true);
this->layers[this->selectedIndex]->hide([]() {});
}
this->selectedIndex = index;
}
}
int LayerView::getLayerIndex()
{
return this->selectedIndex;
}
View* LayerView::getDefaultFocus()
{
if (this->selectedIndex >= 0 && this->selectedIndex < static_cast<int>(this->layers.size()))
{
View* newFocus = this->layers[this->selectedIndex]->getDefaultFocus();
if (newFocus)
{
return newFocus;
}
}
return nullptr;
}
void LayerView::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
if (this->selectedIndex >= 0 && this->selectedIndex < static_cast<int>(this->layers.size()))
this->layers[this->selectedIndex]->frame(ctx);
}
void LayerView::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
if (this->selectedIndex >= 0 && this->selectedIndex < static_cast<int>(this->layers.size()))
{
this->layers[this->selectedIndex]->setBoundaries(this->getX(), this->getY(), this->getWidth(), this->getHeight());
this->layers[this->selectedIndex]->invalidate();
}
}
void LayerView::willAppear(bool resetState)
{
if (this->selectedIndex >= 0 && this->selectedIndex < static_cast<int>(this->layers.size()))
this->layers[this->selectedIndex]->willAppear(true);
}
void LayerView::willDisappear(bool resetState)
{
if (this->selectedIndex >= 0 && this->selectedIndex < static_cast<int>(this->layers.size()))
this->layers[this->selectedIndex]->willDisappear(true);
}
}

View File

@@ -0,0 +1,623 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019-2020 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <borealis/animations.hpp>
#include <borealis/application.hpp>
#include <borealis/dropdown.hpp>
#include <borealis/header.hpp>
#include <borealis/list.hpp>
#include <borealis/logger.hpp>
#include <borealis/swkbd.hpp>
#include <borealis/table.hpp>
// TODO: Scrollbar
namespace brls
{
ListContentView::ListContentView(List* list, size_t defaultFocus)
: BoxLayout(BoxLayoutOrientation::VERTICAL, defaultFocus)
, list(list)
{
Style* style = Application::getStyle();
this->setMargins(style->List.marginTopBottom, style->List.marginLeftRight, style->List.marginTopBottom, style->List.marginLeftRight);
this->setSpacing(style->List.spacing);
this->setRememberFocus(true);
}
void ListContentView::customSpacing(View* current, View* next, int* spacing)
{
// Don't add spacing to the first list item
// if it doesn't have a description and the second one is a
// list item too
// Or if the next item is a ListItemGroupSpacing
if (ListItem* currentItem = dynamic_cast<ListItem*>(current))
{
if (currentItem->getReduceDescriptionSpacing())
{
if (next != nullptr)
*spacing /= 2;
}
else if (ListItem* nextItem = dynamic_cast<ListItem*>(next))
{
if (!currentItem->hasDescription())
{
*spacing = 2;
nextItem->setDrawTopSeparator(currentItem->isCollapsed());
}
}
else if (dynamic_cast<ListItemGroupSpacing*>(next))
{
*spacing = 0;
}
else if (dynamic_cast<Table*>(next))
{
*spacing /= 2;
}
}
// Table custom spacing
else if (dynamic_cast<Table*>(current))
{
*spacing /= 2;
}
// ListItemGroupSpacing custom spacing
else if (dynamic_cast<ListItemGroupSpacing*>(current))
{
*spacing /= 2;
}
// Header custom spacing
else if (dynamic_cast<Header*>(current) || dynamic_cast<Header*>(next))
{
if (dynamic_cast<Header*>(current) && dynamic_cast<ListItem*>(next))
{
*spacing = 1;
}
else if (dynamic_cast<Label*>(current) && dynamic_cast<Header*>(next))
{
// Keep default spacing
}
else
{
Style* style = Application::getStyle();
*spacing = style->Header.padding;
}
}
// Call list custom spacing
if (this->list)
this->list->customSpacing(current, next, spacing);
}
ListItem::ListItem(std::string label, std::string description, std::string subLabel)
: label(label)
, subLabel(subLabel)
{
Style* style = Application::getStyle();
this->setHeight(subLabel != "" ? style->List.Item.heightWithSubLabel : style->List.Item.height);
this->setTextSize(style->Label.listItemFontSize);
if (description != "")
{
this->descriptionView = new Label(LabelStyle::DESCRIPTION, description, true);
this->descriptionView->setParent(this);
}
this->registerAction("OK", Key::A, [this] { return this->onClick(); });
}
void ListItem::setThumbnail(Image* image)
{
if (this->thumbnailView)
delete this->thumbnailView;
if (image != NULL)
{
this->thumbnailView = image;
this->thumbnailView->setParent(this);
this->invalidate();
}
}
void ListItem::setThumbnail(std::string imagePath)
{
if (this->thumbnailView)
this->thumbnailView->setImage(imagePath);
else
this->thumbnailView = new Image(imagePath);
this->thumbnailView->setParent(this);
this->thumbnailView->setScaleType(ImageScaleType::FIT);
this->invalidate();
}
void ListItem::setThumbnail(unsigned char* buffer, size_t bufferSize)
{
if (this->thumbnailView)
this->thumbnailView->setImage(buffer, bufferSize);
else
this->thumbnailView = new Image(buffer, bufferSize);
this->thumbnailView->setParent(this);
this->thumbnailView->setScaleType(ImageScaleType::FIT);
this->invalidate();
}
bool ListItem::getReduceDescriptionSpacing()
{
return this->reduceDescriptionSpacing;
}
void ListItem::setReduceDescriptionSpacing(bool value)
{
this->reduceDescriptionSpacing = value;
}
void ListItem::setIndented(bool indented)
{
this->indented = indented;
}
void ListItem::setTextSize(unsigned textSize)
{
this->textSize = textSize;
}
void ListItem::setChecked(bool checked)
{
this->checked = checked;
}
bool ListItem::onClick()
{
return this->clickEvent.fire(this);
}
GenericEvent* ListItem::getClickEvent()
{
return &this->clickEvent;
}
void ListItem::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
// Description
if (this->descriptionView)
{
unsigned indent = style->List.Item.descriptionIndent;
if (this->indented)
indent += style->List.Item.indent;
this->height = style->List.Item.height;
this->descriptionView->setBoundaries(this->x + indent, this->y + this->height + style->List.Item.descriptionSpacing, this->width - indent * 2, 0);
this->descriptionView->invalidate(true); // we must call layout directly
this->height += this->descriptionView->getHeight() + style->List.Item.descriptionSpacing;
}
// Thumbnail
if (this->thumbnailView)
{
Style* style = Application::getStyle();
unsigned thumbnailSize = height - style->List.Item.thumbnailPadding * 2;
this->thumbnailView->setBoundaries(
x + style->List.Item.thumbnailPadding,
y + style->List.Item.thumbnailPadding,
thumbnailSize,
thumbnailSize);
this->thumbnailView->invalidate();
}
}
void ListItem::getHighlightInsets(unsigned* top, unsigned* right, unsigned* bottom, unsigned* left)
{
Style* style = Application::getStyle();
View::getHighlightInsets(top, right, bottom, left);
if (descriptionView)
*bottom = -(descriptionView->getHeight() + style->List.Item.descriptionSpacing);
if (indented)
*left = -style->List.Item.indent;
}
void ListItem::resetValueAnimation()
{
this->valueAnimation = 0.0f;
menu_animation_ctx_tag tag = (uintptr_t) & this->valueAnimation;
menu_animation_kill_by_tag(&tag);
}
void ListItem::setValue(std::string value, bool faint, bool animate)
{
this->oldValue = this->value;
this->oldValueFaint = this->valueFaint;
this->value = value;
this->valueFaint = faint;
this->resetValueAnimation();
if (animate && this->oldValue != "")
{
Style* style = Application::getStyle();
menu_animation_ctx_tag tag = (uintptr_t) & this->valueAnimation;
menu_animation_ctx_entry_t entry;
entry.cb = [this](void* userdata) { this->resetValueAnimation(); };
entry.duration = style->AnimationDuration.highlight;
entry.easing_enum = EASING_IN_OUT_QUAD;
entry.subject = &this->valueAnimation;
entry.tag = tag;
entry.target_value = 1.0f;
entry.tick = [](void* userdata) {};
entry.userdata = nullptr;
menu_animation_push(&entry);
}
}
std::string ListItem::getValue()
{
return this->value;
}
void ListItem::setDrawTopSeparator(bool draw)
{
this->drawTopSeparator = draw;
}
View* ListItem::getDefaultFocus()
{
if (this->collapseState != 1.0f)
return nullptr;
return this;
}
void ListItem::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
unsigned baseHeight = this->height;
bool hasSubLabel = this->subLabel != "";
bool hasThumbnail = this->thumbnailView;
unsigned leftPadding = hasThumbnail ? this->thumbnailView->getWidth() + style->List.Item.thumbnailPadding * 2 : style->List.Item.padding;
if (this->indented)
{
x += style->List.Item.indent;
width -= style->List.Item.indent;
}
// Description
if (this->descriptionView)
{
// Don't count description as part of list item
baseHeight -= this->descriptionView->getHeight() + style->List.Item.descriptionSpacing;
this->descriptionView->frame(ctx);
}
// Value
unsigned valueX = x + width - style->List.Item.padding;
unsigned valueY = y + (hasSubLabel ? baseHeight - baseHeight / 3 : baseHeight / 2);
nvgTextAlign(vg, NVG_ALIGN_RIGHT | (hasSubLabel ? NVG_ALIGN_TOP : NVG_ALIGN_MIDDLE));
nvgFontFaceId(vg, ctx->fontStash->regular);
if (this->valueAnimation != 0.0f)
{
//Old value
NVGcolor valueColor = a(this->oldValueFaint ? ctx->theme->listItemFaintValueColor : ctx->theme->listItemValueColor);
valueColor.a *= (1.0f - this->valueAnimation);
nvgFillColor(vg, valueColor);
nvgFontSize(vg, style->List.Item.valueSize * (1.0f - this->valueAnimation));
nvgBeginPath(vg);
nvgText(vg, valueX, valueY, this->oldValue.c_str(), nullptr);
//New value
valueColor = a(this->valueFaint ? ctx->theme->listItemFaintValueColor : ctx->theme->listItemValueColor);
valueColor.a *= this->valueAnimation;
nvgFillColor(vg, valueColor);
nvgFontSize(vg, style->List.Item.valueSize * this->valueAnimation);
nvgBeginPath(vg);
nvgText(vg, valueX, valueY, this->value.c_str(), nullptr);
}
else
{
nvgFillColor(vg, a(this->valueFaint ? ctx->theme->listItemFaintValueColor : ctx->theme->listItemValueColor));
nvgFontSize(vg, style->List.Item.valueSize);
nvgFontFaceId(vg, ctx->fontStash->regular);
nvgBeginPath(vg);
nvgText(vg, valueX, valueY, this->value.c_str(), nullptr);
}
// Checked marker
if (this->checked)
{
unsigned radius = style->List.Item.selectRadius;
unsigned centerX = x + width - radius - style->List.Item.padding;
unsigned centerY = y + baseHeight / 2;
float radiusf = (float)radius;
int thickness = roundf(radiusf * 0.10f);
// Background
nvgFillColor(vg, a(ctx->theme->listItemValueColor));
nvgBeginPath(vg);
nvgCircle(vg, centerX, centerY, radiusf);
nvgFill(vg);
// Check mark
nvgFillColor(vg, a(ctx->theme->backgroundColorRGB));
// Long stroke
nvgSave(vg);
nvgTranslate(vg, centerX, centerY);
nvgRotate(vg, -NVG_PI / 4.0f);
nvgBeginPath(vg);
nvgRect(vg, -(radiusf * 0.55f), 0, radiusf * 1.3f, thickness);
nvgFill(vg);
nvgRestore(vg);
// Short stroke
nvgSave(vg);
nvgTranslate(vg, centerX - (radiusf * 0.65f), centerY);
nvgRotate(vg, NVG_PI / 4.0f);
nvgBeginPath(vg);
nvgRect(vg, 0, -(thickness / 2), radiusf * 0.53f, thickness);
nvgFill(vg);
nvgRestore(vg);
}
// Label
nvgFillColor(vg, a(ctx->theme->textColor));
nvgFontSize(vg, this->textSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
nvgFontFaceId(vg, ctx->fontStash->regular);
nvgBeginPath(vg);
nvgText(vg, x + leftPadding, y + baseHeight / (hasSubLabel ? 3 : 2), this->label.c_str(), nullptr);
// Sub Label
if (hasSubLabel)
{
nvgFillColor(vg, a(ctx->theme->descriptionColor));
nvgFontSize(vg, style->Label.descriptionFontSize);
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
nvgFontFaceId(vg, ctx->fontStash->regular);
nvgBeginPath(vg);
nvgText(vg, x + leftPadding, y + baseHeight - baseHeight / 3, this->subLabel.c_str(), nullptr);
}
// Thumbnail
if (hasThumbnail)
this->thumbnailView->frame(ctx);
// Separators
// Offset by one to be hidden by highlight
nvgFillColor(vg, a(ctx->theme->listItemSeparatorColor));
// Top
if (this->drawTopSeparator)
{
nvgBeginPath(vg);
nvgRect(vg, x, y - 1, width, 1);
nvgFill(vg);
}
// Bottom
nvgBeginPath(vg);
nvgRect(vg, x, y + 1 + baseHeight, width, 1);
nvgFill(vg);
}
bool ListItem::hasDescription()
{
return this->descriptionView;
}
std::string ListItem::getLabel()
{
return this->label;
}
ListItem::~ListItem()
{
if (this->descriptionView)
delete this->descriptionView;
if (this->thumbnailView)
delete this->thumbnailView;
this->resetValueAnimation();
}
ToggleListItem::ToggleListItem(std::string label, bool initialValue, std::string description, std::string onValue, std::string offValue)
: ListItem(label, description)
, toggleState(initialValue)
, onValue(onValue)
, offValue(offValue)
{
this->updateValue();
}
void ToggleListItem::updateValue()
{
if (this->toggleState)
this->setValue(this->onValue, false);
else
this->setValue(this->offValue, true);
}
bool ToggleListItem::onClick()
{
this->toggleState = !this->toggleState;
this->updateValue();
ListItem::onClick();
return true;
}
bool ToggleListItem::getToggleState()
{
return this->toggleState;
}
InputListItem::InputListItem(std::string label, std::string initialValue, std::string helpText, std::string description, int maxInputLength)
: ListItem(label, description)
, helpText(helpText)
, maxInputLength(maxInputLength)
{
this->setValue(initialValue, false);
}
bool InputListItem::onClick()
{
Swkbd::openForText([&](std::string text) {
this->setValue(text, false);
},
this->helpText, "", this->maxInputLength, this->getValue());
ListItem::onClick();
return true;
}
IntegerInputListItem::IntegerInputListItem(std::string label, int initialValue, std::string helpText, std::string description, int maxInputLength)
: InputListItem(label, std::to_string(initialValue), helpText, description, maxInputLength)
{
}
bool IntegerInputListItem::onClick()
{
Swkbd::openForNumber([&](int number) {
this->setValue(std::to_string(number), false);
},
this->helpText, "", this->maxInputLength, this->getValue());
ListItem::onClick();
return true;
}
ListItemGroupSpacing::ListItemGroupSpacing(bool separator)
: Rectangle(nvgRGBA(0, 0, 0, 0))
{
ThemeValues* theme = Application::getThemeValues();
if (separator)
this->setColor(theme->listItemSeparatorColor);
}
SelectListItem::SelectListItem(std::string label, std::vector<std::string> values, unsigned selectedValue)
: ListItem(label, "")
, values(values)
, selectedValue(selectedValue)
{
this->setValue(values[selectedValue], false, false);
this->getClickEvent()->subscribe([this](View* view) {
ValueSelectedEvent::Callback valueCallback = [this](int result) {
if (result == -1)
return;
this->setValue(this->values[result], false, false);
this->selectedValue = result;
this->valueEvent.fire(result);
};
Dropdown::open(this->getLabel(), this->values, valueCallback, this->selectedValue);
});
}
void SelectListItem::setSelectedValue(unsigned value)
{
if (value >= 0 && value < this->values.size())
{
this->selectedValue = value;
this->setValue(this->values[value], false, false);
}
}
ValueSelectedEvent* SelectListItem::getValueSelectedEvent()
{
return &this->valueEvent;
}
List::List(size_t defaultFocus)
{
this->layout = new ListContentView(this, defaultFocus);
this->layout->setResize(true);
this->layout->setParent(this);
this->setContentView(this->layout);
}
// Wrapped BoxLayout methods
void List::addView(View* view, bool fill)
{
this->layout->addView(view, fill);
}
void List::clear(bool free)
{
this->layout->clear(free);
}
void List::setMargins(unsigned top, unsigned right, unsigned bottom, unsigned left)
{
this->layout->setMargins(
top,
right,
bottom,
left);
}
void List::setSpacing(unsigned spacing)
{
this->layout->setSpacing(spacing);
}
unsigned List::getSpacing()
{
return this->layout->getSpacing();
}
void List::setMarginBottom(unsigned bottom)
{
this->layout->setMarginBottom(bottom);
}
void List::customSpacing(View* current, View* next, int* spacing)
{
// Nothing to do by default
}
List::~List()
{
// ScrollView already deletes the content view
}
} // namespace brls

View File

@@ -0,0 +1,73 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include <stdio.h>
#include <borealis/logger.hpp>
namespace brls
{
static LogLevel g_logLevel = LogLevel::INFO;
void Logger::setLogLevel(LogLevel newLogLevel)
{
g_logLevel = newLogLevel;
}
void Logger::log(LogLevel logLevel, const char* prefix, const char* color, const char* format, va_list ap)
{
if (g_logLevel < logLevel)
return;
printf("\033%s[%s]\033[0m ", color, prefix);
vprintf(format, ap);
printf("\n");
#ifdef __MINGW32__
fflush(0);
#endif
}
void Logger::error(const char* format, ...)
{
va_list ap;
va_start(ap, format);
Logger::log(LogLevel::ERROR, "ERROR", "[0;31m", format, ap);
va_end(ap);
}
void Logger::info(const char* format, ...)
{
va_list ap;
va_start(ap, format);
Logger::log(LogLevel::INFO, "INFO", "[0;34m", format, ap);
va_end(ap);
}
void Logger::debug(const char* format, ...)
{
va_list ap;
va_start(ap, format);
Logger::log(LogLevel::DEBUG, "DEBUG", "[0;32m", format, ap);
va_end(ap);
}
} // namespace brls

View File

@@ -0,0 +1,48 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/material_icon.hpp>
namespace brls
{
MaterialIcon::MaterialIcon(std::string icon)
: icon(icon)
{
}
void MaterialIcon::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
NVGcolor color = a(ctx->theme->textColor);
nvgTextLineHeight(vg, 1.0f);
nvgFillColor(vg, color);
nvgFontSize(vg, height);
nvgFontFaceId(vg, ctx->fontStash->material);
nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
nvgBeginPath(vg);
nvgText(vg, this->middleX, this->middleY, this->icon.c_str(), nullptr);
}
void MaterialIcon::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
this->middleX = this->getX() + this->getWidth() / 2;
this->middleY = this->getY() + this->getHeight() / 2;
}
}; // namespace brls

View File

@@ -0,0 +1,205 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/application.hpp>
#include <borealis/logger.hpp>
#include <borealis/notification_manager.hpp>
#include <cstring>
// TODO: add a timeout duration enum for longer notifications
namespace brls
{
NotificationManager::NotificationManager()
{
std::memset(this->notifications, 0, sizeof(Notification*) * BRLS_NOTIFICATIONS_MAX);
}
void NotificationManager::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
for (size_t i = 0; i < BRLS_NOTIFICATIONS_MAX; i++)
{
if (this->notifications[i])
{
Notification* notification = this->notifications[i];
float alpha = notification->getAlpha();
float translation = 0.0f;
if (alpha != 1.0f)
{
translation = (1.0f - alpha) * (float)style->Notification.slideAnimation;
nvgTranslate(vg, translation, 0);
}
this->notifications[i]->frame(ctx);
if (alpha != 1.0f)
nvgTranslate(vg, -translation, 0);
}
}
}
void NotificationManager::notify(std::string text)
{
// Find the first available notification slot
bool found = false;
size_t i = 0;
for (i = 0; i < BRLS_NOTIFICATIONS_MAX; i++)
{
if (!this->notifications[i])
{
found = true;
break;
}
}
if (!found)
{
brls::Logger::info("Discarding notification \"%s\"", text.c_str());
return;
}
// Create the notification
brls::Logger::info("Showing notification \"%s\"", text.c_str());
Notification* notification = new Notification(text);
notification->setParent(this);
notification->show([]() {});
// Timeout timer
menu_timer_ctx_entry_t entry;
entry.duration = Application::getStyle()->AnimationDuration.notificationTimeout;
entry.tick = [](void*) {};
entry.userdata = nullptr;
entry.cb = [this, notification, i](void* userdata) {
notification->hide([this, notification, i]() {
delete notification;
this->notifications[i] = nullptr;
});
};
menu_timer_start(&notification->timeoutTimer, &entry);
this->notifications[i] = notification;
// Layout the notification
this->layoutNotification(i);
}
void NotificationManager::layoutNotification(size_t index)
{
Notification* notification = this->notifications[index];
if (!notification)
return;
Style* style = Application::getStyle();
// Get the position of the last notification
Notification* lastNotification = nullptr;
for (size_t i = 0; i < index; i++)
{
if (this->notifications[i])
lastNotification = this->notifications[i];
}
unsigned y = lastNotification ? (lastNotification->getY() + lastNotification->getHeight()) : 0;
// Layout the notification
unsigned width = style->Notification.width;
notification->setBoundaries(
this->getX() + this->getWidth() - width,
this->getY() + y,
width,
0 // height is dynamic
);
notification->invalidate(); // TODO: call layout directly to fix posting multiple notifications in one frame
}
void NotificationManager::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
for (size_t i = 0; i < BRLS_NOTIFICATIONS_MAX; i++)
{
if (this->notifications[i])
this->layoutNotification(i);
}
}
NotificationManager::~NotificationManager()
{
for (size_t i = 0; i < BRLS_NOTIFICATIONS_MAX; i++)
{
if (this->notifications[i])
delete this->notifications[i];
}
}
Notification::Notification(std::string text)
{
this->setBackground(Background::BACKDROP);
this->label = new Label(LabelStyle::NOTIFICATION, text, true);
label->setParent(this);
}
Notification::~Notification()
{
delete this->label;
}
void Notification::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
this->label->frame(ctx);
}
void Notification::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
unsigned padding = style->Notification.padding;
unsigned fontSize = style->Label.notificationFontSize;
float lineHeight = style->Label.notificationLineHeight;
// Layout the label
this->label->setWidth(this->getWidth() - padding * 2);
this->label->setHeight(0); // height is dynamic
this->label->invalidate(true); // layout directly to update height
unsigned minLabelHeight = (unsigned int)(lineHeight * fontSize) + fontSize; // 2 lines
unsigned labelYAdvance = padding;
if (this->label->getHeight() < minLabelHeight)
{
labelYAdvance += (minLabelHeight - this->label->getHeight()) / 2;
}
this->label->setBoundaries(
this->getX() + padding,
this->getY() + labelYAdvance,
this->label->getWidth(),
this->label->getHeight());
// Update our own height
this->setHeight(std::max(
this->label->getHeight() + padding * 2,
minLabelHeight + padding * 2));
}
}; // namespace brls

View File

@@ -0,0 +1,163 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 natinusala
Copyright (C) 2019 WerWolv
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/animations.hpp>
#include <borealis/application.hpp>
#include <borealis/logger.hpp>
#include <borealis/popup_frame.hpp>
namespace brls
{
PopupFrame::PopupFrame(std::string title, unsigned char* imageBuffer, size_t imageBufferSize, AppletFrame* contentView, std::string subTitleLeft, std::string subTitleRight)
: contentView(contentView)
{
if (this->contentView)
{
this->contentView->setParent(this);
this->contentView->setHeaderStyle(HeaderStyle::POPUP);
this->contentView->setTitle(title);
this->contentView->setSubtitle(subTitleLeft, subTitleRight);
this->contentView->setIcon(imageBuffer, imageBufferSize);
this->contentView->invalidate();
}
contentView->setAnimateHint(true);
this->registerAction("Back", Key::B, [this] { return this->onCancel(); });
}
PopupFrame::PopupFrame(std::string title, std::string imagePath, AppletFrame* contentView, std::string subTitleLeft, std::string subTitleRight)
: contentView(contentView)
{
if (this->contentView)
{
this->contentView->setParent(this);
this->contentView->setHeaderStyle(HeaderStyle::POPUP);
this->contentView->setTitle(title);
this->contentView->setSubtitle(subTitleLeft, subTitleRight);
this->contentView->setIcon(imagePath);
this->contentView->invalidate();
}
contentView->setAnimateHint(true);
this->registerAction("Back", Key::B, [this] { return this->onCancel(); });
}
PopupFrame::PopupFrame(std::string title, AppletFrame* contentView, std::string subTitleLeft, std::string subTitleRight)
: contentView(contentView)
{
if (this->contentView)
{
this->contentView->setParent(this);
this->contentView->setHeaderStyle(HeaderStyle::POPUP);
this->contentView->setTitle(title);
this->contentView->setSubtitle(subTitleLeft, subTitleRight);
this->contentView->invalidate();
}
contentView->setAnimateHint(true);
this->registerAction("Back", Key::B, [this] { return this->onCancel(); });
}
void PopupFrame::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
// Backdrop
nvgFillColor(vg, a(ctx->theme->dropdownBackgroundColor));
nvgBeginPath(vg);
nvgRect(vg, 0, y, width, height);
nvgFill(vg);
// Background
nvgFillColor(vg, a(ctx->theme->backgroundColorRGB));
nvgBeginPath(vg);
nvgRect(vg, style->PopupFrame.edgePadding, y, width - style->PopupFrame.edgePadding * 2, height);
nvgFill(vg);
// TODO: Shadow
// Content view
nvgSave(vg);
nvgScissor(vg, style->PopupFrame.edgePadding, 0, style->PopupFrame.contentWidth, height);
this->contentView->frame(ctx);
nvgRestore(vg);
}
bool PopupFrame::onCancel()
{
Application::popView();
return true;
}
unsigned PopupFrame::getShowAnimationDuration(ViewAnimation animation)
{
return View::getShowAnimationDuration(animation) / 2;
}
void PopupFrame::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
this->contentView->setBoundaries(style->PopupFrame.edgePadding, 0, style->PopupFrame.contentWidth, this->getHeight());
this->contentView->invalidate();
}
View* PopupFrame::getDefaultFocus()
{
if (this->contentView)
return this->contentView->getDefaultFocus();
return nullptr;
}
void PopupFrame::open(std::string title, unsigned char* imageBuffer, size_t imageBufferSize, AppletFrame* contentView, std::string subTitleLeft, std::string subTitleRight)
{
PopupFrame* popupFrame = new PopupFrame(title, imageBuffer, imageBufferSize, contentView, subTitleLeft, subTitleRight);
Application::pushView(popupFrame);
}
void PopupFrame::open(std::string title, std::string imagePath, AppletFrame* contentView, std::string subTitleLeft, std::string subTitleRight)
{
PopupFrame* popupFrame = new PopupFrame(title, imagePath, contentView, subTitleLeft, subTitleRight);
Application::pushView(popupFrame);
}
void PopupFrame::open(std::string title, AppletFrame* contentView, std::string subTitleLeft, std::string subTitleRight)
{
PopupFrame* popupFrame = new PopupFrame(title, contentView, subTitleLeft, subTitleRight);
Application::pushView(popupFrame);
}
void PopupFrame::willAppear(bool resetState)
{
this->contentView->willAppear(resetState);
}
void PopupFrame::willDisappear(bool resetState)
{
this->contentView->willDisappear(resetState);
}
PopupFrame::~PopupFrame()
{
if (this->contentView)
delete this->contentView;
}
} // namespace brls

View File

@@ -0,0 +1,134 @@
/*
Borealis, a Nintendo Switch UI Library
Copyright (C) 2019 Billy Laws
Copyright (C) 2019 p-sam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <borealis/application.hpp>
#include <borealis/progress_display.hpp>
namespace brls
{
ProgressDisplay::ProgressDisplay(ProgressDisplayFlags progressFlags)
{
if (progressFlags & ProgressDisplayFlags::PERCENTAGE)
this->label = new Label(LabelStyle::DIALOG, "0%", false);
if (progressFlags & ProgressDisplayFlags::SPINNER)
this->spinner = new ProgressSpinner();
}
void ProgressDisplay::setProgress(int current, int max)
{
if (current > max)
return;
this->progressPercentage = ((current * 100) / max);
if (!this->label)
return;
std::string labelText = std::to_string((unsigned)this->progressPercentage);
labelText += "%";
this->label->setText(labelText);
}
void ProgressDisplay::layout(NVGcontext* vg, Style* style, FontStash* stash)
{
if (this->label)
{
this->label->setWidth(style->ProgressDisplay.percentageLabelWidth);
this->label->invalidate(true);
this->label->setBoundaries(
this->x + this->width - this->label->getWidth() / 2,
this->y + this->height / 2 - this->label->getHeight() / 2,
this->label->getWidth(),
this->label->getHeight());
}
if (this->spinner)
{
this->spinner->setWidth(this->height);
this->spinner->setHeight(this->height);
this->spinner->setBoundaries(
this->x,
this->y,
this->spinner->getWidth(),
this->spinner->getHeight());
}
}
void ProgressDisplay::draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, Style* style, FrameContext* ctx)
{
unsigned progressBarWidth = width;
unsigned progressBarX = x;
if (this->label)
{
progressBarWidth -= this->label->getWidth();
this->label->frame(ctx);
}
if (this->spinner)
{
// 1.25 accounts for the extra width of the curve on sides of progress bar
progressBarWidth -= this->spinner->getWidth() * 1.25f;
progressBarX += this->spinner->getWidth() * 1.25f;
this->spinner->frame(ctx);
}
nvgBeginPath(vg);
nvgMoveTo(vg, progressBarX, y + height / 2);
nvgLineTo(vg, progressBarX + progressBarWidth, y + height / 2);
nvgStrokeColor(vg, a(ctx->theme->listItemSeparatorColor));
nvgStrokeWidth(vg, height / 3);
nvgLineCap(vg, NVG_ROUND);
nvgStroke(vg);
if (this->progressPercentage > 0.0f)
{
nvgBeginPath(vg);
nvgMoveTo(vg, progressBarX, y + height / 2);
nvgLineTo(vg, progressBarX + ((float)progressBarWidth * this->progressPercentage) / 100, y + height / 2);
nvgStrokeColor(vg, a(ctx->theme->listItemValueColor));
nvgStrokeWidth(vg, height / 3);
nvgLineCap(vg, NVG_ROUND);
nvgStroke(vg);
}
}
void ProgressDisplay::willAppear(bool resetState)
{
if (this->spinner)
this->spinner->willAppear(resetState);
}
void ProgressDisplay::willDisappear(bool resetState)
{
if (this->spinner)
this->spinner->willDisappear(resetState);
}
ProgressDisplay::~ProgressDisplay()
{
if (this->spinner)
delete this->spinner;
if (this->label)
delete this->label;
}
} // namespace brls

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