Compare commits
197 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b29242862b | |||
| 32e2fb5d33 | |||
| 04efd729d6 | |||
| 21359936b8 | |||
| be95b5a954 | |||
| bbaa08d7f0 | |||
| 50b10c4bac | |||
| 9c85cb354a | |||
| d79274a5d9 | |||
| 4778656110 | |||
| 3cf6593342 | |||
| bbc585881a | |||
| 159ae5e47c | |||
| 6a071c42d2 | |||
| 1f275eb077 | |||
| c589db6add | |||
| fa5277ecdb | |||
| 4265372099 | |||
| 03d5794183 | |||
| 827a901153 | |||
| 50192eb4ad | |||
| a02fd4cddd | |||
| 4d5900aaa1 | |||
| 5ca67332ee | |||
| 172137f1a0 | |||
| 0cfcee95c7 | |||
| 52ebdd42c6 | |||
| eb8c8db899 | |||
| 37199c5f90 | |||
| 8c089f4e2a | |||
| 12dc4d0527 | |||
| 0c1b954e07 | |||
| e4c3565ebe | |||
| 9af501b75d | |||
| 7f11710e0a | |||
| ce859cf526 | |||
| 119d1692c9 | |||
| 02473ea7d5 | |||
| c7f3c2cedf | |||
| 1ab771c3ad | |||
| 494c41dd5a | |||
| f114436120 | |||
| b7be6a4316 | |||
| c59c035d74 | |||
| 4052bfb4ad | |||
| fdd4d019ef | |||
| e9cf2d43f1 | |||
| a5d040df3d | |||
| f8b8af47ad | |||
| 2d1af6beee | |||
| b5910ad0ba | |||
| 3e5469a974 | |||
| 979e53b87b | |||
| 084bd225dc | |||
| c562c1d6be | |||
| b4746529e1 | |||
| 7f1e66e94b | |||
| 1e47252214 | |||
| 0bcbe3a703 | |||
| 1cb83c91a6 | |||
| 51a8dd4919 | |||
| 1f74b25fd1 | |||
| f4004b1271 | |||
| 8c57de1605 | |||
| 0c214cb5b9 | |||
| 8dc1913db8 | |||
| fdf4909f97 | |||
| 4036e37bbe | |||
| 30b07878ba | |||
| 7610554b1e | |||
| f9371f36a4 | |||
| 93f010c988 | |||
| d02bf6dab1 | |||
| 99770653bb | |||
| 2afef2b609 | |||
| 11120b5b1e | |||
| bf1750664c | |||
| 864523327f | |||
| 1166c3910d | |||
| 7d5a38ea6c | |||
| faf6a9876c | |||
| 550844e5e8 | |||
| a45baa0e78 | |||
| d061dc4342 | |||
| 4e4dbb6821 | |||
| 79ed7e143e | |||
| a2e170bb93 | |||
| b1fdc02465 | |||
| e9e671d101 | |||
| 078a6cb0cb | |||
| 9346f0b33d | |||
| 9b52ee4558 | |||
| c6aa4ec687 | |||
| 365c6ad753 | |||
| c04e65c254 | |||
| 8e9d253687 | |||
| 69807dd208 | |||
| 6552968df4 | |||
| 063d38a991 | |||
| 0d7a0c0817 | |||
| 90c846d1ad | |||
| 3e489782e1 | |||
| 642913b0d1 | |||
| ea7a0d4652 | |||
| 7382e7a5c8 | |||
| 1383441b15 | |||
| 3a9a0d9cb3 | |||
| af94bf4a59 | |||
| 99654721eb | |||
| e043cc0bc3 | |||
| af04f8b8e9 | |||
| 94967e0f6d | |||
| 9408100a80 | |||
| 0c75913bf2 | |||
| 3c8547160d | |||
| 1b3dba329a | |||
| 3bc0c2a625 | |||
| 94543f129e | |||
| 5b667f5e52 | |||
| c975a51ae7 | |||
| f29410d821 | |||
| 4e2401c11a | |||
| 664d8c8732 | |||
| 25faca8ea7 | |||
| 07c7f96fb2 | |||
| 13aa0a1f24 | |||
| 741a8c3034 | |||
| da46d924e9 | |||
| a2d7b2f905 | |||
| 7a9d9e575b | |||
| ab6a5784fa | |||
| 0608336c60 | |||
| d108c2873d | |||
| 9872d4bc4f | |||
| 284934ebfd | |||
| c64e1ae104 | |||
| 02dbb2ed54 | |||
| c9d28c30f0 | |||
| 241ecae867 | |||
| 81eefcaa76 | |||
| 3b13f5e43b | |||
| 4d9488033f | |||
| e524def8c0 | |||
| 8e35eedf22 | |||
| 0ce308047e | |||
| 480c79edba | |||
| 7db60fe7da | |||
| ddd5a2b1c6 | |||
| 53058ae73f | |||
| 650c9d0d62 | |||
| ca12a77670 | |||
| 5849c9a4cd | |||
| 7f4165fc05 | |||
| a87812c6a1 | |||
| 56284bff6c | |||
| bfff7b58fd | |||
| 6e7f687df4 | |||
| 41983bc0ca | |||
| 0057159066 | |||
| 79e8cdf595 | |||
| d25cb12bff | |||
| c723db39c7 | |||
| d13e18a8ed | |||
| a454670de5 | |||
| fe9945ad6c | |||
| b9cbc708e8 | |||
| 81e143a3d2 | |||
| fd24d5a0a2 | |||
| 05ff9e8d4e | |||
| 05d80fba38 | |||
| 402273d91b | |||
| d456b9d554 | |||
| 5a8b9a9706 | |||
| 8c27465325 | |||
| d47575f2c5 | |||
| 0704b645df | |||
| 9edbbf2af4 | |||
| 5e982a7812 | |||
| ecb3342145 | |||
| c09a4f4eb6 | |||
| 8f2e5f5666 | |||
| c50f2bd4f6 | |||
| 8d45dd3b73 | |||
| dea5de91d2 | |||
| 67159e3be7 | |||
| fb4d80b169 | |||
| a8896e940a | |||
| dd862dff66 | |||
| f10d40a0a2 | |||
| e6f200b960 | |||
| e7c1e6fc27 | |||
| 15f9472b15 | |||
| 109566fc8f | |||
| db637b5a4c | |||
| 7760777c06 | |||
| fa46fb90fb | |||
| 1cbe23ed7b |
@@ -7,9 +7,6 @@
|
||||
[submodule "dynarmic"]
|
||||
path = externals/dynarmic
|
||||
url = https://github.com/MerryMage/dynarmic.git
|
||||
[submodule "soundtouch"]
|
||||
path = externals/soundtouch
|
||||
url = https://github.com/citra-emu/ext-soundtouch.git
|
||||
[submodule "libressl"]
|
||||
path = externals/libressl
|
||||
url = https://github.com/citra-emu/ext-libressl-portable.git
|
||||
|
||||
@@ -77,6 +77,6 @@ If you wish to support us a different way, please join our [Discord](https://dis
|
||||
|
||||
## License
|
||||
|
||||
yuzu is licensed under the GPLv2 (or any later version). Refer to the [license.txt](https://github.com/yuzu-emu/yuzu/blob/master/license.txt) file.
|
||||
yuzu is licensed under the GPLv3 (or any later version). Refer to the [license.txt](https://github.com/yuzu-emu/yuzu/blob/master/license.txt) file.
|
||||
|
||||
The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) is exempt from GPLv2 for the contributions from all these contributors [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq). They may only use the code from these contributors under Mozilla Public License, version 2.0.
|
||||
The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) is exempt from GPLv3 for the contributions from all these contributors [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq). They may only use the code from these contributors under Mozilla Public License, version 2.0.
|
||||
|
||||
Vendored
+595
-540
File diff suppressed because it is too large
Load Diff
Vendored
+567
-522
File diff suppressed because it is too large
Load Diff
Vendored
+567
-522
File diff suppressed because it is too large
Load Diff
Vendored
+642
-597
File diff suppressed because it is too large
Load Diff
Vendored
+6261
File diff suppressed because it is too large
Load Diff
Vendored
+606
-551
File diff suppressed because it is too large
Load Diff
Vendored
+566
-521
File diff suppressed because it is too large
Load Diff
Vendored
+570
-523
File diff suppressed because it is too large
Load Diff
Vendored
+568
-523
File diff suppressed because it is too large
Load Diff
Vendored
+568
-523
File diff suppressed because it is too large
Load Diff
Vendored
+567
-522
File diff suppressed because it is too large
Load Diff
Vendored
+568
-523
File diff suppressed because it is too large
Load Diff
Vendored
+943
-849
File diff suppressed because it is too large
Load Diff
Vendored
+566
-521
File diff suppressed because it is too large
Load Diff
Vendored
+568
-523
File diff suppressed because it is too large
Load Diff
Vendored
+582
-527
File diff suppressed because it is too large
Load Diff
Vendored
+581
-526
File diff suppressed because it is too large
Load Diff
Vendored
+579
-534
File diff suppressed because it is too large
Load Diff
Vendored
+566
-521
File diff suppressed because it is too large
Load Diff
Vendored
+570
-525
File diff suppressed because it is too large
Load Diff
Vendored
+566
-521
File diff suppressed because it is too large
Load Diff
Vendored
+566
-521
File diff suppressed because it is too large
Load Diff
Vendored
+578
-523
File diff suppressed because it is too large
Load Diff
Vendored
+578
-523
File diff suppressed because it is too large
Load Diff
Vendored
-3
@@ -68,9 +68,6 @@ if (YUZU_USE_EXTERNAL_SDL2)
|
||||
add_library(SDL2 ALIAS SDL2-static)
|
||||
endif()
|
||||
|
||||
# SoundTouch
|
||||
add_subdirectory(soundtouch)
|
||||
|
||||
# Cubeb
|
||||
if(ENABLE_CUBEB)
|
||||
set(BUILD_TESTS OFF CACHE BOOL "")
|
||||
|
||||
Vendored
+1
-1
Submodule externals/dynarmic updated: e1a266b929...644172477e
Vendored
-1
Submodule externals/soundtouch deleted from 060181eaf2
+588
-279
@@ -1,285 +1,626 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
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
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
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
|
||||
this service 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.
|
||||
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 make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
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 give the recipients all the rights that
|
||||
you have. 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.
|
||||
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.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
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.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
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.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
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.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
0. Definitions.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
"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.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
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) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
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.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
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.
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
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.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
1. Source Code.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
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.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
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.
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
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.
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
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.
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
2. Basic Permissions.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
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.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
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.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
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.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
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
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
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.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
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.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
14. Revised Versions of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
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 a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
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.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
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.
|
||||
|
||||
NO WARRANTY
|
||||
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.
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE 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.
|
||||
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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
16. Limitation of Liability.
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
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
|
||||
@@ -287,15 +628,15 @@ 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
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
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
|
||||
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 2 of the License, or
|
||||
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,
|
||||
@@ -303,63 +644,31 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
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, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
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 is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
<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, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
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 your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
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/>.
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This 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.
|
||||
|
||||
|
||||
The icons used in this project have the following licenses:
|
||||
|
||||
Icon Name | License | Origin/Author
|
||||
--- | --- | ---
|
||||
checked.png | CC BY-ND 3.0 | https://icons8.com
|
||||
failed.png | CC BY-ND 3.0 | https://icons8.com
|
||||
lock.png | CC BY-ND 3.0 | https://icons8.com
|
||||
plus_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
bad_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
chip.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team
|
||||
sd_card.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
plus_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
bad_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
chip.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
sd_card.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
|
||||
star.png | CC BY-ND 3.0 | https://icons8.com
|
||||
|
||||
Note:
|
||||
Some icons are different in different themes, and they are separately listed
|
||||
only when they have different licenses/origins.
|
||||
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>.
|
||||
+2
-6
@@ -65,12 +65,14 @@ if (MSVC)
|
||||
/we4305 # 'context': truncation from 'type1' to 'type2'
|
||||
/we4388 # 'expression': signed/unsigned mismatch
|
||||
/we4389 # 'operator': signed/unsigned mismatch
|
||||
/we4505 # 'function': unreferenced local function has been removed
|
||||
/we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
|
||||
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||
/we4555 # Expression has no effect; expected expression with side-effect
|
||||
/we4715 # 'function': not all control paths return a value
|
||||
/we4834 # Discarding return value of function with 'nodiscard' attribute
|
||||
/we5038 # data member 'member1' will be initialized after data member 'member2'
|
||||
/we5245 # 'function': unreferenced function with internal linkage has been removed
|
||||
)
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
@@ -103,12 +105,6 @@ else()
|
||||
-Wno-unused-parameter
|
||||
)
|
||||
|
||||
# TODO: Remove when we update to a GCC compiler that enables this
|
||||
# by default (i.e. GCC 10 or newer).
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
add_compile_options(-fconcepts)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options("-mcx16")
|
||||
endif()
|
||||
|
||||
@@ -36,8 +36,6 @@ add_library(audio_core STATIC
|
||||
splitter_context.h
|
||||
stream.cpp
|
||||
stream.h
|
||||
time_stretch.cpp
|
||||
time_stretch.h
|
||||
voice_context.cpp
|
||||
voice_context.h
|
||||
|
||||
@@ -63,7 +61,6 @@ if (NOT MSVC)
|
||||
endif()
|
||||
|
||||
target_link_libraries(audio_core PUBLIC common core)
|
||||
target_link_libraries(audio_core PRIVATE SoundTouch)
|
||||
|
||||
if(ENABLE_CUBEB)
|
||||
target_link_libraries(audio_core PRIVATE cubeb)
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <cstring>
|
||||
#include "audio_core/cubeb_sink.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/ring_buffer.h"
|
||||
@@ -23,8 +22,7 @@ class CubebSinkStream final : public SinkStream {
|
||||
public:
|
||||
CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
|
||||
const std::string& name)
|
||||
: ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
|
||||
num_channels} {
|
||||
: ctx{ctx_}, num_channels{std::min(num_channels_, 6u)} {
|
||||
|
||||
cubeb_stream_params params{};
|
||||
params.rate = sample_rate;
|
||||
@@ -131,7 +129,6 @@ private:
|
||||
Common::RingBuffer<s16, 0x10000> queue;
|
||||
std::array<s16, 2> last_frame{};
|
||||
std::atomic<bool> should_flush{};
|
||||
TimeStretcher time_stretch;
|
||||
|
||||
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames);
|
||||
@@ -205,25 +202,7 @@ long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void*
|
||||
|
||||
const std::size_t num_channels = impl->GetNumChannels();
|
||||
const std::size_t samples_to_write = num_channels * num_frames;
|
||||
std::size_t samples_written;
|
||||
|
||||
/*
|
||||
if (Settings::values.enable_audio_stretching.GetValue()) {
|
||||
const std::vector<s16> in{impl->queue.Pop()};
|
||||
const std::size_t num_in{in.size() / num_channels};
|
||||
s16* const out{reinterpret_cast<s16*>(buffer)};
|
||||
const std::size_t out_frames =
|
||||
impl->time_stretch.Process(in.data(), num_in, out, num_frames);
|
||||
samples_written = out_frames * num_channels;
|
||||
|
||||
if (impl->should_flush) {
|
||||
impl->time_stretch.Flush();
|
||||
impl->should_flush = false;
|
||||
}
|
||||
} else {
|
||||
samples_written = impl->queue.Pop(buffer, samples_to_write);
|
||||
}*/
|
||||
samples_written = impl->queue.Pop(buffer, samples_to_write);
|
||||
const std::size_t samples_written = impl->queue.Pop(buffer, samples_to_write);
|
||||
|
||||
if (samples_written >= num_channels) {
|
||||
std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <cstring>
|
||||
#include "audio_core/sdl2_sink.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
//#include "common/settings.h"
|
||||
@@ -27,7 +26,7 @@ namespace AudioCore {
|
||||
class SDLSinkStream final : public SinkStream {
|
||||
public:
|
||||
SDLSinkStream(u32 sample_rate, u32 num_channels_, const std::string& output_device)
|
||||
: num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate, num_channels} {
|
||||
: num_channels{std::min(num_channels_, 6u)} {
|
||||
|
||||
SDL_AudioSpec spec;
|
||||
spec.freq = sample_rate;
|
||||
@@ -116,7 +115,6 @@ private:
|
||||
SDL_AudioDeviceID dev = 0;
|
||||
u32 num_channels{};
|
||||
std::atomic<bool> should_flush{};
|
||||
TimeStretcher time_stretch;
|
||||
};
|
||||
|
||||
SDLSink::SDLSink(std::string_view target_device_name) {
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
|
||||
m_sound_touch.setChannels(channel_count);
|
||||
m_sound_touch.setSampleRate(sample_rate);
|
||||
m_sound_touch.setPitch(1.0);
|
||||
m_sound_touch.setTempo(1.0);
|
||||
}
|
||||
|
||||
void TimeStretcher::Clear() {
|
||||
m_sound_touch.clear();
|
||||
}
|
||||
|
||||
void TimeStretcher::Flush() {
|
||||
m_sound_touch.flush();
|
||||
}
|
||||
|
||||
std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
|
||||
std::size_t num_out) {
|
||||
const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
|
||||
|
||||
// We were given actual_samples number of samples, and num_samples were requested from us.
|
||||
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
|
||||
|
||||
const double max_latency = 0.25; // seconds
|
||||
const double max_backlog = m_sample_rate * max_latency;
|
||||
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
|
||||
if (backlog_fullness > 4.0) {
|
||||
// Too many samples in backlog: Don't push anymore on
|
||||
num_in = 0;
|
||||
}
|
||||
|
||||
// We ideally want the backlog to be about 50% full.
|
||||
// This gives some headroom both ways to prevent underflow and overflow.
|
||||
// We tweak current_ratio to encourage this.
|
||||
constexpr double tweak_time_scale = 0.05; // seconds
|
||||
const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
|
||||
current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
|
||||
|
||||
// This low-pass filter smoothes out variance in the calculated stretch ratio.
|
||||
// The time-scale determines how responsive this filter is.
|
||||
constexpr double lpf_time_scale = 0.712; // seconds
|
||||
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
|
||||
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
|
||||
|
||||
// Place a lower limit of 5% speed. When a game boots up, there will be
|
||||
// many silence samples. These do not need to be timestretched.
|
||||
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
|
||||
m_sound_touch.setTempo(m_stretch_ratio);
|
||||
|
||||
LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
|
||||
backlog_fullness);
|
||||
|
||||
m_sound_touch.putSamples(in, static_cast<u32>(num_in));
|
||||
return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <SoundTouch.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class TimeStretcher {
|
||||
public:
|
||||
TimeStretcher(u32 sample_rate, u32 channel_count);
|
||||
|
||||
/// @param in Input sample buffer
|
||||
/// @param num_in Number of input frames in `in`
|
||||
/// @param out Output sample buffer
|
||||
/// @param num_out Desired number of output frames in `out`
|
||||
/// @returns Actual number of frames written to `out`
|
||||
std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
|
||||
|
||||
void Clear();
|
||||
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
u32 m_sample_rate;
|
||||
soundtouch::SoundTouch m_sound_touch;
|
||||
double m_stretch_ratio = 1.0;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -46,6 +46,50 @@ namespace Common {
|
||||
reinterpret_cast<__int64*>(expected.data())) != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
|
||||
u8& actual) {
|
||||
actual =
|
||||
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
|
||||
u16& actual) {
|
||||
actual =
|
||||
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
|
||||
u32& actual) {
|
||||
actual =
|
||||
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
|
||||
u64& actual) {
|
||||
actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
|
||||
expected);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
|
||||
u128& actual) {
|
||||
const bool result =
|
||||
_InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
|
||||
value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
|
||||
actual = expected;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
|
||||
u128 result{};
|
||||
_InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
|
||||
result[0], reinterpret_cast<__int64*>(result.data()));
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
|
||||
@@ -72,6 +116,52 @@ namespace Common {
|
||||
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
|
||||
u8& actual) {
|
||||
actual = __sync_val_compare_and_swap(pointer, expected, value);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
|
||||
u16& actual) {
|
||||
actual = __sync_val_compare_and_swap(pointer, expected, value);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
|
||||
u32& actual) {
|
||||
actual = __sync_val_compare_and_swap(pointer, expected, value);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
|
||||
u64& actual) {
|
||||
actual = __sync_val_compare_and_swap(pointer, expected, value);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
|
||||
u128& actual) {
|
||||
unsigned __int128 value_a;
|
||||
unsigned __int128 expected_a;
|
||||
unsigned __int128 actual_a;
|
||||
std::memcpy(&value_a, value.data(), sizeof(u128));
|
||||
std::memcpy(&expected_a, expected.data(), sizeof(u128));
|
||||
actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
|
||||
std::memcpy(actual.data(), &actual_a, sizeof(u128));
|
||||
return actual_a == expected_a;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
|
||||
unsigned __int128 zeros_a = 0;
|
||||
unsigned __int128 result_a =
|
||||
__sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
|
||||
|
||||
u128 result;
|
||||
std::memcpy(result.data(), &result_a, sizeof(u128));
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -149,7 +149,7 @@ public:
|
||||
}
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length) {
|
||||
std::lock_guard lock{placeholder_mutex};
|
||||
std::scoped_lock lock{placeholder_mutex};
|
||||
|
||||
// Unmap until there are no more placeholders
|
||||
while (UnmapOnePlaceholder(virtual_offset, length)) {
|
||||
@@ -169,7 +169,7 @@ public:
|
||||
}
|
||||
const size_t virtual_end = virtual_offset + length;
|
||||
|
||||
std::lock_guard lock{placeholder_mutex};
|
||||
std::scoped_lock lock{placeholder_mutex};
|
||||
auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
|
||||
while (it != end) {
|
||||
const size_t offset = std::max(it->lower(), virtual_offset);
|
||||
|
||||
@@ -101,6 +101,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Service, GRC) \
|
||||
SUB(Service, HID) \
|
||||
SUB(Service, IRS) \
|
||||
SUB(Service, JIT) \
|
||||
SUB(Service, LBL) \
|
||||
SUB(Service, LDN) \
|
||||
SUB(Service, LDR) \
|
||||
@@ -119,6 +120,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Service, NPNS) \
|
||||
SUB(Service, NS) \
|
||||
SUB(Service, NVDRV) \
|
||||
SUB(Service, NVFlinger) \
|
||||
SUB(Service, OLSC) \
|
||||
SUB(Service, PCIE) \
|
||||
SUB(Service, PCTL) \
|
||||
|
||||
@@ -69,6 +69,7 @@ enum class Class : u8 {
|
||||
Service_GRC, ///< The game recording service
|
||||
Service_HID, ///< The HID (Human interface device) service
|
||||
Service_IRS, ///< The IRS service
|
||||
Service_JIT, ///< The JIT service
|
||||
Service_LBL, ///< The LBL (LCD backlight) service
|
||||
Service_LDN, ///< The LDN (Local domain network) service
|
||||
Service_LDR, ///< The loader service
|
||||
@@ -87,6 +88,7 @@ enum class Class : u8 {
|
||||
Service_NPNS, ///< The NPNS service
|
||||
Service_NS, ///< The NS services
|
||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||
Service_NVFlinger, ///< The NVFlinger service
|
||||
Service_OLSC, ///< The OLSC service
|
||||
Service_PCIE, ///< The PCIe service
|
||||
Service_PCTL, ///< The PCTL (Parental control) service
|
||||
|
||||
+45
-5
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
@@ -20,10 +21,32 @@ struct Rectangle {
|
||||
|
||||
constexpr Rectangle() = default;
|
||||
|
||||
constexpr Rectangle(T width, T height) : right(width), bottom(height) {}
|
||||
|
||||
constexpr Rectangle(T left_, T top_, T right_, T bottom_)
|
||||
: left(left_), top(top_), right(right_), bottom(bottom_) {}
|
||||
|
||||
[[nodiscard]] T GetWidth() const {
|
||||
[[nodiscard]] constexpr T Left() const {
|
||||
return left;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T Top() const {
|
||||
return top;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T Right() const {
|
||||
return right;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T Bottom() const {
|
||||
return bottom;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsEmpty() const {
|
||||
return (GetWidth() <= 0) || (GetHeight() <= 0);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T GetWidth() const {
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
return std::abs(right - left);
|
||||
} else {
|
||||
@@ -31,7 +54,7 @@ struct Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] T GetHeight() const {
|
||||
[[nodiscard]] constexpr T GetHeight() const {
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
return std::abs(bottom - top);
|
||||
} else {
|
||||
@@ -39,18 +62,35 @@ struct Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] Rectangle<T> TranslateX(const T x) const {
|
||||
[[nodiscard]] constexpr Rectangle<T> TranslateX(const T x) const {
|
||||
return Rectangle{left + x, top, right + x, bottom};
|
||||
}
|
||||
|
||||
[[nodiscard]] Rectangle<T> TranslateY(const T y) const {
|
||||
[[nodiscard]] constexpr Rectangle<T> TranslateY(const T y) const {
|
||||
return Rectangle{left, top + y, right, bottom + y};
|
||||
}
|
||||
|
||||
[[nodiscard]] Rectangle<T> Scale(const float s) const {
|
||||
[[nodiscard]] constexpr Rectangle<T> Scale(const float s) const {
|
||||
return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s),
|
||||
static_cast<T>(static_cast<float>(top + GetHeight()) * s)};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const {
|
||||
return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) &&
|
||||
(bottom == rhs.bottom);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool Intersect(const Rectangle<T>& with, Rectangle<T>* result) const {
|
||||
result->left = std::max(left, with.left);
|
||||
result->top = std::max(top, with.top);
|
||||
result->right = std::min(right, with.right);
|
||||
result->bottom = std::min(bottom, with.bottom);
|
||||
return !result->IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -38,6 +38,7 @@ enum class CPUAccuracy : u32 {
|
||||
Auto = 0,
|
||||
Accurate = 1,
|
||||
Unsafe = 2,
|
||||
Paranoid = 3,
|
||||
};
|
||||
|
||||
enum class FullscreenMode : u32 {
|
||||
@@ -470,7 +471,7 @@ struct Values {
|
||||
|
||||
// Cpu
|
||||
RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
|
||||
CPUAccuracy::Unsafe, "cpu_accuracy"};
|
||||
CPUAccuracy::Paranoid, "cpu_accuracy"};
|
||||
// TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
|
||||
BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
|
||||
BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ namespace Common {
|
||||
class Event {
|
||||
public:
|
||||
void Set() {
|
||||
std::lock_guard lk{mutex};
|
||||
std::scoped_lock lk{mutex};
|
||||
if (!is_set) {
|
||||
is_set = true;
|
||||
condvar.notify_one();
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
// line before cv.wait
|
||||
// TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
|
||||
// See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
|
||||
std::lock_guard lock{cv_mutex};
|
||||
std::scoped_lock lock{cv_mutex};
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ public:
|
||||
|
||||
template <typename Arg>
|
||||
void Push(Arg&& t) {
|
||||
std::lock_guard lock{write_lock};
|
||||
std::scoped_lock lock{write_lock};
|
||||
spsc_queue.Push(t);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,25 +10,49 @@
|
||||
#include "common/uint128.h"
|
||||
#include "common/x64/native_clock.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
__forceinline static u64 FencedRDTSC() {
|
||||
_mm_lfence();
|
||||
_ReadWriteBarrier();
|
||||
const u64 result = __rdtsc();
|
||||
_mm_lfence();
|
||||
_ReadWriteBarrier();
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
static u64 FencedRDTSC() {
|
||||
u64 result;
|
||||
asm volatile("lfence\n\t"
|
||||
"rdtsc\n\t"
|
||||
"shl $32, %%rdx\n\t"
|
||||
"or %%rdx, %0\n\t"
|
||||
"lfence"
|
||||
: "=a"(result)
|
||||
:
|
||||
: "rdx", "memory", "cc");
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
u64 EstimateRDTSCFrequency() {
|
||||
// Discard the first result measuring the rdtsc.
|
||||
_mm_mfence();
|
||||
__rdtsc();
|
||||
FencedRDTSC();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{1});
|
||||
_mm_mfence();
|
||||
__rdtsc();
|
||||
FencedRDTSC();
|
||||
|
||||
// Get the current time.
|
||||
const auto start_time = std::chrono::steady_clock::now();
|
||||
_mm_mfence();
|
||||
const u64 tsc_start = __rdtsc();
|
||||
const u64 tsc_start = FencedRDTSC();
|
||||
// Wait for 200 milliseconds.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{200});
|
||||
const auto end_time = std::chrono::steady_clock::now();
|
||||
_mm_mfence();
|
||||
const u64 tsc_end = __rdtsc();
|
||||
const u64 tsc_end = FencedRDTSC();
|
||||
// Calculate differences.
|
||||
const u64 timer_diff = static_cast<u64>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
|
||||
@@ -42,8 +66,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
|
||||
u64 rtsc_frequency_)
|
||||
: WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
|
||||
rtsc_frequency_} {
|
||||
_mm_mfence();
|
||||
time_point.inner.last_measure = __rdtsc();
|
||||
time_point.inner.last_measure = FencedRDTSC();
|
||||
time_point.inner.accumulated_ticks = 0U;
|
||||
ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
|
||||
us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
|
||||
@@ -55,10 +78,10 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
|
||||
u64 NativeClock::GetRTSC() {
|
||||
TimePoint new_time_point{};
|
||||
TimePoint current_time_point{};
|
||||
|
||||
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
|
||||
do {
|
||||
current_time_point.pack = time_point.pack;
|
||||
_mm_mfence();
|
||||
const u64 current_measure = __rdtsc();
|
||||
const u64 current_measure = FencedRDTSC();
|
||||
u64 diff = current_measure - current_time_point.inner.last_measure;
|
||||
diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0)
|
||||
new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure
|
||||
@@ -66,7 +89,7 @@ u64 NativeClock::GetRTSC() {
|
||||
: current_time_point.inner.last_measure;
|
||||
new_time_point.inner.accumulated_ticks = current_time_point.inner.accumulated_ticks + diff;
|
||||
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
|
||||
current_time_point.pack));
|
||||
current_time_point.pack, current_time_point.pack));
|
||||
/// The clock cannot be more precise than the guest timer, remove the lower bits
|
||||
return new_time_point.inner.accumulated_ticks & inaccuracy_mask;
|
||||
}
|
||||
@@ -75,13 +98,13 @@ void NativeClock::Pause(bool is_paused) {
|
||||
if (!is_paused) {
|
||||
TimePoint current_time_point{};
|
||||
TimePoint new_time_point{};
|
||||
|
||||
current_time_point.pack = Common::AtomicLoad128(time_point.pack.data());
|
||||
do {
|
||||
current_time_point.pack = time_point.pack;
|
||||
new_time_point.pack = current_time_point.pack;
|
||||
_mm_mfence();
|
||||
new_time_point.inner.last_measure = __rdtsc();
|
||||
new_time_point.inner.last_measure = FencedRDTSC();
|
||||
} while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack,
|
||||
current_time_point.pack));
|
||||
current_time_point.pack, current_time_point.pack));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-2
@@ -13,6 +13,8 @@ add_library(core STATIC
|
||||
arm/dynarmic/arm_exclusive_monitor.h
|
||||
arm/exclusive_monitor.cpp
|
||||
arm/exclusive_monitor.h
|
||||
arm/symbols.cpp
|
||||
arm/symbols.h
|
||||
constants.cpp
|
||||
constants.h
|
||||
core.cpp
|
||||
@@ -458,6 +460,8 @@ add_library(core STATIC
|
||||
hle/service/hid/controllers/touchscreen.h
|
||||
hle/service/hid/controllers/xpad.cpp
|
||||
hle/service/hid/controllers/xpad.h
|
||||
hle/service/jit/jit.cpp
|
||||
hle/service/jit/jit.h
|
||||
hle/service/lbl/lbl.cpp
|
||||
hle/service/lbl/lbl.h
|
||||
hle/service/ldn/errors.h
|
||||
@@ -535,10 +539,35 @@ add_library(core STATIC
|
||||
hle/service/nvdrv/nvmemp.h
|
||||
hle/service/nvdrv/syncpoint_manager.cpp
|
||||
hle/service/nvdrv/syncpoint_manager.h
|
||||
hle/service/nvflinger/buffer_queue.cpp
|
||||
hle/service/nvflinger/buffer_queue.h
|
||||
hle/service/nvflinger/binder.h
|
||||
hle/service/nvflinger/buffer_item.h
|
||||
hle/service/nvflinger/buffer_item_consumer.cpp
|
||||
hle/service/nvflinger/buffer_item_consumer.h
|
||||
hle/service/nvflinger/buffer_queue_consumer.cpp
|
||||
hle/service/nvflinger/buffer_queue_consumer.h
|
||||
hle/service/nvflinger/buffer_queue_core.cpp
|
||||
hle/service/nvflinger/buffer_queue_core.h
|
||||
hle/service/nvflinger/buffer_queue_defs.h
|
||||
hle/service/nvflinger/buffer_queue_producer.cpp
|
||||
hle/service/nvflinger/buffer_queue_producer.h
|
||||
hle/service/nvflinger/buffer_slot.h
|
||||
hle/service/nvflinger/buffer_transform_flags.h
|
||||
hle/service/nvflinger/consumer_base.cpp
|
||||
hle/service/nvflinger/consumer_base.h
|
||||
hle/service/nvflinger/consumer_listener.h
|
||||
hle/service/nvflinger/graphic_buffer_producer.cpp
|
||||
hle/service/nvflinger/graphic_buffer_producer.h
|
||||
hle/service/nvflinger/hos_binder_driver_server.cpp
|
||||
hle/service/nvflinger/hos_binder_driver_server.h
|
||||
hle/service/nvflinger/nvflinger.cpp
|
||||
hle/service/nvflinger/nvflinger.h
|
||||
hle/service/nvflinger/parcel.h
|
||||
hle/service/nvflinger/pixel_format.h
|
||||
hle/service/nvflinger/producer_listener.h
|
||||
hle/service/nvflinger/status.h
|
||||
hle/service/nvflinger/ui/fence.h
|
||||
hle/service/nvflinger/ui/graphic_buffer.h
|
||||
hle/service/nvflinger/window.h
|
||||
hle/service/olsc/olsc.cpp
|
||||
hle/service/olsc/olsc.h
|
||||
hle/service/pcie/pcie.cpp
|
||||
|
||||
+12
-129
@@ -8,134 +8,13 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/symbols.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
namespace {
|
||||
|
||||
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
|
||||
|
||||
enum class ELFSymbolType : u8 {
|
||||
None = 0,
|
||||
Object = 1,
|
||||
Function = 2,
|
||||
Section = 3,
|
||||
File = 4,
|
||||
Common = 5,
|
||||
TLS = 6,
|
||||
};
|
||||
|
||||
enum class ELFSymbolBinding : u8 {
|
||||
Local = 0,
|
||||
Global = 1,
|
||||
Weak = 2,
|
||||
};
|
||||
|
||||
enum class ELFSymbolVisibility : u8 {
|
||||
Default = 0,
|
||||
Internal = 1,
|
||||
Hidden = 2,
|
||||
Protected = 3,
|
||||
};
|
||||
|
||||
struct ELFSymbol {
|
||||
u32 name_index;
|
||||
union {
|
||||
u8 info;
|
||||
|
||||
BitField<0, 4, ELFSymbolType> type;
|
||||
BitField<4, 4, ELFSymbolBinding> binding;
|
||||
};
|
||||
ELFSymbolVisibility visibility;
|
||||
u16 sh_index;
|
||||
u64 value;
|
||||
u64 size;
|
||||
};
|
||||
static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
|
||||
|
||||
using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
|
||||
|
||||
Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {
|
||||
const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
|
||||
|
||||
if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
|
||||
memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
|
||||
|
||||
VAddr string_table_offset{};
|
||||
VAddr symbol_table_offset{};
|
||||
u64 symbol_entry_size{};
|
||||
|
||||
VAddr dynamic_index = dynamic_offset;
|
||||
while (true) {
|
||||
const u64 tag = memory.Read64(dynamic_index);
|
||||
const u64 value = memory.Read64(dynamic_index + 0x8);
|
||||
dynamic_index += 0x10;
|
||||
|
||||
if (tag == ELF_DYNAMIC_TAG_NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
|
||||
string_table_offset = value;
|
||||
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
|
||||
symbol_table_offset = value;
|
||||
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
|
||||
symbol_entry_size = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto string_table_address = text_offset + string_table_offset;
|
||||
const auto symbol_table_address = text_offset + symbol_table_offset;
|
||||
|
||||
Symbols out;
|
||||
|
||||
VAddr symbol_index = symbol_table_address;
|
||||
while (symbol_index < string_table_address) {
|
||||
ELFSymbol symbol{};
|
||||
memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
|
||||
|
||||
VAddr string_offset = string_table_address + symbol.name_index;
|
||||
std::string name;
|
||||
for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
|
||||
name += static_cast<char>(c);
|
||||
}
|
||||
|
||||
symbol_index += symbol_entry_size;
|
||||
out.push_back({symbol, name});
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
|
||||
const auto iter =
|
||||
std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
|
||||
const auto& symbol = pair.first;
|
||||
const auto end_address = symbol.value + symbol.size;
|
||||
return func_address >= symbol.value && func_address < end_address;
|
||||
});
|
||||
|
||||
if (iter == symbols.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
constexpr u64 SEGMENT_BASE = 0x7100000000ull;
|
||||
|
||||
@@ -169,9 +48,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
|
||||
return {};
|
||||
}
|
||||
|
||||
std::map<std::string, Symbols> symbols;
|
||||
std::map<std::string, Symbols::Symbols> symbols;
|
||||
for (const auto& module : modules) {
|
||||
symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
|
||||
symbols.insert_or_assign(module.second,
|
||||
Symbols::GetSymbols(module.first, system.Memory(),
|
||||
system.CurrentProcess()->Is64BitProcess()));
|
||||
}
|
||||
|
||||
for (auto& entry : out) {
|
||||
@@ -193,7 +74,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
|
||||
|
||||
const auto symbol_set = symbols.find(entry.module);
|
||||
if (symbol_set != symbols.end()) {
|
||||
const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
|
||||
const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
|
||||
if (symbol.has_value()) {
|
||||
// TODO(DarkLordZach): Add demangling of symbol names.
|
||||
entry.name = *symbol;
|
||||
@@ -225,9 +106,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::map<std::string, Symbols> symbols;
|
||||
std::map<std::string, Symbols::Symbols> symbols;
|
||||
for (const auto& module : modules) {
|
||||
symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
|
||||
symbols.insert_or_assign(module.second,
|
||||
Symbols::GetSymbols(module.first, system.Memory(),
|
||||
system.CurrentProcess()->Is64BitProcess()));
|
||||
}
|
||||
|
||||
for (auto& entry : out) {
|
||||
@@ -249,7 +132,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
|
||||
|
||||
const auto symbol_set = symbols.find(entry.module);
|
||||
if (symbol_set != symbols.end()) {
|
||||
const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
|
||||
const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
|
||||
if (symbol.has_value()) {
|
||||
// TODO(DarkLordZach): Add demangling of symbol names.
|
||||
entry.name = *symbol;
|
||||
|
||||
@@ -171,6 +171,9 @@ public:
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
/// Signal an interrupt and ask the core to halt as soon as possible.
|
||||
virtual void SignalInterrupt() = 0;
|
||||
|
||||
struct BacktraceEntry {
|
||||
std::string module;
|
||||
u64 address;
|
||||
|
||||
@@ -25,6 +25,9 @@ namespace Core {
|
||||
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||
|
||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
|
||||
@@ -70,11 +73,13 @@ public:
|
||||
}
|
||||
|
||||
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
|
||||
parent.LogBacktrace();
|
||||
UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
|
||||
MemoryReadCode(pc));
|
||||
}
|
||||
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
parent.LogBacktrace();
|
||||
LOG_CRITICAL(Core_ARM,
|
||||
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
|
||||
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
|
||||
@@ -82,15 +87,13 @@ public:
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
parent.svc_called = true;
|
||||
parent.svc_swi = swi;
|
||||
parent.jit->HaltExecution();
|
||||
parent.jit->HaltExecution(svc_call);
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
if (parent.uses_wall_clock) {
|
||||
return;
|
||||
}
|
||||
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
|
||||
|
||||
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
|
||||
// rough approximation of the amount of executed ticks in the system, it may be thrown off
|
||||
// if not all cores are doing a similar amount of work. Instead of doing this, we should
|
||||
@@ -106,12 +109,8 @@ public:
|
||||
}
|
||||
|
||||
u64 GetTicksRemaining() override {
|
||||
if (parent.uses_wall_clock) {
|
||||
if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
|
||||
return minimum_run_cycles;
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
|
||||
|
||||
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
|
||||
}
|
||||
|
||||
@@ -146,6 +145,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
|
||||
// Timing
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
config.enable_cycle_counting = !uses_wall_clock;
|
||||
|
||||
// Code cache size
|
||||
config.code_cache_size = 512_MiB;
|
||||
@@ -186,35 +186,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
if (!Settings::values.cpuopt_recompile_exclusives) {
|
||||
config.recompile_on_exclusive_fastmem_failure = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unsafe optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
|
||||
config.unsafe_optimizations = true;
|
||||
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
|
||||
config.unsafe_optimizations = true;
|
||||
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
|
||||
// Curated optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
|
||||
config.unsafe_optimizations = true;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
}
|
||||
|
||||
// Curated optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
|
||||
config.unsafe_optimizations = true;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
// Paranoia mode for debugging optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
|
||||
config.unsafe_optimizations = false;
|
||||
config.optimizations = Dynarmic::no_optimizations;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
@@ -222,13 +228,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
|
||||
|
||||
void ARM_Dynarmic_32::Run() {
|
||||
while (true) {
|
||||
jit->Run();
|
||||
if (!svc_called) {
|
||||
break;
|
||||
const auto hr = jit->Run();
|
||||
if (Has(hr, svc_call)) {
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
}
|
||||
svc_called = false;
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
if (shutdown) {
|
||||
if (Has(hr, break_loop)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -314,8 +318,11 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::PrepareReschedule() {
|
||||
jit->HaltExecution();
|
||||
shutdown = true;
|
||||
jit->HaltExecution(break_loop);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SignalInterrupt() {
|
||||
jit->HaltExecution(break_loop);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::ClearInstructionCache() {
|
||||
|
||||
@@ -57,6 +57,7 @@ public:
|
||||
void LoadContext(const ThreadContext64& ctx) override {}
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void SignalInterrupt() override;
|
||||
void ClearExclusiveState() override;
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
@@ -83,9 +84,6 @@ private:
|
||||
|
||||
// SVC callback
|
||||
u32 svc_swi{};
|
||||
bool svc_called{};
|
||||
|
||||
bool shutdown{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace Core {
|
||||
using Vector = Dynarmic::A64::Vector;
|
||||
using namespace Common::Literals;
|
||||
|
||||
constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
|
||||
constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
|
||||
|
||||
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
|
||||
@@ -81,6 +84,7 @@ public:
|
||||
}
|
||||
|
||||
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
||||
parent.LogBacktrace();
|
||||
LOG_ERROR(Core_ARM,
|
||||
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
|
||||
num_instructions, MemoryReadCode(pc));
|
||||
@@ -93,17 +97,19 @@ public:
|
||||
static constexpr u64 ICACHE_LINE_SIZE = 64;
|
||||
|
||||
const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
|
||||
parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
|
||||
parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE);
|
||||
break;
|
||||
}
|
||||
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
|
||||
parent.ClearInstructionCache();
|
||||
parent.system.InvalidateCpuInstructionCaches();
|
||||
break;
|
||||
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
|
||||
default:
|
||||
LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
|
||||
break;
|
||||
}
|
||||
|
||||
parent.jit->HaltExecution(Dynarmic::HaltReason::CacheInvalidation);
|
||||
}
|
||||
|
||||
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
|
||||
@@ -116,21 +122,19 @@ public:
|
||||
return;
|
||||
case Dynarmic::A64::Exception::Breakpoint:
|
||||
default:
|
||||
parent.LogBacktrace();
|
||||
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
|
||||
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
|
||||
}
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
parent.svc_called = true;
|
||||
parent.svc_swi = swi;
|
||||
parent.jit->HaltExecution();
|
||||
parent.jit->HaltExecution(svc_call);
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
if (parent.uses_wall_clock) {
|
||||
return;
|
||||
}
|
||||
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
|
||||
|
||||
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
|
||||
// rough approximation of the amount of executed ticks in the system, it may be thrown off
|
||||
@@ -145,12 +149,8 @@ public:
|
||||
}
|
||||
|
||||
u64 GetTicksRemaining() override {
|
||||
if (parent.uses_wall_clock) {
|
||||
if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
|
||||
return minimum_run_cycles;
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
|
||||
|
||||
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
|
||||
}
|
||||
|
||||
@@ -206,6 +206,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
|
||||
// Timing
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
config.enable_cycle_counting = !uses_wall_clock;
|
||||
|
||||
// Code cache size
|
||||
config.code_cache_size = 512_MiB;
|
||||
@@ -246,35 +247,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
if (!Settings::values.cpuopt_recompile_exclusives) {
|
||||
config.recompile_on_exclusive_fastmem_failure = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unsafe optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
|
||||
config.unsafe_optimizations = true;
|
||||
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_fastmem_check) {
|
||||
config.fastmem_address_space_bits = 64;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
|
||||
config.unsafe_optimizations = true;
|
||||
if (Settings::values.cpuopt_unsafe_unfuse_fma) {
|
||||
// Curated optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
|
||||
config.unsafe_optimizations = true;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_fastmem_check) {
|
||||
config.fastmem_address_space_bits = 64;
|
||||
}
|
||||
if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
}
|
||||
}
|
||||
|
||||
// Curated optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
|
||||
config.unsafe_optimizations = true;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
config.fastmem_address_space_bits = 64;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
// Paranoia mode for debugging optimizations
|
||||
if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
|
||||
config.unsafe_optimizations = false;
|
||||
config.optimizations = Dynarmic::no_optimizations;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_shared<Dynarmic::A64::Jit>(config);
|
||||
@@ -282,13 +289,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
|
||||
|
||||
void ARM_Dynarmic_64::Run() {
|
||||
while (true) {
|
||||
jit->Run();
|
||||
if (!svc_called) {
|
||||
break;
|
||||
const auto hr = jit->Run();
|
||||
if (Has(hr, svc_call)) {
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
}
|
||||
svc_called = false;
|
||||
Kernel::Svc::Call(system, svc_swi);
|
||||
if (shutdown) {
|
||||
if (Has(hr, break_loop)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -379,8 +384,11 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::PrepareReschedule() {
|
||||
jit->HaltExecution();
|
||||
shutdown = true;
|
||||
jit->HaltExecution(break_loop);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::SignalInterrupt() {
|
||||
jit->HaltExecution(break_loop);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::ClearInstructionCache() {
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
void LoadContext(const ThreadContext64& ctx) override;
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void SignalInterrupt() override;
|
||||
void ClearExclusiveState() override;
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
@@ -77,9 +78,6 @@ private:
|
||||
|
||||
// SVC callback
|
||||
u32 svc_swi{};
|
||||
bool svc_called{};
|
||||
|
||||
bool shutdown{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/arm/symbols.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
namespace {
|
||||
|
||||
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
|
||||
constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
|
||||
|
||||
enum class ELFSymbolType : u8 {
|
||||
None = 0,
|
||||
Object = 1,
|
||||
Function = 2,
|
||||
Section = 3,
|
||||
File = 4,
|
||||
Common = 5,
|
||||
TLS = 6,
|
||||
};
|
||||
|
||||
enum class ELFSymbolBinding : u8 {
|
||||
Local = 0,
|
||||
Global = 1,
|
||||
Weak = 2,
|
||||
};
|
||||
|
||||
enum class ELFSymbolVisibility : u8 {
|
||||
Default = 0,
|
||||
Internal = 1,
|
||||
Hidden = 2,
|
||||
Protected = 3,
|
||||
};
|
||||
|
||||
struct ELF64Symbol {
|
||||
u32 name_index;
|
||||
union {
|
||||
u8 info;
|
||||
|
||||
BitField<0, 4, ELFSymbolType> type;
|
||||
BitField<4, 4, ELFSymbolBinding> binding;
|
||||
};
|
||||
ELFSymbolVisibility visibility;
|
||||
u16 sh_index;
|
||||
u64 value;
|
||||
u64 size;
|
||||
};
|
||||
static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
|
||||
|
||||
struct ELF32Symbol {
|
||||
u32 name_index;
|
||||
u32 value;
|
||||
u32 size;
|
||||
union {
|
||||
u8 info;
|
||||
|
||||
BitField<0, 4, ELFSymbolType> type;
|
||||
BitField<4, 4, ELFSymbolBinding> binding;
|
||||
};
|
||||
ELFSymbolVisibility visibility;
|
||||
u16 sh_index;
|
||||
};
|
||||
static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
namespace Symbols {
|
||||
|
||||
template <typename Word, typename ELFSymbol, typename ByteReader>
|
||||
static Symbols GetSymbols(ByteReader ReadBytes) {
|
||||
const auto Read8{[&](u64 index) {
|
||||
u8 ret;
|
||||
ReadBytes(&ret, index, sizeof(u8));
|
||||
return ret;
|
||||
}};
|
||||
|
||||
const auto Read32{[&](u64 index) {
|
||||
u32 ret;
|
||||
ReadBytes(&ret, index, sizeof(u32));
|
||||
return ret;
|
||||
}};
|
||||
|
||||
const auto ReadWord{[&](u64 index) {
|
||||
Word ret;
|
||||
ReadBytes(&ret, index, sizeof(Word));
|
||||
return ret;
|
||||
}};
|
||||
|
||||
const u32 mod_offset = Read32(4);
|
||||
|
||||
if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VAddr string_table_offset{};
|
||||
VAddr symbol_table_offset{};
|
||||
u64 symbol_entry_size{};
|
||||
|
||||
const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
|
||||
|
||||
VAddr dynamic_index = dynamic_offset;
|
||||
while (true) {
|
||||
const Word tag = ReadWord(dynamic_index);
|
||||
const Word value = ReadWord(dynamic_index + sizeof(Word));
|
||||
dynamic_index += 2 * sizeof(Word);
|
||||
|
||||
if (tag == ELF_DYNAMIC_TAG_NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tag == ELF_DYNAMIC_TAG_STRTAB) {
|
||||
string_table_offset = value;
|
||||
} else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
|
||||
symbol_table_offset = value;
|
||||
} else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
|
||||
symbol_entry_size = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Symbols out;
|
||||
|
||||
VAddr symbol_index = symbol_table_offset;
|
||||
while (symbol_index < string_table_offset) {
|
||||
ELFSymbol symbol{};
|
||||
ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
|
||||
|
||||
VAddr string_offset = string_table_offset + symbol.name_index;
|
||||
std::string name;
|
||||
for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
|
||||
name += static_cast<char>(c);
|
||||
}
|
||||
|
||||
symbol_index += symbol_entry_size;
|
||||
out[name] = std::make_pair(symbol.value, symbol.size);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
|
||||
const auto ReadBytes{
|
||||
[&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
|
||||
|
||||
if (is_64) {
|
||||
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
|
||||
} else {
|
||||
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
|
||||
}
|
||||
}
|
||||
|
||||
Symbols GetSymbols(std::span<const u8> data, bool is_64) {
|
||||
const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
|
||||
std::memcpy(ptr, data.data() + offset, size);
|
||||
}};
|
||||
|
||||
if (is_64) {
|
||||
return GetSymbols<u64, ELF64Symbol>(ReadBytes);
|
||||
} else {
|
||||
return GetSymbols<u32, ELF32Symbol>(ReadBytes);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
|
||||
const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
|
||||
const auto& [name, sym_info] = pair;
|
||||
const auto& [start_address, size] = sym_info;
|
||||
const auto end_address = start_address + size;
|
||||
return addr >= start_address && addr < end_address;
|
||||
});
|
||||
|
||||
if (iter == symbols.cend()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return iter->first;
|
||||
}
|
||||
|
||||
} // namespace Symbols
|
||||
} // namespace Core
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
} // namespace Core::Memory
|
||||
|
||||
namespace Core::Symbols {
|
||||
|
||||
using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
|
||||
|
||||
Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
|
||||
Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
|
||||
std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr);
|
||||
|
||||
} // namespace Core::Symbols
|
||||
@@ -387,15 +387,17 @@ std::vector<NcaID> RegisteredCache::AccumulateFiles() const {
|
||||
continue;
|
||||
|
||||
for (const auto& nca_dir : d2_dir->GetSubdirectories()) {
|
||||
if (!FollowsNcaIdFormat(nca_dir->GetName()))
|
||||
if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20)));
|
||||
}
|
||||
|
||||
for (const auto& nca_file : d2_dir->GetFiles()) {
|
||||
if (!FollowsNcaIdFormat(nca_file->GetName()))
|
||||
if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ids.push_back(
|
||||
Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20)));
|
||||
|
||||
@@ -132,7 +132,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
|
||||
}
|
||||
|
||||
void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
auto& raw_status = console.motion_values.raw_status;
|
||||
auto& emulated = console.motion_values.emulated;
|
||||
|
||||
@@ -151,6 +151,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
emulated.UpdateOrientation(raw_status.delta_timestamp);
|
||||
|
||||
if (is_configuring) {
|
||||
lock.unlock();
|
||||
TriggerOnChange(ConsoleTriggerType::Motion);
|
||||
return;
|
||||
}
|
||||
@@ -166,6 +167,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
// Find what is this value
|
||||
motion.verticalization_error = 0.0f;
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(ConsoleTriggerType::Motion);
|
||||
}
|
||||
|
||||
@@ -173,11 +175,12 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
|
||||
if (index >= console.touch_values.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
console.touch_values[index] = TransformToTouch(callback);
|
||||
|
||||
if (is_configuring) {
|
||||
lock.unlock();
|
||||
TriggerOnChange(ConsoleTriggerType::Touch);
|
||||
return;
|
||||
}
|
||||
@@ -189,26 +192,32 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
|
||||
.pressed = console.touch_values[index].pressed.value,
|
||||
};
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(ConsoleTriggerType::Touch);
|
||||
}
|
||||
|
||||
ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return console.motion_values;
|
||||
}
|
||||
|
||||
TouchValues EmulatedConsole::GetTouchValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return console.touch_values;
|
||||
}
|
||||
|
||||
ConsoleMotion EmulatedConsole::GetMotion() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return console.motion_state;
|
||||
}
|
||||
|
||||
TouchFingerState EmulatedConsole::GetTouch() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return console.touch_state;
|
||||
}
|
||||
|
||||
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const ConsoleUpdateCallback& poller = poller_pair.second;
|
||||
if (poller.on_change) {
|
||||
@@ -218,13 +227,13 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
|
||||
}
|
||||
|
||||
int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
callback_list.insert_or_assign(last_callback_key, update_callback);
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
void EmulatedConsole::DeleteCallback(int key) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
const auto& iterator = callback_list.find(key);
|
||||
if (iterator == callback_list.end()) {
|
||||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
|
||||
|
||||
@@ -183,6 +183,7 @@ private:
|
||||
TouchDevices touch_devices;
|
||||
|
||||
mutable std::mutex mutex;
|
||||
mutable std::mutex callback_mutex;
|
||||
std::unordered_map<int, ConsoleUpdateCallback> callback_list;
|
||||
int last_callback_key = 0;
|
||||
|
||||
|
||||
@@ -353,14 +353,17 @@ void EmulatedController::DisableConfiguration() {
|
||||
}
|
||||
|
||||
void EmulatedController::EnableSystemButtons() {
|
||||
std::scoped_lock lock{mutex};
|
||||
system_buttons_enabled = true;
|
||||
}
|
||||
|
||||
void EmulatedController::DisableSystemButtons() {
|
||||
std::scoped_lock lock{mutex};
|
||||
system_buttons_enabled = false;
|
||||
}
|
||||
|
||||
void EmulatedController::ResetSystemButtons() {
|
||||
std::scoped_lock lock{mutex};
|
||||
controller.home_button_state.home.Assign(false);
|
||||
controller.capture_button_state.capture.Assign(false);
|
||||
}
|
||||
@@ -494,139 +497,141 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
if (index >= controller.button_values.size()) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
bool value_changed = false;
|
||||
const auto new_status = TransformToButton(callback);
|
||||
auto& current_status = controller.button_values[index];
|
||||
std::unique_lock lock{mutex};
|
||||
bool value_changed = false;
|
||||
const auto new_status = TransformToButton(callback);
|
||||
auto& current_status = controller.button_values[index];
|
||||
|
||||
// Only read button values that have the same uuid or are pressed once
|
||||
if (current_status.uuid != uuid) {
|
||||
if (!new_status.value) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
current_status.toggle = new_status.toggle;
|
||||
current_status.uuid = uuid;
|
||||
|
||||
// Update button status with current
|
||||
if (!current_status.toggle) {
|
||||
current_status.locked = false;
|
||||
if (current_status.value != new_status.value) {
|
||||
current_status.value = new_status.value;
|
||||
value_changed = true;
|
||||
}
|
||||
} else {
|
||||
// Toggle button and lock status
|
||||
if (new_status.value && !current_status.locked) {
|
||||
current_status.locked = true;
|
||||
current_status.value = !current_status.value;
|
||||
value_changed = true;
|
||||
}
|
||||
|
||||
// Unlock button ready for next press
|
||||
if (!new_status.value && current_status.locked) {
|
||||
current_status.locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!value_changed) {
|
||||
// Only read button values that have the same uuid or are pressed once
|
||||
if (current_status.uuid != uuid) {
|
||||
if (!new_status.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_configuring) {
|
||||
controller.npad_button_state.raw = NpadButton::None;
|
||||
controller.debug_pad_button_state.raw = 0;
|
||||
TriggerOnChange(ControllerTriggerType::Button, false);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
case Settings::NativeButton::A:
|
||||
controller.npad_button_state.a.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.a.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::B:
|
||||
controller.npad_button_state.b.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.b.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::X:
|
||||
controller.npad_button_state.x.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.x.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Y:
|
||||
controller.npad_button_state.y.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.y.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::LStick:
|
||||
controller.npad_button_state.stick_l.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::RStick:
|
||||
controller.npad_button_state.stick_r.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::L:
|
||||
controller.npad_button_state.l.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.l.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::R:
|
||||
controller.npad_button_state.r.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.r.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::ZL:
|
||||
controller.npad_button_state.zl.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.zl.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::ZR:
|
||||
controller.npad_button_state.zr.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.zr.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Plus:
|
||||
controller.npad_button_state.plus.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.plus.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Minus:
|
||||
controller.npad_button_state.minus.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.minus.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::DLeft:
|
||||
controller.npad_button_state.left.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.d_left.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::DUp:
|
||||
controller.npad_button_state.up.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.d_up.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::DRight:
|
||||
controller.npad_button_state.right.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.d_right.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::DDown:
|
||||
controller.npad_button_state.down.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.d_down.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::SL:
|
||||
controller.npad_button_state.left_sl.Assign(current_status.value);
|
||||
controller.npad_button_state.right_sl.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::SR:
|
||||
controller.npad_button_state.left_sr.Assign(current_status.value);
|
||||
controller.npad_button_state.right_sr.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Home:
|
||||
if (!system_buttons_enabled) {
|
||||
break;
|
||||
}
|
||||
controller.home_button_state.home.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Screenshot:
|
||||
if (!system_buttons_enabled) {
|
||||
break;
|
||||
}
|
||||
controller.capture_button_state.capture.Assign(current_status.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current_status.toggle = new_status.toggle;
|
||||
current_status.uuid = uuid;
|
||||
|
||||
// Update button status with current
|
||||
if (!current_status.toggle) {
|
||||
current_status.locked = false;
|
||||
if (current_status.value != new_status.value) {
|
||||
current_status.value = new_status.value;
|
||||
value_changed = true;
|
||||
}
|
||||
} else {
|
||||
// Toggle button and lock status
|
||||
if (new_status.value && !current_status.locked) {
|
||||
current_status.locked = true;
|
||||
current_status.value = !current_status.value;
|
||||
value_changed = true;
|
||||
}
|
||||
|
||||
// Unlock button ready for next press
|
||||
if (!new_status.value && current_status.locked) {
|
||||
current_status.locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!value_changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_configuring) {
|
||||
controller.npad_button_state.raw = NpadButton::None;
|
||||
controller.debug_pad_button_state.raw = 0;
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Button, false);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
case Settings::NativeButton::A:
|
||||
controller.npad_button_state.a.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.a.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::B:
|
||||
controller.npad_button_state.b.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.b.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::X:
|
||||
controller.npad_button_state.x.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.x.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Y:
|
||||
controller.npad_button_state.y.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.y.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::LStick:
|
||||
controller.npad_button_state.stick_l.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::RStick:
|
||||
controller.npad_button_state.stick_r.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::L:
|
||||
controller.npad_button_state.l.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.l.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::R:
|
||||
controller.npad_button_state.r.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.r.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::ZL:
|
||||
controller.npad_button_state.zl.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.zl.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::ZR:
|
||||
controller.npad_button_state.zr.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.zr.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Plus:
|
||||
controller.npad_button_state.plus.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.plus.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Minus:
|
||||
controller.npad_button_state.minus.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.minus.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::DLeft:
|
||||
controller.npad_button_state.left.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.d_left.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::DUp:
|
||||
controller.npad_button_state.up.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.d_up.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::DRight:
|
||||
controller.npad_button_state.right.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.d_right.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::DDown:
|
||||
controller.npad_button_state.down.Assign(current_status.value);
|
||||
controller.debug_pad_button_state.d_down.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::SL:
|
||||
controller.npad_button_state.left_sl.Assign(current_status.value);
|
||||
controller.npad_button_state.right_sl.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::SR:
|
||||
controller.npad_button_state.left_sr.Assign(current_status.value);
|
||||
controller.npad_button_state.right_sr.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Home:
|
||||
if (!system_buttons_enabled) {
|
||||
break;
|
||||
}
|
||||
controller.home_button_state.home.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Screenshot:
|
||||
if (!system_buttons_enabled) {
|
||||
break;
|
||||
}
|
||||
controller.capture_button_state.capture.Assign(current_status.value);
|
||||
break;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (!is_connected) {
|
||||
if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
|
||||
Connect();
|
||||
@@ -643,7 +648,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
|
||||
if (index >= controller.stick_values.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
const auto stick_value = TransformToStick(callback);
|
||||
|
||||
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
|
||||
@@ -659,6 +664,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
|
||||
if (is_configuring) {
|
||||
controller.analog_stick_state.left = {};
|
||||
controller.analog_stick_state.right = {};
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Stick, false);
|
||||
return;
|
||||
}
|
||||
@@ -685,6 +691,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
|
||||
break;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Stick, true);
|
||||
}
|
||||
|
||||
@@ -693,7 +700,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
|
||||
if (index >= controller.trigger_values.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
const auto trigger_value = TransformToTrigger(callback);
|
||||
|
||||
// Only read trigger values that have the same uuid or are pressed once
|
||||
@@ -709,6 +716,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
|
||||
if (is_configuring) {
|
||||
controller.gc_trigger_state.left = 0;
|
||||
controller.gc_trigger_state.right = 0;
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Trigger, false);
|
||||
return;
|
||||
}
|
||||
@@ -727,6 +735,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
|
||||
break;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Trigger, true);
|
||||
}
|
||||
|
||||
@@ -735,7 +744,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
|
||||
if (index >= controller.motion_values.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
auto& raw_status = controller.motion_values[index].raw_status;
|
||||
auto& emulated = controller.motion_values[index].emulated;
|
||||
|
||||
@@ -756,6 +765,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
|
||||
force_update_motion = raw_status.force_update;
|
||||
|
||||
if (is_configuring) {
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Motion, false);
|
||||
return;
|
||||
}
|
||||
@@ -767,6 +777,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
|
||||
motion.orientation = emulated.GetOrientation();
|
||||
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Motion, true);
|
||||
}
|
||||
|
||||
@@ -775,10 +786,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
|
||||
if (index >= controller.battery_values.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
controller.battery_values[index] = TransformToBattery(callback);
|
||||
|
||||
if (is_configuring) {
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Battery, false);
|
||||
return;
|
||||
}
|
||||
@@ -835,6 +847,8 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Battery, true);
|
||||
}
|
||||
|
||||
@@ -932,6 +946,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
switch (type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
@@ -947,6 +962,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
switch (type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
@@ -982,40 +998,44 @@ void EmulatedController::Connect(bool use_temporary_value) {
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
if (is_configuring) {
|
||||
tmp_is_connected = true;
|
||||
TriggerOnChange(ControllerTriggerType::Connected, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_connected) {
|
||||
return;
|
||||
}
|
||||
is_connected = true;
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
tmp_is_connected = true;
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Connected, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_connected) {
|
||||
return;
|
||||
}
|
||||
is_connected = true;
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Connected, true);
|
||||
}
|
||||
|
||||
void EmulatedController::Disconnect() {
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
if (is_configuring) {
|
||||
tmp_is_connected = false;
|
||||
TriggerOnChange(ControllerTriggerType::Disconnected, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_connected) {
|
||||
return;
|
||||
}
|
||||
is_connected = false;
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
tmp_is_connected = false;
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Disconnected, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_connected) {
|
||||
return;
|
||||
}
|
||||
is_connected = false;
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Disconnected, true);
|
||||
}
|
||||
|
||||
bool EmulatedController::IsConnected(bool get_temporary_value) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
if (get_temporary_value && is_configuring) {
|
||||
return tmp_is_connected;
|
||||
}
|
||||
@@ -1029,10 +1049,12 @@ bool EmulatedController::IsVibrationEnabled() const {
|
||||
}
|
||||
|
||||
NpadIdType EmulatedController::GetNpadIdType() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return npad_id_type;
|
||||
}
|
||||
|
||||
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
|
||||
std::scoped_lock lock{mutex};
|
||||
if (get_temporary_value && is_configuring) {
|
||||
return tmp_npad_type;
|
||||
}
|
||||
@@ -1040,27 +1062,28 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
|
||||
}
|
||||
|
||||
void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
if (is_configuring) {
|
||||
if (tmp_npad_type == npad_type_) {
|
||||
return;
|
||||
}
|
||||
tmp_npad_type = npad_type_;
|
||||
TriggerOnChange(ControllerTriggerType::Type, false);
|
||||
if (is_configuring) {
|
||||
if (tmp_npad_type == npad_type_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (npad_type == npad_type_) {
|
||||
return;
|
||||
}
|
||||
if (is_connected) {
|
||||
LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
|
||||
NpadIdTypeToIndex(npad_id_type));
|
||||
}
|
||||
npad_type = npad_type_;
|
||||
tmp_npad_type = npad_type_;
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Type, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (npad_type == npad_type_) {
|
||||
return;
|
||||
}
|
||||
if (is_connected) {
|
||||
LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
|
||||
NpadIdTypeToIndex(npad_id_type));
|
||||
}
|
||||
npad_type = npad_type_;
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Type, true);
|
||||
}
|
||||
|
||||
@@ -1088,30 +1111,37 @@ LedPattern EmulatedController::GetLedPattern() const {
|
||||
}
|
||||
|
||||
ButtonValues EmulatedController::GetButtonsValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return controller.button_values;
|
||||
}
|
||||
|
||||
SticksValues EmulatedController::GetSticksValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return controller.stick_values;
|
||||
}
|
||||
|
||||
TriggerValues EmulatedController::GetTriggersValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return controller.trigger_values;
|
||||
}
|
||||
|
||||
ControllerMotionValues EmulatedController::GetMotionValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return controller.motion_values;
|
||||
}
|
||||
|
||||
ColorValues EmulatedController::GetColorsValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return controller.color_values;
|
||||
}
|
||||
|
||||
BatteryValues EmulatedController::GetBatteryValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return controller.battery_values;
|
||||
}
|
||||
|
||||
HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
@@ -1119,6 +1149,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||
}
|
||||
|
||||
CaptureButtonState EmulatedController::GetCaptureButtons() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
@@ -1126,6 +1157,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const {
|
||||
}
|
||||
|
||||
NpadButtonState EmulatedController::GetNpadButtons() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
@@ -1133,6 +1165,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
|
||||
}
|
||||
|
||||
DebugPadButton EmulatedController::GetDebugPadButtons() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
@@ -1140,20 +1173,27 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
|
||||
}
|
||||
|
||||
AnalogSticks EmulatedController::GetSticks() const {
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Some drivers like stick from buttons need constant refreshing
|
||||
for (auto& device : stick_devices) {
|
||||
if (!device) {
|
||||
continue;
|
||||
}
|
||||
lock.unlock();
|
||||
device->SoftUpdate();
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
return controller.analog_stick_state;
|
||||
}
|
||||
|
||||
NpadGcTriggerState EmulatedController::GetTriggers() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
@@ -1161,26 +1201,35 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
|
||||
}
|
||||
|
||||
MotionState EmulatedController::GetMotions() const {
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
// Some drivers like mouse motion need constant refreshing
|
||||
if (force_update_motion) {
|
||||
for (auto& device : motion_devices) {
|
||||
if (!device) {
|
||||
continue;
|
||||
}
|
||||
lock.unlock();
|
||||
device->ForceUpdate();
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
return controller.motion_state;
|
||||
}
|
||||
|
||||
ControllerColors EmulatedController::GetColors() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return controller.colors_state;
|
||||
}
|
||||
|
||||
BatteryLevelState EmulatedController::GetBattery() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return controller.battery_state;
|
||||
}
|
||||
|
||||
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const ControllerUpdateCallback& poller = poller_pair.second;
|
||||
if (!is_npad_service_update && poller.is_npad_service) {
|
||||
@@ -1193,13 +1242,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
|
||||
}
|
||||
|
||||
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
void EmulatedController::DeleteCallback(int key) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
const auto& iterator = callback_list.find(key);
|
||||
if (iterator == callback_list.end()) {
|
||||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
|
||||
|
||||
@@ -400,7 +400,7 @@ private:
|
||||
*/
|
||||
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
|
||||
|
||||
NpadIdType npad_id_type;
|
||||
const NpadIdType npad_id_type;
|
||||
NpadStyleIndex npad_type{NpadStyleIndex::None};
|
||||
NpadStyleTag supported_style_tag{NpadStyleSet::All};
|
||||
bool is_connected{false};
|
||||
@@ -434,6 +434,7 @@ private:
|
||||
StickDevices tas_stick_devices;
|
||||
|
||||
mutable std::mutex mutex;
|
||||
mutable std::mutex callback_mutex;
|
||||
std::unordered_map<int, ControllerUpdateCallback> callback_list;
|
||||
int last_callback_key = 0;
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
|
||||
if (index >= device_status.keyboard_values.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
bool value_changed = false;
|
||||
const auto new_status = TransformToButton(callback);
|
||||
auto& current_status = device_status.keyboard_values[index];
|
||||
@@ -201,6 +201,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
|
||||
}
|
||||
|
||||
if (is_configuring) {
|
||||
lock.unlock();
|
||||
TriggerOnChange(DeviceTriggerType::Keyboard);
|
||||
return;
|
||||
}
|
||||
@@ -208,6 +209,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
|
||||
// Index should be converted from NativeKeyboard to KeyboardKeyIndex
|
||||
UpdateKey(index, current_status.value);
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(DeviceTriggerType::Keyboard);
|
||||
}
|
||||
|
||||
@@ -227,7 +229,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
|
||||
if (index >= device_status.keyboard_moddifier_values.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
bool value_changed = false;
|
||||
const auto new_status = TransformToButton(callback);
|
||||
auto& current_status = device_status.keyboard_moddifier_values[index];
|
||||
@@ -259,6 +261,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
|
||||
}
|
||||
|
||||
if (is_configuring) {
|
||||
lock.unlock();
|
||||
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
|
||||
return;
|
||||
}
|
||||
@@ -289,6 +292,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
|
||||
break;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
|
||||
}
|
||||
|
||||
@@ -297,7 +301,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
|
||||
if (index >= device_status.mouse_button_values.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
bool value_changed = false;
|
||||
const auto new_status = TransformToButton(callback);
|
||||
auto& current_status = device_status.mouse_button_values[index];
|
||||
@@ -329,6 +333,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
|
||||
}
|
||||
|
||||
if (is_configuring) {
|
||||
lock.unlock();
|
||||
TriggerOnChange(DeviceTriggerType::Mouse);
|
||||
return;
|
||||
}
|
||||
@@ -351,6 +356,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
|
||||
break;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(DeviceTriggerType::Mouse);
|
||||
}
|
||||
|
||||
@@ -359,13 +365,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
|
||||
if (index >= device_status.mouse_analog_values.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
const auto analog_value = TransformToAnalog(callback);
|
||||
|
||||
device_status.mouse_analog_values[index] = analog_value;
|
||||
|
||||
if (is_configuring) {
|
||||
device_status.mouse_position_state = {};
|
||||
lock.unlock();
|
||||
TriggerOnChange(DeviceTriggerType::Mouse);
|
||||
return;
|
||||
}
|
||||
@@ -379,17 +386,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
|
||||
break;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(DeviceTriggerType::Mouse);
|
||||
}
|
||||
|
||||
void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::unique_lock lock{mutex};
|
||||
const auto touch_value = TransformToTouch(callback);
|
||||
|
||||
device_status.mouse_stick_value = touch_value;
|
||||
|
||||
if (is_configuring) {
|
||||
device_status.mouse_position_state = {};
|
||||
lock.unlock();
|
||||
TriggerOnChange(DeviceTriggerType::Mouse);
|
||||
return;
|
||||
}
|
||||
@@ -397,42 +406,52 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
|
||||
device_status.mouse_position_state.x = touch_value.x.value;
|
||||
device_status.mouse_position_state.y = touch_value.y.value;
|
||||
|
||||
lock.unlock();
|
||||
TriggerOnChange(DeviceTriggerType::Mouse);
|
||||
}
|
||||
|
||||
KeyboardValues EmulatedDevices::GetKeyboardValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return device_status.keyboard_values;
|
||||
}
|
||||
|
||||
KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return device_status.keyboard_moddifier_values;
|
||||
}
|
||||
|
||||
MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return device_status.mouse_button_values;
|
||||
}
|
||||
|
||||
KeyboardKey EmulatedDevices::GetKeyboard() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return device_status.keyboard_state;
|
||||
}
|
||||
|
||||
KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return device_status.keyboard_moddifier_state;
|
||||
}
|
||||
|
||||
MouseButton EmulatedDevices::GetMouseButtons() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return device_status.mouse_button_state;
|
||||
}
|
||||
|
||||
MousePosition EmulatedDevices::GetMousePosition() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return device_status.mouse_position_state;
|
||||
}
|
||||
|
||||
AnalogStickState EmulatedDevices::GetMouseWheel() const {
|
||||
std::scoped_lock lock{mutex};
|
||||
return device_status.mouse_wheel_state;
|
||||
}
|
||||
|
||||
void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
for (const auto& poller_pair : callback_list) {
|
||||
const InterfaceUpdateCallback& poller = poller_pair.second;
|
||||
if (poller.on_change) {
|
||||
@@ -442,13 +461,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
|
||||
}
|
||||
|
||||
int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
|
||||
return last_callback_key++;
|
||||
}
|
||||
|
||||
void EmulatedDevices::DeleteCallback(int key) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::scoped_lock lock{callback_mutex};
|
||||
const auto& iterator = callback_list.find(key);
|
||||
if (iterator == callback_list.end()) {
|
||||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
|
||||
|
||||
@@ -200,6 +200,7 @@ private:
|
||||
MouseStickDevice mouse_stick_device;
|
||||
|
||||
mutable std::mutex mutex;
|
||||
mutable std::mutex callback_mutex;
|
||||
std::unordered_map<int, InterfaceUpdateCallback> callback_list;
|
||||
int last_callback_key = 0;
|
||||
|
||||
|
||||
@@ -24,8 +24,15 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_)
|
||||
: kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {}
|
||||
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
|
||||
ServiceThreadType thread_type)
|
||||
: kernel{kernel_} {
|
||||
if (thread_type == ServiceThreadType::CreateNew) {
|
||||
service_thread = kernel.CreateServiceThread(service_name_);
|
||||
} else {
|
||||
service_thread = kernel.GetDefaultServiceThread();
|
||||
}
|
||||
}
|
||||
|
||||
SessionRequestHandler::~SessionRequestHandler() {
|
||||
kernel.ReleaseServiceThread(service_thread);
|
||||
|
||||
@@ -33,6 +33,11 @@ namespace Service {
|
||||
class ServiceFrameworkBase;
|
||||
}
|
||||
|
||||
enum class ServiceThreadType {
|
||||
Default,
|
||||
CreateNew,
|
||||
};
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class Domain;
|
||||
@@ -57,7 +62,8 @@ enum class ThreadWakeupReason;
|
||||
*/
|
||||
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
|
||||
public:
|
||||
SessionRequestHandler(KernelCore& kernel, const char* service_name_);
|
||||
SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
|
||||
ServiceThreadType thread_type);
|
||||
virtual ~SessionRequestHandler();
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,8 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
|
||||
auto& page_table = m_owner->PageTable();
|
||||
|
||||
// Construct the page group.
|
||||
m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize));
|
||||
m_page_group =
|
||||
KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
|
||||
|
||||
// Lock the memory.
|
||||
R_TRY(page_table.LockForCodeMemory(addr, size))
|
||||
|
||||
@@ -89,6 +89,10 @@ public:
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
bool Empty() const {
|
||||
return nodes.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<Node> nodes;
|
||||
};
|
||||
|
||||
@@ -486,6 +486,58 @@ VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
|
||||
return address;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) {
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
const size_t size = num_pages * PageSize;
|
||||
|
||||
// We're making a new group, not adding to an existing one.
|
||||
R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
|
||||
|
||||
// Begin traversal.
|
||||
Common::PageTable::TraversalContext context;
|
||||
Common::PageTable::TraversalEntry next_entry;
|
||||
R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory);
|
||||
|
||||
// Prepare tracking variables.
|
||||
PAddr cur_addr = next_entry.phys_addr;
|
||||
size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
|
||||
size_t tot_size = cur_size;
|
||||
|
||||
// Iterate, adding to group as we go.
|
||||
const auto& memory_layout = system.Kernel().MemoryLayout();
|
||||
while (tot_size < size) {
|
||||
R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context),
|
||||
ResultInvalidCurrentMemory);
|
||||
|
||||
if (next_entry.phys_addr != (cur_addr + cur_size)) {
|
||||
const size_t cur_pages = cur_size / PageSize;
|
||||
|
||||
R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
|
||||
R_TRY(pg.AddBlock(cur_addr, cur_pages));
|
||||
|
||||
cur_addr = next_entry.phys_addr;
|
||||
cur_size = next_entry.block_size;
|
||||
} else {
|
||||
cur_size += next_entry.block_size;
|
||||
}
|
||||
|
||||
tot_size += next_entry.block_size;
|
||||
}
|
||||
|
||||
// Ensure we add the right amount for the last block.
|
||||
if (tot_size > size) {
|
||||
cur_size -= (tot_size - size);
|
||||
}
|
||||
|
||||
// Add the last block.
|
||||
const size_t cur_pages = cur_size / PageSize;
|
||||
R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
|
||||
R_TRY(pg.AddBlock(cur_addr, cur_pages));
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
|
||||
KPageTable& src_page_table, VAddr src_addr) {
|
||||
KScopedLightLock lk(general_lock);
|
||||
@@ -1223,6 +1275,31 @@ ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryS
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr) {
|
||||
// Ensure that the page group isn't null.
|
||||
ASSERT(out != nullptr);
|
||||
|
||||
// Make sure that the region we're mapping is valid for the table.
|
||||
const size_t size = num_pages * PageSize;
|
||||
R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Check if state allows us to create the group.
|
||||
R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
|
||||
state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
|
||||
attr_mask, attr));
|
||||
|
||||
// Create a new page group for the region.
|
||||
R_TRY(this->MakePageGroup(*out, address, num_pages));
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
|
||||
Svc::MemoryPermission svc_perm) {
|
||||
const size_t num_pages = size / PageSize;
|
||||
@@ -1605,57 +1682,21 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
|
||||
}
|
||||
|
||||
ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
|
||||
|
||||
KMemoryPermission old_perm{};
|
||||
|
||||
if (const ResultCode result{CheckMemoryState(
|
||||
nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
|
||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
|
||||
|
||||
block_manager->UpdateLock(
|
||||
addr, size / PageSize,
|
||||
[](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
|
||||
block->ShareToDevice(permission);
|
||||
},
|
||||
new_perm);
|
||||
|
||||
return ResultSuccess;
|
||||
return this->LockMemoryAndOpen(
|
||||
nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
|
||||
KMemoryAttribute::All, KMemoryAttribute::None,
|
||||
static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
|
||||
KMemoryPermission::KernelReadWrite),
|
||||
KMemoryAttribute::Locked);
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
|
||||
|
||||
KMemoryPermission old_perm{};
|
||||
|
||||
if (const ResultCode result{CheckMemoryState(
|
||||
nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
|
||||
KMemoryAttribute::All, KMemoryAttribute::Locked)};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
|
||||
|
||||
block_manager->UpdateLock(
|
||||
addr, size / PageSize,
|
||||
[](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
|
||||
block->UnshareToDevice(permission);
|
||||
},
|
||||
new_perm);
|
||||
|
||||
return ResultSuccess;
|
||||
return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
|
||||
KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::All,
|
||||
KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
|
||||
KMemoryAttribute::Locked, nullptr);
|
||||
}
|
||||
|
||||
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
|
||||
@@ -1991,4 +2032,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr,
|
||||
size_t size, KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
||||
KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
|
||||
// Validate basic preconditions.
|
||||
ASSERT((lock_attr & attr) == KMemoryAttribute::None);
|
||||
ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
|
||||
KMemoryAttribute::None);
|
||||
|
||||
// Validate the lock request.
|
||||
const size_t num_pages = size / PageSize;
|
||||
R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Check that the output page group is empty, if it exists.
|
||||
if (out_pg) {
|
||||
ASSERT(out_pg->GetNumPages() == 0);
|
||||
}
|
||||
|
||||
// Check the state.
|
||||
KMemoryState old_state{};
|
||||
KMemoryPermission old_perm{};
|
||||
KMemoryAttribute old_attr{};
|
||||
size_t num_allocator_blocks{};
|
||||
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
|
||||
std::addressof(old_attr), std::addressof(num_allocator_blocks),
|
||||
addr, size, state_mask | KMemoryState::FlagReferenceCounted,
|
||||
state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
|
||||
attr_mask, attr));
|
||||
|
||||
// Get the physical address, if we're supposed to.
|
||||
if (out_paddr != nullptr) {
|
||||
ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
|
||||
}
|
||||
|
||||
// Make the page group, if we're supposed to.
|
||||
if (out_pg != nullptr) {
|
||||
R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
|
||||
}
|
||||
|
||||
// Decide on new perm and attr.
|
||||
new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
|
||||
KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
|
||||
|
||||
// Update permission, if we need to.
|
||||
if (new_perm != old_perm) {
|
||||
R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
|
||||
}
|
||||
|
||||
// Apply the memory block updates.
|
||||
block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr, KMemoryPermission new_perm,
|
||||
KMemoryAttribute lock_attr, const KPageLinkedList* pg) {
|
||||
// Validate basic preconditions.
|
||||
ASSERT((attr_mask & lock_attr) == lock_attr);
|
||||
ASSERT((attr & lock_attr) == lock_attr);
|
||||
|
||||
// Validate the unlock request.
|
||||
const size_t num_pages = size / PageSize;
|
||||
R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Check the state.
|
||||
KMemoryState old_state{};
|
||||
KMemoryPermission old_perm{};
|
||||
KMemoryAttribute old_attr{};
|
||||
size_t num_allocator_blocks{};
|
||||
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
|
||||
std::addressof(old_attr), std::addressof(num_allocator_blocks),
|
||||
addr, size, state_mask | KMemoryState::FlagReferenceCounted,
|
||||
state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
|
||||
attr_mask, attr));
|
||||
|
||||
// Check the page group.
|
||||
if (pg != nullptr) {
|
||||
UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
|
||||
}
|
||||
|
||||
// Decide on new perm and attr.
|
||||
new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
|
||||
KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
|
||||
|
||||
// Update permission, if we need to.
|
||||
if (new_perm != old_perm) {
|
||||
R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
|
||||
}
|
||||
|
||||
// Apply the memory block updates.
|
||||
block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_memory_block.h"
|
||||
#include "core/hle/kernel/k_memory_layout.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@@ -71,6 +72,10 @@ public:
|
||||
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
|
||||
ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
|
||||
ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
|
||||
ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr);
|
||||
|
||||
Common::PageTable& PageTableImpl() {
|
||||
return page_table_impl;
|
||||
@@ -159,10 +164,37 @@ private:
|
||||
attr_mask, attr, ignore_attr);
|
||||
}
|
||||
|
||||
ResultCode LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
||||
KMemoryPermission new_perm, KMemoryAttribute lock_attr);
|
||||
ResultCode UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
||||
KMemoryPermission new_perm, KMemoryAttribute lock_attr,
|
||||
const KPageLinkedList* pg);
|
||||
|
||||
ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
|
||||
|
||||
bool IsLockedByCurrentThread() const {
|
||||
return general_lock.IsLockedByCurrentThread();
|
||||
}
|
||||
|
||||
bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr);
|
||||
}
|
||||
|
||||
bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
*out = GetPhysicalAddr(virt_addr);
|
||||
|
||||
return *out != 0;
|
||||
}
|
||||
|
||||
mutable KLightLock general_lock;
|
||||
mutable KLightLock map_physical_memory_lock;
|
||||
|
||||
@@ -322,6 +354,7 @@ private:
|
||||
bool is_aslr_enabled{};
|
||||
|
||||
u32 heap_fill_value{};
|
||||
const KMemoryRegion* cached_physical_heap_region{};
|
||||
|
||||
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
|
||||
KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
|
||||
|
||||
@@ -61,6 +61,7 @@ struct KernelCore::Impl {
|
||||
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
||||
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
|
||||
global_handle_table->Initialize(KHandleTable::MaxTableSize);
|
||||
default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
|
||||
|
||||
is_phantom_mode_for_singlecore = false;
|
||||
|
||||
@@ -98,7 +99,7 @@ struct KernelCore::Impl {
|
||||
// Close all open server ports.
|
||||
std::unordered_set<KServerPort*> server_ports_;
|
||||
{
|
||||
std::lock_guard lk(server_ports_lock);
|
||||
std::scoped_lock lk{server_ports_lock};
|
||||
server_ports_ = server_ports;
|
||||
server_ports.clear();
|
||||
}
|
||||
@@ -156,7 +157,7 @@ struct KernelCore::Impl {
|
||||
|
||||
// Close kernel objects that were not freed on shutdown
|
||||
{
|
||||
std::lock_guard lk(registered_in_use_objects_lock);
|
||||
std::scoped_lock lk{registered_in_use_objects_lock};
|
||||
if (registered_in_use_objects.size()) {
|
||||
for (auto& object : registered_in_use_objects) {
|
||||
object->Close();
|
||||
@@ -177,7 +178,7 @@ struct KernelCore::Impl {
|
||||
|
||||
// Track kernel objects that were not freed on shutdown
|
||||
{
|
||||
std::lock_guard lk(registered_objects_lock);
|
||||
std::scoped_lock lk{registered_objects_lock};
|
||||
if (registered_objects.size()) {
|
||||
LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
|
||||
registered_objects.size());
|
||||
@@ -659,7 +660,7 @@ struct KernelCore::Impl {
|
||||
|
||||
KClientPort* port = &search->second(system.ServiceManager(), system);
|
||||
{
|
||||
std::lock_guard lk(server_ports_lock);
|
||||
std::scoped_lock lk{server_ports_lock};
|
||||
server_ports.insert(&port->GetParent()->GetServerPort());
|
||||
}
|
||||
return port;
|
||||
@@ -677,6 +678,12 @@ struct KernelCore::Impl {
|
||||
|
||||
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
|
||||
if (auto strong_ptr = service_thread.lock()) {
|
||||
if (strong_ptr == default_service_thread.lock()) {
|
||||
// Nothing to do here, the service is using default_service_thread, which will be
|
||||
// released on shutdown.
|
||||
return;
|
||||
}
|
||||
|
||||
service_threads_manager.QueueWork(
|
||||
[this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
|
||||
}
|
||||
@@ -739,7 +746,8 @@ struct KernelCore::Impl {
|
||||
std::unique_ptr<KMemoryLayout> memory_layout;
|
||||
|
||||
// Threads used for services
|
||||
std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
|
||||
std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
|
||||
std::weak_ptr<ServiceThread> default_service_thread;
|
||||
Common::ThreadWorker service_threads_manager;
|
||||
|
||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
|
||||
@@ -921,22 +929,22 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
|
||||
}
|
||||
|
||||
void KernelCore::RegisterKernelObject(KAutoObject* object) {
|
||||
std::lock_guard lk(impl->registered_objects_lock);
|
||||
std::scoped_lock lk{impl->registered_objects_lock};
|
||||
impl->registered_objects.insert(object);
|
||||
}
|
||||
|
||||
void KernelCore::UnregisterKernelObject(KAutoObject* object) {
|
||||
std::lock_guard lk(impl->registered_objects_lock);
|
||||
std::scoped_lock lk{impl->registered_objects_lock};
|
||||
impl->registered_objects.erase(object);
|
||||
}
|
||||
|
||||
void KernelCore::RegisterInUseObject(KAutoObject* object) {
|
||||
std::lock_guard lk(impl->registered_in_use_objects_lock);
|
||||
std::scoped_lock lk{impl->registered_in_use_objects_lock};
|
||||
impl->registered_in_use_objects.insert(object);
|
||||
}
|
||||
|
||||
void KernelCore::UnregisterInUseObject(KAutoObject* object) {
|
||||
std::lock_guard lk(impl->registered_in_use_objects_lock);
|
||||
std::scoped_lock lk{impl->registered_in_use_objects_lock};
|
||||
impl->registered_in_use_objects.erase(object);
|
||||
}
|
||||
|
||||
@@ -1065,6 +1073,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::
|
||||
return impl->CreateServiceThread(*this, name);
|
||||
}
|
||||
|
||||
std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
|
||||
return impl->default_service_thread;
|
||||
}
|
||||
|
||||
void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
|
||||
impl->ReleaseServiceThread(service_thread);
|
||||
}
|
||||
|
||||
@@ -271,15 +271,25 @@ public:
|
||||
void ExitSVCProfile();
|
||||
|
||||
/**
|
||||
* Creates an HLE service thread, which are used to execute service routines asynchronously.
|
||||
* While these are allocated per ServerSession, these need to be owned and managed outside
|
||||
* of ServerSession to avoid a circular dependency.
|
||||
* Creates a host thread to execute HLE service requests, which are used to execute service
|
||||
* routines asynchronously. While these are allocated per ServerSession, these need to be owned
|
||||
* and managed outside of ServerSession to avoid a circular dependency. In general, most
|
||||
* services can just use the default service thread, and not need their own host service thread.
|
||||
* See GetDefaultServiceThread.
|
||||
* @param name String name for the ServerSession creating this thread, used for debug
|
||||
* purposes.
|
||||
* @returns The a weak pointer newly created service thread.
|
||||
*/
|
||||
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
|
||||
|
||||
/**
|
||||
* Gets the default host service thread, which executes HLE service requests. Unless service
|
||||
* requests need to block on the host, the default service thread should be used in favor of
|
||||
* creating a new service thread.
|
||||
* @returns The a weak pointer for the default service thread.
|
||||
*/
|
||||
std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
|
||||
|
||||
/**
|
||||
* Releases a HLE service thread, instructing KernelCore to free it. This should be called when
|
||||
* the ServerSession associated with the thread is destroyed.
|
||||
|
||||
@@ -58,6 +58,7 @@ bool PhysicalCore::IsInterrupted() const {
|
||||
void PhysicalCore::Interrupt() {
|
||||
guard->lock();
|
||||
interrupts[core_index].SetInterrupt(true);
|
||||
arm_interface->SignalInterrupt();
|
||||
guard->unlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -1362,8 +1362,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
|
||||
ResultInvalidMemoryRegion);
|
||||
|
||||
// Create a new page group.
|
||||
KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address);
|
||||
KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
|
||||
KPageLinkedList pg;
|
||||
R_TRY(src_pt.MakeAndOpenPageGroup(
|
||||
std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
|
||||
KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
|
||||
KMemoryAttribute::All, KMemoryAttribute::None));
|
||||
|
||||
// Map the group.
|
||||
R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
|
||||
@@ -1408,8 +1411,8 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
|
||||
}
|
||||
|
||||
static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}",
|
||||
static_cast<void*>(out), address, size);
|
||||
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
|
||||
|
||||
// Get kernel instance.
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
@@ -1664,7 +1667,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
|
||||
return ResultInvalidAddress;
|
||||
}
|
||||
|
||||
if (size == 0 || Common::Is4KBAligned(size)) {
|
||||
if (size == 0 || !Common::Is4KBAligned(size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
|
||||
return ResultInvalidSize;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
|
||||
}
|
||||
|
||||
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::scoped_lock lock{mutex};
|
||||
if (nanoseconds > 0) {
|
||||
ASSERT(thread);
|
||||
ASSERT(thread->GetState() != ThreadState::Runnable);
|
||||
@@ -35,7 +35,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
|
||||
}
|
||||
|
||||
void TimeManager::UnscheduleTimeEvent(KThread* thread) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::scoped_lock lock{mutex};
|
||||
system.CoreTiming().UnscheduleEvent(time_manager_event_type,
|
||||
reinterpret_cast<uintptr_t>(thread));
|
||||
}
|
||||
|
||||
@@ -1337,7 +1337,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
{200, nullptr, "GetLastApplicationExitReason"},
|
||||
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
|
||||
{1000, nullptr, "CreateMovieMaker"},
|
||||
{1001, nullptr, "PrepareForJit"},
|
||||
{1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -1787,6 +1787,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe
|
||||
rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
|
||||
Core::System& system) {
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>(system);
|
||||
|
||||
@@ -336,6 +336,7 @@ private:
|
||||
void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
|
||||
void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx);
|
||||
void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx);
|
||||
void PrepareForJit(Kernel::HLERequestContext& ctx);
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ void SoftwareKeyboard::InitializeForeground() {
|
||||
ASSERT(work_buffer_storage != nullptr);
|
||||
|
||||
if (swkbd_config_common.initial_string_length == 0) {
|
||||
InitializeFrontendKeyboard();
|
||||
InitializeFrontendNormalKeyboard();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
|
||||
|
||||
LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
|
||||
|
||||
InitializeFrontendKeyboard();
|
||||
InitializeFrontendNormalKeyboard();
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
|
||||
@@ -480,129 +480,173 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) {
|
||||
ReplyDefault();
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::InitializeFrontendKeyboard() {
|
||||
if (is_background) {
|
||||
const auto& appear_arg = swkbd_calc_arg.appear_arg;
|
||||
void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
|
||||
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
|
||||
|
||||
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
appear_arg.ok_text.data(), appear_arg.ok_text.size());
|
||||
std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
|
||||
|
||||
const u32 max_text_length =
|
||||
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
|
||||
? appear_arg.max_text_length
|
||||
: DEFAULT_MAX_TEXT_LENGTH;
|
||||
std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
|
||||
|
||||
const u32 min_text_length =
|
||||
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
|
||||
std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
|
||||
|
||||
const s32 initial_cursor_position =
|
||||
current_cursor_position > 0 ? current_cursor_position : 0;
|
||||
const u32 max_text_length =
|
||||
swkbd_config_common.max_text_length > 0 &&
|
||||
swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
|
||||
? swkbd_config_common.max_text_length
|
||||
: DEFAULT_MAX_TEXT_LENGTH;
|
||||
|
||||
const auto text_draw_type =
|
||||
max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
|
||||
const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
|
||||
? swkbd_config_common.min_text_length
|
||||
: 0;
|
||||
|
||||
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
|
||||
.ok_text{std::move(ok_text)},
|
||||
.header_text{},
|
||||
.sub_text{},
|
||||
.guide_text{},
|
||||
.initial_text{current_text},
|
||||
.max_text_length{max_text_length},
|
||||
.min_text_length{min_text_length},
|
||||
.initial_cursor_position{initial_cursor_position},
|
||||
.type{appear_arg.type},
|
||||
.password_mode{SwkbdPasswordMode::Disabled},
|
||||
.text_draw_type{text_draw_type},
|
||||
.key_disable_flags{appear_arg.key_disable_flags},
|
||||
.use_blur_background{false},
|
||||
.enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
|
||||
.enable_return_button{appear_arg.enable_return_button},
|
||||
.disable_cancel_button{appear_arg.disable_cancel_button},
|
||||
};
|
||||
const s32 initial_cursor_position = [this] {
|
||||
switch (swkbd_config_common.initial_cursor_position) {
|
||||
case SwkbdInitialCursorPosition::Start:
|
||||
default:
|
||||
return 0;
|
||||
case SwkbdInitialCursorPosition::End:
|
||||
return static_cast<s32>(initial_text.size());
|
||||
}
|
||||
}();
|
||||
|
||||
frontend.InitializeKeyboard(
|
||||
true, std::move(initialize_parameters), {},
|
||||
[this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
|
||||
SubmitTextInline(reply_type, submitted_text, cursor_position);
|
||||
});
|
||||
} else {
|
||||
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
|
||||
const auto text_draw_type = [this, max_text_length] {
|
||||
switch (swkbd_config_common.text_draw_type) {
|
||||
case SwkbdTextDrawType::Line:
|
||||
default:
|
||||
return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
|
||||
case SwkbdTextDrawType::Box:
|
||||
case SwkbdTextDrawType::DownloadCode:
|
||||
return swkbd_config_common.text_draw_type;
|
||||
}
|
||||
}();
|
||||
|
||||
std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
|
||||
const auto enable_return_button =
|
||||
text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false;
|
||||
|
||||
std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
|
||||
const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
|
||||
? swkbd_config_new.disable_cancel_button
|
||||
: false;
|
||||
|
||||
std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
|
||||
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
|
||||
.ok_text{std::move(ok_text)},
|
||||
.header_text{std::move(header_text)},
|
||||
.sub_text{std::move(sub_text)},
|
||||
.guide_text{std::move(guide_text)},
|
||||
.initial_text{initial_text},
|
||||
.max_text_length{max_text_length},
|
||||
.min_text_length{min_text_length},
|
||||
.initial_cursor_position{initial_cursor_position},
|
||||
.type{swkbd_config_common.type},
|
||||
.password_mode{swkbd_config_common.password_mode},
|
||||
.text_draw_type{text_draw_type},
|
||||
.key_disable_flags{swkbd_config_common.key_disable_flags},
|
||||
.use_blur_background{swkbd_config_common.use_blur_background},
|
||||
.enable_backspace_button{true},
|
||||
.enable_return_button{enable_return_button},
|
||||
.disable_cancel_button{disable_cancel_button},
|
||||
};
|
||||
|
||||
const u32 max_text_length =
|
||||
swkbd_config_common.max_text_length > 0 &&
|
||||
swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
|
||||
? swkbd_config_common.max_text_length
|
||||
: DEFAULT_MAX_TEXT_LENGTH;
|
||||
frontend.InitializeKeyboard(
|
||||
false, std::move(initialize_parameters),
|
||||
[this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
|
||||
SubmitTextNormal(result, submitted_text, confirmed);
|
||||
},
|
||||
{});
|
||||
}
|
||||
|
||||
const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
|
||||
? swkbd_config_common.min_text_length
|
||||
: 0;
|
||||
void SoftwareKeyboard::InitializeFrontendInlineKeyboard(
|
||||
Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
|
||||
frontend.InitializeKeyboard(
|
||||
true, std::move(initialize_parameters), {},
|
||||
[this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
|
||||
SubmitTextInline(reply_type, submitted_text, cursor_position);
|
||||
});
|
||||
}
|
||||
|
||||
const s32 initial_cursor_position = [this] {
|
||||
switch (swkbd_config_common.initial_cursor_position) {
|
||||
case SwkbdInitialCursorPosition::Start:
|
||||
default:
|
||||
return 0;
|
||||
case SwkbdInitialCursorPosition::End:
|
||||
return static_cast<s32>(initial_text.size());
|
||||
}
|
||||
}();
|
||||
void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
|
||||
const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
|
||||
|
||||
const auto text_draw_type = [this, max_text_length] {
|
||||
switch (swkbd_config_common.text_draw_type) {
|
||||
case SwkbdTextDrawType::Line:
|
||||
default:
|
||||
return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
|
||||
case SwkbdTextDrawType::Box:
|
||||
case SwkbdTextDrawType::DownloadCode:
|
||||
return swkbd_config_common.text_draw_type;
|
||||
}
|
||||
}();
|
||||
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
appear_arg.ok_text.data(), appear_arg.ok_text.size());
|
||||
|
||||
const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box
|
||||
? swkbd_config_common.enable_return_button
|
||||
: false;
|
||||
const u32 max_text_length =
|
||||
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
|
||||
? appear_arg.max_text_length
|
||||
: DEFAULT_MAX_TEXT_LENGTH;
|
||||
|
||||
const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
|
||||
? swkbd_config_new.disable_cancel_button
|
||||
: false;
|
||||
const u32 min_text_length =
|
||||
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
|
||||
|
||||
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
|
||||
.ok_text{std::move(ok_text)},
|
||||
.header_text{std::move(header_text)},
|
||||
.sub_text{std::move(sub_text)},
|
||||
.guide_text{std::move(guide_text)},
|
||||
.initial_text{initial_text},
|
||||
.max_text_length{max_text_length},
|
||||
.min_text_length{min_text_length},
|
||||
.initial_cursor_position{initial_cursor_position},
|
||||
.type{swkbd_config_common.type},
|
||||
.password_mode{swkbd_config_common.password_mode},
|
||||
.text_draw_type{text_draw_type},
|
||||
.key_disable_flags{swkbd_config_common.key_disable_flags},
|
||||
.use_blur_background{swkbd_config_common.use_blur_background},
|
||||
.enable_backspace_button{true},
|
||||
.enable_return_button{enable_return_button},
|
||||
.disable_cancel_button{disable_cancel_button},
|
||||
};
|
||||
const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
|
||||
|
||||
frontend.InitializeKeyboard(
|
||||
false, std::move(initialize_parameters),
|
||||
[this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
|
||||
SubmitTextNormal(result, submitted_text, confirmed);
|
||||
},
|
||||
{});
|
||||
}
|
||||
const auto text_draw_type =
|
||||
max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
|
||||
|
||||
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
|
||||
.ok_text{std::move(ok_text)},
|
||||
.header_text{},
|
||||
.sub_text{},
|
||||
.guide_text{},
|
||||
.initial_text{current_text},
|
||||
.max_text_length{max_text_length},
|
||||
.min_text_length{min_text_length},
|
||||
.initial_cursor_position{initial_cursor_position},
|
||||
.type{appear_arg.type},
|
||||
.password_mode{SwkbdPasswordMode::Disabled},
|
||||
.text_draw_type{text_draw_type},
|
||||
.key_disable_flags{appear_arg.key_disable_flags},
|
||||
.use_blur_background{false},
|
||||
.enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
|
||||
.enable_return_button{appear_arg.enable_return_button},
|
||||
.disable_cancel_button{appear_arg.disable_cancel_button},
|
||||
};
|
||||
|
||||
InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
|
||||
const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
|
||||
|
||||
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
appear_arg.ok_text.data(), appear_arg.ok_text.size());
|
||||
|
||||
const u32 max_text_length =
|
||||
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
|
||||
? appear_arg.max_text_length
|
||||
: DEFAULT_MAX_TEXT_LENGTH;
|
||||
|
||||
const u32 min_text_length =
|
||||
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
|
||||
|
||||
const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
|
||||
|
||||
const auto text_draw_type =
|
||||
max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
|
||||
|
||||
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
|
||||
.ok_text{std::move(ok_text)},
|
||||
.header_text{},
|
||||
.sub_text{},
|
||||
.guide_text{},
|
||||
.initial_text{current_text},
|
||||
.max_text_length{max_text_length},
|
||||
.min_text_length{min_text_length},
|
||||
.initial_cursor_position{initial_cursor_position},
|
||||
.type{appear_arg.type},
|
||||
.password_mode{SwkbdPasswordMode::Disabled},
|
||||
.text_draw_type{text_draw_type},
|
||||
.key_disable_flags{appear_arg.key_disable_flags},
|
||||
.use_blur_background{false},
|
||||
.enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
|
||||
.enable_return_button{appear_arg.enable_return_button},
|
||||
.disable_cancel_button{appear_arg.disable_cancel_button},
|
||||
};
|
||||
|
||||
InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::ShowNormalKeyboard() {
|
||||
@@ -614,14 +658,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul
|
||||
frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message));
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::ShowInlineKeyboard() {
|
||||
void SoftwareKeyboard::ShowInlineKeyboard(
|
||||
Core::Frontend::InlineAppearParameters appear_parameters) {
|
||||
frontend.ShowInlineKeyboard(std::move(appear_parameters));
|
||||
|
||||
ChangeState(SwkbdState::InitializedIsShown);
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::ShowInlineKeyboardOld() {
|
||||
if (swkbd_state != SwkbdState::InitializedIsHidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeState(SwkbdState::InitializedIsAppearing);
|
||||
|
||||
const auto& appear_arg = swkbd_calc_arg.appear_arg;
|
||||
const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
|
||||
|
||||
const u32 max_text_length =
|
||||
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
|
||||
@@ -634,21 +685,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() {
|
||||
Core::Frontend::InlineAppearParameters appear_parameters{
|
||||
.max_text_length{max_text_length},
|
||||
.min_text_length{min_text_length},
|
||||
.key_top_scale_x{swkbd_calc_arg.key_top_scale_x},
|
||||
.key_top_scale_y{swkbd_calc_arg.key_top_scale_y},
|
||||
.key_top_translate_x{swkbd_calc_arg.key_top_translate_x},
|
||||
.key_top_translate_y{swkbd_calc_arg.key_top_translate_y},
|
||||
.key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x},
|
||||
.key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y},
|
||||
.key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x},
|
||||
.key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y},
|
||||
.type{appear_arg.type},
|
||||
.key_disable_flags{appear_arg.key_disable_flags},
|
||||
.key_top_as_floating{swkbd_calc_arg.key_top_as_floating},
|
||||
.enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
|
||||
.key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating},
|
||||
.enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
|
||||
.enable_return_button{appear_arg.enable_return_button},
|
||||
.disable_cancel_button{appear_arg.disable_cancel_button},
|
||||
};
|
||||
|
||||
frontend.ShowInlineKeyboard(std::move(appear_parameters));
|
||||
ShowInlineKeyboard(std::move(appear_parameters));
|
||||
}
|
||||
|
||||
ChangeState(SwkbdState::InitializedIsShown);
|
||||
void SoftwareKeyboard::ShowInlineKeyboardNew() {
|
||||
if (swkbd_state != SwkbdState::InitializedIsHidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeState(SwkbdState::InitializedIsAppearing);
|
||||
|
||||
const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
|
||||
|
||||
const u32 max_text_length =
|
||||
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
|
||||
? appear_arg.max_text_length
|
||||
: DEFAULT_MAX_TEXT_LENGTH;
|
||||
|
||||
const u32 min_text_length =
|
||||
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
|
||||
|
||||
Core::Frontend::InlineAppearParameters appear_parameters{
|
||||
.max_text_length{max_text_length},
|
||||
.min_text_length{min_text_length},
|
||||
.key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x},
|
||||
.key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y},
|
||||
.key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x},
|
||||
.key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y},
|
||||
.type{appear_arg.type},
|
||||
.key_disable_flags{appear_arg.key_disable_flags},
|
||||
.key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating},
|
||||
.enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
|
||||
.enable_return_button{appear_arg.enable_return_button},
|
||||
.disable_cancel_button{appear_arg.disable_cancel_button},
|
||||
};
|
||||
|
||||
ShowInlineKeyboard(std::move(appear_parameters));
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::HideInlineKeyboard() {
|
||||
@@ -693,6 +777,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
|
||||
|
||||
void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
|
||||
LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
|
||||
|
||||
ReplyReleasedUserWordInfo();
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
|
||||
@@ -702,53 +788,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat
|
||||
void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
|
||||
LOG_DEBUG(Service_AM, "Processing Request: Calc");
|
||||
|
||||
ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg));
|
||||
ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon));
|
||||
|
||||
std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand),
|
||||
sizeof(SwkbdCalcArg));
|
||||
std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand),
|
||||
sizeof(SwkbdCalcArgCommon));
|
||||
|
||||
if (swkbd_calc_arg.flags.set_input_text) {
|
||||
switch (swkbd_calc_arg_common.calc_arg_size) {
|
||||
case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld):
|
||||
ASSERT(request_data.size() ==
|
||||
sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld));
|
||||
std::memcpy(&swkbd_calc_arg_old,
|
||||
request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
|
||||
sizeof(SwkbdCalcArgOld));
|
||||
RequestCalcOld();
|
||||
break;
|
||||
case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew):
|
||||
ASSERT(request_data.size() ==
|
||||
sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
|
||||
std::memcpy(&swkbd_calc_arg_new,
|
||||
request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
|
||||
sizeof(SwkbdCalcArgNew));
|
||||
RequestCalcNew();
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size);
|
||||
ASSERT(request_data.size() >=
|
||||
sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
|
||||
std::memcpy(&swkbd_calc_arg_new,
|
||||
request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
|
||||
sizeof(SwkbdCalcArgNew));
|
||||
RequestCalcNew();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::RequestCalcOld() {
|
||||
if (swkbd_calc_arg_common.flags.set_input_text) {
|
||||
current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size());
|
||||
swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size());
|
||||
}
|
||||
|
||||
if (swkbd_calc_arg.flags.set_cursor_position) {
|
||||
current_cursor_position = swkbd_calc_arg.cursor_position;
|
||||
if (swkbd_calc_arg_common.flags.set_cursor_position) {
|
||||
current_cursor_position = swkbd_calc_arg_old.cursor_position;
|
||||
}
|
||||
|
||||
if (swkbd_calc_arg.flags.set_utf8_mode) {
|
||||
inline_use_utf8 = swkbd_calc_arg.utf8_mode;
|
||||
if (swkbd_calc_arg_common.flags.set_utf8_mode) {
|
||||
inline_use_utf8 = swkbd_calc_arg_old.utf8_mode;
|
||||
}
|
||||
|
||||
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
|
||||
swkbd_calc_arg.flags.unset_customize_dic) {
|
||||
swkbd_calc_arg_common.flags.unset_customize_dic) {
|
||||
ReplyUnsetCustomizeDic();
|
||||
}
|
||||
|
||||
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
|
||||
swkbd_calc_arg.flags.unset_user_word_info) {
|
||||
swkbd_calc_arg_common.flags.unset_user_word_info) {
|
||||
ReplyReleasedUserWordInfo();
|
||||
}
|
||||
|
||||
if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) {
|
||||
InitializeFrontendKeyboard();
|
||||
if (swkbd_state == SwkbdState::NotInitialized &&
|
||||
swkbd_calc_arg_common.flags.set_initialize_arg) {
|
||||
InitializeFrontendInlineKeyboardOld();
|
||||
|
||||
ChangeState(SwkbdState::InitializedIsHidden);
|
||||
|
||||
ReplyFinishedInitialize();
|
||||
}
|
||||
|
||||
if (!swkbd_calc_arg.flags.set_initialize_arg &&
|
||||
(swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) {
|
||||
if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
|
||||
(swkbd_calc_arg_common.flags.set_input_text ||
|
||||
swkbd_calc_arg_common.flags.set_cursor_position)) {
|
||||
InlineTextChanged();
|
||||
}
|
||||
|
||||
if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) {
|
||||
ShowInlineKeyboard();
|
||||
if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
|
||||
ShowInlineKeyboardOld();
|
||||
return;
|
||||
}
|
||||
|
||||
if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) {
|
||||
if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
|
||||
HideInlineKeyboard();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::RequestCalcNew() {
|
||||
if (swkbd_calc_arg_common.flags.set_input_text) {
|
||||
current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size());
|
||||
}
|
||||
|
||||
if (swkbd_calc_arg_common.flags.set_cursor_position) {
|
||||
current_cursor_position = swkbd_calc_arg_new.cursor_position;
|
||||
}
|
||||
|
||||
if (swkbd_calc_arg_common.flags.set_utf8_mode) {
|
||||
inline_use_utf8 = swkbd_calc_arg_new.utf8_mode;
|
||||
}
|
||||
|
||||
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
|
||||
swkbd_calc_arg_common.flags.unset_customize_dic) {
|
||||
ReplyUnsetCustomizeDic();
|
||||
}
|
||||
|
||||
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
|
||||
swkbd_calc_arg_common.flags.unset_user_word_info) {
|
||||
ReplyReleasedUserWordInfo();
|
||||
}
|
||||
|
||||
if (swkbd_state == SwkbdState::NotInitialized &&
|
||||
swkbd_calc_arg_common.flags.set_initialize_arg) {
|
||||
InitializeFrontendInlineKeyboardNew();
|
||||
|
||||
ChangeState(SwkbdState::InitializedIsHidden);
|
||||
|
||||
ReplyFinishedInitialize();
|
||||
}
|
||||
|
||||
if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
|
||||
(swkbd_calc_arg_common.flags.set_input_text ||
|
||||
swkbd_calc_arg_common.flags.set_cursor_position)) {
|
||||
InlineTextChanged();
|
||||
}
|
||||
|
||||
if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
|
||||
ShowInlineKeyboardNew();
|
||||
return;
|
||||
}
|
||||
|
||||
if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
|
||||
HideInlineKeyboard();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Core::Frontend {
|
||||
struct KeyboardInitializeParameters;
|
||||
struct InlineAppearParameters;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
class SoftwareKeyboard final : public Applet {
|
||||
@@ -78,13 +83,22 @@ private:
|
||||
void ChangeState(SwkbdState state);
|
||||
|
||||
/**
|
||||
* Signals the frontend to initialize the software keyboard with common parameters.
|
||||
* This initializes either the normal software keyboard or the inline software keyboard
|
||||
* depending on the state of is_background.
|
||||
* Signals the frontend to initialize the normal software keyboard with common parameters.
|
||||
* Note that this does not cause the keyboard to appear.
|
||||
* Use the respective Show*Keyboard() functions to cause the respective keyboards to appear.
|
||||
* Use the ShowNormalKeyboard() functions to cause the keyboard to appear.
|
||||
*/
|
||||
void InitializeFrontendKeyboard();
|
||||
void InitializeFrontendNormalKeyboard();
|
||||
|
||||
/**
|
||||
* Signals the frontend to initialize the inline software keyboard with common parameters.
|
||||
* Note that this does not cause the keyboard to appear.
|
||||
* Use the ShowInlineKeyboard() to cause the keyboard to appear.
|
||||
*/
|
||||
void InitializeFrontendInlineKeyboard(
|
||||
Core::Frontend::KeyboardInitializeParameters initialize_parameters);
|
||||
|
||||
void InitializeFrontendInlineKeyboardOld();
|
||||
void InitializeFrontendInlineKeyboardNew();
|
||||
|
||||
/// Signals the frontend to show the normal software keyboard.
|
||||
void ShowNormalKeyboard();
|
||||
@@ -94,7 +108,10 @@ private:
|
||||
std::u16string text_check_message);
|
||||
|
||||
/// Signals the frontend to show the inline software keyboard.
|
||||
void ShowInlineKeyboard();
|
||||
void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters);
|
||||
|
||||
void ShowInlineKeyboardOld();
|
||||
void ShowInlineKeyboardNew();
|
||||
|
||||
/// Signals the frontend to hide the inline software keyboard.
|
||||
void HideInlineKeyboard();
|
||||
@@ -111,6 +128,8 @@ private:
|
||||
void RequestSetUserWordInfo(const std::vector<u8>& request_data);
|
||||
void RequestSetCustomizeDic(const std::vector<u8>& request_data);
|
||||
void RequestCalc(const std::vector<u8>& request_data);
|
||||
void RequestCalcOld();
|
||||
void RequestCalcNew();
|
||||
void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
|
||||
void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
|
||||
void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
|
||||
@@ -149,7 +168,9 @@ private:
|
||||
|
||||
SwkbdState swkbd_state{SwkbdState::NotInitialized};
|
||||
SwkbdInitializeArg swkbd_initialize_arg;
|
||||
SwkbdCalcArg swkbd_calc_arg;
|
||||
SwkbdCalcArgCommon swkbd_calc_arg_common;
|
||||
SwkbdCalcArgOld swkbd_calc_arg_old;
|
||||
SwkbdCalcArgNew swkbd_calc_arg_new;
|
||||
bool use_changed_string_v2{false};
|
||||
bool use_moved_cursor_v2{false};
|
||||
bool inline_use_utf8{false};
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
@@ -216,7 +217,7 @@ struct SwkbdInitializeArg {
|
||||
};
|
||||
static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
|
||||
|
||||
struct SwkbdAppearArg {
|
||||
struct SwkbdAppearArgOld {
|
||||
SwkbdType type{};
|
||||
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
|
||||
char16_t left_optional_symbol_key{};
|
||||
@@ -229,19 +230,46 @@ struct SwkbdAppearArg {
|
||||
bool enable_return_button{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u32 flags{};
|
||||
INSERT_PADDING_WORDS(6);
|
||||
bool is_use_save_data{};
|
||||
INSERT_PADDING_BYTES(7);
|
||||
Common::UUID user_id{};
|
||||
};
|
||||
static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size.");
|
||||
static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size.");
|
||||
|
||||
struct SwkbdCalcArg {
|
||||
struct SwkbdAppearArgNew {
|
||||
SwkbdType type{};
|
||||
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
|
||||
char16_t left_optional_symbol_key{};
|
||||
char16_t right_optional_symbol_key{};
|
||||
bool use_prediction{};
|
||||
bool disable_cancel_button{};
|
||||
SwkbdKeyDisableFlags key_disable_flags{};
|
||||
u32 max_text_length{};
|
||||
u32 min_text_length{};
|
||||
bool enable_return_button{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u32 flags{};
|
||||
bool is_use_save_data{};
|
||||
INSERT_PADDING_BYTES(7);
|
||||
Common::UUID user_id{};
|
||||
u64 start_sampling_number{};
|
||||
INSERT_PADDING_WORDS(8);
|
||||
};
|
||||
static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size.");
|
||||
|
||||
struct SwkbdCalcArgCommon {
|
||||
u32 unknown{};
|
||||
u16 calc_arg_size{};
|
||||
INSERT_PADDING_BYTES(2);
|
||||
SwkbdCalcArgFlags flags{};
|
||||
SwkbdInitializeArg initialize_arg{};
|
||||
};
|
||||
static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size.");
|
||||
|
||||
struct SwkbdCalcArgOld {
|
||||
f32 volume{};
|
||||
s32 cursor_position{};
|
||||
SwkbdAppearArg appear_arg{};
|
||||
SwkbdAppearArgOld appear_arg{};
|
||||
std::array<char16_t, 0x1FA> input_text{};
|
||||
bool utf8_mode{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
@@ -265,7 +293,39 @@ struct SwkbdCalcArg {
|
||||
u8 se_group{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size.");
|
||||
static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon),
|
||||
"SwkbdCalcArgOld has incorrect size.");
|
||||
|
||||
struct SwkbdCalcArgNew {
|
||||
SwkbdAppearArgNew appear_arg{};
|
||||
f32 volume{};
|
||||
s32 cursor_position{};
|
||||
std::array<char16_t, 0x1FA> input_text{};
|
||||
bool utf8_mode{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
bool enable_backspace_button{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
bool key_top_as_floating{};
|
||||
bool footer_scalable{};
|
||||
bool alpha_enabled_in_input_mode{};
|
||||
u8 input_mode_fade_type{};
|
||||
bool disable_touch{};
|
||||
bool disable_hardware_keyboard{};
|
||||
INSERT_PADDING_BYTES(8);
|
||||
f32 key_top_scale_x{};
|
||||
f32 key_top_scale_y{};
|
||||
f32 key_top_translate_x{};
|
||||
f32 key_top_translate_y{};
|
||||
f32 key_top_bg_alpha{};
|
||||
f32 footer_bg_alpha{};
|
||||
f32 balloon_scale{};
|
||||
INSERT_PADDING_WORDS(4);
|
||||
u8 se_group{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
INSERT_PADDING_WORDS(8);
|
||||
};
|
||||
static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon),
|
||||
"SwkbdCalcArgNew has incorrect size.");
|
||||
|
||||
struct SwkbdChangedStringArg {
|
||||
u32 text_length{};
|
||||
|
||||
@@ -446,6 +446,14 @@ void WebBrowser::ExecuteLogin() {
|
||||
}
|
||||
|
||||
void WebBrowser::ExecuteOffline() {
|
||||
// TODO (Morph): This is a hack for WebSession foreground web applets such as those used by
|
||||
// Super Mario 3D All-Stars.
|
||||
// TODO (Morph): Implement WebSession.
|
||||
if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) {
|
||||
LOG_WARNING(Service_AM, "WebSession is not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document));
|
||||
|
||||
if (!Common::FS::Exists(main_url)) {
|
||||
|
||||
@@ -41,9 +41,10 @@ public:
|
||||
explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
|
||||
AudioCore::AudioOut& audio_core_, std::string&& device_name_,
|
||||
std::string&& unique_name)
|
||||
: ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_},
|
||||
device_name{std::move(device_name_)}, audio_params{audio_params_},
|
||||
main_memory{system.Memory()}, service_context{system_, "IAudioOut"} {
|
||||
: ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
|
||||
audio_core{audio_core_}, device_name{std::move(device_name_)},
|
||||
audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
|
||||
"IAudioOut"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
|
||||
|
||||
@@ -24,7 +24,8 @@ public:
|
||||
explicit IAudioRenderer(Core::System& system_,
|
||||
const AudioCommon::AudioRendererParameter& audren_params,
|
||||
const std::size_t instance_number)
|
||||
: ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} {
|
||||
: ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
|
||||
service_context{system_, "IAudioRenderer"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
|
||||
|
||||
@@ -174,7 +174,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
|
||||
ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
|
||||
|
||||
ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
|
||||
"Could not write all of the bytes but everything else has succeded.");
|
||||
"Could not write all of the bytes but everything else has succeeded.");
|
||||
|
||||
if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
|
||||
@@ -58,7 +58,8 @@ enum class FileSystemType : u8 {
|
||||
class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_)
|
||||
: ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) {
|
||||
: ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew},
|
||||
backend(std::move(backend_)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Read, "Read"},
|
||||
{1, nullptr, "Write"},
|
||||
@@ -116,7 +117,8 @@ private:
|
||||
class IFile final : public ServiceFramework<IFile> {
|
||||
public:
|
||||
explicit IFile(Core::System& system_, FileSys::VirtualFile backend_)
|
||||
: ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
|
||||
: ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew},
|
||||
backend(std::move(backend_)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IFile::Read, "Read"},
|
||||
{1, &IFile::Write, "Write"},
|
||||
@@ -252,7 +254,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
|
||||
class IDirectory final : public ServiceFramework<IDirectory> {
|
||||
public:
|
||||
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_)
|
||||
: ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
|
||||
: ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew},
|
||||
backend(std::move(backend_)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IDirectory::Read, "Read"},
|
||||
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
|
||||
@@ -308,8 +311,8 @@ private:
|
||||
class IFileSystem final : public ServiceFramework<IFileSystem> {
|
||||
public:
|
||||
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
|
||||
: ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
|
||||
size_)} {
|
||||
: ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew},
|
||||
backend{std::move(backend_)}, size{std::move(size_)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IFileSystem::CreateFile, "CreateFile"},
|
||||
{1, &IFileSystem::DeleteFile, "DeleteFile"},
|
||||
|
||||
@@ -262,11 +262,6 @@ void Controller_NPad::OnInit() {
|
||||
service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
|
||||
}
|
||||
|
||||
if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
|
||||
// We want to support all controllers
|
||||
hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
|
||||
}
|
||||
|
||||
supported_npad_id_types.resize(npad_id_list.size());
|
||||
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
|
||||
npad_id_list.size() * sizeof(Core::HID::NpadIdType));
|
||||
@@ -288,14 +283,6 @@ void Controller_NPad::OnInit() {
|
||||
WriteEmptyEntry(npad);
|
||||
}
|
||||
}
|
||||
|
||||
// Connect controllers
|
||||
for (auto& controller : controller_data) {
|
||||
const auto& device = controller.device;
|
||||
if (device->IsConnected()) {
|
||||
AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
|
||||
@@ -320,6 +307,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
|
||||
}
|
||||
|
||||
void Controller_NPad::OnRelease() {
|
||||
is_controller_initialized = false;
|
||||
for (std::size_t i = 0; i < controller_data.size(); ++i) {
|
||||
auto& controller = controller_data[i];
|
||||
service_context.CloseEvent(controller.styleset_changed_event);
|
||||
@@ -330,7 +318,7 @@ void Controller_NPad::OnRelease() {
|
||||
}
|
||||
|
||||
void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
|
||||
std::lock_guard lock{mutex};
|
||||
std::scoped_lock lock{mutex};
|
||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||
const auto controller_type = controller.device->GetNpadStyleIndex();
|
||||
if (!controller.device->IsConnected()) {
|
||||
@@ -651,9 +639,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
|
||||
|
||||
void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
|
||||
hid_core.SetSupportedStyleTag(style_set);
|
||||
|
||||
if (is_controller_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Once SetSupportedStyleSet is called controllers are fully initialized
|
||||
is_controller_initialized = true;
|
||||
|
||||
// Connect all active controllers
|
||||
for (auto& controller : controller_data) {
|
||||
const auto& device = controller.device;
|
||||
if (device->IsConnected()) {
|
||||
AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
|
||||
if (!is_controller_initialized) {
|
||||
return {Core::HID::NpadStyleSet::None};
|
||||
}
|
||||
return hid_core.GetSupportedStyleTag();
|
||||
}
|
||||
|
||||
|
||||
@@ -191,16 +191,16 @@ private:
|
||||
|
||||
// This is nn::hid::detail::NpadFullKeyColorState
|
||||
struct NpadFullKeyColorState {
|
||||
ColorAttribute attribute;
|
||||
Core::HID::NpadControllerColor fullkey;
|
||||
ColorAttribute attribute{ColorAttribute::NoController};
|
||||
Core::HID::NpadControllerColor fullkey{};
|
||||
};
|
||||
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
|
||||
|
||||
// This is nn::hid::detail::NpadJoyColorState
|
||||
struct NpadJoyColorState {
|
||||
ColorAttribute attribute;
|
||||
Core::HID::NpadControllerColor left;
|
||||
Core::HID::NpadControllerColor right;
|
||||
ColorAttribute attribute{ColorAttribute::NoController};
|
||||
Core::HID::NpadControllerColor left{};
|
||||
Core::HID::NpadControllerColor right{};
|
||||
};
|
||||
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
|
||||
|
||||
@@ -226,11 +226,11 @@ private:
|
||||
// This is nn::hid::NpadPalmaState
|
||||
// This is nn::hid::NpadSystemExtState
|
||||
struct NPadGenericState {
|
||||
s64_le sampling_number;
|
||||
Core::HID::NpadButtonState npad_buttons;
|
||||
Core::HID::AnalogStickState l_stick;
|
||||
Core::HID::AnalogStickState r_stick;
|
||||
NpadAttribute connection_status;
|
||||
s64_le sampling_number{};
|
||||
Core::HID::NpadButtonState npad_buttons{};
|
||||
Core::HID::AnalogStickState l_stick{};
|
||||
Core::HID::AnalogStickState r_stick{};
|
||||
NpadAttribute connection_status{};
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
};
|
||||
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
|
||||
@@ -253,7 +253,7 @@ private:
|
||||
Common::Vec3f gyro{};
|
||||
Common::Vec3f rotation{};
|
||||
std::array<Common::Vec3f, 3> orientation{};
|
||||
SixAxisSensorAttribute attribute;
|
||||
SixAxisSensorAttribute attribute{};
|
||||
INSERT_PADDING_BYTES(4); // Reserved
|
||||
};
|
||||
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
|
||||
@@ -325,11 +325,11 @@ private:
|
||||
|
||||
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
|
||||
struct NfcXcdDeviceHandleStateImpl {
|
||||
u64 handle;
|
||||
bool is_available;
|
||||
bool is_activated;
|
||||
u64 handle{};
|
||||
bool is_available{};
|
||||
bool is_activated{};
|
||||
INSERT_PADDING_BYTES(0x6); // Reserved
|
||||
u64 sampling_number;
|
||||
u64 sampling_number{};
|
||||
};
|
||||
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
|
||||
"NfcXcdDeviceHandleStateImpl is an invalid size");
|
||||
@@ -366,8 +366,8 @@ private:
|
||||
};
|
||||
|
||||
struct AppletFooterUi {
|
||||
AppletFooterUiAttributes attributes;
|
||||
AppletFooterUiType type;
|
||||
AppletFooterUiAttributes attributes{};
|
||||
AppletFooterUiType type{AppletFooterUiType::None};
|
||||
INSERT_PADDING_BYTES(0x5B); // Reserved
|
||||
};
|
||||
static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
|
||||
@@ -404,41 +404,41 @@ private:
|
||||
|
||||
// This is nn::hid::detail::NpadInternalState
|
||||
struct NpadInternalState {
|
||||
Core::HID::NpadStyleTag style_tag;
|
||||
NpadJoyAssignmentMode assignment_mode;
|
||||
NpadFullKeyColorState fullkey_color;
|
||||
NpadJoyColorState joycon_color;
|
||||
Lifo<NPadGenericState, hid_entry_count> fullkey_lifo;
|
||||
Lifo<NPadGenericState, hid_entry_count> handheld_lifo;
|
||||
Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo;
|
||||
Lifo<NPadGenericState, hid_entry_count> joy_left_lifo;
|
||||
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo;
|
||||
Lifo<NPadGenericState, hid_entry_count> palma_lifo;
|
||||
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo;
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo;
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo;
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo;
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo;
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo;
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo;
|
||||
DeviceType device_type;
|
||||
Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
|
||||
NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
|
||||
NpadFullKeyColorState fullkey_color{};
|
||||
NpadJoyColorState joycon_color{};
|
||||
Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
|
||||
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
|
||||
Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
|
||||
DeviceType device_type{};
|
||||
INSERT_PADDING_BYTES(0x4); // Reserved
|
||||
NPadSystemProperties system_properties;
|
||||
NpadSystemButtonProperties button_properties;
|
||||
Core::HID::NpadBatteryLevel battery_level_dual;
|
||||
Core::HID::NpadBatteryLevel battery_level_left;
|
||||
Core::HID::NpadBatteryLevel battery_level_right;
|
||||
NPadSystemProperties system_properties{};
|
||||
NpadSystemButtonProperties button_properties{};
|
||||
Core::HID::NpadBatteryLevel battery_level_dual{};
|
||||
Core::HID::NpadBatteryLevel battery_level_left{};
|
||||
Core::HID::NpadBatteryLevel battery_level_right{};
|
||||
union {
|
||||
Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{};
|
||||
AppletFooterUi applet_footer;
|
||||
AppletFooterUi applet_footer{};
|
||||
Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo;
|
||||
};
|
||||
INSERT_PADDING_BYTES(0x20); // Unknown
|
||||
Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo;
|
||||
NpadLarkType lark_type_l_and_main;
|
||||
NpadLarkType lark_type_r;
|
||||
NpadLuciaType lucia_type;
|
||||
NpadLagonType lagon_type;
|
||||
NpadLagerType lager_type;
|
||||
Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
|
||||
NpadLarkType lark_type_l_and_main{};
|
||||
NpadLarkType lark_type_r{};
|
||||
NpadLuciaType lucia_type{};
|
||||
NpadLagonType lagon_type{};
|
||||
NpadLagerType lager_type{};
|
||||
// FW 13.x Investigate there is some sort of bitflag related to joycons
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
INSERT_PADDING_BYTES(0xc08); // Unknown
|
||||
@@ -511,7 +511,8 @@ private:
|
||||
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
|
||||
NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
|
||||
bool permit_vibration_session_enabled{false};
|
||||
bool analog_stick_use_center_clamp{};
|
||||
bool analog_stick_use_center_clamp{false};
|
||||
bool is_in_lr_assignment_mode{false};
|
||||
bool is_controller_initialized{false};
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -878,6 +878,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
|
||||
parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
|
||||
|
||||
// Games expect this event to be signaled after calling this function
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SignalStyleSetChangedEvent(parameters.npad_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/jit/jit.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::JIT {
|
||||
|
||||
class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
|
||||
public:
|
||||
explicit IJitEnvironment(Core::System& system_) : ServiceFramework{system_, "IJitEnvironment"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GenerateCode"},
|
||||
{1, nullptr, "Control"},
|
||||
{1000, nullptr, "LoadPlugin"},
|
||||
{1001, nullptr, "GetCodeAddress"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class JITU final : public ServiceFramework<JITU> {
|
||||
public:
|
||||
explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
void CreateJitEnvironment(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_JIT, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IJitEnvironment>(system);
|
||||
}
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
|
||||
std::make_shared<JITU>(system)->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::JIT
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
|
||||
namespace Service::JIT {
|
||||
|
||||
/// Registers all JIT services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
|
||||
|
||||
} // namespace Service::JIT
|
||||
@@ -38,18 +38,16 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
|
||||
void nvdisp_disp0::OnOpen(DeviceFD fd) {}
|
||||
void nvdisp_disp0::OnClose(DeviceFD fd) {}
|
||||
|
||||
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
|
||||
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
|
||||
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
|
||||
u32 height, u32 stride, android::BufferTransformFlags transform,
|
||||
const Common::Rectangle<int>& crop_rect) {
|
||||
const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
|
||||
LOG_TRACE(Service,
|
||||
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
|
||||
addr, offset, width, height, stride, format);
|
||||
|
||||
const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format);
|
||||
const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform);
|
||||
const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
|
||||
stride, pixel_format, transform_flags, crop_rect};
|
||||
const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
|
||||
stride, format, transform, crop_rect};
|
||||
|
||||
system.GetPerfStats().EndSystemFrame();
|
||||
system.GPU().SwapBuffers(&framebuffer);
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "core/hle/service/nvflinger/buffer_transform_flags.h"
|
||||
#include "core/hle/service/nvflinger/pixel_format.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
@@ -31,8 +32,8 @@ public:
|
||||
void OnClose(DeviceFD fd) override;
|
||||
|
||||
/// Performs a screen flip, drawing the buffer pointed to by the handle.
|
||||
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
|
||||
NVFlinger::BufferQueue::BufferTransformFlags transform,
|
||||
void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height,
|
||||
u32 stride, android::BufferTransformFlags transform,
|
||||
const Common::Rectangle<int>& crop_rect);
|
||||
|
||||
private:
|
||||
|
||||
@@ -134,7 +134,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
|
||||
}
|
||||
|
||||
EventState status = events_interface.status[event_id];
|
||||
const bool bad_parameter = status != EventState::Free && status != EventState::Registered;
|
||||
const bool bad_parameter = status == EventState::Busy;
|
||||
if (bad_parameter) {
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::BadParameter;
|
||||
|
||||
@@ -187,7 +187,7 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
|
||||
static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
|
||||
return {
|
||||
Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
|
||||
Tegra::SubmissionMode::Increasing),
|
||||
@@ -198,7 +198,8 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) {
|
||||
static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence,
|
||||
u32 add_increment) {
|
||||
std::vector<Tegra::CommandHeader> result{
|
||||
Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
|
||||
Tegra::SubmissionMode::Increasing),
|
||||
@@ -213,7 +214,7 @@ static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence,
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence,
|
||||
static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence,
|
||||
u32 add_increment) {
|
||||
std::vector<Tegra::CommandHeader> result{
|
||||
Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
|
||||
|
||||
@@ -109,7 +109,7 @@ private:
|
||||
static_assert(sizeof(IoctlGetErrorNotification) == 16,
|
||||
"IoctlGetErrorNotification is incorrect size");
|
||||
|
||||
static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
|
||||
static_assert(sizeof(NvFence) == 8, "Fence is incorrect size");
|
||||
|
||||
struct IoctlAllocGpfifoEx {
|
||||
u32_le num_entries{};
|
||||
@@ -127,7 +127,7 @@ private:
|
||||
u32_le num_entries{}; // in
|
||||
u32_le flags{}; // in
|
||||
u32_le unk0{}; // in (1 works)
|
||||
Fence fence_out{}; // out
|
||||
NvFence fence_out{}; // out
|
||||
u32_le unk1{}; // in
|
||||
u32_le unk2{}; // in
|
||||
u32_le unk3{}; // in
|
||||
@@ -153,13 +153,13 @@ private:
|
||||
BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
|
||||
BitField<8, 1, u32_le> increment; // increment the returned fence
|
||||
} flags;
|
||||
Fence fence_out{}; // returned new fence object for others to wait on
|
||||
NvFence fence_out{}; // returned new fence object for others to wait on
|
||||
|
||||
u32 AddIncrementValue() const {
|
||||
return flags.add_increment.Value() << 1;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
|
||||
static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence),
|
||||
"IoctlSubmitGpfifo is incorrect size");
|
||||
|
||||
struct IoctlGetWaitbase {
|
||||
@@ -194,7 +194,7 @@ private:
|
||||
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
SyncpointManager& syncpoint_manager;
|
||||
Fence channel_fence;
|
||||
NvFence channel_fence;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -16,17 +16,11 @@ using DeviceFD = s32;
|
||||
|
||||
constexpr DeviceFD INVALID_NVDRV_FD = -1;
|
||||
|
||||
struct Fence {
|
||||
struct NvFence {
|
||||
s32 id;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Fence) == 8, "Fence has wrong size");
|
||||
|
||||
struct MultiFence {
|
||||
u32 num_fences;
|
||||
std::array<Fence, 4> fences;
|
||||
};
|
||||
static_assert(sizeof(NvFence) == 8, "NvFence has wrong size");
|
||||
|
||||
enum class NvResult : u32 {
|
||||
Success = 0x0,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvdrv/syncpoint_manager.h"
|
||||
#include "core/hle/service/nvflinger/ui/fence.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -37,7 +38,7 @@ class nvdevice;
|
||||
/// Represents an Nvidia event
|
||||
struct NvEvent {
|
||||
Kernel::KEvent* event{};
|
||||
Fence fence{};
|
||||
NvFence fence{};
|
||||
};
|
||||
|
||||
struct EventInterface {
|
||||
|
||||
@@ -26,7 +26,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<DeviceFD>(0);
|
||||
rb.PushEnum(NvResult::NotInitialized);
|
||||
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
if (!is_initialized) {
|
||||
ServiceError(ctx, NvResult::NotInitialized);
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
if (!is_initialized) {
|
||||
ServiceError(ctx, NvResult::NotInitialized);
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
if (!is_initialized) {
|
||||
ServiceError(ctx, NvResult::NotInitialized);
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
if (!is_initialized) {
|
||||
ServiceError(ctx, NvResult::NotInitialized);
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
if (!is_initialized) {
|
||||
ServiceError(ctx, NvResult::NotInitialized);
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
|
||||
LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name)
|
||||
: ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} {
|
||||
: ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &NVDRV::Open, "Open"},
|
||||
{1, &NVDRV::Ioctl1, "Ioctl"},
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Copyright 2014 The Android Open Source Project
|
||||
// Parts of this implementation were base on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class HLERequestContext;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
enum class TransactionId {
|
||||
RequestBuffer = 1,
|
||||
SetBufferCount = 2,
|
||||
DequeueBuffer = 3,
|
||||
DetachBuffer = 4,
|
||||
DetachNextBuffer = 5,
|
||||
AttachBuffer = 6,
|
||||
QueueBuffer = 7,
|
||||
CancelBuffer = 8,
|
||||
Query = 9,
|
||||
Connect = 10,
|
||||
Disconnect = 11,
|
||||
AllocateBuffers = 13,
|
||||
SetPreallocatedBuffer = 14,
|
||||
GetBufferHistory = 17,
|
||||
};
|
||||
|
||||
class IBinder {
|
||||
public:
|
||||
virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
|
||||
u32 flags) = 0;
|
||||
virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Copyright 2014 The Android Open Source Project
|
||||
// Parts of this implementation were base on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvflinger/ui/fence.h"
|
||||
#include "core/hle/service/nvflinger/window.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class GraphicBuffer;
|
||||
|
||||
class BufferItem final {
|
||||
public:
|
||||
constexpr BufferItem() = default;
|
||||
|
||||
std::shared_ptr<GraphicBuffer> graphic_buffer;
|
||||
Fence fence;
|
||||
Common::Rectangle<s32> crop;
|
||||
NativeWindowTransform transform{};
|
||||
u32 scaling_mode{};
|
||||
s64 timestamp{};
|
||||
bool is_auto_timestamp{};
|
||||
u64 frame_number{};
|
||||
|
||||
// The default value for buf, used to indicate this doesn't correspond to a slot.
|
||||
static constexpr s32 INVALID_BUFFER_SLOT = -1;
|
||||
union {
|
||||
s32 slot{INVALID_BUFFER_SLOT};
|
||||
s32 buf;
|
||||
};
|
||||
|
||||
bool is_droppable{};
|
||||
bool acquire_called{};
|
||||
bool transform_to_display_inverse{};
|
||||
s32 swap_interval{};
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
@@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Copyright 2012 The Android Open Source Project
|
||||
// Parts of this implementation were base on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item_consumer.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
|
||||
: ConsumerBase{std::move(consumer_)} {}
|
||||
|
||||
Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
|
||||
bool wait_for_fence) {
|
||||
if (!item) {
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
|
||||
if (status != Status::NoBufferAvailable) {
|
||||
LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
if (wait_for_fence) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
item->graphic_buffer = slots[item->slot].graphic_buffer;
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
|
||||
status != Status::NoError) {
|
||||
LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status);
|
||||
}
|
||||
|
||||
if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer);
|
||||
status != Status::NoError) {
|
||||
LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
} // namespace Service::android
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Copyright 2012 The Android Open Source Project
|
||||
// Parts of this implementation were base on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/consumer_base.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferItem;
|
||||
|
||||
class BufferItemConsumer final : public ConsumerBase {
|
||||
public:
|
||||
explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
|
||||
Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
|
||||
bool wait_for_fence = true);
|
||||
Status ReleaseBuffer(const BufferItem& item, Fence& release_fence);
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
@@ -1,206 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
: id(id_), layer_id(layer_id_), service_context{service_context_} {
|
||||
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
|
||||
}
|
||||
|
||||
BufferQueue::~BufferQueue() {
|
||||
service_context.CloseEvent(buffer_wait_event);
|
||||
}
|
||||
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
|
||||
ASSERT(slot < buffer_slots);
|
||||
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
|
||||
|
||||
{
|
||||
std::unique_lock lock{free_buffers_mutex};
|
||||
free_buffers.push_back(slot);
|
||||
}
|
||||
free_buffers_condition.notify_one();
|
||||
|
||||
buffers[slot] = {
|
||||
.slot = slot,
|
||||
.status = Buffer::Status::Free,
|
||||
.igbp_buffer = igbp_buffer,
|
||||
.transform = {},
|
||||
.crop_rect = {},
|
||||
.swap_interval = 0,
|
||||
.multi_fence = {},
|
||||
};
|
||||
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
|
||||
u32 height) {
|
||||
// Wait for first request before trying to dequeue
|
||||
{
|
||||
std::unique_lock lock{free_buffers_mutex};
|
||||
free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
|
||||
}
|
||||
|
||||
if (!is_connect) {
|
||||
// Buffer was disconnected while the thread was blocked, this is most likely due to
|
||||
// emulation being stopped
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_lock lock{free_buffers_mutex};
|
||||
|
||||
auto f_itr = free_buffers.begin();
|
||||
auto slot = buffers.size();
|
||||
|
||||
while (f_itr != free_buffers.end()) {
|
||||
const Buffer& buffer = buffers[*f_itr];
|
||||
if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width &&
|
||||
buffer.igbp_buffer.height == height) {
|
||||
slot = *f_itr;
|
||||
free_buffers.erase(f_itr);
|
||||
break;
|
||||
}
|
||||
++f_itr;
|
||||
}
|
||||
if (slot == buffers.size()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
buffers[slot].status = Buffer::Status::Dequeued;
|
||||
return {{buffers[slot].slot, &buffers[slot].multi_fence}};
|
||||
}
|
||||
|
||||
const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
|
||||
ASSERT(slot < buffers.size());
|
||||
ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
|
||||
ASSERT(buffers[slot].slot == slot);
|
||||
|
||||
return buffers[slot].igbp_buffer;
|
||||
}
|
||||
|
||||
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
|
||||
const Common::Rectangle<int>& crop_rect, u32 swap_interval,
|
||||
Service::Nvidia::MultiFence& multi_fence) {
|
||||
ASSERT(slot < buffers.size());
|
||||
ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
|
||||
ASSERT(buffers[slot].slot == slot);
|
||||
|
||||
buffers[slot].status = Buffer::Status::Queued;
|
||||
buffers[slot].transform = transform;
|
||||
buffers[slot].crop_rect = crop_rect;
|
||||
buffers[slot].swap_interval = swap_interval;
|
||||
buffers[slot].multi_fence = multi_fence;
|
||||
std::unique_lock lock{queue_sequence_mutex};
|
||||
queue_sequence.push_back(slot);
|
||||
}
|
||||
|
||||
void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
|
||||
ASSERT(slot < buffers.size());
|
||||
ASSERT(buffers[slot].status != Buffer::Status::Free);
|
||||
ASSERT(buffers[slot].slot == slot);
|
||||
|
||||
buffers[slot].status = Buffer::Status::Free;
|
||||
buffers[slot].multi_fence = multi_fence;
|
||||
buffers[slot].swap_interval = 0;
|
||||
|
||||
{
|
||||
std::unique_lock lock{free_buffers_mutex};
|
||||
free_buffers.push_back(slot);
|
||||
}
|
||||
free_buffers_condition.notify_one();
|
||||
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
|
||||
std::unique_lock lock{queue_sequence_mutex};
|
||||
std::size_t buffer_slot = buffers.size();
|
||||
// Iterate to find a queued buffer matching the requested slot.
|
||||
while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
|
||||
const auto slot = static_cast<std::size_t>(queue_sequence.front());
|
||||
ASSERT(slot < buffers.size());
|
||||
if (buffers[slot].status == Buffer::Status::Queued) {
|
||||
ASSERT(buffers[slot].slot == slot);
|
||||
buffer_slot = slot;
|
||||
}
|
||||
queue_sequence.pop_front();
|
||||
}
|
||||
if (buffer_slot == buffers.size()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
buffers[buffer_slot].status = Buffer::Status::Acquired;
|
||||
return {{buffers[buffer_slot]}};
|
||||
}
|
||||
|
||||
void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||
ASSERT(slot < buffers.size());
|
||||
ASSERT(buffers[slot].status == Buffer::Status::Acquired);
|
||||
ASSERT(buffers[slot].slot == slot);
|
||||
|
||||
buffers[slot].status = Buffer::Status::Free;
|
||||
{
|
||||
std::unique_lock lock{free_buffers_mutex};
|
||||
free_buffers.push_back(slot);
|
||||
}
|
||||
free_buffers_condition.notify_one();
|
||||
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
void BufferQueue::Connect() {
|
||||
std::unique_lock lock{queue_sequence_mutex};
|
||||
queue_sequence.clear();
|
||||
is_connect = true;
|
||||
}
|
||||
|
||||
void BufferQueue::Disconnect() {
|
||||
buffers.fill({});
|
||||
{
|
||||
std::unique_lock lock{queue_sequence_mutex};
|
||||
queue_sequence.clear();
|
||||
}
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
is_connect = false;
|
||||
free_buffers_condition.notify_one();
|
||||
}
|
||||
|
||||
u32 BufferQueue::Query(QueryType type) {
|
||||
LOG_WARNING(Service, "(STUBBED) called type={}", type);
|
||||
|
||||
switch (type) {
|
||||
case QueryType::NativeWindowFormat:
|
||||
return static_cast<u32>(PixelFormat::RGBA8888);
|
||||
case QueryType::NativeWindowWidth:
|
||||
case QueryType::NativeWindowHeight:
|
||||
break;
|
||||
case QueryType::NativeWindowMinUndequeuedBuffers:
|
||||
return 0;
|
||||
case QueryType::NativeWindowConsumerUsageBits:
|
||||
return 0;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
|
||||
return buffer_wait_event->GetWritableEvent();
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
|
||||
return buffer_wait_event->GetReadableEvent();
|
||||
}
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user