Compare commits
78 Commits
qemu-exten
...
master
Author | SHA1 | Date |
---|---|---|
Jonas Letzbor | 435efaab31 | |
Jonas Letzbor | 23bd46bef9 | |
Mariusz Bialonczyk | 2b9a886edd | |
Andri Yngvason | 64449b249a | |
Andri Yngvason | ed52288aca | |
Andri Yngvason | 232bcadd4a | |
Andri Yngvason | 087a78e551 | |
Andri Yngvason | 1baf3a902c | |
Andri Yngvason | 1baca2f9b4 | |
Andri Yngvason | 9ef4184e87 | |
Andri Yngvason | df203e5908 | |
Jan Beich | ca6036f4cf | |
Andri Yngvason | 81c9d8452c | |
Andri Yngvason | f1bba8e9bb | |
Andri Yngvason | fcc6f737ea | |
Andri Yngvason | c649bb8cb3 | |
Andri Yngvason | 2e901f8e71 | |
Andri Yngvason | d4d5f2e75b | |
Andri Yngvason | 73310b5669 | |
Andri Yngvason | 178a10853c | |
Andri Yngvason | 6faab5d6d1 | |
Andri Yngvason | 88ff40f21c | |
Andri Yngvason | 589679f851 | |
Andri Yngvason | 5dfac14981 | |
Andri Yngvason | 29b33672ea | |
Andri Yngvason | 505b03a47e | |
Andri Yngvason | 47ba673c8b | |
Andri Yngvason | d0818501f8 | |
Andri Yngvason | 3a3d129e8f | |
Andri Yngvason | af59624f83 | |
Andri Yngvason | f6a634f30b | |
Andri Yngvason | 469db1ba3a | |
Andri Yngvason | 37b3431e00 | |
Andri Yngvason | cf56a1f416 | |
Andri Yngvason | 2b99d0c019 | |
Andri Yngvason | 1d9fcb5ebc | |
Andri Yngvason | 8970accb86 | |
Andri Yngvason | d4faccba28 | |
Andri Yngvason | 9a2a318991 | |
Andri Yngvason | 760db16923 | |
Andri Yngvason | f38e43e096 | |
Andri Yngvason | f4d40df8f6 | |
Andri Yngvason | 23d1c82943 | |
Andri Yngvason | 3e8b6c311c | |
Daniel Lublin | 0ea00b45ef | |
Jan Beich | 83886345b1 | |
Andri Yngvason | b725a08b47 | |
Andri Yngvason | 3a96498113 | |
Andri Yngvason | 5b4694e1fd | |
Andri Yngvason | 7e89a36f43 | |
Andri Yngvason | 72c497e61d | |
Andri Yngvason | 09c851750e | |
Andri Yngvason | 0b757d6623 | |
Andri Yngvason | 93869627ed | |
Andri Yngvason | a69d5adc7a | |
Andri Yngvason | c4ffd8eef6 | |
Andri Yngvason | 3e652be8d6 | |
Andri Yngvason | 14299b6cff | |
Andri Yngvason | ed4c8b1cd0 | |
Andri Yngvason | 698ac6947b | |
Andri Yngvason | 040a87836d | |
Andri Yngvason | 9afe099377 | |
Andri Yngvason | e6ea9068f0 | |
Andri Yngvason | 07e8e5c303 | |
Andri Yngvason | 6bba21283f | |
Andri Yngvason | ec51388fec | |
Andri Yngvason | 9c7a4d6b07 | |
Andri Yngvason | 15b5d5a33f | |
Andri Yngvason | 545be152ce | |
Andri Yngvason | 80d18ac6aa | |
Andri Yngvason | 767f7ce816 | |
Andri Yngvason | 1a713b6642 | |
Andri Yngvason | 1ca82ce2e2 | |
Andri Yngvason | 2414e23dd6 | |
Andri Yngvason | 977f34224c | |
Andri Yngvason | 86283fd8d1 | |
Andri Yngvason | 67676b224e | |
Andri Yngvason | 7c8c29beb2 |
|
@ -1,2 +1,4 @@
|
||||||
build*
|
build*
|
||||||
subprojects
|
subprojects
|
||||||
|
.clang_complete
|
||||||
|
.vscode
|
|
@ -0,0 +1,340 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
59 Temple Place - Suite 330, Boston, MA
|
||||||
|
02111-1307, USA.
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
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 Library General Public License instead.) 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
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".
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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) 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,
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest 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
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Appendix: How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey 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) 19yy <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||||
|
Gnomovision 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.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
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 Library General
|
||||||
|
Public License instead of this License.
|
|
@ -6,7 +6,6 @@ Expect bugs and missing features.
|
||||||
|
|
||||||
## Runtime Dependencies
|
## Runtime Dependencies
|
||||||
* aml
|
* aml
|
||||||
* libvncclient
|
|
||||||
* libwayland
|
* libwayland
|
||||||
* libxkbcommon
|
* libxkbcommon
|
||||||
* pixman
|
* pixman
|
||||||
|
@ -19,19 +18,13 @@ Expect bugs and missing features.
|
||||||
* wayland-protocols
|
* wayland-protocols
|
||||||
|
|
||||||
## Building & Running
|
## Building & Running
|
||||||
At the time of writing, an unreleased version of libvncclient is required. So,
|
|
||||||
you must either use bleeding edge git based packages for the project, or build
|
|
||||||
libvncclient as a subproject.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone https://github.com/any1/aml.git
|
git clone https://github.com/any1/aml.git
|
||||||
git clone https://github.com/LibVNC/libvncserver.git
|
|
||||||
git clone https://github.com/any1/wlvncc.git
|
git clone https://github.com/any1/wlvncc.git
|
||||||
|
|
||||||
mkdir wlvncc/subprojects
|
mkdir wlvncc/subprojects
|
||||||
cd wlvncc/subprojects
|
cd wlvncc/subprojects
|
||||||
ln -s ../../aml .
|
ln -s ../../aml .
|
||||||
ln -s ../../libvncserver .
|
|
||||||
cd -
|
cd -
|
||||||
|
|
||||||
meson build
|
meson build
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
|
||||||
|
struct wl_buffer;
|
||||||
|
struct gbm_bo;
|
||||||
|
|
||||||
|
enum buffer_type {
|
||||||
|
BUFFER_UNSPEC = 0,
|
||||||
|
BUFFER_WL_SHM,
|
||||||
|
BUFFER_DMABUF,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct buffer {
|
||||||
|
enum buffer_type type;
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
int32_t scale;
|
||||||
|
size_t size;
|
||||||
|
uint32_t format;
|
||||||
|
struct wl_buffer* wl_buffer;
|
||||||
|
bool is_attached;
|
||||||
|
bool please_clean_up;
|
||||||
|
struct pixman_region16 damage;
|
||||||
|
|
||||||
|
// wl_shm:
|
||||||
|
void* pixels;
|
||||||
|
int stride;
|
||||||
|
|
||||||
|
// dmabuf:
|
||||||
|
struct gbm_bo* bo;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct buffer* buffer_create_shm(int width, int height, int stride, uint32_t format);
|
||||||
|
struct buffer* buffer_create_dmabuf(int width, int height, uint32_t format);
|
||||||
|
void buffer_destroy(struct buffer* self);
|
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef _RFB_CRYPTO_H
|
||||||
|
#define _RFB_CRYPTO_H 1
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define SHA1_HASH_SIZE 20
|
||||||
|
#define MD5_HASH_SIZE 16
|
||||||
|
|
||||||
|
/* Generates an MD5 hash of 'in' and writes it to 'out', which must be 16 bytes in size. */
|
||||||
|
int hash_md5(void *out, const void *in, const size_t in_len);
|
||||||
|
|
||||||
|
/* Generates an SHA1 hash of 'in' and writes it to 'out', which must be 20 bytes in size. */
|
||||||
|
int hash_sha1(void *out, const void *in, const size_t in_len);
|
||||||
|
|
||||||
|
/* Fill 'out' with 'len' random bytes. */
|
||||||
|
void random_bytes(void *out, size_t len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Takes the 8-byte key in 'key', reverses the bits in each byte of key as required by the RFB protocol,
|
||||||
|
encrypts 'in' with the resulting key using single-key 56-bit DES and writes the result to 'out'.
|
||||||
|
*/
|
||||||
|
int encrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Takes the 8-byte key in 'key', reverses the bits in each byte of key as required by the RFB protocol,
|
||||||
|
decrypts 'in' with the resulting key using single-key 56-bit DES and writes the result to 'out'.
|
||||||
|
*/
|
||||||
|
int decrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len);
|
||||||
|
|
||||||
|
/* Encrypts 'in' with the the 16-byte key in 'key' using AES-128-ECB and writes the result to 'out'. */
|
||||||
|
int encrypt_aes128ecb(void *out, int *out_len, const unsigned char key[16], const void *in, const size_t in_len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generates a Diffie-Hellman public-private keypair using the generator value 'gen' and prime modulo
|
||||||
|
'prime', writing the result to 'pub_out' and 'priv_out', which must be 'keylen' in size.
|
||||||
|
*/
|
||||||
|
int dh_generate_keypair(uint8_t *priv_out, uint8_t *pub_out, const uint8_t *gen, const size_t gen_len, const uint8_t *prime, const size_t keylen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Computes the shared Diffie-Hellman secret using the private key 'priv', the other side's public
|
||||||
|
key 'pub' and the modulo prime 'prime' and writes it to 'shared_out', which must be 'keylen' in size.
|
||||||
|
*/
|
||||||
|
int dh_compute_shared_key(uint8_t *shared_out, const uint8_t *priv, const uint8_t *pub, const uint8_t *prime, const size_t keylen);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include "wlr-data-control-unstable-v1.h"
|
||||||
|
#include "seat.h"
|
||||||
|
|
||||||
|
typedef void (*vnc_write_clipboard_t)(char* text, size_t size);
|
||||||
|
|
||||||
|
struct data_control {
|
||||||
|
struct wl_display* wl_display;
|
||||||
|
struct nvnc* server;
|
||||||
|
struct zwlr_data_control_manager_v1* manager;
|
||||||
|
struct zwlr_data_control_device_v1* device;
|
||||||
|
struct zwlr_data_control_source_v1* selection;
|
||||||
|
struct zwlr_data_control_source_v1* primary_selection;
|
||||||
|
struct zwlr_data_control_offer_v1* offer;
|
||||||
|
const char* mime_type;
|
||||||
|
char* cb_data;
|
||||||
|
size_t cb_len;
|
||||||
|
vnc_write_clipboard_t vnc_write_clipboard;
|
||||||
|
};
|
||||||
|
|
||||||
|
void data_control_init(struct data_control* self, struct seat* seat, struct zwlr_data_control_manager_v1 *manager);
|
||||||
|
void data_control_to_clipboard(struct data_control* self, const char* text, size_t len);
|
||||||
|
void data_control_destroy(struct data_control* self);
|
||||||
|
|
||||||
|
#pragma once
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define OPEN_H264_MAX_CONTEXTS 64
|
||||||
|
|
||||||
|
#include "rfbclient.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct AVFrame;
|
||||||
|
struct open_h264;
|
||||||
|
struct open_h264_context;
|
||||||
|
|
||||||
|
struct open_h264* open_h264_create(rfbClient* client);
|
||||||
|
void open_h264_destroy(struct open_h264*);
|
||||||
|
|
||||||
|
struct AVFrame* open_h264_decode_rect(struct open_h264* self,
|
||||||
|
rfbFramebufferUpdateRectHeader* message);
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 - 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
struct wl_output* wl_output;
|
||||||
|
struct wl_list link;
|
||||||
|
|
||||||
|
uint32_t id;
|
||||||
|
int32_t scale;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct output* output_new(struct wl_output* wl_output, uint32_t id);
|
||||||
|
void output_destroy(struct output* output);
|
||||||
|
|
||||||
|
void output_list_destroy(struct wl_list* list);
|
||||||
|
struct output* output_find_by_id(struct wl_list* list, uint32_t id);
|
||||||
|
int32_t output_list_get_max_scale(const struct wl_list* list);
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
enum wl_shm_format drm_format_to_wl_shm(uint32_t in);
|
||||||
|
uint32_t drm_format_from_wl_shm(enum wl_shm_format in);
|
||||||
|
bool drm_format_to_pixman_fmt(pixman_format_code_t* dst, uint32_t src);
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct pixman_region16;
|
||||||
|
|
||||||
|
void region_scale(struct pixman_region16* dst, struct pixman_region16* src,
|
||||||
|
double scale);
|
||||||
|
void region_translate(struct pixman_region16* dst, struct pixman_region16* src,
|
||||||
|
int x, int y);
|
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct buffer;
|
||||||
|
struct image;
|
||||||
|
struct vnc_av_frame;
|
||||||
|
|
||||||
|
int egl_init(void);
|
||||||
|
void egl_finish(void);
|
||||||
|
|
||||||
|
void render_image_egl(struct buffer* dst, const struct image* src, double scale,
|
||||||
|
int pos_x, int pos_y);
|
||||||
|
void render_av_frames_egl(struct buffer* dst, struct vnc_av_frame** src,
|
||||||
|
int n_av_frames, double scale, int x_pos, int y_pos);
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pixman.h>
|
||||||
|
|
||||||
|
struct buffer;
|
||||||
|
|
||||||
|
struct image {
|
||||||
|
int width, height, stride;
|
||||||
|
uint32_t format;
|
||||||
|
void* pixels;
|
||||||
|
struct pixman_region16* damage;
|
||||||
|
};
|
||||||
|
|
||||||
|
void render_image(struct buffer* dst, const struct image* src, double scale,
|
||||||
|
int pos_x, int pos_y);
|
|
@ -0,0 +1,791 @@
|
||||||
|
#ifndef RFBCLIENT_H
|
||||||
|
#define RFBCLIENT_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup libvncclient_api LibVNCClient API Reference
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 D. R. Commander. All Rights Reserved.
|
||||||
|
* Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
|
||||||
|
* Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
|
||||||
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file rfbclient.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rfbproto.h"
|
||||||
|
#include "keysym.h"
|
||||||
|
#include "threading.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
#include <sasl/sasl.h>
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
||||||
|
#define rfbClientSwap16IfLE(s) \
|
||||||
|
(*(char *)&client->endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s))
|
||||||
|
|
||||||
|
#define rfbClientSwap32IfLE(l) \
|
||||||
|
(*(char *)&client->endianTest ? ((((l) >> 24) & 0x000000ff) | \
|
||||||
|
(((l) & 0x00ff0000) >> 8) | \
|
||||||
|
(((l) & 0x0000ff00) << 8) | \
|
||||||
|
(((l) & 0x000000ff) << 24)) : (l))
|
||||||
|
|
||||||
|
#define rfbClientSwap64IfLE(l) \
|
||||||
|
(*(char *)&client->endianTest ? ((((l) >> 56 ) & 0x00000000000000ffULL) | \
|
||||||
|
(((l) & 0x00ff000000000000ULL) >> 40) | \
|
||||||
|
(((l) & 0x0000ff0000000000ULL) >> 24) | \
|
||||||
|
(((l) & 0x000000ff00000000ULL) >> 8) | \
|
||||||
|
(((l) & 0x00000000ff000000ULL) << 8) | \
|
||||||
|
(((l) & 0x0000000000ff0000ULL) << 24) | \
|
||||||
|
(((l) & 0x000000000000ff00ULL) << 40) | \
|
||||||
|
(((l) & 0x00000000000000ffULL) << 56)) : (l))
|
||||||
|
|
||||||
|
#define FLASH_PORT_OFFSET 5400
|
||||||
|
#define LISTEN_PORT_OFFSET 5500
|
||||||
|
#define TUNNEL_PORT_OFFSET 5500
|
||||||
|
#define SERVER_PORT_OFFSET 5900
|
||||||
|
|
||||||
|
#define DEFAULT_CONNECT_TIMEOUT 60
|
||||||
|
#define DEFAULT_READ_TIMEOUT 0
|
||||||
|
|
||||||
|
#define DEFAULT_SSH_CMD "/usr/bin/ssh"
|
||||||
|
#define DEFAULT_TUNNEL_CMD \
|
||||||
|
(DEFAULT_SSH_CMD " -f -L %L:localhost:%R %H sleep 20")
|
||||||
|
#define DEFAULT_VIA_CMD \
|
||||||
|
(DEFAULT_SSH_CMD " -f -L %L:%H:%R %G sleep 20")
|
||||||
|
|
||||||
|
#if(defined __cplusplus)
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** vncrec */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FILE* file;
|
||||||
|
struct timeval tv;
|
||||||
|
rfbBool readTimestamp;
|
||||||
|
rfbBool doNotSleep;
|
||||||
|
} rfbVNCRec;
|
||||||
|
|
||||||
|
/** client data */
|
||||||
|
|
||||||
|
typedef struct rfbClientData {
|
||||||
|
void* tag;
|
||||||
|
void* data;
|
||||||
|
struct rfbClientData* next;
|
||||||
|
} rfbClientData;
|
||||||
|
|
||||||
|
/** app data (belongs into rfbClient?) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
rfbBool shareDesktop;
|
||||||
|
rfbBool viewOnly;
|
||||||
|
|
||||||
|
const char* encodingsString;
|
||||||
|
|
||||||
|
rfbBool useBGR233;
|
||||||
|
int nColours;
|
||||||
|
rfbBool forceOwnCmap;
|
||||||
|
rfbBool forceTrueColour;
|
||||||
|
int requestedDepth;
|
||||||
|
|
||||||
|
int compressLevel;
|
||||||
|
int qualityLevel;
|
||||||
|
rfbBool enableJPEG;
|
||||||
|
rfbBool useRemoteCursor;
|
||||||
|
rfbBool palmVNC; /**< use palmvnc specific SetScale (vs ultravnc) */
|
||||||
|
int scaleSetting; /**< 0 means no scale set, else 1/scaleSetting */
|
||||||
|
} AppData;
|
||||||
|
|
||||||
|
/** For GetCredentialProc callback function to return */
|
||||||
|
typedef union _rfbCredential
|
||||||
|
{
|
||||||
|
/** X509 (VeNCrypt) */
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char *x509CACertFile;
|
||||||
|
char *x509CACrlFile;
|
||||||
|
char *x509ClientCertFile;
|
||||||
|
char *x509ClientKeyFile;
|
||||||
|
uint8_t x509CrlVerifyMode; /* Only required for OpenSSL - see meanings below */
|
||||||
|
} x509Credential;
|
||||||
|
/** Plain (VeNCrypt), MSLogon (UltraVNC) */
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char *username;
|
||||||
|
char *password;
|
||||||
|
} userCredential;
|
||||||
|
} rfbCredential;
|
||||||
|
|
||||||
|
#define rfbCredentialTypeX509 1
|
||||||
|
#define rfbCredentialTypeUser 2
|
||||||
|
|
||||||
|
/* When using OpenSSL, CRLs can be included in both the x509CACrlFile and appended
|
||||||
|
to the x509CACertFile as is common with OpenSSL. When rfbX509CrlVerifyAll is
|
||||||
|
specified the CRL list must include CRLs for all certificates in the chain */
|
||||||
|
#define rfbX509CrlVerifyNone 0 /* No CRL checking is performed */
|
||||||
|
#define rfbX509CrlVerifyClient 1 /* Only the leaf server certificate is checked */
|
||||||
|
#define rfbX509CrlVerifyAll 2 /* All certificates in the server chain are checked */
|
||||||
|
|
||||||
|
struct _rfbClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a text chat message. If your application should accept text messages
|
||||||
|
* from the server, define a function with this prototype and set
|
||||||
|
* client->HandleTextChat to a pointer to that function subsequent to your
|
||||||
|
* rfbGetClient() call.
|
||||||
|
* @param client The client which called the text chat handler
|
||||||
|
* @param value text length if text != NULL, or one of rfbTextChatOpen,
|
||||||
|
* rfbTextChatClose, rfbTextChatFinished if text == NULL
|
||||||
|
* @param text The text message from the server
|
||||||
|
*/
|
||||||
|
typedef void (*HandleTextChatProc)(struct _rfbClient* client, int value, char *text);
|
||||||
|
/**
|
||||||
|
* Handles XVP server messages. If your application sends XVP messages to the
|
||||||
|
* server, you'll want to handle the server's XVP_FAIL and XVP_INIT responses.
|
||||||
|
* Define a function with this prototype and set client->HandleXvpMsg to a
|
||||||
|
* pointer to that function subsequent to your rfbGetClient() call.
|
||||||
|
* @param client The client which called the XVP message handler
|
||||||
|
* @param version The highest XVP extension version that the server supports
|
||||||
|
* @param opcode The opcode. 0 is XVP_FAIL, 1 is XVP_INIT
|
||||||
|
*/
|
||||||
|
typedef void (*HandleXvpMsgProc)(struct _rfbClient* client, uint8_t version, uint8_t opcode);
|
||||||
|
typedef void (*HandleKeyboardLedStateProc)(struct _rfbClient* client, int value, int pad);
|
||||||
|
typedef rfbBool (*HandleCursorPosProc)(struct _rfbClient* client, int x, int y);
|
||||||
|
typedef void (*SoftCursorLockAreaProc)(struct _rfbClient* client, int x, int y, int w, int h);
|
||||||
|
typedef void (*SoftCursorUnlockScreenProc)(struct _rfbClient* client);
|
||||||
|
/**
|
||||||
|
Callback indicating that a rectangular area of the client's framebuffer was updated.
|
||||||
|
As a server will usually send several rects per rfbFramebufferUpdate message, this
|
||||||
|
callback is usually called multiple times per rfbFramebufferUpdate message.
|
||||||
|
@param client The client whose framebuffer was (partially) updated
|
||||||
|
@param x The x-coordinate of the upper left corner of the updated rectangle
|
||||||
|
@param y The y-coordinate of the upper left corner of the updated rectangle
|
||||||
|
@param w The width of the updated rectangle
|
||||||
|
@param h The heigth of the updated rectangle
|
||||||
|
*/
|
||||||
|
typedef void (*GotFrameBufferUpdateProc)(struct _rfbClient* client, int x, int y, int w, int h);
|
||||||
|
/**
|
||||||
|
Callback indicating that a client has completely processed an rfbFramebufferUpdate
|
||||||
|
message sent by a server.
|
||||||
|
This is called exactly once per each handled rfbFramebufferUpdate message.
|
||||||
|
@param client The client which finished processing an rfbFramebufferUpdate
|
||||||
|
*/
|
||||||
|
typedef void (*FinishedFrameBufferUpdateProc)(struct _rfbClient* client);
|
||||||
|
typedef void (*StartingFrameBufferUpdateProc)(struct _rfbClient* client);
|
||||||
|
typedef void (*CancelledFrameBufferUpdateProc)(struct _rfbClient* client);
|
||||||
|
typedef char* (*GetPasswordProc)(struct _rfbClient* client);
|
||||||
|
typedef rfbCredential* (*GetCredentialProc)(struct _rfbClient* client, int credentialType);
|
||||||
|
typedef rfbBool (*MallocFrameBufferProc)(struct _rfbClient* client);
|
||||||
|
typedef void (*GotXCutTextProc)(struct _rfbClient* client, const char *text, int textlen);
|
||||||
|
typedef void (*BellProc)(struct _rfbClient* client);
|
||||||
|
/**
|
||||||
|
Called when a cursor shape update was received from the server. The decoded cursor shape
|
||||||
|
will be in client->rcSource. It's up to the application to do something with this, e.g. draw
|
||||||
|
into a viewer's window. If you want the server to draw the cursor into the framebuffer, be
|
||||||
|
careful not to announce remote cursor support, i.e. not include rfbEncodingXCursor or
|
||||||
|
rfbEncodingRichCursor in SetFormatAndEncodings().
|
||||||
|
*/
|
||||||
|
typedef void (*GotCursorShapeProc)(struct _rfbClient* client, int xhot, int yhot, int width, int height, int bytesPerPixel);
|
||||||
|
typedef void (*GotCopyRectProc)(struct _rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y);
|
||||||
|
typedef void (*GotFillRectProc)(struct _rfbClient* client, int x, int y, int w, int h, uint32_t colour);
|
||||||
|
typedef void (*GotBitmapProc)(struct _rfbClient* client, const uint8_t* buffer, int x, int y, int w, int h);
|
||||||
|
typedef rfbBool (*GotJpegProc)(struct _rfbClient* client, const uint8_t* buffer, int length, int x, int y, int w, int h);
|
||||||
|
typedef rfbBool (*LockWriteToTLSProc)(struct _rfbClient* client); /** @deprecated */
|
||||||
|
typedef rfbBool (*UnlockWriteToTLSProc)(struct _rfbClient* client); /** @deprecated */
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
typedef char* (*GetUserProc)(struct _rfbClient* client);
|
||||||
|
typedef char* (*GetSASLMechanismProc)(struct _rfbClient* client, char* mechlist);
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
||||||
|
typedef struct _rfbClient {
|
||||||
|
uint8_t* frameBuffer;
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
int endianTest;
|
||||||
|
|
||||||
|
AppData appData;
|
||||||
|
|
||||||
|
const char* programName;
|
||||||
|
char* serverHost;
|
||||||
|
int serverPort;
|
||||||
|
rfbBool listenSpecified;
|
||||||
|
int listenPort, flashPort;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int x, y, w, h;
|
||||||
|
} updateRect;
|
||||||
|
|
||||||
|
/** Note that the CoRRE encoding uses this buffer and assumes it is big enough
|
||||||
|
to hold 255 * 255 * 32 bits -> 260100 bytes. 640*480 = 307200 bytes.
|
||||||
|
Hextile also assumes it is big enough to hold 16 * 16 * 32 bits.
|
||||||
|
Tight encoding assumes BUFFER_SIZE is at least 16384 bytes. */
|
||||||
|
|
||||||
|
#define RFB_BUFFER_SIZE (640*480)
|
||||||
|
char buffer[RFB_BUFFER_SIZE];
|
||||||
|
|
||||||
|
/* rfbproto.c */
|
||||||
|
|
||||||
|
rfbSocket sock;
|
||||||
|
rfbBool canUseCoRRE;
|
||||||
|
rfbBool canUseHextile;
|
||||||
|
char *desktopName;
|
||||||
|
rfbPixelFormat format;
|
||||||
|
rfbServerInitMsg si;
|
||||||
|
|
||||||
|
/* sockets.c */
|
||||||
|
#define RFB_BUF_SIZE 8192
|
||||||
|
char buf[RFB_BUF_SIZE];
|
||||||
|
char *bufoutptr;
|
||||||
|
unsigned int buffered;
|
||||||
|
|
||||||
|
/* The zlib encoding requires expansion/decompression/deflation of the
|
||||||
|
compressed data in the "buffer" above into another, result buffer.
|
||||||
|
However, the size of the result buffer can be determined precisely
|
||||||
|
based on the bitsPerPixel, height and width of the rectangle. We
|
||||||
|
allocate this buffer one time to be the full size of the buffer. */
|
||||||
|
|
||||||
|
/* Ultra Encoding uses this buffer too */
|
||||||
|
|
||||||
|
int ultra_buffer_size;
|
||||||
|
char *ultra_buffer;
|
||||||
|
|
||||||
|
int raw_buffer_size;
|
||||||
|
char *raw_buffer;
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||||
|
z_stream decompStream;
|
||||||
|
rfbBool decompStreamInited;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||||
|
/*
|
||||||
|
* Variables for the ``tight'' encoding implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Separate buffer for compressed data. */
|
||||||
|
#define ZLIB_BUFFER_SIZE 30000
|
||||||
|
char zlib_buffer[ZLIB_BUFFER_SIZE];
|
||||||
|
|
||||||
|
/* Four independent compression streams for zlib library. */
|
||||||
|
z_stream zlibStream[4];
|
||||||
|
rfbBool zlibStreamActive[4];
|
||||||
|
|
||||||
|
/* Filter stuff. Should be initialized by filter initialization code. */
|
||||||
|
rfbBool cutZeros;
|
||||||
|
int rectWidth, rectColors;
|
||||||
|
char tightPalette[256*4];
|
||||||
|
uint8_t tightPrevRow[2048*3*sizeof(uint16_t)];
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
||||||
|
/** JPEG decoder state (obsolete-- do not use). */
|
||||||
|
rfbBool jpegError;
|
||||||
|
|
||||||
|
struct jpeg_source_mgr* jpegSrcManager;
|
||||||
|
void* jpegBufferPtr;
|
||||||
|
size_t jpegBufferLen;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* cursor.c */
|
||||||
|
/** Holds cursor shape data when received from server. */
|
||||||
|
uint8_t *rcSource, *rcMask;
|
||||||
|
|
||||||
|
/** private data pointer */
|
||||||
|
rfbClientData* clientData;
|
||||||
|
|
||||||
|
rfbVNCRec* vncRec;
|
||||||
|
|
||||||
|
/* Keyboard State support (is 'Caps Lock' set on the remote display???) */
|
||||||
|
int KeyboardLedStateEnabled;
|
||||||
|
int CurrentKeyboardLedState;
|
||||||
|
|
||||||
|
int canHandleNewFBSize;
|
||||||
|
|
||||||
|
/* hooks */
|
||||||
|
HandleTextChatProc HandleTextChat;
|
||||||
|
HandleKeyboardLedStateProc HandleKeyboardLedState;
|
||||||
|
HandleCursorPosProc HandleCursorPos;
|
||||||
|
SoftCursorLockAreaProc SoftCursorLockArea;
|
||||||
|
SoftCursorUnlockScreenProc SoftCursorUnlockScreen;
|
||||||
|
GotFrameBufferUpdateProc GotFrameBufferUpdate;
|
||||||
|
/** the pointer returned by GetPassword will be freed after use! */
|
||||||
|
GetPasswordProc GetPassword;
|
||||||
|
MallocFrameBufferProc MallocFrameBuffer;
|
||||||
|
GotXCutTextProc GotXCutText;
|
||||||
|
BellProc Bell;
|
||||||
|
|
||||||
|
GotCursorShapeProc GotCursorShape;
|
||||||
|
GotCopyRectProc GotCopyRect;
|
||||||
|
|
||||||
|
/** Which messages are supported by the server
|
||||||
|
* This is a *guess* for most servers.
|
||||||
|
* (If we can even detect the type of server)
|
||||||
|
*
|
||||||
|
* If the server supports the "rfbEncodingSupportedMessages"
|
||||||
|
* then this will be updated when the encoding is received to
|
||||||
|
* accurately reflect the servers capabilities.
|
||||||
|
*/
|
||||||
|
rfbSupportedMessages supportedMessages;
|
||||||
|
|
||||||
|
/** negotiated protocol version */
|
||||||
|
int major, minor;
|
||||||
|
|
||||||
|
/** The selected security types */
|
||||||
|
uint32_t authScheme, subAuthScheme;
|
||||||
|
|
||||||
|
/** The TLS session for Anonymous TLS and VeNCrypt */
|
||||||
|
void* tlsSession;
|
||||||
|
|
||||||
|
/** To support security types that requires user input (except VNC password
|
||||||
|
* authentication), for example VeNCrypt and MSLogon, this callback function
|
||||||
|
* must be set before the authentication. Otherwise, it implicates that the
|
||||||
|
* caller application does not support it and related security types should
|
||||||
|
* be bypassed.
|
||||||
|
*/
|
||||||
|
GetCredentialProc GetCredential;
|
||||||
|
|
||||||
|
/** The 0-terminated security types supported by the client.
|
||||||
|
* Set by function SetClientAuthSchemes() */
|
||||||
|
uint32_t *clientAuthSchemes;
|
||||||
|
|
||||||
|
/** When the server is a repeater, this specifies the final destination */
|
||||||
|
char *destHost;
|
||||||
|
int destPort;
|
||||||
|
|
||||||
|
/** the QoS IP DSCP for this client */
|
||||||
|
int QoS_DSCP;
|
||||||
|
|
||||||
|
/** hook to handle xvp server messages */
|
||||||
|
HandleXvpMsgProc HandleXvpMsg;
|
||||||
|
|
||||||
|
/* listen.c */
|
||||||
|
rfbSocket listenSock;
|
||||||
|
|
||||||
|
FinishedFrameBufferUpdateProc FinishedFrameBufferUpdate;
|
||||||
|
|
||||||
|
char *listenAddress;
|
||||||
|
/* IPv6 listen socket, address and port*/
|
||||||
|
rfbSocket listen6Sock;
|
||||||
|
char* listen6Address;
|
||||||
|
int listen6Port;
|
||||||
|
|
||||||
|
/* Output Window ID. When set, client application enables libvncclient to perform direct rendering in its window */
|
||||||
|
unsigned long outputWindow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These lock/unlock hooks are not used anymore. LibVNCClient will now use
|
||||||
|
* platform-specific synchronization library to protect concurrent TLS R/W.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
LockWriteToTLSProc LockWriteToTLS;
|
||||||
|
UnlockWriteToTLSProc UnlockWriteToTLS;
|
||||||
|
|
||||||
|
/** Hooks for custom rendering
|
||||||
|
*
|
||||||
|
* VNC rendering boils down to 3 activities:
|
||||||
|
* - GotCopyRect: copy an area of the framebuffer
|
||||||
|
* - GotFillRect: fill an area of the framebuffer with a solid color
|
||||||
|
* - GotBitmap: copy the bitmap in the buffer into the framebuffer
|
||||||
|
* The client application should either set all three of these or none!
|
||||||
|
*/
|
||||||
|
GotFillRectProc GotFillRect;
|
||||||
|
GotBitmapProc GotBitmap;
|
||||||
|
/** Hook for custom JPEG decoding and rendering */
|
||||||
|
GotJpegProc GotJpeg;
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
sasl_conn_t *saslconn;
|
||||||
|
const char *saslDecoded;
|
||||||
|
unsigned int saslDecodedLength;
|
||||||
|
unsigned int saslDecodedOffset;
|
||||||
|
sasl_secret_t *saslSecret;
|
||||||
|
|
||||||
|
/* Callback to allow the client to choose a preferred mechanism. The string returned will
|
||||||
|
be freed once no longer required. */
|
||||||
|
GetSASLMechanismProc GetSASLMechanism;
|
||||||
|
GetUserProc GetUser;
|
||||||
|
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
||||||
|
/** JPEG decoder state. */
|
||||||
|
void *tjhnd;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
/* timeout in seconds for select() after connect() */
|
||||||
|
unsigned int connectTimeout;
|
||||||
|
|
||||||
|
/* timeout in seconds when reading from half-open connections in
|
||||||
|
* ReadFromRFBServer() - keep at 0 to disable timeout detection and handling */
|
||||||
|
unsigned int readTimeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutex to protect concurrent TLS read/write.
|
||||||
|
* For internal use only.
|
||||||
|
*/
|
||||||
|
MUTEX(tlsRwMutex);
|
||||||
|
|
||||||
|
rfbBool requestedResize;
|
||||||
|
/**
|
||||||
|
* Used for intended dimensions, rfbClient.width and rfbClient.height are used to manage the real framebuffer dimensions.
|
||||||
|
*/
|
||||||
|
rfbExtDesktopScreen screen;
|
||||||
|
|
||||||
|
StartingFrameBufferUpdateProc StartingFrameBufferUpdate;
|
||||||
|
CancelledFrameBufferUpdateProc CancelledFrameBufferUpdate;
|
||||||
|
} rfbClient;
|
||||||
|
|
||||||
|
/* cursor.c */
|
||||||
|
/**
|
||||||
|
* Handles XCursor and RichCursor shape updates from the server.
|
||||||
|
* We emulate cursor operating on the frame buffer (that is
|
||||||
|
* why we call it "software cursor"). This decodes the received cursor
|
||||||
|
* shape and hands it over to GotCursorShapeProc, if set.
|
||||||
|
*/
|
||||||
|
extern rfbBool HandleCursorShape(rfbClient* client,int xhot, int yhot, int width, int height, uint32_t enc);
|
||||||
|
|
||||||
|
/* listen.c */
|
||||||
|
|
||||||
|
extern void listenForIncomingConnections(rfbClient* viewer);
|
||||||
|
extern int listenForIncomingConnectionsNoFork(rfbClient* viewer, int usec_timeout);
|
||||||
|
|
||||||
|
/* rfbproto.c */
|
||||||
|
|
||||||
|
extern rfbBool rfbEnableClientLogging;
|
||||||
|
typedef void (*rfbClientLogProc)(const char *format, ...);
|
||||||
|
extern rfbClientLogProc rfbClientLog,rfbClientErr;
|
||||||
|
extern rfbBool ConnectToRFBServer(rfbClient* client,const char *hostname, int port);
|
||||||
|
extern rfbBool ConnectToRFBRepeater(rfbClient* client,const char *repeaterHost, int repeaterPort, const char *destHost, int destPort);
|
||||||
|
extern void SetClientAuthSchemes(rfbClient* client,const uint32_t *authSchemes, int size);
|
||||||
|
extern rfbBool InitialiseRFBConnection(rfbClient* client);
|
||||||
|
/**
|
||||||
|
* Sends format and encoding parameters to the server. Your application can
|
||||||
|
* modify the 'client' data structure directly. However some changes to this
|
||||||
|
* structure must be communicated back to the server. For instance, if you
|
||||||
|
* change the encoding to hextile, the server needs to know that it should send
|
||||||
|
* framebuffer updates in hextile format. Likewise if you change the pixel
|
||||||
|
* format of the framebuffer, the server must be notified about this as well.
|
||||||
|
* Call this function to propagate your changes of the local 'client' structure
|
||||||
|
* over to the server.
|
||||||
|
* @li Encoding type
|
||||||
|
* @li RFB protocol extensions announced via pseudo-encodings
|
||||||
|
* @li Framebuffer pixel format (like RGB vs ARGB)
|
||||||
|
* @li Remote cursor support
|
||||||
|
* @param client The client in which the format or encodings have been changed
|
||||||
|
* @return true if the format or encodings were sent to the server successfully,
|
||||||
|
* false otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool SetFormatAndEncodings(rfbClient* client);
|
||||||
|
extern rfbBool SendIncrementalFramebufferUpdateRequest(rfbClient* client);
|
||||||
|
/**
|
||||||
|
* Sends a framebuffer update request to the server. A VNC client may request an
|
||||||
|
* update from the server at any time. You can also specify which portions of
|
||||||
|
* the screen you want updated. This can be handy if a pointer is at certain
|
||||||
|
* location and the user pressed a mouse button, for instance. Then you can
|
||||||
|
* immediately request an update of the region around the pointer from the
|
||||||
|
* server.
|
||||||
|
* @note The coordinate system is a left-handed Cartesian coordinate system with
|
||||||
|
* the Z axis (unused) pointing out of the screen. Alternately you can think of
|
||||||
|
* it as a right-handed Cartesian coordinate system with the Z axis pointing
|
||||||
|
* into the screen. The origin is at the upper left corner of the framebuffer.
|
||||||
|
* @param client The client through which to send the request
|
||||||
|
* @param x The horizontal position of the update request rectangle
|
||||||
|
* @param y The vertical position of the update request rectangle
|
||||||
|
* @param w The width of the update request rectangle
|
||||||
|
* @param h The height of the update request rectangle
|
||||||
|
* @param incremental false: server sends rectangle even if nothing changed.
|
||||||
|
* true: server only sends changed parts of rectangle.
|
||||||
|
* @return true if the update request was sent successfully, false otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool SendFramebufferUpdateRequest(rfbClient* client,
|
||||||
|
int x, int y, int w, int h,
|
||||||
|
rfbBool incremental);
|
||||||
|
extern rfbBool SendScaleSetting(rfbClient* client,int scaleSetting);
|
||||||
|
/**
|
||||||
|
* Sends a pointer event to the server. A pointer event includes a cursor
|
||||||
|
* location and a button mask. The button mask indicates which buttons on the
|
||||||
|
* pointing device are pressed. Each button is represented by a bit in the
|
||||||
|
* button mask. A 1 indicates the button is pressed while a 0 indicates that it
|
||||||
|
* is not pressed. You may use these pre-defined button masks by ORing them
|
||||||
|
* together: rfbButton1Mask, rfbButton2Mask, rfbButton3Mask, rfbButton4Mask
|
||||||
|
* rfbButton5Mask
|
||||||
|
* @note The cursor location is relative to the client's framebuffer, not the
|
||||||
|
* client's screen itself.
|
||||||
|
* @note The coordinate system is a left-handed Cartesian coordinate system with
|
||||||
|
* the Z axis (unused) pointing out of the screen. Alternately you can think of
|
||||||
|
* it as a right-handed Cartesian coordinate system with the Z axis pointing
|
||||||
|
* into the screen. The origin is at the upper left corner of the screen.
|
||||||
|
* @param client The client through which to send the pointer event
|
||||||
|
* @param x the horizontal location of the cursor
|
||||||
|
* @param y the vertical location of the cursor
|
||||||
|
* @param buttonMask the button mask indicating which buttons are pressed
|
||||||
|
* @return true if the pointer event was sent successfully, false otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool SendPointerEvent(rfbClient* client,int x, int y, int buttonMask);
|
||||||
|
/**
|
||||||
|
* Sends a SetDesktopSize event to the server.
|
||||||
|
* @param client The client through which to send the SetDesktopSize event
|
||||||
|
* @param width The width of the update request rectangle
|
||||||
|
* @param height The height of the update request rectangle
|
||||||
|
* @return true if the SetDesktopSize event was send successfully, false otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool SendExtDesktopSize(rfbClient* client, uint16_t width, uint16_t height);
|
||||||
|
/**
|
||||||
|
* Sends a key event to the server. If your application is not merely a VNC
|
||||||
|
* viewer (i.e. it controls the server), you'll want to send the keys that the
|
||||||
|
* user presses to the server. Use this function to do that.
|
||||||
|
* @param client The client through which to send the key event
|
||||||
|
* @param key An rfbKeySym defined in keysym.h
|
||||||
|
* @param down true if this was a key down event, false otherwise
|
||||||
|
* @return true if the key event was send successfully, false otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool SendKeyEvent(rfbClient* client,uint32_t key, rfbBool down);
|
||||||
|
/**
|
||||||
|
* The same as SendKeyEvent, except a key code will be sent along with the
|
||||||
|
* symbol if the server supports extended key events.
|
||||||
|
* @param client The client through which to send the key event
|
||||||
|
* @param keysym An rfbKeySym defined in keysym.h
|
||||||
|
* @param keycode An XT key code
|
||||||
|
* @param down true if this was a key down event, false otherwise
|
||||||
|
* @return true if the extended key event is supported and was sent
|
||||||
|
* successfully, false otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool SendExtendedKeyEvent(rfbClient* client, uint32_t keysym, uint32_t keycode, rfbBool down);
|
||||||
|
/**
|
||||||
|
* Places a string on the server's clipboard. Use this function if you want to
|
||||||
|
* be able to copy and paste between the server and your application. For
|
||||||
|
* instance, when your application is notified that the user copied some text
|
||||||
|
* onto the clipboard, you would call this function to synchronize the server's
|
||||||
|
* clipboard with your local clipboard.
|
||||||
|
* @param client The client structure through which to send the client cut text
|
||||||
|
* message
|
||||||
|
* @param str The string to send (doesn't need to be NULL terminated)
|
||||||
|
* @param len The length of the string
|
||||||
|
* @return true if the client cut message was sent successfully, false otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool SendClientCutText(rfbClient* client,char *str, int len);
|
||||||
|
/**
|
||||||
|
* Handles messages from the RFB server. You must call this function
|
||||||
|
* intermittently so LibVNCClient can parse messages from the server. For
|
||||||
|
* example, if your app has a draw loop, you could place a call to this
|
||||||
|
* function within that draw loop.
|
||||||
|
* @note You must call WaitForMessage() before you call this function.
|
||||||
|
* @param client The client which will handle the RFB server messages
|
||||||
|
* @return true if the client was able to handle the RFB server messages, false
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool HandleRFBServerMessage(rfbClient* client);
|
||||||
|
|
||||||
|
extern rfbBool ReadToBuffer(rfbClient* client);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a text chat message to the server.
|
||||||
|
* @param client The client through which to send the message
|
||||||
|
* @param text The text to send
|
||||||
|
* @return true if the text was sent successfully, false otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool TextChatSend(rfbClient* client, char *text);
|
||||||
|
/**
|
||||||
|
* Opens a text chat window on the server.
|
||||||
|
* @param client The client through which to send the message
|
||||||
|
* @return true if the window was opened successfully, false otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool TextChatOpen(rfbClient* client);
|
||||||
|
/**
|
||||||
|
* Closes the text chat window on the server.
|
||||||
|
* @param client The client through which to send the message
|
||||||
|
* @return true if the window was closed successfully, false otherwise
|
||||||
|
*/
|
||||||
|
extern rfbBool TextChatClose(rfbClient* client);
|
||||||
|
extern rfbBool TextChatFinish(rfbClient* client);
|
||||||
|
extern rfbBool PermitServerInput(rfbClient* client, int enabled);
|
||||||
|
extern rfbBool SendXvpMsg(rfbClient* client, uint8_t version, uint8_t code);
|
||||||
|
|
||||||
|
extern void PrintPixelFormat(rfbPixelFormat *format);
|
||||||
|
|
||||||
|
extern rfbBool SupportsClient2Server(rfbClient* client, int messageType);
|
||||||
|
extern rfbBool SupportsServer2Client(rfbClient* client, int messageType);
|
||||||
|
|
||||||
|
/* client data */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates a client data tag with the given pointer. LibVNCClient has
|
||||||
|
* several events to which you can associate your own handlers. These handlers
|
||||||
|
* have the client structure as one of their parameters. Sometimes, you may want
|
||||||
|
* to make data from elsewhere in your application available to these handlers
|
||||||
|
* without using a global variable. To do this, you call
|
||||||
|
* rfbClientSetClientData() and associate the data with a tag. Then, your
|
||||||
|
* handler can call rfbClientGetClientData() and get the a pointer to the data
|
||||||
|
* associated with that tag.
|
||||||
|
* @param client The client in which to set the client data
|
||||||
|
* @param tag A unique tag which identifies the data
|
||||||
|
* @param data A pointer to the data to associate with the tag
|
||||||
|
*/
|
||||||
|
void rfbClientSetClientData(rfbClient* client, void* tag, void* data);
|
||||||
|
/**
|
||||||
|
* Returns a pointer to the client data associated with the given tag. See the
|
||||||
|
* the documentation for rfbClientSetClientData() for a discussion of how you
|
||||||
|
* can use client data.
|
||||||
|
* @param client The client from which to get the client data
|
||||||
|
* @param tag The tag which identifies the client data
|
||||||
|
* @return a pointer to the client data
|
||||||
|
*/
|
||||||
|
void* rfbClientGetClientData(rfbClient* client, void* tag);
|
||||||
|
|
||||||
|
/* protocol extensions */
|
||||||
|
|
||||||
|
typedef struct _rfbClientProtocolExtension {
|
||||||
|
int* encodings;
|
||||||
|
/** returns TRUE if the encoding was handled */
|
||||||
|
rfbBool (*handleEncoding)(rfbClient* cl,
|
||||||
|
rfbFramebufferUpdateRectHeader* rect);
|
||||||
|
/** returns TRUE if it handled the message */
|
||||||
|
rfbBool (*handleMessage)(rfbClient* cl,
|
||||||
|
rfbServerToClientMsg* message);
|
||||||
|
struct _rfbClientProtocolExtension* next;
|
||||||
|
uint32_t const* securityTypes;
|
||||||
|
/** returns TRUE if it handled the authentication */
|
||||||
|
rfbBool (*handleAuthentication)(rfbClient* cl, uint32_t authScheme);
|
||||||
|
} rfbClientProtocolExtension;
|
||||||
|
|
||||||
|
void rfbClientRegisterExtension(rfbClientProtocolExtension* e);
|
||||||
|
|
||||||
|
/* sockets.c */
|
||||||
|
|
||||||
|
extern rfbBool errorMessageOnReadFailure;
|
||||||
|
|
||||||
|
extern rfbBool ReadFromRFBServer(rfbClient* client, char *out, unsigned int n);
|
||||||
|
extern rfbBool WriteToRFBServer(rfbClient* client, const char *buf, unsigned int n);
|
||||||
|
/**
|
||||||
|
Tries to connect to an IPv4 host.
|
||||||
|
@param host Binary IPv4 address
|
||||||
|
@param port Port
|
||||||
|
@return A blocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||||
|
*/
|
||||||
|
extern rfbSocket ConnectClientToTcpAddr(unsigned int host, int port);
|
||||||
|
/**
|
||||||
|
Tries to connect to an IPv4 or IPv6 host.
|
||||||
|
@param hostname A hostname or IP address
|
||||||
|
@param port Port
|
||||||
|
@return A blocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||||
|
*/
|
||||||
|
extern rfbSocket ConnectClientToTcpAddr6(const char *hostname, int port);
|
||||||
|
/**
|
||||||
|
Tries to connect to a Unix socket.
|
||||||
|
@param sockFile Path of the socket file
|
||||||
|
@return A blocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||||
|
*/
|
||||||
|
extern rfbSocket ConnectClientToUnixSock(const char *sockFile);
|
||||||
|
/**
|
||||||
|
Tries to connect to an IPv4 host using the given timeout value.
|
||||||
|
@param host Binary IPv4 address
|
||||||
|
@param port Port
|
||||||
|
@param timeout The time in seconds to wait for a connection
|
||||||
|
@return A nonblocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||||
|
*/
|
||||||
|
extern rfbSocket ConnectClientToTcpAddrWithTimeout(unsigned int host, int port, unsigned int timeout);
|
||||||
|
/**
|
||||||
|
Tries to connect to an IPv4 or IPv6 host using the given timeout value.
|
||||||
|
@param hostname A hostname or IP address
|
||||||
|
@param port Port
|
||||||
|
@param timeout The time in seconds to wait for a connection
|
||||||
|
@return A nonblocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||||
|
*/
|
||||||
|
extern rfbSocket ConnectClientToTcpAddr6WithTimeout(const char *hostname, int port, unsigned int timeout);
|
||||||
|
/**
|
||||||
|
Tries to connect to a Unix socket using the given timeout value.
|
||||||
|
@param sockFile Path of the socket file
|
||||||
|
@param timeout The time in seconds to wait for a connection
|
||||||
|
@return A nonblocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||||
|
*/
|
||||||
|
extern rfbSocket ConnectClientToUnixSockWithTimeout(const char *sockFile, unsigned int timeout);
|
||||||
|
extern rfbBool SetNonBlocking(rfbSocket sock);
|
||||||
|
extern rfbBool SetBlocking(rfbSocket sock);
|
||||||
|
extern rfbBool SetDSCP(rfbSocket sock, int dscp);
|
||||||
|
|
||||||
|
extern rfbBool SameMachine(rfbSocket sock);
|
||||||
|
|
||||||
|
/* vncviewer.c */
|
||||||
|
/**
|
||||||
|
* Allocates and returns a pointer to an rfbClient structure. This will probably
|
||||||
|
* be the first LibVNCClient function your client code calls. Most libVNCClient
|
||||||
|
* functions operate on an rfbClient structure, and this function allocates
|
||||||
|
* memory for that structure. When you're done with the rfbClient structure
|
||||||
|
* pointer this function returns, you should free the memory rfbGetClient()
|
||||||
|
* allocated by calling rfbClientCleanup().
|
||||||
|
*
|
||||||
|
* A pixel is one dot on the screen. The number of bytes in a pixel will depend
|
||||||
|
* on the number of samples in that pixel and the number of bits in each sample.
|
||||||
|
* A sample represents one of the primary colors in a color model. The RGB
|
||||||
|
* color model uses red, green, and blue samples respectively. Suppose you
|
||||||
|
* wanted to use 16-bit RGB color: You would have three samples per pixel (one
|
||||||
|
* for each primary color), five bits per sample (the quotient of 16 RGB bits
|
||||||
|
* divided by three samples), and two bytes per pixel (the smallest multiple of
|
||||||
|
* eight bits in which the 16-bit pixel will fit). If you wanted 32-bit RGB
|
||||||
|
* color, you would have three samples per pixel again, eight bits per sample
|
||||||
|
* (since that's how 32-bit color is defined), and four bytes per pixel (the
|
||||||
|
* smallest multiple of eight bits in which the 32-bit pixel will fit.
|
||||||
|
* @param bitsPerSample The number of bits in a sample
|
||||||
|
* @param samplesPerPixel The number of samples in a pixel
|
||||||
|
* @param bytesPerPixel The number of bytes in a pixel
|
||||||
|
* @return a pointer to the allocated rfbClient structure
|
||||||
|
*/
|
||||||
|
rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,int bytesPerPixel);
|
||||||
|
/**
|
||||||
|
* Cleans up the client structure and releases the memory allocated for it. You
|
||||||
|
* should call this when you're done with the rfbClient structure that you
|
||||||
|
* allocated with rfbGetClient().
|
||||||
|
* @note rfbClientCleanup() does not touch client->frameBuffer.
|
||||||
|
* @param client The client to clean up
|
||||||
|
*/
|
||||||
|
void rfbClientCleanup(rfbClient* client);
|
||||||
|
|
||||||
|
#if(defined __cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@page libvncclient_doc LibVNCClient Documentation
|
||||||
|
@section example_code Example Code
|
||||||
|
See SDLvncviewer.c for a rather complete client example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef RFBSASL_H
|
||||||
|
#define RFBSASL_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 S. Waterman. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
|
||||||
|
#include "rfbclient.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform the SASL authentication process
|
||||||
|
*/
|
||||||
|
rfbBool HandleSASLAuth(rfbClient *client);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read from SASL when the SASL SSF is in use.
|
||||||
|
*/
|
||||||
|
int ReadFromSASL(rfbClient* client, char *out, unsigned int n);
|
||||||
|
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
||||||
|
#endif /* RFBSASL_H */
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* LibVNCServer/LibVNCClient common platform socket defines and includes.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 Christian Beier <dontmind@freeshell.org>
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RFB_COMMON_SOCKETS_H
|
||||||
|
#define _RFB_COMMON_SOCKETS_H
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#endif /* _RFB_COMMON_SOCKETS_H */
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* LibVNCServer/LibVNCClient common platform threading defines and includes.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 Christian Beier <dontmind@freeshell.org>
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _RFB_COMMON_THREADING_H
|
||||||
|
#define _RFB_COMMON_THREADING_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
|
||||||
|
#include <pthread.h>
|
||||||
|
#if 0 /* debugging */
|
||||||
|
#define LOCK(mutex) (rfbLog("%s:%d LOCK(%s,0x%x)\n",__FILE__,__LINE__,#mutex,&(mutex)), pthread_mutex_lock(&(mutex)))
|
||||||
|
#define UNLOCK(mutex) (rfbLog("%s:%d UNLOCK(%s,0x%x)\n",__FILE__,__LINE__,#mutex,&(mutex)), pthread_mutex_unlock(&(mutex)))
|
||||||
|
#define MUTEX(mutex) pthread_mutex_t (mutex)
|
||||||
|
#define INIT_MUTEX(mutex) (rfbLog("%s:%d INIT_MUTEX(%s,0x%x)\n",__FILE__,__LINE__,#mutex,&(mutex)), pthread_mutex_init(&(mutex),NULL))
|
||||||
|
#define TINI_MUTEX(mutex) (rfbLog("%s:%d TINI_MUTEX(%s)\n",__FILE__,__LINE__,#mutex), pthread_mutex_destroy(&(mutex)))
|
||||||
|
#define TSIGNAL(cond) (rfbLog("%s:%d TSIGNAL(%s)\n",__FILE__,__LINE__,#cond), pthread_cond_signal(&(cond)))
|
||||||
|
#define WAIT(cond,mutex) (rfbLog("%s:%d WAIT(%s,%s)\n",__FILE__,__LINE__,#cond,#mutex), pthread_cond_wait(&(cond),&(mutex)))
|
||||||
|
#define COND(cond) pthread_cond_t (cond)
|
||||||
|
#define INIT_COND(cond) (rfbLog("%s:%d INIT_COND(%s)\n",__FILE__,__LINE__,#cond), pthread_cond_init(&(cond),NULL))
|
||||||
|
#define TINI_COND(cond) (rfbLog("%s:%d TINI_COND(%s)\n",__FILE__,__LINE__,#cond), pthread_cond_destroy(&(cond)))
|
||||||
|
#define IF_PTHREADS(x) x
|
||||||
|
#else
|
||||||
|
#if !NONETWORK
|
||||||
|
#define LOCK(mutex) pthread_mutex_lock(&(mutex))
|
||||||
|
#define UNLOCK(mutex) pthread_mutex_unlock(&(mutex))
|
||||||
|
#endif
|
||||||
|
#define MUTEX(mutex) pthread_mutex_t (mutex)
|
||||||
|
#define MUTEX_SIZE (sizeof(pthread_mutex_t))
|
||||||
|
#define INIT_MUTEX(mutex) pthread_mutex_init(&(mutex),NULL)
|
||||||
|
#define TINI_MUTEX(mutex) pthread_mutex_destroy(&(mutex))
|
||||||
|
#define TSIGNAL(cond) pthread_cond_signal(&(cond))
|
||||||
|
#define WAIT(cond,mutex) pthread_cond_wait(&(cond),&(mutex))
|
||||||
|
#define COND(cond) pthread_cond_t (cond)
|
||||||
|
#define INIT_COND(cond) pthread_cond_init(&(cond),NULL)
|
||||||
|
#define TINI_COND(cond) pthread_cond_destroy(&(cond))
|
||||||
|
#define IF_PTHREADS(x) x
|
||||||
|
#define THREAD_ROUTINE_RETURN_TYPE void*
|
||||||
|
#define THREAD_ROUTINE_RETURN_VALUE NULL
|
||||||
|
#define THREAD_SLEEP_MS(ms) usleep(ms*1000)
|
||||||
|
#define THREAD_JOIN(thread) pthread_join(thread, NULL)
|
||||||
|
#define CURRENT_THREAD_ID pthread_self()
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define LOCK(mutex)
|
||||||
|
#define UNLOCK(mutex)
|
||||||
|
#define MUTEX(mutex)
|
||||||
|
#define INIT_MUTEX(mutex)
|
||||||
|
#define TINI_MUTEX(mutex)
|
||||||
|
#define TSIGNAL(cond)
|
||||||
|
#define WAIT(cond,mutex) this_is_unsupported
|
||||||
|
#define COND(cond)
|
||||||
|
#define INIT_COND(cond)
|
||||||
|
#define TINI_COND(cond)
|
||||||
|
#define IF_PTHREADS(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _RFB_COMMON_THREADING_H */
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 - 2020 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static inline uint64_t timespec_to_us(const struct timespec* ts)
|
||||||
|
{
|
||||||
|
return (uint64_t)ts->tv_sec * UINT64_C(1000000) +
|
||||||
|
(uint64_t)ts->tv_nsec / UINT64_C(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t timespec_to_ms(const struct timespec* ts)
|
||||||
|
{
|
||||||
|
return (uint64_t)ts->tv_sec * UINT64_C(1000) +
|
||||||
|
(uint64_t)ts->tv_nsec / UINT64_C(1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t gettime_us(void)
|
||||||
|
{
|
||||||
|
struct timespec ts = { 0 };
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
return timespec_to_us(&ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t gettime_ms(void)
|
||||||
|
{
|
||||||
|
struct timespec ts = { 0 };
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
return timespec_to_ms(&ts);
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef TLS_H
|
||||||
|
#define TLS_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 Vic Lee.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Handle Anonymous TLS Authentication (18) with the server.
|
||||||
|
* After authentication, client->tlsSession will be set.
|
||||||
|
*/
|
||||||
|
rfbBool HandleAnonTLSAuth(rfbClient* client);
|
||||||
|
|
||||||
|
/* Handle VeNCrypt Authentication (19) with the server.
|
||||||
|
* The callback function GetX509Credential will be called.
|
||||||
|
* After authentication, client->tlsSession will be set.
|
||||||
|
*/
|
||||||
|
rfbBool HandleVeNCryptAuth(rfbClient* client);
|
||||||
|
|
||||||
|
/* Read desired bytes from TLS session.
|
||||||
|
* It's a wrapper function over gnutls_record_recv() and return values
|
||||||
|
* are same as read(), that is, >0 for actual bytes read, 0 for EOF,
|
||||||
|
* or EAGAIN, EINTR.
|
||||||
|
* This should be a non-blocking call. Blocking is handled in sockets.c.
|
||||||
|
*/
|
||||||
|
int ReadFromTLS(rfbClient* client, char *out, unsigned int n);
|
||||||
|
|
||||||
|
/* Write desired bytes to TLS session.
|
||||||
|
* It's a wrapper function over gnutls_record_send() and it will be
|
||||||
|
* blocking call, until all bytes are written or error returned.
|
||||||
|
*/
|
||||||
|
int WriteToTLS(rfbClient* client, const char *buf, unsigned int n);
|
||||||
|
|
||||||
|
/* Free TLS resources */
|
||||||
|
void FreeTLS(rfbClient* client);
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
/* Get the number of bits in the current cipher */
|
||||||
|
int GetTLSCipherBits(rfbClient* client);
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
||||||
|
#endif /* TLS_H */
|
|
@ -0,0 +1,534 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C)2009-2012 D. R. Commander. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __TURBOJPEG_H__
|
||||||
|
#define __TURBOJPEG_H__
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(DLLDEFINE)
|
||||||
|
#define DLLEXPORT __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define DLLEXPORT
|
||||||
|
#endif
|
||||||
|
#define DLLCALL
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable:4996)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup TurboJPEG Lite
|
||||||
|
* TurboJPEG API. This API provides an interface for generating and decoding
|
||||||
|
* JPEG images in memory.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of chrominance subsampling options
|
||||||
|
*/
|
||||||
|
#define TJ_NUMSAMP 5
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chrominance subsampling options.
|
||||||
|
* When an image is converted from the RGB to the YCbCr colorspace as part of
|
||||||
|
* the JPEG compression process, some of the Cb and Cr (chrominance) components
|
||||||
|
* can be discarded or averaged together to produce a smaller image with little
|
||||||
|
* perceptible loss of image clarity (the human eye is more sensitive to small
|
||||||
|
* changes in brightness than small changes in color.) This is called
|
||||||
|
* "chrominance subsampling".
|
||||||
|
*/
|
||||||
|
enum TJSAMP
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 4:4:4 chrominance subsampling (no chrominance subsampling). The JPEG or
|
||||||
|
* YUV image will contain one chrominance component for every pixel in the
|
||||||
|
* source image.
|
||||||
|
*/
|
||||||
|
TJSAMP_444=0,
|
||||||
|
/**
|
||||||
|
* 4:2:2 chrominance subsampling. The JPEG or YUV image will contain one
|
||||||
|
* chrominance component for every 2x1 block of pixels in the source image.
|
||||||
|
*/
|
||||||
|
TJSAMP_422,
|
||||||
|
/**
|
||||||
|
* 4:2:0 chrominance subsampling. The JPEG or YUV image will contain one
|
||||||
|
* chrominance component for every 2x2 block of pixels in the source image.
|
||||||
|
*/
|
||||||
|
TJSAMP_420,
|
||||||
|
/**
|
||||||
|
* Grayscale. The JPEG or YUV image will contain no chrominance components.
|
||||||
|
*/
|
||||||
|
TJSAMP_GRAY,
|
||||||
|
/**
|
||||||
|
* 4:4:0 chrominance subsampling. The JPEG or YUV image will contain one
|
||||||
|
* chrominance component for every 1x2 block of pixels in the source image.
|
||||||
|
*/
|
||||||
|
TJSAMP_440
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCU block width (in pixels) for a given level of chrominance subsampling.
|
||||||
|
* MCU block sizes:
|
||||||
|
* - 8x8 for no subsampling or grayscale
|
||||||
|
* - 16x8 for 4:2:2
|
||||||
|
* - 8x16 for 4:4:0
|
||||||
|
* - 16x16 for 4:2:0
|
||||||
|
*/
|
||||||
|
static const int tjMCUWidth[TJ_NUMSAMP] = {8, 16, 16, 8, 8};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCU block height (in pixels) for a given level of chrominance subsampling.
|
||||||
|
* MCU block sizes:
|
||||||
|
* - 8x8 for no subsampling or grayscale
|
||||||
|
* - 16x8 for 4:2:2
|
||||||
|
* - 8x16 for 4:4:0
|
||||||
|
* - 16x16 for 4:2:0
|
||||||
|
*/
|
||||||
|
static const int tjMCUHeight[TJ_NUMSAMP] = {8, 8, 16, 8, 16};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of pixel formats
|
||||||
|
*/
|
||||||
|
#define TJ_NUMPF 11
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pixel formats
|
||||||
|
*/
|
||||||
|
enum TJPF
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* RGB pixel format. The red, green, and blue components in the image are
|
||||||
|
* stored in 3-byte pixels in the order R, G, B from lowest to highest byte
|
||||||
|
* address within each pixel.
|
||||||
|
*/
|
||||||
|
TJPF_RGB=0,
|
||||||
|
/**
|
||||||
|
* BGR pixel format. The red, green, and blue components in the image are
|
||||||
|
* stored in 3-byte pixels in the order B, G, R from lowest to highest byte
|
||||||
|
* address within each pixel.
|
||||||
|
*/
|
||||||
|
TJPF_BGR,
|
||||||
|
/**
|
||||||
|
* RGBX pixel format. The red, green, and blue components in the image are
|
||||||
|
* stored in 4-byte pixels in the order R, G, B from lowest to highest byte
|
||||||
|
* address within each pixel. The X component is ignored when compressing
|
||||||
|
* and undefined when decompressing.
|
||||||
|
*/
|
||||||
|
TJPF_RGBX,
|
||||||
|
/**
|
||||||
|
* BGRX pixel format. The red, green, and blue components in the image are
|
||||||
|
* stored in 4-byte pixels in the order B, G, R from lowest to highest byte
|
||||||
|
* address within each pixel. The X component is ignored when compressing
|
||||||
|
* and undefined when decompressing.
|
||||||
|
*/
|
||||||
|
TJPF_BGRX,
|
||||||
|
/**
|
||||||
|
* XBGR pixel format. The red, green, and blue components in the image are
|
||||||
|
* stored in 4-byte pixels in the order R, G, B from highest to lowest byte
|
||||||
|
* address within each pixel. The X component is ignored when compressing
|
||||||
|
* and undefined when decompressing.
|
||||||
|
*/
|
||||||
|
TJPF_XBGR,
|
||||||
|
/**
|
||||||
|
* XRGB pixel format. The red, green, and blue components in the image are
|
||||||
|
* stored in 4-byte pixels in the order B, G, R from highest to lowest byte
|
||||||
|
* address within each pixel. The X component is ignored when compressing
|
||||||
|
* and undefined when decompressing.
|
||||||
|
*/
|
||||||
|
TJPF_XRGB,
|
||||||
|
/**
|
||||||
|
* Grayscale pixel format. Each 1-byte pixel represents a luminance
|
||||||
|
* (brightness) level from 0 to 255.
|
||||||
|
*/
|
||||||
|
TJPF_GRAY,
|
||||||
|
/**
|
||||||
|
* RGBA pixel format. This is the same as @ref TJPF_RGBX, except that when
|
||||||
|
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||||
|
* interpreted as an opaque alpha channel.
|
||||||
|
*/
|
||||||
|
TJPF_RGBA,
|
||||||
|
/**
|
||||||
|
* BGRA pixel format. This is the same as @ref TJPF_BGRX, except that when
|
||||||
|
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||||
|
* interpreted as an opaque alpha channel.
|
||||||
|
*/
|
||||||
|
TJPF_BGRA,
|
||||||
|
/**
|
||||||
|
* ABGR pixel format. This is the same as @ref TJPF_XBGR, except that when
|
||||||
|
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||||
|
* interpreted as an opaque alpha channel.
|
||||||
|
*/
|
||||||
|
TJPF_ABGR,
|
||||||
|
/**
|
||||||
|
* ARGB pixel format. This is the same as @ref TJPF_XRGB, except that when
|
||||||
|
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||||
|
* interpreted as an opaque alpha channel.
|
||||||
|
*/
|
||||||
|
TJPF_ARGB
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Red offset (in bytes) for a given pixel format. This specifies the number
|
||||||
|
* of bytes that the red component is offset from the start of the pixel. For
|
||||||
|
* instance, if a pixel of format TJ_BGRX is stored in <tt>char pixel[]</tt>,
|
||||||
|
* then the red component will be <tt>pixel[tjRedOffset[TJ_BGRX]]</tt>.
|
||||||
|
*/
|
||||||
|
static const int tjRedOffset[TJ_NUMPF] = {0, 2, 0, 2, 3, 1, 0, 0, 2, 3, 1};
|
||||||
|
/**
|
||||||
|
* Green offset (in bytes) for a given pixel format. This specifies the number
|
||||||
|
* of bytes that the green component is offset from the start of the pixel.
|
||||||
|
* For instance, if a pixel of format TJ_BGRX is stored in
|
||||||
|
* <tt>char pixel[]</tt>, then the green component will be
|
||||||
|
* <tt>pixel[tjGreenOffset[TJ_BGRX]]</tt>.
|
||||||
|
*/
|
||||||
|
static const int tjGreenOffset[TJ_NUMPF] = {1, 1, 1, 1, 2, 2, 0, 1, 1, 2, 2};
|
||||||
|
/**
|
||||||
|
* Blue offset (in bytes) for a given pixel format. This specifies the number
|
||||||
|
* of bytes that the Blue component is offset from the start of the pixel. For
|
||||||
|
* instance, if a pixel of format TJ_BGRX is stored in <tt>char pixel[]</tt>,
|
||||||
|
* then the blue component will be <tt>pixel[tjBlueOffset[TJ_BGRX]]</tt>.
|
||||||
|
*/
|
||||||
|
static const int tjBlueOffset[TJ_NUMPF] = {2, 0, 2, 0, 1, 3, 0, 2, 0, 1, 3};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pixel size (in bytes) for a given pixel format.
|
||||||
|
*/
|
||||||
|
static const int tjPixelSize[TJ_NUMPF] = {3, 3, 4, 4, 4, 4, 1, 4, 4, 4, 4};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The uncompressed source/destination image is stored in bottom-up (Windows,
|
||||||
|
* OpenGL) order, not top-down (X11) order.
|
||||||
|
*/
|
||||||
|
#define TJFLAG_BOTTOMUP 2
|
||||||
|
/**
|
||||||
|
* Turn off CPU auto-detection and force TurboJPEG to use MMX code (IPP and
|
||||||
|
* 32-bit libjpeg-turbo versions only.)
|
||||||
|
*/
|
||||||
|
#define TJFLAG_FORCEMMX 8
|
||||||
|
/**
|
||||||
|
* Turn off CPU auto-detection and force TurboJPEG to use SSE code (32-bit IPP
|
||||||
|
* and 32-bit libjpeg-turbo versions only)
|
||||||
|
*/
|
||||||
|
#define TJFLAG_FORCESSE 16
|
||||||
|
/**
|
||||||
|
* Turn off CPU auto-detection and force TurboJPEG to use SSE2 code (32-bit IPP
|
||||||
|
* and 32-bit libjpeg-turbo versions only)
|
||||||
|
*/
|
||||||
|
#define TJFLAG_FORCESSE2 32
|
||||||
|
/**
|
||||||
|
* Turn off CPU auto-detection and force TurboJPEG to use SSE3 code (64-bit IPP
|
||||||
|
* version only)
|
||||||
|
*/
|
||||||
|
#define TJFLAG_FORCESSE3 128
|
||||||
|
/**
|
||||||
|
* Use fast, inaccurate chrominance upsampling routines in the JPEG
|
||||||
|
* decompressor (libjpeg and libjpeg-turbo versions only)
|
||||||
|
*/
|
||||||
|
#define TJFLAG_FASTUPSAMPLE 256
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scaling factor
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Numerator
|
||||||
|
*/
|
||||||
|
int num;
|
||||||
|
/**
|
||||||
|
* Denominator
|
||||||
|
*/
|
||||||
|
int denom;
|
||||||
|
} tjscalingfactor;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TurboJPEG instance handle
|
||||||
|
*/
|
||||||
|
typedef void* tjhandle;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pad the given width to the nearest 32-bit boundary
|
||||||
|
*/
|
||||||
|
#define TJPAD(width) (((width)+3)&(~3))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the scaled value of <tt>dimension</tt> using the given scaling
|
||||||
|
* factor. This macro performs the integer equivalent of <tt>ceil(dimension *
|
||||||
|
* scalingFactor)</tt>.
|
||||||
|
*/
|
||||||
|
#define TJSCALED(dimension, scalingFactor) ((dimension * scalingFactor.num \
|
||||||
|
+ scalingFactor.denom - 1) / scalingFactor.denom)
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a TurboJPEG compressor instance.
|
||||||
|
*
|
||||||
|
* @return a handle to the newly-created instance, or NULL if an error
|
||||||
|
* occurred (see #tjGetErrorStr().)
|
||||||
|
*/
|
||||||
|
DLLEXPORT tjhandle DLLCALL tjInitCompress(void);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress an RGB or grayscale image into a JPEG image.
|
||||||
|
*
|
||||||
|
* @param handle a handle to a TurboJPEG compressor or transformer instance
|
||||||
|
* @param srcBuf pointer to an image buffer containing RGB or grayscale pixels
|
||||||
|
* to be compressed
|
||||||
|
* @param width width (in pixels) of the source image
|
||||||
|
* @param pitch bytes per line of the source image. Normally, this should be
|
||||||
|
* <tt>width * #tjPixelSize[pixelFormat]</tt> if the image is unpadded,
|
||||||
|
* or <tt>#TJPAD(width * #tjPixelSize[pixelFormat])</tt> if each line of
|
||||||
|
* the image is padded to the nearest 32-bit boundary, as is the case
|
||||||
|
* for Windows bitmaps. You can also be clever and use this parameter
|
||||||
|
* to skip lines, etc. Setting this parameter to 0 is the equivalent of
|
||||||
|
* setting it to <tt>width * #tjPixelSize[pixelFormat]</tt>.
|
||||||
|
* @param height height (in pixels) of the source image
|
||||||
|
* @param pixelFormat pixel format of the source image (see @ref TJPF
|
||||||
|
* "Pixel formats".)
|
||||||
|
* @param jpegBuf address of a pointer to an image buffer that will receive the
|
||||||
|
* JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer
|
||||||
|
* to accommodate the size of the JPEG image. Thus, you can choose to:
|
||||||
|
* -# pre-allocate the JPEG buffer with an arbitrary size using
|
||||||
|
* #tjAlloc() and let TurboJPEG grow the buffer as needed,
|
||||||
|
* -# set <tt>*jpegBuf</tt> to NULL to tell TurboJPEG to allocate the
|
||||||
|
* buffer for you, or
|
||||||
|
* -# pre-allocate the buffer to a "worst case" size determined by
|
||||||
|
* calling #tjBufSize(). This should ensure that the buffer never has
|
||||||
|
* to be re-allocated (setting #TJFLAG_NOREALLOC guarantees this.)
|
||||||
|
* .
|
||||||
|
* If you choose option 1, <tt>*jpegSize</tt> should be set to the
|
||||||
|
* size of your pre-allocated buffer. In any case, unless you have
|
||||||
|
* set #TJFLAG_NOREALLOC, you should always check <tt>*jpegBuf</tt> upon
|
||||||
|
* return from this function, as it may have changed.
|
||||||
|
* @param jpegSize pointer to an unsigned long variable that holds the size of
|
||||||
|
* the JPEG image buffer. If <tt>*jpegBuf</tt> points to a
|
||||||
|
* pre-allocated buffer, then <tt>*jpegSize</tt> should be set to the
|
||||||
|
* size of the buffer. Upon return, <tt>*jpegSize</tt> will contain the
|
||||||
|
* size of the JPEG image (in bytes.)
|
||||||
|
* @param jpegSubsamp the level of chrominance subsampling to be used when
|
||||||
|
* generating the JPEG image (see @ref TJSAMP
|
||||||
|
* "Chrominance subsampling options".)
|
||||||
|
* @param jpegQual the image quality of the generated JPEG image (1 = worst,
|
||||||
|
100 = best)
|
||||||
|
* @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP
|
||||||
|
* "flags".
|
||||||
|
*
|
||||||
|
* @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
|
||||||
|
*/
|
||||||
|
DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, unsigned char *srcBuf,
|
||||||
|
int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf,
|
||||||
|
unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum size of the buffer (in bytes) required to hold a JPEG image with
|
||||||
|
* the given parameters. The number of bytes returned by this function is
|
||||||
|
* larger than the size of the uncompressed source image. The reason for this
|
||||||
|
* is that the JPEG format uses 16-bit coefficients, and it is thus possible
|
||||||
|
* for a very high-quality JPEG image with very high frequency content to
|
||||||
|
* expand rather than compress when converted to the JPEG format. Such images
|
||||||
|
* represent a very rare corner case, but since there is no way to predict the
|
||||||
|
* size of a JPEG image prior to compression, the corner case has to be
|
||||||
|
* handled.
|
||||||
|
*
|
||||||
|
* @param width width of the image (in pixels)
|
||||||
|
* @param height height of the image (in pixels)
|
||||||
|
* @param jpegSubsamp the level of chrominance subsampling to be used when
|
||||||
|
* generating the JPEG image (see @ref TJSAMP
|
||||||
|
* "Chrominance subsampling options".)
|
||||||
|
*
|
||||||
|
* @return the maximum size of the buffer (in bytes) required to hold the
|
||||||
|
* image, or -1 if the arguments are out of bounds.
|
||||||
|
*/
|
||||||
|
DLLEXPORT unsigned long DLLCALL tjBufSize(int width, int height,
|
||||||
|
int jpegSubsamp);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a TurboJPEG decompressor instance.
|
||||||
|
*
|
||||||
|
* @return a handle to the newly-created instance, or NULL if an error
|
||||||
|
* occurred (see #tjGetErrorStr().)
|
||||||
|
*/
|
||||||
|
DLLEXPORT tjhandle DLLCALL tjInitDecompress(void);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve information about a JPEG image without decompressing it.
|
||||||
|
*
|
||||||
|
* @param handle a handle to a TurboJPEG decompressor or transformer instance
|
||||||
|
* @param jpegBuf pointer to a buffer containing a JPEG image
|
||||||
|
* @param jpegSize size of the JPEG image (in bytes)
|
||||||
|
* @param width pointer to an integer variable that will receive the width (in
|
||||||
|
* pixels) of the JPEG image
|
||||||
|
* @param height pointer to an integer variable that will receive the height
|
||||||
|
* (in pixels) of the JPEG image
|
||||||
|
* @param jpegSubsamp pointer to an integer variable that will receive the
|
||||||
|
* level of chrominance subsampling used when compressing the JPEG image
|
||||||
|
* (see @ref TJSAMP "Chrominance subsampling options".)
|
||||||
|
*
|
||||||
|
* @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
|
||||||
|
*/
|
||||||
|
DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle handle,
|
||||||
|
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height,
|
||||||
|
int *jpegSubsamp);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of fractional scaling factors that the JPEG decompressor in
|
||||||
|
* this implementation of TurboJPEG supports.
|
||||||
|
*
|
||||||
|
* @param numscalingfactors pointer to an integer variable that will receive
|
||||||
|
* the number of elements in the list
|
||||||
|
*
|
||||||
|
* @return a pointer to a list of fractional scaling factors, or NULL if an
|
||||||
|
* error is encountered (see #tjGetErrorStr().)
|
||||||
|
*/
|
||||||
|
DLLEXPORT tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompress a JPEG image to an RGB or grayscale image.
|
||||||
|
*
|
||||||
|
* @param handle a handle to a TurboJPEG decompressor or transformer instance
|
||||||
|
* @param jpegBuf pointer to a buffer containing the JPEG image to decompress
|
||||||
|
* @param jpegSize size of the JPEG image (in bytes)
|
||||||
|
* @param dstBuf pointer to an image buffer that will receive the decompressed
|
||||||
|
* image. This buffer should normally be <tt>pitch * scaledHeight</tt>
|
||||||
|
* bytes in size, where <tt>scaledHeight</tt> can be determined by
|
||||||
|
* calling #TJSCALED() with the JPEG image height and one of the scaling
|
||||||
|
* factors returned by #tjGetScalingFactors(). The dstBuf pointer may
|
||||||
|
* also be used to decompress into a specific region of a larger buffer.
|
||||||
|
* @param width desired width (in pixels) of the destination image. If this is
|
||||||
|
* smaller than the width of the JPEG image being decompressed, then
|
||||||
|
* TurboJPEG will use scaling in the JPEG decompressor to generate the
|
||||||
|
* largest possible image that will fit within the desired width. If
|
||||||
|
* width is set to 0, then only the height will be considered when
|
||||||
|
* determining the scaled image size.
|
||||||
|
* @param pitch bytes per line of the destination image. Normally, this is
|
||||||
|
* <tt>scaledWidth * #tjPixelSize[pixelFormat]</tt> if the decompressed
|
||||||
|
* image is unpadded, else <tt>#TJPAD(scaledWidth *
|
||||||
|
* #tjPixelSize[pixelFormat])</tt> if each line of the decompressed
|
||||||
|
* image is padded to the nearest 32-bit boundary, as is the case for
|
||||||
|
* Windows bitmaps. (NOTE: <tt>scaledWidth</tt> can be determined by
|
||||||
|
* calling #TJSCALED() with the JPEG image width and one of the scaling
|
||||||
|
* factors returned by #tjGetScalingFactors().) You can also be clever
|
||||||
|
* and use the pitch parameter to skip lines, etc. Setting this
|
||||||
|
* parameter to 0 is the equivalent of setting it to <tt>scaledWidth
|
||||||
|
* * #tjPixelSize[pixelFormat]</tt>.
|
||||||
|
* @param height desired height (in pixels) of the destination image. If this
|
||||||
|
* is smaller than the height of the JPEG image being decompressed, then
|
||||||
|
* TurboJPEG will use scaling in the JPEG decompressor to generate the
|
||||||
|
* largest possible image that will fit within the desired height. If
|
||||||
|
* height is set to 0, then only the width will be considered when
|
||||||
|
* determining the scaled image size.
|
||||||
|
* @param pixelFormat pixel format of the destination image (see @ref
|
||||||
|
* TJPF "Pixel formats".)
|
||||||
|
* @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP
|
||||||
|
* "flags".
|
||||||
|
*
|
||||||
|
* @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
|
||||||
|
*/
|
||||||
|
DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle,
|
||||||
|
unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf,
|
||||||
|
int width, int pitch, int height, int pixelFormat, int flags);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a TurboJPEG compressor, decompressor, or transformer instance.
|
||||||
|
*
|
||||||
|
* @param handle a handle to a TurboJPEG compressor, decompressor or
|
||||||
|
* transformer instance
|
||||||
|
*
|
||||||
|
* @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
|
||||||
|
*/
|
||||||
|
DLLEXPORT int DLLCALL tjDestroy(tjhandle handle);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a descriptive error message explaining why the last command failed.
|
||||||
|
*
|
||||||
|
* @return a descriptive error message explaining why the last command failed.
|
||||||
|
*/
|
||||||
|
DLLEXPORT char* DLLCALL tjGetErrorStr(void);
|
||||||
|
|
||||||
|
|
||||||
|
/* Backward compatibility functions and macros (nothing to see here) */
|
||||||
|
#define NUMSUBOPT TJ_NUMSAMP
|
||||||
|
#define TJ_444 TJSAMP_444
|
||||||
|
#define TJ_422 TJSAMP_422
|
||||||
|
#define TJ_420 TJSAMP_420
|
||||||
|
#define TJ_411 TJSAMP_420
|
||||||
|
#define TJ_GRAYSCALE TJSAMP_GRAY
|
||||||
|
|
||||||
|
#define TJ_BGR 1
|
||||||
|
#define TJ_BOTTOMUP TJFLAG_BOTTOMUP
|
||||||
|
#define TJ_FORCEMMX TJFLAG_FORCEMMX
|
||||||
|
#define TJ_FORCESSE TJFLAG_FORCESSE
|
||||||
|
#define TJ_FORCESSE2 TJFLAG_FORCESSE2
|
||||||
|
#define TJ_ALPHAFIRST 64
|
||||||
|
#define TJ_FORCESSE3 TJFLAG_FORCESSE3
|
||||||
|
#define TJ_FASTUPSAMPLE TJFLAG_FASTUPSAMPLE
|
||||||
|
|
||||||
|
DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height);
|
||||||
|
|
||||||
|
DLLEXPORT int DLLCALL tjCompress(tjhandle handle, unsigned char *srcBuf,
|
||||||
|
int width, int pitch, int height, int pixelSize, unsigned char *dstBuf,
|
||||||
|
unsigned long *compressedSize, int jpegSubsamp, int jpegQual, int flags);
|
||||||
|
|
||||||
|
DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle handle,
|
||||||
|
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height);
|
||||||
|
|
||||||
|
DLLEXPORT int DLLCALL tjDecompress(tjhandle handle,
|
||||||
|
unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf,
|
||||||
|
int width, int pitch, int height, int pixelSize, int flags);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_USDT
|
||||||
|
#include <sys/sdt.h>
|
||||||
|
#else
|
||||||
|
#define DTRACE_PROBE(...)
|
||||||
|
#define DTRACE_PROBE1(...)
|
||||||
|
#define DTRACE_PROBE2(...)
|
||||||
|
#define DTRACE_PROBE3(...)
|
||||||
|
#define DTRACE_PROBE4(...)
|
||||||
|
#define DTRACE_PROBE5(...)
|
||||||
|
#define DTRACE_PROBE6(...)
|
||||||
|
#endif
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Andri Yngvason
|
* Copyright (c) 2020 - 2022 Andri Yngvason
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -16,28 +16,52 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "rfbclient.h"
|
||||||
|
#include "data-control.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <pixman.h>
|
#include <pixman.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <rfb/rfbclient.h>
|
|
||||||
|
#define VNC_CLIENT_MAX_AV_FRAMES 64
|
||||||
|
|
||||||
|
struct open_h264;
|
||||||
|
struct AVFrame;
|
||||||
|
|
||||||
|
struct vnc_av_frame {
|
||||||
|
struct AVFrame* frame;
|
||||||
|
int x, y, width, height;
|
||||||
|
};
|
||||||
|
|
||||||
struct vnc_client {
|
struct vnc_client {
|
||||||
rfbClient* client;
|
rfbClient* client;
|
||||||
|
|
||||||
|
struct data_control* data_control;
|
||||||
|
struct open_h264* open_h264;
|
||||||
|
bool current_rect_is_av_frame;
|
||||||
|
struct vnc_av_frame* av_frames[VNC_CLIENT_MAX_AV_FRAMES];
|
||||||
|
int n_av_frames;
|
||||||
|
uint64_t pts;
|
||||||
|
|
||||||
int (*alloc_fb)(struct vnc_client*);
|
int (*alloc_fb)(struct vnc_client*);
|
||||||
void (*update_fb)(struct vnc_client*);
|
void (*update_fb)(struct vnc_client*);
|
||||||
|
void (*cut_text)(struct vnc_client*, const char*, size_t);
|
||||||
|
|
||||||
void* userdata;
|
void* userdata;
|
||||||
struct pixman_region16 damage;
|
struct pixman_region16 damage;
|
||||||
|
|
||||||
|
bool handler_lock;
|
||||||
|
bool is_updating;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vnc_client* vnc_client_create(void);
|
struct vnc_client* vnc_client_create(struct data_control* data_control);
|
||||||
void vnc_client_destroy(struct vnc_client* self);
|
void vnc_client_destroy(struct vnc_client* self);
|
||||||
|
|
||||||
int vnc_client_connect(struct vnc_client* self, const char* address, int port);
|
int vnc_client_connect(struct vnc_client* self, const char* address, int port);
|
||||||
|
int vnc_client_init(struct vnc_client* self);
|
||||||
|
|
||||||
int vnc_client_set_pixel_format(struct vnc_client* self,
|
int vnc_client_set_pixel_format(struct vnc_client* self, uint32_t format);
|
||||||
enum wl_shm_format format);
|
|
||||||
|
|
||||||
int vnc_client_get_fd(const struct vnc_client* self);
|
int vnc_client_get_fd(const struct vnc_client* self);
|
||||||
int vnc_client_get_width(const struct vnc_client* self);
|
int vnc_client_get_width(const struct vnc_client* self);
|
||||||
|
@ -54,3 +78,8 @@ void vnc_client_send_keyboard_event(struct vnc_client* self, uint32_t symbol,
|
||||||
void vnc_client_set_encodings(struct vnc_client* self, const char* encodings);
|
void vnc_client_set_encodings(struct vnc_client* self, const char* encodings);
|
||||||
void vnc_client_set_quality_level(struct vnc_client* self, int value);
|
void vnc_client_set_quality_level(struct vnc_client* self, int value);
|
||||||
void vnc_client_set_compression_level(struct vnc_client* self, int value);
|
void vnc_client_set_compression_level(struct vnc_client* self, int value);
|
||||||
|
void vnc_client_send_cut_text(struct vnc_client* self, const char* text,
|
||||||
|
size_t len);
|
||||||
|
void vnc_client_clear_av_frames(struct vnc_client* self);
|
||||||
|
rfbCredential* handle_vnc_authentication(struct _rfbClient *client, int credentialType);
|
||||||
|
void cut_text (struct vnc_client* self, const char* text, size_t size);
|
104
meson.build
104
meson.build
|
@ -16,6 +16,7 @@ prefix = get_option('prefix')
|
||||||
|
|
||||||
c_args = [
|
c_args = [
|
||||||
'-D_GNU_SOURCE',
|
'-D_GNU_SOURCE',
|
||||||
|
'-DAML_UNSTABLE_API=1',
|
||||||
]
|
]
|
||||||
|
|
||||||
if buildtype != 'debug' and buildtype != 'debugoptimized'
|
if buildtype != 'debug' and buildtype != 'debugoptimized'
|
||||||
|
@ -28,24 +29,26 @@ cc = meson.get_compiler('c')
|
||||||
|
|
||||||
libm = cc.find_library('m', required: false)
|
libm = cc.find_library('m', required: false)
|
||||||
librt = cc.find_library('rt', required: false)
|
librt = cc.find_library('rt', required: false)
|
||||||
|
pthread = cc.find_library('pthread', required: false)
|
||||||
|
|
||||||
xkbcommon = dependency('xkbcommon')
|
xkbcommon = dependency('xkbcommon')
|
||||||
pixman = dependency('pixman-1')
|
pixman = dependency('pixman-1')
|
||||||
wayland_client = dependency('wayland-client')
|
wayland_client = dependency('wayland-client')
|
||||||
wayland_cursor = dependency('wayland-cursor')
|
wayland_cursor = dependency('wayland-cursor')
|
||||||
|
drm = dependency('libdrm')
|
||||||
libvncserver_opt = cmake.subproject_options()
|
gbm = dependency('gbm')
|
||||||
libvncserver_opt.add_cmake_defines({
|
egl = dependency('egl')
|
||||||
'BUILD_SHARED_LIBS': false,
|
glesv2 = dependency('glesv2')
|
||||||
'LIBVNCSERVER_INSTALL': false,
|
lavc = dependency('libavcodec')
|
||||||
})
|
lavu = dependency('libavutil')
|
||||||
|
gcrypt = dependency('libgcrypt', required: false)
|
||||||
libvncserver_project = cmake.subproject('libvncserver', options: libvncserver_opt)
|
openssl = dependency('openssl', required: false)
|
||||||
if libvncserver_project.found()
|
gnutls = dependency('gnutls', required: false)
|
||||||
libvncclient = libvncserver_project.dependency('vncclient')
|
sasl = dependency('libsasl2', required: false)
|
||||||
else
|
libjpeg = dependency('libjpeg', required: false)
|
||||||
libvncclient = dependency('libvncclient')
|
libpng = dependency('libpng', required: false)
|
||||||
endif
|
lzo = dependency('lzo2', required: false)
|
||||||
|
libz = dependency('zlib', required: false)
|
||||||
|
|
||||||
aml_project = subproject('aml', required: false,
|
aml_project = subproject('aml', required: false,
|
||||||
default_options: ['default_library=static'])
|
default_options: ['default_library=static'])
|
||||||
|
@ -55,7 +58,7 @@ else
|
||||||
aml = dependency('aml')
|
aml = dependency('aml')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
inc = include_directories('include')
|
inc = include_directories('include', 'src/encodings')
|
||||||
|
|
||||||
subdir('protocols')
|
subdir('protocols')
|
||||||
|
|
||||||
|
@ -63,11 +66,23 @@ sources = [
|
||||||
'src/main.c',
|
'src/main.c',
|
||||||
'src/shm.c',
|
'src/shm.c',
|
||||||
'src/seat.c',
|
'src/seat.c',
|
||||||
|
'src/output.c',
|
||||||
'src/pointer.c',
|
'src/pointer.c',
|
||||||
'src/keyboard.c',
|
'src/keyboard.c',
|
||||||
'src/vnc.c',
|
'src/vnc.c',
|
||||||
|
'src/data-control.c',
|
||||||
'src/strlcpy.c',
|
'src/strlcpy.c',
|
||||||
'src/evdev-to-qnum.c',
|
'src/evdev-to-qnum.c',
|
||||||
|
'src/pixels.c',
|
||||||
|
'src/region.c',
|
||||||
|
'src/renderer.c',
|
||||||
|
'src/renderer-egl.c',
|
||||||
|
'src/buffer.c',
|
||||||
|
'src/open-h264.c',
|
||||||
|
'src/cursor.c',
|
||||||
|
'src/rfbproto.c',
|
||||||
|
'src/sockets.c',
|
||||||
|
'src/vncviewer.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
@ -78,7 +93,12 @@ dependencies = [
|
||||||
aml,
|
aml,
|
||||||
wayland_client,
|
wayland_client,
|
||||||
wayland_cursor,
|
wayland_cursor,
|
||||||
libvncclient,
|
drm,
|
||||||
|
gbm,
|
||||||
|
egl,
|
||||||
|
glesv2,
|
||||||
|
lavc,
|
||||||
|
lavu,
|
||||||
client_protos,
|
client_protos,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -86,6 +106,60 @@ config = configuration_data()
|
||||||
|
|
||||||
config.set('PREFIX', '"' + prefix + '"')
|
config.set('PREFIX', '"' + prefix + '"')
|
||||||
|
|
||||||
|
if gcrypt.found()
|
||||||
|
sources += 'src/crypto_libgcrypt.c'
|
||||||
|
dependencies += gcrypt
|
||||||
|
elif openssl.found()
|
||||||
|
sources += 'src/crypto_openssl.c'
|
||||||
|
dependencies += openssl
|
||||||
|
else
|
||||||
|
sources += 'src/crypto_included.c'
|
||||||
|
endif
|
||||||
|
|
||||||
|
if gnutls.found()
|
||||||
|
sources += 'src/tls_gnutls.c'
|
||||||
|
dependencies += gnutls
|
||||||
|
config.set('LIBVNCSERVER_HAVE_GNUTLS', true)
|
||||||
|
elif openssl.found()
|
||||||
|
sources += 'src/tls_openssl.c'
|
||||||
|
dependencies += openssl
|
||||||
|
config.set('LIBVNCSERVER_HAVE_LIBSSL', true)
|
||||||
|
else
|
||||||
|
sources += 'src/tls_none.c'
|
||||||
|
endif
|
||||||
|
|
||||||
|
if sasl.found()
|
||||||
|
dependencies += sasl
|
||||||
|
sources += 'src/sasl.c'
|
||||||
|
config.set('LIBVNCSERVER_HAVE_SASL', true)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if libjpeg.found()
|
||||||
|
sources += 'src/turbojpeg.c'
|
||||||
|
dependencies += libjpeg
|
||||||
|
config.set('LIBVNCSERVER_HAVE_LIBJPEG', true)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if libpng.found()
|
||||||
|
dependencies += libpng
|
||||||
|
config.set('LIBVNCSERVER_HAVE_LIBPNG', true)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if pthread.found()
|
||||||
|
dependencies += pthread
|
||||||
|
config.set('LIBVNCSERVER_HAVE_PTHREAD', true)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if libz.found()
|
||||||
|
dependencies += libz
|
||||||
|
config.set('LIBVNCSERVER_HAVE_LIBZ', true)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if lzo.found()
|
||||||
|
dependencies += lzo
|
||||||
|
config.set('LIBVNCSERVER_HAVE_LZO', true)
|
||||||
|
endif
|
||||||
|
|
||||||
if host_system == 'linux' and cc.has_header('sys/sdt.h')
|
if host_system == 'linux' and cc.has_header('sys/sdt.h')
|
||||||
config.set('HAVE_USDT', true)
|
config.set('HAVE_USDT', true)
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -0,0 +1,586 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="linux_dmabuf_unstable_v1">
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2014, 2015 Collabora, Ltd.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="zwp_linux_dmabuf_v1" version="4">
|
||||||
|
<description summary="factory for creating dmabuf-based wl_buffers">
|
||||||
|
Following the interfaces from:
|
||||||
|
https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt
|
||||||
|
https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
|
||||||
|
and the Linux DRM sub-system's AddFb2 ioctl.
|
||||||
|
|
||||||
|
This interface offers ways to create generic dmabuf-based wl_buffers.
|
||||||
|
|
||||||
|
Clients can use the get_surface_feedback request to get dmabuf feedback
|
||||||
|
for a particular surface. If the client wants to retrieve feedback not
|
||||||
|
tied to a surface, they can use the get_default_feedback request.
|
||||||
|
|
||||||
|
The following are required from clients:
|
||||||
|
|
||||||
|
- Clients must ensure that either all data in the dma-buf is
|
||||||
|
coherent for all subsequent read access or that coherency is
|
||||||
|
correctly handled by the underlying kernel-side dma-buf
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
- Don't make any more attachments after sending the buffer to the
|
||||||
|
compositor. Making more attachments later increases the risk of
|
||||||
|
the compositor not being able to use (re-import) an existing
|
||||||
|
dmabuf-based wl_buffer.
|
||||||
|
|
||||||
|
The underlying graphics stack must ensure the following:
|
||||||
|
|
||||||
|
- The dmabuf file descriptors relayed to the server will stay valid
|
||||||
|
for the whole lifetime of the wl_buffer. This means the server may
|
||||||
|
at any time use those fds to import the dmabuf into any kernel
|
||||||
|
sub-system that might accept it.
|
||||||
|
|
||||||
|
However, when the underlying graphics stack fails to deliver the
|
||||||
|
promise, because of e.g. a device hot-unplug which raises internal
|
||||||
|
errors, after the wl_buffer has been successfully created the
|
||||||
|
compositor must not raise protocol errors to the client when dmabuf
|
||||||
|
import later fails.
|
||||||
|
|
||||||
|
To create a wl_buffer from one or more dmabufs, a client creates a
|
||||||
|
zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params
|
||||||
|
request. All planes required by the intended format are added with
|
||||||
|
the 'add' request. Finally, a 'create' or 'create_immed' request is
|
||||||
|
issued, which has the following outcome depending on the import success.
|
||||||
|
|
||||||
|
The 'create' request,
|
||||||
|
- on success, triggers a 'created' event which provides the final
|
||||||
|
wl_buffer to the client.
|
||||||
|
- on failure, triggers a 'failed' event to convey that the server
|
||||||
|
cannot use the dmabufs received from the client.
|
||||||
|
|
||||||
|
For the 'create_immed' request,
|
||||||
|
- on success, the server immediately imports the added dmabufs to
|
||||||
|
create a wl_buffer. No event is sent from the server in this case.
|
||||||
|
- on failure, the server can choose to either:
|
||||||
|
- terminate the client by raising a fatal error.
|
||||||
|
- mark the wl_buffer as failed, and send a 'failed' event to the
|
||||||
|
client. If the client uses a failed wl_buffer as an argument to any
|
||||||
|
request, the behaviour is compositor implementation-defined.
|
||||||
|
|
||||||
|
For all DRM formats and unless specified in another protocol extension,
|
||||||
|
pre-multiplied alpha is used for pixel values.
|
||||||
|
|
||||||
|
Warning! The protocol described in this file is experimental and
|
||||||
|
backward incompatible changes may be made. Backward compatible changes
|
||||||
|
may be added together with the corresponding interface version bump.
|
||||||
|
Backward incompatible changes are done by bumping the version number in
|
||||||
|
the protocol and interface names and resetting the interface version.
|
||||||
|
Once the protocol is to be declared stable, the 'z' prefix and the
|
||||||
|
version number in the protocol and interface names are removed and the
|
||||||
|
interface version number is reset.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="unbind the factory">
|
||||||
|
Objects created through this interface, especially wl_buffers, will
|
||||||
|
remain valid.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="create_params">
|
||||||
|
<description summary="create a temporary object for buffer parameters">
|
||||||
|
This temporary object is used to collect multiple dmabuf handles into
|
||||||
|
a single batch to create a wl_buffer. It can only be used once and
|
||||||
|
should be destroyed after a 'created' or 'failed' event has been
|
||||||
|
received.
|
||||||
|
</description>
|
||||||
|
<arg name="params_id" type="new_id" interface="zwp_linux_buffer_params_v1"
|
||||||
|
summary="the new temporary"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="format">
|
||||||
|
<description summary="supported buffer format">
|
||||||
|
This event advertises one buffer format that the server supports.
|
||||||
|
All the supported formats are advertised once when the client
|
||||||
|
binds to this interface. A roundtrip after binding guarantees
|
||||||
|
that the client has received all supported formats.
|
||||||
|
|
||||||
|
For the definition of the format codes, see the
|
||||||
|
zwp_linux_buffer_params_v1::create request.
|
||||||
|
|
||||||
|
Starting version 4, the format event is deprecated and must not be
|
||||||
|
sent by compositors. Instead, use get_default_feedback or
|
||||||
|
get_surface_feedback.
|
||||||
|
</description>
|
||||||
|
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="modifier" since="3">
|
||||||
|
<description summary="supported buffer format modifier">
|
||||||
|
This event advertises the formats that the server supports, along with
|
||||||
|
the modifiers supported for each format. All the supported modifiers
|
||||||
|
for all the supported formats are advertised once when the client
|
||||||
|
binds to this interface. A roundtrip after binding guarantees that
|
||||||
|
the client has received all supported format-modifier pairs.
|
||||||
|
|
||||||
|
For legacy support, DRM_FORMAT_MOD_INVALID (that is, modifier_hi ==
|
||||||
|
0x00ffffff and modifier_lo == 0xffffffff) is allowed in this event.
|
||||||
|
It indicates that the server can support the format with an implicit
|
||||||
|
modifier. When a plane has DRM_FORMAT_MOD_INVALID as its modifier, it
|
||||||
|
is as if no explicit modifier is specified. The effective modifier
|
||||||
|
will be derived from the dmabuf.
|
||||||
|
|
||||||
|
A compositor that sends valid modifiers and DRM_FORMAT_MOD_INVALID for
|
||||||
|
a given format supports both explicit modifiers and implicit modifiers.
|
||||||
|
|
||||||
|
For the definition of the format and modifier codes, see the
|
||||||
|
zwp_linux_buffer_params_v1::create and zwp_linux_buffer_params_v1::add
|
||||||
|
requests.
|
||||||
|
|
||||||
|
Starting version 4, the modifier event is deprecated and must not be
|
||||||
|
sent by compositors. Instead, use get_default_feedback or
|
||||||
|
get_surface_feedback.
|
||||||
|
</description>
|
||||||
|
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
|
||||||
|
<arg name="modifier_hi" type="uint"
|
||||||
|
summary="high 32 bits of layout modifier"/>
|
||||||
|
<arg name="modifier_lo" type="uint"
|
||||||
|
summary="low 32 bits of layout modifier"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<!-- Version 4 additions -->
|
||||||
|
|
||||||
|
<request name="get_default_feedback" since="4">
|
||||||
|
<description summary="get default feedback">
|
||||||
|
This request creates a new wp_linux_dmabuf_feedback object not bound
|
||||||
|
to a particular surface. This object will deliver feedback about dmabuf
|
||||||
|
parameters to use if the client doesn't support per-surface feedback
|
||||||
|
(see get_surface_feedback).
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwp_linux_dmabuf_feedback_v1"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_surface_feedback" since="4">
|
||||||
|
<description summary="get feedback for a surface">
|
||||||
|
This request creates a new wp_linux_dmabuf_feedback object for the
|
||||||
|
specified wl_surface. This object will deliver feedback about dmabuf
|
||||||
|
parameters to use for buffers attached to this surface.
|
||||||
|
|
||||||
|
If the surface is destroyed before the wp_linux_dmabuf_feedback object,
|
||||||
|
the feedback object becomes inert.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwp_linux_dmabuf_feedback_v1"/>
|
||||||
|
<arg name="surface" type="object" interface="wl_surface"/>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwp_linux_buffer_params_v1" version="4">
|
||||||
|
<description summary="parameters for creating a dmabuf-based wl_buffer">
|
||||||
|
This temporary object is a collection of dmabufs and other
|
||||||
|
parameters that together form a single logical buffer. The temporary
|
||||||
|
object may eventually create one wl_buffer unless cancelled by
|
||||||
|
destroying it before requesting 'create'.
|
||||||
|
|
||||||
|
Single-planar formats only require one dmabuf, however
|
||||||
|
multi-planar formats may require more than one dmabuf. For all
|
||||||
|
formats, an 'add' request must be called once per plane (even if the
|
||||||
|
underlying dmabuf fd is identical).
|
||||||
|
|
||||||
|
You must use consecutive plane indices ('plane_idx' argument for 'add')
|
||||||
|
from zero to the number of planes used by the drm_fourcc format code.
|
||||||
|
All planes required by the format must be given exactly once, but can
|
||||||
|
be given in any order. Each plane index can be set only once.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="already_used" value="0"
|
||||||
|
summary="the dmabuf_batch object has already been used to create a wl_buffer"/>
|
||||||
|
<entry name="plane_idx" value="1"
|
||||||
|
summary="plane index out of bounds"/>
|
||||||
|
<entry name="plane_set" value="2"
|
||||||
|
summary="the plane index was already set"/>
|
||||||
|
<entry name="incomplete" value="3"
|
||||||
|
summary="missing or too many planes to create a buffer"/>
|
||||||
|
<entry name="invalid_format" value="4"
|
||||||
|
summary="format not supported"/>
|
||||||
|
<entry name="invalid_dimensions" value="5"
|
||||||
|
summary="invalid width or height"/>
|
||||||
|
<entry name="out_of_bounds" value="6"
|
||||||
|
summary="offset + stride * height goes out of dmabuf bounds"/>
|
||||||
|
<entry name="invalid_wl_buffer" value="7"
|
||||||
|
summary="invalid wl_buffer resulted from importing dmabufs via
|
||||||
|
the create_immed request on given buffer_params"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="delete this object, used or not">
|
||||||
|
Cleans up the temporary data sent to the server for dmabuf-based
|
||||||
|
wl_buffer creation.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="add">
|
||||||
|
<description summary="add a dmabuf to the temporary set">
|
||||||
|
This request adds one dmabuf to the set in this
|
||||||
|
zwp_linux_buffer_params_v1.
|
||||||
|
|
||||||
|
The 64-bit unsigned value combined from modifier_hi and modifier_lo
|
||||||
|
is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the
|
||||||
|
fb modifier, which is defined in drm_mode.h of Linux UAPI.
|
||||||
|
This is an opaque token. Drivers use this token to express tiling,
|
||||||
|
compression, etc. driver-specific modifications to the base format
|
||||||
|
defined by the DRM fourcc code.
|
||||||
|
|
||||||
|
Starting from version 4, the invalid_format protocol error is sent if
|
||||||
|
the format + modifier pair was not advertised as supported.
|
||||||
|
|
||||||
|
This request raises the PLANE_IDX error if plane_idx is too large.
|
||||||
|
The error PLANE_SET is raised if attempting to set a plane that
|
||||||
|
was already set.
|
||||||
|
</description>
|
||||||
|
<arg name="fd" type="fd" summary="dmabuf fd"/>
|
||||||
|
<arg name="plane_idx" type="uint" summary="plane index"/>
|
||||||
|
<arg name="offset" type="uint" summary="offset in bytes"/>
|
||||||
|
<arg name="stride" type="uint" summary="stride in bytes"/>
|
||||||
|
<arg name="modifier_hi" type="uint"
|
||||||
|
summary="high 32 bits of layout modifier"/>
|
||||||
|
<arg name="modifier_lo" type="uint"
|
||||||
|
summary="low 32 bits of layout modifier"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="flags" bitfield="true">
|
||||||
|
<entry name="y_invert" value="1" summary="contents are y-inverted"/>
|
||||||
|
<entry name="interlaced" value="2" summary="content is interlaced"/>
|
||||||
|
<entry name="bottom_first" value="4" summary="bottom field first"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<request name="create">
|
||||||
|
<description summary="create a wl_buffer from the given dmabufs">
|
||||||
|
This asks for creation of a wl_buffer from the added dmabuf
|
||||||
|
buffers. The wl_buffer is not created immediately but returned via
|
||||||
|
the 'created' event if the dmabuf sharing succeeds. The sharing
|
||||||
|
may fail at runtime for reasons a client cannot predict, in
|
||||||
|
which case the 'failed' event is triggered.
|
||||||
|
|
||||||
|
The 'format' argument is a DRM_FORMAT code, as defined by the
|
||||||
|
libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the
|
||||||
|
authoritative source on how the format codes should work.
|
||||||
|
|
||||||
|
The 'flags' is a bitfield of the flags defined in enum "flags".
|
||||||
|
'y_invert' means the that the image needs to be y-flipped.
|
||||||
|
|
||||||
|
Flag 'interlaced' means that the frame in the buffer is not
|
||||||
|
progressive as usual, but interlaced. An interlaced buffer as
|
||||||
|
supported here must always contain both top and bottom fields.
|
||||||
|
The top field always begins on the first pixel row. The temporal
|
||||||
|
ordering between the two fields is top field first, unless
|
||||||
|
'bottom_first' is specified. It is undefined whether 'bottom_first'
|
||||||
|
is ignored if 'interlaced' is not set.
|
||||||
|
|
||||||
|
This protocol does not convey any information about field rate,
|
||||||
|
duration, or timing, other than the relative ordering between the
|
||||||
|
two fields in one buffer. A compositor may have to estimate the
|
||||||
|
intended field rate from the incoming buffer rate. It is undefined
|
||||||
|
whether the time of receiving wl_surface.commit with a new buffer
|
||||||
|
attached, applying the wl_surface state, wl_surface.frame callback
|
||||||
|
trigger, presentation, or any other point in the compositor cycle
|
||||||
|
is used to measure the frame or field times. There is no support
|
||||||
|
for detecting missed or late frames/fields/buffers either, and
|
||||||
|
there is no support whatsoever for cooperating with interlaced
|
||||||
|
compositor output.
|
||||||
|
|
||||||
|
The composited image quality resulting from the use of interlaced
|
||||||
|
buffers is explicitly undefined. A compositor may use elaborate
|
||||||
|
hardware features or software to deinterlace and create progressive
|
||||||
|
output frames from a sequence of interlaced input buffers, or it
|
||||||
|
may produce substandard image quality. However, compositors that
|
||||||
|
cannot guarantee reasonable image quality in all cases are recommended
|
||||||
|
to just reject all interlaced buffers.
|
||||||
|
|
||||||
|
Any argument errors, including non-positive width or height,
|
||||||
|
mismatch between the number of planes and the format, bad
|
||||||
|
format, bad offset or stride, may be indicated by fatal protocol
|
||||||
|
errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS,
|
||||||
|
OUT_OF_BOUNDS.
|
||||||
|
|
||||||
|
Dmabuf import errors in the server that are not obvious client
|
||||||
|
bugs are returned via the 'failed' event as non-fatal. This
|
||||||
|
allows attempting dmabuf sharing and falling back in the client
|
||||||
|
if it fails.
|
||||||
|
|
||||||
|
This request can be sent only once in the object's lifetime, after
|
||||||
|
which the only legal request is destroy. This object should be
|
||||||
|
destroyed after issuing a 'create' request. Attempting to use this
|
||||||
|
object after issuing 'create' raises ALREADY_USED protocol error.
|
||||||
|
|
||||||
|
It is not mandatory to issue 'create'. If a client wants to
|
||||||
|
cancel the buffer creation, it can just destroy this object.
|
||||||
|
</description>
|
||||||
|
<arg name="width" type="int" summary="base plane width in pixels"/>
|
||||||
|
<arg name="height" type="int" summary="base plane height in pixels"/>
|
||||||
|
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
|
||||||
|
<arg name="flags" type="uint" enum="flags" summary="see enum flags"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="created">
|
||||||
|
<description summary="buffer creation succeeded">
|
||||||
|
This event indicates that the attempted buffer creation was
|
||||||
|
successful. It provides the new wl_buffer referencing the dmabuf(s).
|
||||||
|
|
||||||
|
Upon receiving this event, the client should destroy the
|
||||||
|
zlinux_dmabuf_params object.
|
||||||
|
</description>
|
||||||
|
<arg name="buffer" type="new_id" interface="wl_buffer"
|
||||||
|
summary="the newly created wl_buffer"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="failed">
|
||||||
|
<description summary="buffer creation failed">
|
||||||
|
This event indicates that the attempted buffer creation has
|
||||||
|
failed. It usually means that one of the dmabuf constraints
|
||||||
|
has not been fulfilled.
|
||||||
|
|
||||||
|
Upon receiving this event, the client should destroy the
|
||||||
|
zlinux_buffer_params object.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="create_immed" since="2">
|
||||||
|
<description summary="immediately create a wl_buffer from the given
|
||||||
|
dmabufs">
|
||||||
|
This asks for immediate creation of a wl_buffer by importing the
|
||||||
|
added dmabufs.
|
||||||
|
|
||||||
|
In case of import success, no event is sent from the server, and the
|
||||||
|
wl_buffer is ready to be used by the client.
|
||||||
|
|
||||||
|
Upon import failure, either of the following may happen, as seen fit
|
||||||
|
by the implementation:
|
||||||
|
- the client is terminated with one of the following fatal protocol
|
||||||
|
errors:
|
||||||
|
- INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS,
|
||||||
|
in case of argument errors such as mismatch between the number
|
||||||
|
of planes and the format, bad format, non-positive width or
|
||||||
|
height, or bad offset or stride.
|
||||||
|
- INVALID_WL_BUFFER, in case the cause for failure is unknown or
|
||||||
|
plaform specific.
|
||||||
|
- the server creates an invalid wl_buffer, marks it as failed and
|
||||||
|
sends a 'failed' event to the client. The result of using this
|
||||||
|
invalid wl_buffer as an argument in any request by the client is
|
||||||
|
defined by the compositor implementation.
|
||||||
|
|
||||||
|
This takes the same arguments as a 'create' request, and obeys the
|
||||||
|
same restrictions.
|
||||||
|
</description>
|
||||||
|
<arg name="buffer_id" type="new_id" interface="wl_buffer"
|
||||||
|
summary="id for the newly created wl_buffer"/>
|
||||||
|
<arg name="width" type="int" summary="base plane width in pixels"/>
|
||||||
|
<arg name="height" type="int" summary="base plane height in pixels"/>
|
||||||
|
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
|
||||||
|
<arg name="flags" type="uint" enum="flags" summary="see enum flags"/>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwp_linux_dmabuf_feedback_v1" version="4">
|
||||||
|
<description summary="dmabuf feedback">
|
||||||
|
This object advertises dmabuf parameters feedback. This includes the
|
||||||
|
preferred devices and the supported formats/modifiers.
|
||||||
|
|
||||||
|
The parameters are sent once when this object is created and whenever they
|
||||||
|
change. The done event is always sent once after all parameters have been
|
||||||
|
sent. When a single parameter changes, all parameters are re-sent by the
|
||||||
|
compositor.
|
||||||
|
|
||||||
|
Compositors can re-send the parameters when the current client buffer
|
||||||
|
allocations are sub-optimal. Compositors should not re-send the
|
||||||
|
parameters if re-allocating the buffers would not result in a more optimal
|
||||||
|
configuration. In particular, compositors should avoid sending the exact
|
||||||
|
same parameters multiple times in a row.
|
||||||
|
|
||||||
|
The tranche_target_device and tranche_modifier events are grouped by
|
||||||
|
tranches of preference. For each tranche, a tranche_target_device, one
|
||||||
|
tranche_flags and one or more tranche_modifier events are sent, followed
|
||||||
|
by a tranche_done event finishing the list. The tranches are sent in
|
||||||
|
descending order of preference. All formats and modifiers in the same
|
||||||
|
tranche have the same preference.
|
||||||
|
|
||||||
|
To send parameters, the compositor sends one main_device event, tranches
|
||||||
|
(each consisting of one tranche_target_device event, one tranche_flags
|
||||||
|
event, tranche_modifier events and then a tranche_done event), then one
|
||||||
|
done event.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the feedback object">
|
||||||
|
Using this request a client can tell the server that it is not going to
|
||||||
|
use the wp_linux_dmabuf_feedback object anymore.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="done">
|
||||||
|
<description summary="all feedback has been sent">
|
||||||
|
This event is sent after all parameters of a wp_linux_dmabuf_feedback
|
||||||
|
object have been sent.
|
||||||
|
|
||||||
|
This allows changes to the wp_linux_dmabuf_feedback parameters to be
|
||||||
|
seen as atomic, even if they happen via multiple events.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="format_table">
|
||||||
|
<description summary="format and modifier table">
|
||||||
|
This event provides a file descriptor which can be memory-mapped to
|
||||||
|
access the format and modifier table.
|
||||||
|
|
||||||
|
The table contains a tightly packed array of consecutive format +
|
||||||
|
modifier pairs. Each pair is 16 bytes wide. It contains a format as a
|
||||||
|
32-bit unsigned integer, followed by 4 bytes of unused padding, and a
|
||||||
|
modifier as a 64-bit unsigned integer. The native endianness is used.
|
||||||
|
|
||||||
|
The client must map the file descriptor in read-only private mode.
|
||||||
|
|
||||||
|
Compositors are not allowed to mutate the table file contents once this
|
||||||
|
event has been sent. Instead, compositors must create a new, separate
|
||||||
|
table file and re-send feedback parameters. Compositors are allowed to
|
||||||
|
store duplicate format + modifier pairs in the table.
|
||||||
|
</description>
|
||||||
|
<arg name="fd" type="fd" summary="table file descriptor"/>
|
||||||
|
<arg name="size" type="uint" summary="table size, in bytes"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="main_device">
|
||||||
|
<description summary="preferred main device">
|
||||||
|
This event advertises the main device that the server prefers to use
|
||||||
|
when direct scan-out to the target device isn't possible. The
|
||||||
|
advertised main device may be different for each
|
||||||
|
wp_linux_dmabuf_feedback object, and may change over time.
|
||||||
|
|
||||||
|
There is exactly one main device. The compositor must send at least
|
||||||
|
one preference tranche with tranche_target_device equal to main_device.
|
||||||
|
|
||||||
|
Clients need to create buffers that the main device can import and
|
||||||
|
read from, otherwise creating the dmabuf wl_buffer will fail (see the
|
||||||
|
wp_linux_buffer_params.create and create_immed requests for details).
|
||||||
|
The main device will also likely be kept active by the compositor,
|
||||||
|
so clients can use it instead of waking up another device for power
|
||||||
|
savings.
|
||||||
|
|
||||||
|
In general the device is a DRM node. The DRM node type (primary vs.
|
||||||
|
render) is unspecified. Clients must not rely on the compositor sending
|
||||||
|
a particular node type. Clients cannot check two devices for equality
|
||||||
|
by comparing the dev_t value.
|
||||||
|
|
||||||
|
If explicit modifiers are not supported and the client performs buffer
|
||||||
|
allocations on a different device than the main device, then the client
|
||||||
|
must force the buffer to have a linear layout.
|
||||||
|
</description>
|
||||||
|
<arg name="device" type="array" summary="device dev_t value"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="tranche_done">
|
||||||
|
<description summary="a preference tranche has been sent">
|
||||||
|
This event splits tranche_target_device and tranche_modifier events in
|
||||||
|
preference tranches. It is sent after a set of tranche_target_device
|
||||||
|
and tranche_modifier events; it represents the end of a tranche. The
|
||||||
|
next tranche will have a lower preference.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="tranche_target_device">
|
||||||
|
<description summary="target device">
|
||||||
|
This event advertises the target device that the server prefers to use
|
||||||
|
for a buffer created given this tranche. The advertised target device
|
||||||
|
may be different for each preference tranche, and may change over time.
|
||||||
|
|
||||||
|
There is exactly one target device per tranche.
|
||||||
|
|
||||||
|
The target device may be a scan-out device, for example if the
|
||||||
|
compositor prefers to directly scan-out a buffer created given this
|
||||||
|
tranche. The target device may be a rendering device, for example if
|
||||||
|
the compositor prefers to texture from said buffer.
|
||||||
|
|
||||||
|
The client can use this hint to allocate the buffer in a way that makes
|
||||||
|
it accessible from the target device, ideally directly. The buffer must
|
||||||
|
still be accessible from the main device, either through direct import
|
||||||
|
or through a potentially more expensive fallback path. If the buffer
|
||||||
|
can't be directly imported from the main device then clients must be
|
||||||
|
prepared for the compositor changing the tranche priority or making
|
||||||
|
wl_buffer creation fail (see the wp_linux_buffer_params.create and
|
||||||
|
create_immed requests for details).
|
||||||
|
|
||||||
|
If the device is a DRM node, the DRM node type (primary vs. render) is
|
||||||
|
unspecified. Clients must not rely on the compositor sending a
|
||||||
|
particular node type. Clients cannot check two devices for equality by
|
||||||
|
comparing the dev_t value.
|
||||||
|
|
||||||
|
This event is tied to a preference tranche, see the tranche_done event.
|
||||||
|
</description>
|
||||||
|
<arg name="device" type="array" summary="device dev_t value"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="tranche_formats">
|
||||||
|
<description summary="supported buffer format modifier">
|
||||||
|
This event advertises the format + modifier combinations that the
|
||||||
|
compositor supports.
|
||||||
|
|
||||||
|
It carries an array of indices, each referring to a format + modifier
|
||||||
|
pair in the last received format table (see the format_table event).
|
||||||
|
Each index is a 16-bit unsigned integer in native endianness.
|
||||||
|
|
||||||
|
For legacy support, DRM_FORMAT_MOD_INVALID is an allowed modifier.
|
||||||
|
It indicates that the server can support the format with an implicit
|
||||||
|
modifier. When a buffer has DRM_FORMAT_MOD_INVALID as its modifier, it
|
||||||
|
is as if no explicit modifier is specified. The effective modifier
|
||||||
|
will be derived from the dmabuf.
|
||||||
|
|
||||||
|
A compositor that sends valid modifiers and DRM_FORMAT_MOD_INVALID for
|
||||||
|
a given format supports both explicit modifiers and implicit modifiers.
|
||||||
|
|
||||||
|
Compositors must not send duplicate format + modifier pairs within the
|
||||||
|
same tranche or across two different tranches with the same target
|
||||||
|
device and flags.
|
||||||
|
|
||||||
|
This event is tied to a preference tranche, see the tranche_done event.
|
||||||
|
|
||||||
|
For the definition of the format and modifier codes, see the
|
||||||
|
wp_linux_buffer_params.create request.
|
||||||
|
</description>
|
||||||
|
<arg name="indices" type="array" summary="array of 16-bit indexes"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<enum name="tranche_flags" bitfield="true">
|
||||||
|
<entry name="scanout" value="1" summary="direct scan-out tranche"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<event name="tranche_flags">
|
||||||
|
<description summary="tranche flags">
|
||||||
|
This event sets tranche-specific flags.
|
||||||
|
|
||||||
|
The scanout flag is a hint that direct scan-out may be attempted by the
|
||||||
|
compositor on the target device if the client appropriately allocates a
|
||||||
|
buffer. How to allocate a buffer that can be scanned out on the target
|
||||||
|
device is implementation-defined.
|
||||||
|
|
||||||
|
This event is tied to a preference tranche, see the tranche_done event.
|
||||||
|
</description>
|
||||||
|
<arg name="flags" type="uint" enum="tranche_flags" summary="tranche flags"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
</protocol>
|
|
@ -15,6 +15,8 @@ wayland_scanner_client = generator(
|
||||||
|
|
||||||
client_protocols = [
|
client_protocols = [
|
||||||
'xdg-shell.xml',
|
'xdg-shell.xml',
|
||||||
|
'linux-dmabuf-unstable-v1.xml',
|
||||||
|
'wlr-data-control-unstable-v1.xml'
|
||||||
]
|
]
|
||||||
|
|
||||||
client_protos_src = []
|
client_protos_src = []
|
||||||
|
|
|
@ -0,0 +1,278 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="wlr_data_control_unstable_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2018 Simon Ser
|
||||||
|
Copyright © 2019 Ivan Molodetskikh
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute, and sell this
|
||||||
|
software and its documentation for any purpose is hereby granted
|
||||||
|
without fee, provided that the above copyright notice appear in
|
||||||
|
all copies and that both that copyright notice and this permission
|
||||||
|
notice appear in supporting documentation, and that the name of
|
||||||
|
the copyright holders not be used in advertising or publicity
|
||||||
|
pertaining to distribution of the software without specific,
|
||||||
|
written prior permission. The copyright holders make no
|
||||||
|
representations about the suitability of this software for any
|
||||||
|
purpose. It is provided "as is" without express or implied
|
||||||
|
warranty.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||||
|
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<description summary="control data devices">
|
||||||
|
This protocol allows a privileged client to control data devices. In
|
||||||
|
particular, the client will be able to manage the current selection and take
|
||||||
|
the role of a clipboard manager.
|
||||||
|
|
||||||
|
Warning! The protocol described in this file is experimental and
|
||||||
|
backward incompatible changes may be made. Backward compatible changes
|
||||||
|
may be added together with the corresponding interface version bump.
|
||||||
|
Backward incompatible changes are done by bumping the version number in
|
||||||
|
the protocol and interface names and resetting the interface version.
|
||||||
|
Once the protocol is to be declared stable, the 'z' prefix and the
|
||||||
|
version number in the protocol and interface names are removed and the
|
||||||
|
interface version number is reset.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<interface name="zwlr_data_control_manager_v1" version="2">
|
||||||
|
<description summary="manager to control data devices">
|
||||||
|
This interface is a manager that allows creating per-seat data device
|
||||||
|
controls.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="create_data_source">
|
||||||
|
<description summary="create a new data source">
|
||||||
|
Create a new data source.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwlr_data_control_source_v1"
|
||||||
|
summary="data source to create"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_data_device">
|
||||||
|
<description summary="get a data device for a seat">
|
||||||
|
Create a data device that can be used to manage a seat's selection.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwlr_data_control_device_v1"/>
|
||||||
|
<arg name="seat" type="object" interface="wl_seat"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the manager">
|
||||||
|
All objects created by the manager will still remain valid, until their
|
||||||
|
appropriate destroy request has been called.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwlr_data_control_device_v1" version="2">
|
||||||
|
<description summary="manage a data device for a seat">
|
||||||
|
This interface allows a client to manage a seat's selection.
|
||||||
|
|
||||||
|
When the seat is destroyed, this object becomes inert.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="set_selection">
|
||||||
|
<description summary="copy data to the selection">
|
||||||
|
This request asks the compositor to set the selection to the data from
|
||||||
|
the source on behalf of the client.
|
||||||
|
|
||||||
|
The given source may not be used in any further set_selection or
|
||||||
|
set_primary_selection requests. Attempting to use a previously used
|
||||||
|
source is a protocol error.
|
||||||
|
|
||||||
|
To unset the selection, set the source to NULL.
|
||||||
|
</description>
|
||||||
|
<arg name="source" type="object" interface="zwlr_data_control_source_v1"
|
||||||
|
allow-null="true"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy this data device">
|
||||||
|
Destroys the data device object.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="data_offer">
|
||||||
|
<description summary="introduce a new wlr_data_control_offer">
|
||||||
|
The data_offer event introduces a new wlr_data_control_offer object,
|
||||||
|
which will subsequently be used in either the
|
||||||
|
wlr_data_control_device.selection event (for the regular clipboard
|
||||||
|
selections) or the wlr_data_control_device.primary_selection event (for
|
||||||
|
the primary clipboard selections). Immediately following the
|
||||||
|
wlr_data_control_device.data_offer event, the new data_offer object
|
||||||
|
will send out wlr_data_control_offer.offer events to describe the MIME
|
||||||
|
types it offers.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwlr_data_control_offer_v1"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="selection">
|
||||||
|
<description summary="advertise new selection">
|
||||||
|
The selection event is sent out to notify the client of a new
|
||||||
|
wlr_data_control_offer for the selection for this device. The
|
||||||
|
wlr_data_control_device.data_offer and the wlr_data_control_offer.offer
|
||||||
|
events are sent out immediately before this event to introduce the data
|
||||||
|
offer object. The selection event is sent to a client when a new
|
||||||
|
selection is set. The wlr_data_control_offer is valid until a new
|
||||||
|
wlr_data_control_offer or NULL is received. The client must destroy the
|
||||||
|
previous selection wlr_data_control_offer, if any, upon receiving this
|
||||||
|
event.
|
||||||
|
|
||||||
|
The first selection event is sent upon binding the
|
||||||
|
wlr_data_control_device object.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="object" interface="zwlr_data_control_offer_v1"
|
||||||
|
allow-null="true"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="finished">
|
||||||
|
<description summary="this data control is no longer valid">
|
||||||
|
This data control object is no longer valid and should be destroyed by
|
||||||
|
the client.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<!-- Version 2 additions -->
|
||||||
|
|
||||||
|
<event name="primary_selection" since="2">
|
||||||
|
<description summary="advertise new primary selection">
|
||||||
|
The primary_selection event is sent out to notify the client of a new
|
||||||
|
wlr_data_control_offer for the primary selection for this device. The
|
||||||
|
wlr_data_control_device.data_offer and the wlr_data_control_offer.offer
|
||||||
|
events are sent out immediately before this event to introduce the data
|
||||||
|
offer object. The primary_selection event is sent to a client when a
|
||||||
|
new primary selection is set. The wlr_data_control_offer is valid until
|
||||||
|
a new wlr_data_control_offer or NULL is received. The client must
|
||||||
|
destroy the previous primary selection wlr_data_control_offer, if any,
|
||||||
|
upon receiving this event.
|
||||||
|
|
||||||
|
If the compositor supports primary selection, the first
|
||||||
|
primary_selection event is sent upon binding the
|
||||||
|
wlr_data_control_device object.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="object" interface="zwlr_data_control_offer_v1"
|
||||||
|
allow-null="true"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="set_primary_selection" since="2">
|
||||||
|
<description summary="copy data to the primary selection">
|
||||||
|
This request asks the compositor to set the primary selection to the
|
||||||
|
data from the source on behalf of the client.
|
||||||
|
|
||||||
|
The given source may not be used in any further set_selection or
|
||||||
|
set_primary_selection requests. Attempting to use a previously used
|
||||||
|
source is a protocol error.
|
||||||
|
|
||||||
|
To unset the primary selection, set the source to NULL.
|
||||||
|
|
||||||
|
The compositor will ignore this request if it does not support primary
|
||||||
|
selection.
|
||||||
|
</description>
|
||||||
|
<arg name="source" type="object" interface="zwlr_data_control_source_v1"
|
||||||
|
allow-null="true"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="error" since="2">
|
||||||
|
<entry name="used_source" value="1"
|
||||||
|
summary="source given to set_selection or set_primary_selection was already used before"/>
|
||||||
|
</enum>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwlr_data_control_source_v1" version="1">
|
||||||
|
<description summary="offer to transfer data">
|
||||||
|
The wlr_data_control_source object is the source side of a
|
||||||
|
wlr_data_control_offer. It is created by the source client in a data
|
||||||
|
transfer and provides a way to describe the offered data and a way to
|
||||||
|
respond to requests to transfer the data.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="invalid_offer" value="1"
|
||||||
|
summary="offer sent after wlr_data_control_device.set_selection"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<request name="offer">
|
||||||
|
<description summary="add an offered MIME type">
|
||||||
|
This request adds a MIME type to the set of MIME types advertised to
|
||||||
|
targets. Can be called several times to offer multiple types.
|
||||||
|
|
||||||
|
Calling this after wlr_data_control_device.set_selection is a protocol
|
||||||
|
error.
|
||||||
|
</description>
|
||||||
|
<arg name="mime_type" type="string"
|
||||||
|
summary="MIME type offered by the data source"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy this source">
|
||||||
|
Destroys the data source object.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="send">
|
||||||
|
<description summary="send the data">
|
||||||
|
Request for data from the client. Send the data as the specified MIME
|
||||||
|
type over the passed file descriptor, then close it.
|
||||||
|
</description>
|
||||||
|
<arg name="mime_type" type="string" summary="MIME type for the data"/>
|
||||||
|
<arg name="fd" type="fd" summary="file descriptor for the data"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="cancelled">
|
||||||
|
<description summary="selection was cancelled">
|
||||||
|
This data source is no longer valid. The data source has been replaced
|
||||||
|
by another data source.
|
||||||
|
|
||||||
|
The client should clean up and destroy this data source.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwlr_data_control_offer_v1" version="1">
|
||||||
|
<description summary="offer to transfer data">
|
||||||
|
A wlr_data_control_offer represents a piece of data offered for transfer
|
||||||
|
by another client (the source client). The offer describes the different
|
||||||
|
MIME types that the data can be converted to and provides the mechanism
|
||||||
|
for transferring the data directly from the source client.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="receive">
|
||||||
|
<description summary="request that the data is transferred">
|
||||||
|
To transfer the offered data, the client issues this request and
|
||||||
|
indicates the MIME type it wants to receive. The transfer happens
|
||||||
|
through the passed file descriptor (typically created with the pipe
|
||||||
|
system call). The source client writes the data in the MIME type
|
||||||
|
representation requested and then closes the file descriptor.
|
||||||
|
|
||||||
|
The receiving client reads from the read end of the pipe until EOF and
|
||||||
|
then closes its end, at which point the transfer is complete.
|
||||||
|
|
||||||
|
This request may happen multiple times for different MIME types.
|
||||||
|
</description>
|
||||||
|
<arg name="mime_type" type="string"
|
||||||
|
summary="MIME type desired by receiver"/>
|
||||||
|
<arg name="fd" type="fd" summary="file descriptor for data transfer"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy this offer">
|
||||||
|
Destroys the data offer object.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="offer">
|
||||||
|
<description summary="advertise offered MIME type">
|
||||||
|
Sent immediately after creating the wlr_data_control_offer object.
|
||||||
|
One event per offered MIME type.
|
||||||
|
</description>
|
||||||
|
<arg name="mime_type" type="string" summary="offered MIME type"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "shm.h"
|
||||||
|
#include "pixels.h"
|
||||||
|
#include "linux-dmabuf-unstable-v1.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
/* Origin: main.c */
|
||||||
|
extern struct wl_shm* wl_shm;
|
||||||
|
extern struct gbm_device* gbm_device;
|
||||||
|
extern struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1;
|
||||||
|
|
||||||
|
static void buffer_release(void* data, struct wl_buffer* wl_buffer)
|
||||||
|
{
|
||||||
|
(void)wl_buffer;
|
||||||
|
struct buffer* self = data;
|
||||||
|
self->is_attached = false;
|
||||||
|
|
||||||
|
if (self->please_clean_up) {
|
||||||
|
buffer_destroy(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_buffer_listener buffer_listener = {
|
||||||
|
.release = buffer_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct buffer* buffer_create_shm(int width, int height, int stride,
|
||||||
|
uint32_t format)
|
||||||
|
{
|
||||||
|
assert(wl_shm);
|
||||||
|
|
||||||
|
struct buffer* self = calloc(1, sizeof(*self));
|
||||||
|
if (!self)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
self->type = BUFFER_WL_SHM;
|
||||||
|
self->width = width;
|
||||||
|
self->height = height;
|
||||||
|
self->stride = stride;
|
||||||
|
self->format = format;
|
||||||
|
|
||||||
|
pixman_region_init_rect(&self->damage, 0, 0, width, height);
|
||||||
|
|
||||||
|
self->size = height * stride;
|
||||||
|
int fd = shm_alloc_fd(self->size);
|
||||||
|
if (fd < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
self->pixels = mmap(NULL, self->size, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED, fd, 0);
|
||||||
|
if (!self->pixels)
|
||||||
|
goto mmap_failure;
|
||||||
|
|
||||||
|
struct wl_shm_pool* pool = wl_shm_create_pool(wl_shm, fd, self->size);
|
||||||
|
if (!pool)
|
||||||
|
goto pool_failure;
|
||||||
|
|
||||||
|
self->wl_buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
|
||||||
|
stride, drm_format_to_wl_shm(format));
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
if (!self->wl_buffer)
|
||||||
|
goto shm_failure;
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
wl_buffer_add_listener(self->wl_buffer, &buffer_listener, self);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
|
||||||
|
shm_failure:
|
||||||
|
pool_failure:
|
||||||
|
munmap(self->pixels, self->size);
|
||||||
|
mmap_failure:
|
||||||
|
close(fd);
|
||||||
|
failure:
|
||||||
|
free(self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct buffer* buffer_create_dmabuf(int width, int height, uint32_t format)
|
||||||
|
{
|
||||||
|
assert(gbm_device && zwp_linux_dmabuf_v1);
|
||||||
|
|
||||||
|
struct buffer* self = calloc(1, sizeof(*self));
|
||||||
|
if (!self)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
self->type = BUFFER_DMABUF;
|
||||||
|
self->width = width;
|
||||||
|
self->height = height;
|
||||||
|
self->format = format;
|
||||||
|
|
||||||
|
pixman_region_init_rect(&self->damage, 0, 0, width, height);
|
||||||
|
|
||||||
|
self->bo = gbm_bo_create(gbm_device, width, height, format,
|
||||||
|
GBM_BO_USE_RENDERING);
|
||||||
|
if (!self->bo)
|
||||||
|
goto bo_failure;
|
||||||
|
|
||||||
|
struct zwp_linux_buffer_params_v1* params;
|
||||||
|
params = zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf_v1);
|
||||||
|
if (!params)
|
||||||
|
goto params_failure;
|
||||||
|
|
||||||
|
uint32_t offset = gbm_bo_get_offset(self->bo, 0);
|
||||||
|
uint32_t stride = gbm_bo_get_stride(self->bo);
|
||||||
|
uint64_t mod = gbm_bo_get_modifier(self->bo);
|
||||||
|
int fd = gbm_bo_get_fd(self->bo);
|
||||||
|
if (fd < 0)
|
||||||
|
goto fd_failure;
|
||||||
|
|
||||||
|
zwp_linux_buffer_params_v1_add(params, fd, 0, offset, stride,
|
||||||
|
mod >> 32, mod & 0xffffffff);
|
||||||
|
self->wl_buffer = zwp_linux_buffer_params_v1_create_immed(params, width,
|
||||||
|
height, format, /* flags */ 0);
|
||||||
|
zwp_linux_buffer_params_v1_destroy(params);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
if (!self->wl_buffer)
|
||||||
|
goto buffer_failure;
|
||||||
|
|
||||||
|
wl_buffer_add_listener(self->wl_buffer, &buffer_listener, self);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
|
||||||
|
buffer_failure:
|
||||||
|
fd_failure:
|
||||||
|
zwp_linux_buffer_params_v1_destroy(params);
|
||||||
|
params_failure:
|
||||||
|
gbm_bo_destroy(self->bo);
|
||||||
|
bo_failure:
|
||||||
|
free(self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void buffer_destroy(struct buffer* self)
|
||||||
|
{
|
||||||
|
if (!self)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (self->is_attached)
|
||||||
|
self->please_clean_up = true;
|
||||||
|
|
||||||
|
pixman_region_fini(&self->damage);
|
||||||
|
wl_buffer_destroy(self->wl_buffer);
|
||||||
|
|
||||||
|
switch (self->type) {
|
||||||
|
case BUFFER_WL_SHM:
|
||||||
|
munmap(self->pixels, self->size);
|
||||||
|
break;
|
||||||
|
case BUFFER_DMABUF:
|
||||||
|
gbm_bo_destroy(self->bo);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(self);
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* crypto_included.c - Crypto wrapper (included version)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Gernot Tenchio
|
||||||
|
* Copyright (C) 2019 Christian Beier
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "sha.h"
|
||||||
|
#include "d3des.h"
|
||||||
|
#include "crypto.h"
|
||||||
|
|
||||||
|
|
||||||
|
int hash_md5(void *out, const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hash_sha1(void *out, const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
SHA1Context sha1;
|
||||||
|
if(SHA1Reset(&sha1) != shaSuccess)
|
||||||
|
return 0;
|
||||||
|
if(SHA1Input(&sha1, in, in_len) != shaSuccess)
|
||||||
|
return 0;
|
||||||
|
if(SHA1Result(&sha1, out) != shaSuccess)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void random_bytes(void *out, size_t len)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int encrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
int eightbyteblocks = in_len/8;
|
||||||
|
int i;
|
||||||
|
rfbDesKey((unsigned char*)key, EN0);
|
||||||
|
for(i = 0; i < eightbyteblocks; ++i)
|
||||||
|
rfbDes((unsigned char*)in + i*8, (unsigned char*)out + i*8);
|
||||||
|
|
||||||
|
*out_len = in_len;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int decrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
int eightbyteblocks = in_len/8;
|
||||||
|
int i;
|
||||||
|
rfbDesKey((unsigned char*)key, DE1);
|
||||||
|
for(i = 0; i < eightbyteblocks; ++i)
|
||||||
|
rfbDes((unsigned char*)in + i*8, (unsigned char*)out + i*8);
|
||||||
|
|
||||||
|
*out_len = in_len;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int encrypt_aes128ecb(void *out, int *out_len, const unsigned char key[16], const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dh_generate_keypair(uint8_t *priv_out, uint8_t *pub_out, const uint8_t *gen, const size_t gen_len, const uint8_t *prime, const size_t keylen)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dh_compute_shared_key(uint8_t *shared_out, const uint8_t *priv, const uint8_t *pub, const uint8_t *prime, const size_t keylen)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,271 @@
|
||||||
|
/*
|
||||||
|
* crypto_gnutls.c - Crypto wrapper (libgcrypt version)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Gernot Tenchio
|
||||||
|
* Copyright (C) 2019 Christian Beier
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <gcrypt.h>
|
||||||
|
#include "crypto.h"
|
||||||
|
|
||||||
|
static int mpiToBytes(const gcry_mpi_t value, uint8_t *result, size_t size)
|
||||||
|
{
|
||||||
|
gcry_error_t error;
|
||||||
|
size_t len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
error = gcry_mpi_print(GCRYMPI_FMT_USG, result, size, &len, value);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
return 0;
|
||||||
|
for (i=size-1;i>(int)size-1-(int)len;--i)
|
||||||
|
result[i] = result[i-size+len];
|
||||||
|
for (;i>=0;--i)
|
||||||
|
result[i] = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char reverseByte(unsigned char b) {
|
||||||
|
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
||||||
|
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
||||||
|
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hash_md5(void *out, const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
gcry_error_t error;
|
||||||
|
gcry_md_hd_t md5 = NULL;
|
||||||
|
void *digest;
|
||||||
|
|
||||||
|
error = gcry_md_open(&md5, GCRY_MD_MD5, 0);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
gcry_md_write(md5, in, in_len);
|
||||||
|
|
||||||
|
if(!(digest = gcry_md_read(md5, GCRY_MD_MD5)))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
memcpy(out, digest, gcry_md_get_algo_dlen(GCRY_MD_MD5));
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
gcry_md_close(md5);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hash_sha1(void *out, const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
gcry_error_t error;
|
||||||
|
gcry_md_hd_t sha1 = NULL;
|
||||||
|
void *digest;
|
||||||
|
|
||||||
|
error = gcry_md_open(&sha1, GCRY_MD_SHA1, 0);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
gcry_md_write(sha1, in, in_len);
|
||||||
|
|
||||||
|
if(!(digest = gcry_md_read(sha1, GCRY_MD_SHA1)))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
memcpy(out, digest, gcry_md_get_algo_dlen(GCRY_MD_SHA1));
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
gcry_md_close(sha1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void random_bytes(void *out, size_t len)
|
||||||
|
{
|
||||||
|
gcry_randomize(out, len, GCRY_STRONG_RANDOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
int encrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
gcry_error_t error;
|
||||||
|
gcry_cipher_hd_t des = NULL;
|
||||||
|
unsigned char mungedkey[8];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
mungedkey[i] = reverseByte(key[i]);
|
||||||
|
|
||||||
|
error = gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = gcry_cipher_setkey(des, mungedkey, 8);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = gcry_cipher_encrypt(des, out, in_len, in, in_len);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
*out_len = in_len;
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
gcry_cipher_close(des);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int decrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
gcry_error_t error;
|
||||||
|
gcry_cipher_hd_t des = NULL;
|
||||||
|
unsigned char mungedkey[8];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
mungedkey[i] = reverseByte(key[i]);
|
||||||
|
|
||||||
|
error = gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = gcry_cipher_setkey(des, mungedkey, 8);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = gcry_cipher_decrypt(des, out, in_len, in, in_len);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
*out_len = in_len;
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
gcry_cipher_close(des);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int encrypt_aes128ecb(void *out, int *out_len, const unsigned char key[16], const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
gcry_error_t error;
|
||||||
|
gcry_cipher_hd_t aes = NULL;
|
||||||
|
|
||||||
|
error = gcry_cipher_open(&aes, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, 0);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = gcry_cipher_setkey(aes, key, 16);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = gcry_cipher_encrypt(aes, out, in_len, in, in_len);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
*out_len = in_len;
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
gcry_cipher_close(aes);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dh_generate_keypair(uint8_t *priv_out, uint8_t *pub_out, const uint8_t *gen, const size_t gen_len, const uint8_t *prime, const size_t keylen)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
gcry_error_t error;
|
||||||
|
gcry_mpi_t genmpi = NULL, modmpi = NULL, privmpi = NULL, pubmpi = NULL;
|
||||||
|
|
||||||
|
error = gcry_mpi_scan(&genmpi, GCRYMPI_FMT_USG, gen, gen_len, NULL);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
error = gcry_mpi_scan(&modmpi, GCRYMPI_FMT_USG, prime, keylen, NULL);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
privmpi = gcry_mpi_new(keylen);
|
||||||
|
if (!privmpi)
|
||||||
|
goto out;
|
||||||
|
gcry_mpi_randomize(privmpi, (keylen/8)*8, GCRY_STRONG_RANDOM);
|
||||||
|
|
||||||
|
pubmpi = gcry_mpi_new(keylen);
|
||||||
|
if (!pubmpi)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
gcry_mpi_powm(pubmpi, genmpi, privmpi, modmpi);
|
||||||
|
|
||||||
|
if (!mpiToBytes(pubmpi, pub_out, keylen))
|
||||||
|
goto out;
|
||||||
|
if (!mpiToBytes(privmpi, priv_out, keylen))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
gcry_mpi_release(genmpi);
|
||||||
|
gcry_mpi_release(modmpi);
|
||||||
|
gcry_mpi_release(privmpi);
|
||||||
|
gcry_mpi_release(pubmpi);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dh_compute_shared_key(uint8_t *shared_out, const uint8_t *priv, const uint8_t *pub, const uint8_t *prime, const size_t keylen)
|
||||||
|
{
|
||||||
|
int result = 1;
|
||||||
|
gcry_error_t error;
|
||||||
|
gcry_mpi_t keympi = NULL, modmpi = NULL, privmpi = NULL, pubmpi = NULL;
|
||||||
|
|
||||||
|
error = gcry_mpi_scan(&privmpi, GCRYMPI_FMT_USG, priv, keylen, NULL);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
error = gcry_mpi_scan(&pubmpi, GCRYMPI_FMT_USG, pub, keylen, NULL);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
error = gcry_mpi_scan(&modmpi, GCRYMPI_FMT_USG, prime, keylen, NULL);
|
||||||
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
keympi = gcry_mpi_new(keylen);
|
||||||
|
if (!keympi)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
gcry_mpi_powm(keympi, pubmpi, privmpi, modmpi);
|
||||||
|
|
||||||
|
if (!mpiToBytes(keympi, shared_out, keylen))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
gcry_mpi_release(keympi);
|
||||||
|
gcry_mpi_release(modmpi);
|
||||||
|
gcry_mpi_release(privmpi);
|
||||||
|
gcry_mpi_release(pubmpi);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
/*
|
||||||
|
* crypto_openssl.c - Crypto wrapper (openssl version)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Gernot Tenchio
|
||||||
|
* Copyright (C) 2019 Christian Beier
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
#include <openssl/md5.h>
|
||||||
|
#include <openssl/dh.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#include "crypto.h"
|
||||||
|
|
||||||
|
static unsigned char reverseByte(unsigned char b) {
|
||||||
|
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
||||||
|
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
||||||
|
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hash_md5(void *out, const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
MD5_CTX md5;
|
||||||
|
if(!MD5_Init(&md5))
|
||||||
|
return 0;
|
||||||
|
if(!MD5_Update(&md5, in, in_len))
|
||||||
|
return 0;
|
||||||
|
if(!MD5_Final(out, &md5))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hash_sha1(void *out, const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
SHA_CTX sha1;
|
||||||
|
if(!SHA1_Init(&sha1))
|
||||||
|
return 0;
|
||||||
|
if(!SHA1_Update(&sha1, in, in_len))
|
||||||
|
return 0;
|
||||||
|
if(!SHA1_Final(out, &sha1))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void random_bytes(void *out, size_t len)
|
||||||
|
{
|
||||||
|
RAND_bytes(out, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int encrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
EVP_CIPHER_CTX *des;
|
||||||
|
unsigned char mungedkey[8];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
mungedkey[i] = reverseByte(key[i]);
|
||||||
|
|
||||||
|
if(!(des = EVP_CIPHER_CTX_new()))
|
||||||
|
goto out;
|
||||||
|
if(!EVP_EncryptInit_ex(des, EVP_des_ecb(), NULL, mungedkey, NULL))
|
||||||
|
goto out;
|
||||||
|
if(!EVP_EncryptUpdate(des, out, out_len, in, in_len))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
EVP_CIPHER_CTX_free(des);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int decrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
EVP_CIPHER_CTX *des;
|
||||||
|
unsigned char mungedkey[8];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
mungedkey[i] = reverseByte(key[i]);
|
||||||
|
|
||||||
|
if(!(des = EVP_CIPHER_CTX_new()))
|
||||||
|
goto out;
|
||||||
|
if(!EVP_DecryptInit_ex(des, EVP_des_ecb(), NULL, mungedkey, NULL))
|
||||||
|
goto out;
|
||||||
|
if(!EVP_DecryptUpdate(des, out, out_len, in, in_len))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
EVP_CIPHER_CTX_free(des);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int encrypt_aes128ecb(void *out, int *out_len, const unsigned char key[16], const void *in, const size_t in_len)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
EVP_CIPHER_CTX *aes;
|
||||||
|
|
||||||
|
if(!(aes = EVP_CIPHER_CTX_new()))
|
||||||
|
goto out;
|
||||||
|
EVP_CIPHER_CTX_set_padding(aes, 0);
|
||||||
|
if(!EVP_EncryptInit_ex(aes, EVP_aes_128_ecb(), NULL, key, NULL))
|
||||||
|
goto out;
|
||||||
|
if(!EVP_EncryptUpdate(aes, out, out_len, in, in_len))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
EVP_CIPHER_CTX_free(aes);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dh_generate_keypair(uint8_t *priv_out, uint8_t *pub_out, const uint8_t *gen, const size_t gen_len, const uint8_t *prime, const size_t keylen)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
DH *dh;
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L || \
|
||||||
|
(defined (LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x30500000)
|
||||||
|
const BIGNUM *pub_key = NULL;
|
||||||
|
const BIGNUM *priv_key = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(!(dh = DH_new()))
|
||||||
|
goto out;
|
||||||
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
|
||||||
|
(defined (LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x30500000)
|
||||||
|
dh->p = BN_bin2bn(prime, keylen, NULL);
|
||||||
|
dh->g = BN_bin2bn(gen, gen_len, NULL);
|
||||||
|
#else
|
||||||
|
if(!DH_set0_pqg(dh, BN_bin2bn(prime, keylen, NULL), NULL, BN_bin2bn(gen, gen_len, NULL)))
|
||||||
|
goto out;
|
||||||
|
#endif
|
||||||
|
if(!DH_generate_key(dh))
|
||||||
|
goto out;
|
||||||
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
|
||||||
|
(defined (LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x30500000)
|
||||||
|
if(BN_bn2bin(dh->priv_key, priv_out) == 0)
|
||||||
|
goto out;
|
||||||
|
if(BN_bn2bin(dh->pub_key, pub_out) == 0)
|
||||||
|
goto out;
|
||||||
|
#else
|
||||||
|
DH_get0_key(dh, &pub_key, &priv_key);
|
||||||
|
if(BN_bn2binpad(priv_key, priv_out, keylen) == -1)
|
||||||
|
goto out;
|
||||||
|
if(BN_bn2binpad(pub_key, pub_out, keylen) == -1)
|
||||||
|
goto out;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
DH_free(dh);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dh_compute_shared_key(uint8_t *shared_out, const uint8_t *priv, const uint8_t *pub, const uint8_t *prime, const size_t keylen)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
DH *dh;
|
||||||
|
|
||||||
|
if(!(dh = DH_new()))
|
||||||
|
goto out;
|
||||||
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
|
||||||
|
(defined LIBRESSL_VERSION_NUMBER && LIBRESSL_VERSION_NUMBER < 0x30500000)
|
||||||
|
dh->p = BN_bin2bn(prime, keylen, NULL);
|
||||||
|
dh->priv_key = BN_bin2bn(priv, keylen, NULL);
|
||||||
|
#else
|
||||||
|
if(!DH_set0_pqg(dh, BN_bin2bn(prime, keylen, NULL), NULL, BN_new()))
|
||||||
|
goto out;
|
||||||
|
if(!DH_set0_key(dh, NULL, BN_bin2bn(priv, keylen, NULL)))
|
||||||
|
goto out;
|
||||||
|
#endif
|
||||||
|
if(DH_compute_key(shared_out, BN_bin2bn(pub, keylen, NULL), dh) == -1)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
result = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
DH_free(dh);
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2001,2002 Constantin Kaplinsky. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cursor.c - code to support cursor shape updates (XCursor and
|
||||||
|
* RichCursor preudo-encodings).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rfbclient.h"
|
||||||
|
|
||||||
|
#define OPER_SAVE 0
|
||||||
|
#define OPER_RESTORE 1
|
||||||
|
|
||||||
|
#define MAX_CURSOR_SIZE 1024
|
||||||
|
|
||||||
|
#define RGB24_TO_PIXEL(bpp,r,g,b) \
|
||||||
|
((((uint##bpp##_t)(r) & 0xFF) * client->format.redMax + 127) / 255 \
|
||||||
|
<< client->format.redShift | \
|
||||||
|
(((uint##bpp##_t)(g) & 0xFF) * client->format.greenMax + 127) / 255 \
|
||||||
|
<< client->format.greenShift | \
|
||||||
|
(((uint##bpp##_t)(b) & 0xFF) * client->format.blueMax + 127) / 255 \
|
||||||
|
<< client->format.blueShift)
|
||||||
|
|
||||||
|
|
||||||
|
rfbBool HandleCursorShape(rfbClient* client,int xhot, int yhot, int width, int height, uint32_t enc)
|
||||||
|
{
|
||||||
|
int bytesPerPixel;
|
||||||
|
size_t bytesPerRow, bytesMaskData;
|
||||||
|
rfbXCursorColors rgb;
|
||||||
|
uint32_t colors[2];
|
||||||
|
char *buf;
|
||||||
|
uint8_t *ptr;
|
||||||
|
int x, y, b;
|
||||||
|
|
||||||
|
bytesPerPixel = client->format.bitsPerPixel / 8;
|
||||||
|
bytesPerRow = (width + 7) / 8;
|
||||||
|
bytesMaskData = bytesPerRow * height;
|
||||||
|
|
||||||
|
if (width * height == 0)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (width >= MAX_CURSOR_SIZE || height >= MAX_CURSOR_SIZE)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Allocate memory for pixel data and temporary mask data. */
|
||||||
|
if(client->rcSource)
|
||||||
|
free(client->rcSource);
|
||||||
|
|
||||||
|
client->rcSource = malloc((size_t)width * height * bytesPerPixel);
|
||||||
|
if (client->rcSource == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
buf = malloc(bytesMaskData);
|
||||||
|
if (buf == NULL) {
|
||||||
|
free(client->rcSource);
|
||||||
|
client->rcSource = NULL;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read and decode cursor pixel data, depending on the encoding type. */
|
||||||
|
|
||||||
|
if (enc == rfbEncodingXCursor) {
|
||||||
|
/* Read and convert background and foreground colors. */
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&rgb, sz_rfbXCursorColors)) {
|
||||||
|
free(client->rcSource);
|
||||||
|
client->rcSource = NULL;
|
||||||
|
free(buf);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue);
|
||||||
|
colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue);
|
||||||
|
|
||||||
|
/* Read 1bpp pixel data into a temporary buffer. */
|
||||||
|
if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
|
||||||
|
free(client->rcSource);
|
||||||
|
client->rcSource = NULL;
|
||||||
|
free(buf);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert 1bpp data to byte-wide color indices. */
|
||||||
|
ptr = client->rcSource;
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
for (x = 0; x < width / 8; x++) {
|
||||||
|
for (b = 7; b >= 0; b--) {
|
||||||
|
*ptr = buf[y * bytesPerRow + x] >> b & 1;
|
||||||
|
ptr += bytesPerPixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (b = 7; b > 7 - width % 8; b--) {
|
||||||
|
*ptr = buf[y * bytesPerRow + x] >> b & 1;
|
||||||
|
ptr += bytesPerPixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert indices into the actual pixel values. */
|
||||||
|
switch (bytesPerPixel) {
|
||||||
|
case 1:
|
||||||
|
for (x = 0; x < width * height; x++)
|
||||||
|
client->rcSource[x] = (uint8_t)colors[client->rcSource[x]];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
for (x = 0; x < width * height; x++)
|
||||||
|
((uint16_t *)client->rcSource)[x] = (uint16_t)colors[client->rcSource[x * 2]];
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
for (x = 0; x < width * height; x++)
|
||||||
|
((uint32_t *)client->rcSource)[x] = colors[client->rcSource[x * 4]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { /* enc == rfbEncodingRichCursor */
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)client->rcSource, width * height * bytesPerPixel)) {
|
||||||
|
free(client->rcSource);
|
||||||
|
client->rcSource = NULL;
|
||||||
|
free(buf);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read and decode mask data. */
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
|
||||||
|
free(client->rcSource);
|
||||||
|
client->rcSource = NULL;
|
||||||
|
free(buf);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->rcMask = malloc((size_t)width * height);
|
||||||
|
if (client->rcMask == NULL) {
|
||||||
|
free(client->rcSource);
|
||||||
|
client->rcSource = NULL;
|
||||||
|
free(buf);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = client->rcMask;
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
for (x = 0; x < width / 8; x++) {
|
||||||
|
for (b = 7; b >= 0; b--) {
|
||||||
|
*ptr++ = buf[y * bytesPerRow + x] >> b & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (b = 7; b > 7 - width % 8; b--) {
|
||||||
|
*ptr++ = buf[y * bytesPerRow + x] >> b & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->GotCursorShape != NULL) {
|
||||||
|
client->GotCursorShape(client, xhot, yhot, width, height, bytesPerPixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,308 @@
|
||||||
|
#include "data-control.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <aml.h>
|
||||||
|
#include <seat.h>
|
||||||
|
#include <vnc.h>
|
||||||
|
|
||||||
|
struct receive_context {
|
||||||
|
//struct data_control* data_control;
|
||||||
|
struct zwlr_data_control_offer_v1* offer;
|
||||||
|
int fd;
|
||||||
|
FILE* mem_fp;
|
||||||
|
size_t mem_size;
|
||||||
|
char* mem_data;
|
||||||
|
struct data_control* self;
|
||||||
|
};
|
||||||
|
|
||||||
|
static char* mime_type = "text/plain;charset=utf-8";
|
||||||
|
|
||||||
|
static bool isStringEqual(int lena, char* a, int lenb, char* b) {
|
||||||
|
if (lena != lenb) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare every character
|
||||||
|
for (size_t i = 0; i < lena; ++i) {
|
||||||
|
if (a[i] != b[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_receive(void* handler)
|
||||||
|
{
|
||||||
|
struct receive_context* ctx = aml_get_userdata(handler);
|
||||||
|
int fd = aml_get_fd(handler);
|
||||||
|
assert(ctx->fd == fd);
|
||||||
|
|
||||||
|
char buf[4096];
|
||||||
|
|
||||||
|
ssize_t ret = read(fd, &buf, sizeof(buf));
|
||||||
|
if (ret > 0) {
|
||||||
|
fwrite(&buf, 1, ret, ctx->mem_fp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(ctx->mem_fp);
|
||||||
|
ctx->mem_fp = NULL;
|
||||||
|
|
||||||
|
if (ctx->mem_size && ctx->self->vnc_write_clipboard) {
|
||||||
|
// If we receive clipboard data from the VNC server, we set the clipboard of the client.
|
||||||
|
// This "change" of clipboard data results into this "on_receive" event. That would leed
|
||||||
|
// to an endless loop...
|
||||||
|
// So we check if we send exactle that clipboard string to avoid an loop
|
||||||
|
if (!ctx->self->cb_data || !isStringEqual(ctx->self->cb_len, ctx->self->cb_data, strlen(ctx->mem_data), ctx->mem_data)) {
|
||||||
|
//printf("Received data FROM clipboard: %s\n", ctx->mem_data);
|
||||||
|
ctx->self->vnc_write_clipboard(ctx->mem_data, ctx->mem_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aml_stop(aml_get_default(), handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroy_receive_context(void* raw_ctx)
|
||||||
|
{
|
||||||
|
struct receive_context* ctx = raw_ctx;
|
||||||
|
int fd = ctx->fd;
|
||||||
|
|
||||||
|
if (ctx->mem_fp)
|
||||||
|
fclose(ctx->mem_fp);
|
||||||
|
free(ctx->mem_data);
|
||||||
|
zwlr_data_control_offer_v1_destroy(ctx->offer);
|
||||||
|
close(fd);
|
||||||
|
free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void receive_data(void* data,
|
||||||
|
struct zwlr_data_control_offer_v1* offer)
|
||||||
|
{
|
||||||
|
struct data_control* self = data;
|
||||||
|
int pipe_fd[2];
|
||||||
|
|
||||||
|
if (pipe(pipe_fd) == -1) {
|
||||||
|
printf("pipe() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct receive_context* ctx = calloc(1, sizeof(*ctx));
|
||||||
|
if (!ctx) {
|
||||||
|
printf("OOM");
|
||||||
|
close(pipe_fd[0]);
|
||||||
|
close(pipe_fd[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
zwlr_data_control_offer_v1_receive(self->offer, mime_type, pipe_fd[1]);
|
||||||
|
//wl_display_flush(self->wl_display);
|
||||||
|
close(pipe_fd[1]);
|
||||||
|
|
||||||
|
ctx->self = self;
|
||||||
|
ctx->fd = pipe_fd[0];
|
||||||
|
//ctx->data_control = self;
|
||||||
|
ctx->offer = self->offer;
|
||||||
|
ctx->mem_fp = open_memstream(&ctx->mem_data, &ctx->mem_size);
|
||||||
|
if (!ctx->mem_fp) {
|
||||||
|
close(ctx->fd);
|
||||||
|
free(ctx);
|
||||||
|
printf("open_memstream() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct aml_handler* handler = aml_handler_new(ctx->fd, on_receive,
|
||||||
|
ctx, destroy_receive_context);
|
||||||
|
if (!handler) {
|
||||||
|
close(ctx->fd);
|
||||||
|
free(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aml_start(aml_get_default(), handler);
|
||||||
|
aml_unref(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_control_offer(void* data,
|
||||||
|
struct zwlr_data_control_offer_v1* zwlr_data_control_offer_v1,
|
||||||
|
const char* mime_type)
|
||||||
|
{
|
||||||
|
struct data_control* self = data;
|
||||||
|
|
||||||
|
if (self->offer)
|
||||||
|
return;
|
||||||
|
if (strcmp(mime_type, mime_type) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->offer = zwlr_data_control_offer_v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zwlr_data_control_offer_v1_listener data_control_offer_listener = {
|
||||||
|
data_control_offer
|
||||||
|
};
|
||||||
|
|
||||||
|
static void data_control_device_offer(void* data,
|
||||||
|
struct zwlr_data_control_device_v1* zwlr_data_control_device_v1,
|
||||||
|
struct zwlr_data_control_offer_v1* id)
|
||||||
|
{
|
||||||
|
if (!id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
zwlr_data_control_offer_v1_add_listener(id, &data_control_offer_listener, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_control_device_selection(void* data,
|
||||||
|
struct zwlr_data_control_device_v1* zwlr_data_control_device_v1,
|
||||||
|
struct zwlr_data_control_offer_v1* id)
|
||||||
|
{
|
||||||
|
struct data_control* self = data;
|
||||||
|
if (id && self->offer == id) {
|
||||||
|
receive_data(data, id);
|
||||||
|
self->offer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_control_device_primary_selection(void* data,
|
||||||
|
struct zwlr_data_control_device_v1* zwlr_data_control_device_v1,
|
||||||
|
struct zwlr_data_control_offer_v1* id)
|
||||||
|
{
|
||||||
|
struct data_control* self = data;
|
||||||
|
if (id && self->offer == id) {
|
||||||
|
receive_data(data, id);
|
||||||
|
self->offer = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_control_device_finished(void* data,
|
||||||
|
struct zwlr_data_control_device_v1* zwlr_data_control_device_v1)
|
||||||
|
{
|
||||||
|
zwlr_data_control_device_v1_destroy(zwlr_data_control_device_v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct zwlr_data_control_device_v1_listener data_control_device_listener = {
|
||||||
|
.data_offer = data_control_device_offer,
|
||||||
|
.selection = data_control_device_selection,
|
||||||
|
.finished = data_control_device_finished,
|
||||||
|
.primary_selection = data_control_device_primary_selection
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
data_control_source_send(void* data,
|
||||||
|
struct zwlr_data_control_source_v1* zwlr_data_control_source_v1,
|
||||||
|
const char* mime_type,
|
||||||
|
int32_t fd)
|
||||||
|
{
|
||||||
|
struct data_control* self = data;
|
||||||
|
char* d = self->cb_data;
|
||||||
|
size_t len = self->cb_len;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
assert(d);
|
||||||
|
|
||||||
|
ret = write(fd, d, len);
|
||||||
|
|
||||||
|
if (ret < (int)len)
|
||||||
|
printf("write from clipboard incomplete");
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_control_source_cancelled(void* data,
|
||||||
|
struct zwlr_data_control_source_v1* zwlr_data_control_source_v1)
|
||||||
|
{
|
||||||
|
struct data_control* self = data;
|
||||||
|
|
||||||
|
if (self->selection == zwlr_data_control_source_v1) {
|
||||||
|
self->selection = NULL;
|
||||||
|
}
|
||||||
|
if (self->primary_selection == zwlr_data_control_source_v1) {
|
||||||
|
self->primary_selection = NULL;
|
||||||
|
}
|
||||||
|
zwlr_data_control_source_v1_destroy(zwlr_data_control_source_v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zwlr_data_control_source_v1_listener data_control_source_listener = {
|
||||||
|
.send = data_control_source_send,
|
||||||
|
.cancelled = data_control_source_cancelled
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct zwlr_data_control_source_v1* set_selection(struct data_control* self, bool primary) {
|
||||||
|
struct zwlr_data_control_source_v1* selection;
|
||||||
|
selection = zwlr_data_control_manager_v1_create_data_source(self->manager);
|
||||||
|
if (selection == NULL) {
|
||||||
|
printf("zwlr_data_control_manager_v1_create_data_source() failed");
|
||||||
|
free(self->cb_data);
|
||||||
|
self->cb_data = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
zwlr_data_control_source_v1_add_listener(selection, &data_control_source_listener, self);
|
||||||
|
zwlr_data_control_source_v1_offer(selection, mime_type);
|
||||||
|
|
||||||
|
if (primary)
|
||||||
|
zwlr_data_control_device_v1_set_primary_selection(self->device, selection);
|
||||||
|
else
|
||||||
|
zwlr_data_control_device_v1_set_selection(self->device, selection);
|
||||||
|
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void data_control_init(struct data_control* self, struct seat* seat, struct zwlr_data_control_manager_v1 *manager) {
|
||||||
|
self->manager = manager;
|
||||||
|
self->device = zwlr_data_control_manager_v1_get_data_device(self->manager, seat->wl_seat);
|
||||||
|
self->cb_data = NULL;
|
||||||
|
self->cb_len = 0;
|
||||||
|
|
||||||
|
zwlr_data_control_device_v1_add_listener(self->device, &data_control_device_listener, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void data_control_to_clipboard(struct data_control* self, const char* text, size_t len)
|
||||||
|
{
|
||||||
|
//printf("Writing text TO CLIPBOARD: %s\n", text);
|
||||||
|
if (!len) {
|
||||||
|
printf("%s called with 0 length", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (self->cb_data) {
|
||||||
|
free(self->cb_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
self->cb_data = malloc(len);
|
||||||
|
if (!self->cb_data) {
|
||||||
|
printf("OOM");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(self->cb_data, text, len);
|
||||||
|
self->cb_len = len;
|
||||||
|
// Set copy/paste buffer
|
||||||
|
self->selection = set_selection(self, false);
|
||||||
|
// Set highlight/middle_click buffer
|
||||||
|
self->primary_selection = set_selection(self, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void data_control_destroy(struct data_control* self)
|
||||||
|
{
|
||||||
|
if (self->selection) {
|
||||||
|
zwlr_data_control_source_v1_destroy(self->selection);
|
||||||
|
self->selection = NULL;
|
||||||
|
}
|
||||||
|
if (self->primary_selection) {
|
||||||
|
zwlr_data_control_source_v1_destroy(self->primary_selection);
|
||||||
|
self->primary_selection = NULL;
|
||||||
|
}
|
||||||
|
zwlr_data_control_device_v1_destroy(self->device);
|
||||||
|
free(self->cb_data);
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* corre.c - handle CoRRE encoding.
|
||||||
|
*
|
||||||
|
* This file shouldn't be compiled directly. It is included multiple times by
|
||||||
|
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||||
|
* each value of BPP, this file defines a function which handles a CoRRE
|
||||||
|
* encoded rectangle with BPP bits per pixel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define HandleCoRREBPP CONCAT2E(HandleCoRRE,BPP)
|
||||||
|
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
HandleCoRREBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||||
|
{
|
||||||
|
rfbRREHeader hdr;
|
||||||
|
int i;
|
||||||
|
CARDBPP pix;
|
||||||
|
uint8_t *ptr;
|
||||||
|
int x, y, w, h;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbRREHeader))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
hdr.nSubrects = rfbClientSwap32IfLE(hdr.nSubrects);
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
client->GotFillRect(client, rx, ry, rw, rh, pix);
|
||||||
|
|
||||||
|
if (hdr.nSubrects > RFB_BUFFER_SIZE / (4 + (BPP / 8)) || !ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8))))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
ptr = (uint8_t *)client->buffer;
|
||||||
|
|
||||||
|
for (i = 0; i < hdr.nSubrects; i++) {
|
||||||
|
pix = *(CARDBPP *)ptr;
|
||||||
|
ptr += BPP/8;
|
||||||
|
x = *ptr++;
|
||||||
|
y = *ptr++;
|
||||||
|
w = *ptr++;
|
||||||
|
h = *ptr++;
|
||||||
|
|
||||||
|
client->GotFillRect(client, rx+x, ry+y, w, h, pix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CARDBPP
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hextile.c - handle hextile encoding.
|
||||||
|
*
|
||||||
|
* This file shouldn't be compiled directly. It is included multiple times by
|
||||||
|
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||||
|
* each value of BPP, this file defines a function which handles a hextile
|
||||||
|
* encoded rectangle with BPP bits per pixel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define HandleHextileBPP CONCAT2E(HandleHextile,BPP)
|
||||||
|
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||||
|
{
|
||||||
|
CARDBPP bg = 0, fg;
|
||||||
|
int i;
|
||||||
|
uint8_t *ptr;
|
||||||
|
int x, y, w, h;
|
||||||
|
int sx, sy, sw, sh;
|
||||||
|
uint8_t subencoding;
|
||||||
|
uint8_t nSubrects;
|
||||||
|
|
||||||
|
for (y = ry; y < ry+rh; y += 16) {
|
||||||
|
for (x = rx; x < rx+rw; x += 16) {
|
||||||
|
w = h = 16;
|
||||||
|
if (rx+rw - x < 16)
|
||||||
|
w = rx+rw - x;
|
||||||
|
if (ry+rh - y < 16)
|
||||||
|
h = ry+rh - y;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&subencoding, 1))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (subencoding & rfbHextileRaw) {
|
||||||
|
if (!ReadFromRFBServer(client, client->buffer, w * h * (BPP / 8)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
client->GotBitmap(client, (uint8_t *)client->buffer, x, y, w, h);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subencoding & rfbHextileBackgroundSpecified)
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&bg, sizeof(bg)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
client->GotFillRect(client, x, y, w, h, bg);
|
||||||
|
|
||||||
|
if (subencoding & rfbHextileForegroundSpecified)
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&fg, sizeof(fg)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!(subencoding & rfbHextileAnySubrects)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&nSubrects, 1))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
ptr = (uint8_t*)client->buffer;
|
||||||
|
|
||||||
|
if (subencoding & rfbHextileSubrectsColoured) {
|
||||||
|
if (!ReadFromRFBServer(client, client->buffer, nSubrects * (2 + (BPP / 8))))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (i = 0; i < nSubrects; i++) {
|
||||||
|
#if BPP==8
|
||||||
|
GET_PIXEL8(fg, ptr);
|
||||||
|
#elif BPP==16
|
||||||
|
GET_PIXEL16(fg, ptr);
|
||||||
|
#elif BPP==32
|
||||||
|
GET_PIXEL32(fg, ptr);
|
||||||
|
#else
|
||||||
|
#error "Invalid BPP"
|
||||||
|
#endif
|
||||||
|
sx = rfbHextileExtractX(*ptr);
|
||||||
|
sy = rfbHextileExtractY(*ptr);
|
||||||
|
ptr++;
|
||||||
|
sw = rfbHextileExtractW(*ptr);
|
||||||
|
sh = rfbHextileExtractH(*ptr);
|
||||||
|
ptr++;
|
||||||
|
|
||||||
|
client->GotFillRect(client, x+sx, y+sy, sw, sh, fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!ReadFromRFBServer(client, client->buffer, nSubrects * 2))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (i = 0; i < nSubrects; i++) {
|
||||||
|
sx = rfbHextileExtractX(*ptr);
|
||||||
|
sy = rfbHextileExtractY(*ptr);
|
||||||
|
ptr++;
|
||||||
|
sw = rfbHextileExtractW(*ptr);
|
||||||
|
sh = rfbHextileExtractH(*ptr);
|
||||||
|
ptr++;
|
||||||
|
|
||||||
|
client->GotFillRect(client, x+sx, y+sy, sw, sh, fg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CARDBPP
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rre.c - handle RRE encoding.
|
||||||
|
*
|
||||||
|
* This file shouldn't be compiled directly. It is included multiple times by
|
||||||
|
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||||
|
* each value of BPP, this file defines a function which handles an RRE
|
||||||
|
* encoded rectangle with BPP bits per pixel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define HandleRREBPP CONCAT2E(HandleRRE,BPP)
|
||||||
|
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
HandleRREBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||||
|
{
|
||||||
|
rfbRREHeader hdr;
|
||||||
|
int i;
|
||||||
|
CARDBPP pix;
|
||||||
|
rfbRectangle subrect;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbRREHeader))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
hdr.nSubrects = rfbClientSwap32IfLE(hdr.nSubrects);
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
client->GotFillRect(client, rx, ry, rw, rh, pix);
|
||||||
|
|
||||||
|
for (i = 0; i < hdr.nSubrects; i++) {
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&subrect, sz_rfbRectangle))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
subrect.x = rfbClientSwap16IfLE(subrect.x);
|
||||||
|
subrect.y = rfbClientSwap16IfLE(subrect.y);
|
||||||
|
subrect.w = rfbClientSwap16IfLE(subrect.w);
|
||||||
|
subrect.h = rfbClientSwap16IfLE(subrect.h);
|
||||||
|
|
||||||
|
client->GotFillRect(client, rx+subrect.x, ry+subrect.y, subrect.w, subrect.h, pix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CARDBPP
|
|
@ -0,0 +1,670 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017, 2019 D. R. Commander. All Rights Reserved.
|
||||||
|
* Copyright (C) 2004-2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||||
|
* Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved.
|
||||||
|
* Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
||||||
|
|
||||||
|
#include "turbojpeg.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tight.c - handle ``tight'' encoding.
|
||||||
|
*
|
||||||
|
* This file shouldn't be compiled directly. It is included multiple
|
||||||
|
* times by rfbproto.c, each time with a different definition of the
|
||||||
|
* macro BPP. For each value of BPP, this file defines a function
|
||||||
|
* which handles a tight-encoded rectangle with BPP bits per pixel.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define TIGHT_MIN_TO_COMPRESS 12
|
||||||
|
|
||||||
|
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||||
|
#define filterPtrBPP CONCAT2E(filterPtr,BPP)
|
||||||
|
|
||||||
|
#define HandleTightBPP CONCAT2E(HandleTight,BPP)
|
||||||
|
#define InitFilterCopyBPP CONCAT2E(InitFilterCopy,BPP)
|
||||||
|
#define InitFilterPaletteBPP CONCAT2E(InitFilterPalette,BPP)
|
||||||
|
#define InitFilterGradientBPP CONCAT2E(InitFilterGradient,BPP)
|
||||||
|
#define FilterCopyBPP CONCAT2E(FilterCopy,BPP)
|
||||||
|
#define FilterPaletteBPP CONCAT2E(FilterPalette,BPP)
|
||||||
|
#define FilterGradientBPP CONCAT2E(FilterGradient,BPP)
|
||||||
|
|
||||||
|
#if BPP != 8
|
||||||
|
#define DecompressJpegRectBPP CONCAT2E(DecompressJpegRect,BPP)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RGB_TO_PIXEL
|
||||||
|
|
||||||
|
#define RGB_TO_PIXEL(bpp,r,g,b) \
|
||||||
|
(((CARD##bpp)(r) & client->format.redMax) << client->format.redShift | \
|
||||||
|
((CARD##bpp)(g) & client->format.greenMax) << client->format.greenShift | \
|
||||||
|
((CARD##bpp)(b) & client->format.blueMax) << client->format.blueShift)
|
||||||
|
|
||||||
|
#define RGB24_TO_PIXEL(bpp,r,g,b) \
|
||||||
|
((((CARD##bpp)(r) & 0xFF) * client->format.redMax + 127) / 255 \
|
||||||
|
<< client->format.redShift | \
|
||||||
|
(((CARD##bpp)(g) & 0xFF) * client->format.greenMax + 127) / 255 \
|
||||||
|
<< client->format.greenShift | \
|
||||||
|
(((CARD##bpp)(b) & 0xFF) * client->format.blueMax + 127) / 255 \
|
||||||
|
<< client->format.blueShift)
|
||||||
|
|
||||||
|
#define RGB24_TO_PIXEL32(r,g,b) \
|
||||||
|
(((uint32_t)(r) & 0xFF) << client->format.redShift | \
|
||||||
|
((uint32_t)(g) & 0xFF) << client->format.greenShift | \
|
||||||
|
((uint32_t)(b) & 0xFF) << client->format.blueShift)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Type declarations */
|
||||||
|
|
||||||
|
typedef void (*filterPtrBPP)(rfbClient* client, int, int, int);
|
||||||
|
|
||||||
|
/* Prototypes */
|
||||||
|
|
||||||
|
static int InitFilterCopyBPP (rfbClient* client, int rw, int rh);
|
||||||
|
static int InitFilterPaletteBPP (rfbClient* client, int rw, int rh);
|
||||||
|
static int InitFilterGradientBPP (rfbClient* client, int rw, int rh);
|
||||||
|
static void FilterCopyBPP (rfbClient* client, int srcx, int srcy, int numRows);
|
||||||
|
static void FilterPaletteBPP (rfbClient* client, int srcx, int srcy, int numRows);
|
||||||
|
static void FilterGradientBPP (rfbClient* client, int srcx, int srcy, int numRows);
|
||||||
|
|
||||||
|
#if BPP != 8
|
||||||
|
static rfbBool DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Definitions */
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||||
|
{
|
||||||
|
CARDBPP fill_colour;
|
||||||
|
uint8_t comp_ctl;
|
||||||
|
uint8_t filter_id;
|
||||||
|
filterPtrBPP filterFn;
|
||||||
|
z_streamp zs;
|
||||||
|
int err, stream_id, compressedLen, bitsPixel;
|
||||||
|
int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes;
|
||||||
|
rfbBool readUncompressed = FALSE;
|
||||||
|
|
||||||
|
if (client->frameBuffer == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (rx + rw > client->width || ry + rh > client->height) {
|
||||||
|
rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", rx, ry, rw, rh);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&comp_ctl, 1))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Flush zlib streams if we are told by the server to do so. */
|
||||||
|
for (stream_id = 0; stream_id < 4; stream_id++) {
|
||||||
|
if ((comp_ctl & 1) && client->zlibStreamActive[stream_id]) {
|
||||||
|
if (inflateEnd (&client->zlibStream[stream_id]) != Z_OK &&
|
||||||
|
client->zlibStream[stream_id].msg != NULL)
|
||||||
|
rfbClientLog("inflateEnd: %s\n", client->zlibStream[stream_id].msg);
|
||||||
|
client->zlibStreamActive[stream_id] = FALSE;
|
||||||
|
}
|
||||||
|
comp_ctl >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((comp_ctl & rfbTightNoZlib) == rfbTightNoZlib) {
|
||||||
|
comp_ctl &= ~(rfbTightNoZlib);
|
||||||
|
readUncompressed = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle solid rectangles. */
|
||||||
|
if (comp_ctl == rfbTightFill) {
|
||||||
|
#if BPP == 32
|
||||||
|
if (client->format.depth == 24 && client->format.redMax == 0xFF &&
|
||||||
|
client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) {
|
||||||
|
if (!ReadFromRFBServer(client, client->buffer, 3))
|
||||||
|
return FALSE;
|
||||||
|
fill_colour = RGB24_TO_PIXEL32(client->buffer[0], client->buffer[1], client->buffer[2]);
|
||||||
|
} else {
|
||||||
|
if (!ReadFromRFBServer(client, (char*)&fill_colour, sizeof(fill_colour)))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (!ReadFromRFBServer(client, (char*)&fill_colour, sizeof(fill_colour)))
|
||||||
|
return FALSE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
client->GotFillRect(client, rx, ry, rw, rh, fill_colour);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if BPP == 8
|
||||||
|
if (comp_ctl == rfbTightJpeg) {
|
||||||
|
rfbClientLog("Tight encoding: JPEG is not supported in 8 bpp mode.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (comp_ctl == rfbTightJpeg) {
|
||||||
|
return DecompressJpegRectBPP(client, rx, ry, rw, rh);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Quit on unsupported subencoding value. */
|
||||||
|
if (comp_ctl > rfbTightMaxSubencoding) {
|
||||||
|
rfbClientLog("Tight encoding: bad subencoding value received.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here primary compression mode handling begins.
|
||||||
|
* Data was processed with optional filter + zlib compression.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* First, we should identify a filter to use. */
|
||||||
|
if ((comp_ctl & rfbTightExplicitFilter) != 0) {
|
||||||
|
if (!ReadFromRFBServer(client, (char*)&filter_id, 1))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
switch (filter_id) {
|
||||||
|
case rfbTightFilterCopy:
|
||||||
|
filterFn = FilterCopyBPP;
|
||||||
|
bitsPixel = InitFilterCopyBPP(client, rw, rh);
|
||||||
|
break;
|
||||||
|
case rfbTightFilterPalette:
|
||||||
|
filterFn = FilterPaletteBPP;
|
||||||
|
bitsPixel = InitFilterPaletteBPP(client, rw, rh);
|
||||||
|
break;
|
||||||
|
case rfbTightFilterGradient:
|
||||||
|
filterFn = FilterGradientBPP;
|
||||||
|
bitsPixel = InitFilterGradientBPP(client, rw, rh);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rfbClientLog("Tight encoding: unknown filter code received.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filterFn = FilterCopyBPP;
|
||||||
|
bitsPixel = InitFilterCopyBPP(client, rw, rh);
|
||||||
|
}
|
||||||
|
if (bitsPixel == 0) {
|
||||||
|
rfbClientLog("Tight encoding: error receiving palette.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine if the data should be decompressed or just copied. */
|
||||||
|
rowSize = (rw * bitsPixel + 7) / 8;
|
||||||
|
if (rh * rowSize < TIGHT_MIN_TO_COMPRESS) {
|
||||||
|
if (!ReadFromRFBServer(client, (char*)client->buffer, rh * rowSize))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
filterFn(client, rx, ry, rh);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the length (1..3 bytes) of compressed data following. */
|
||||||
|
compressedLen = (int)ReadCompactLen(client);
|
||||||
|
if (compressedLen <= 0) {
|
||||||
|
rfbClientLog("Incorrect data received from the server.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (readUncompressed) {
|
||||||
|
if (compressedLen > RFB_BUFFER_SIZE) {
|
||||||
|
rfbClientErr("Received uncompressed byte count exceeds our buffer size.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char*)client->buffer, compressedLen))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
filterFn(client, rx, ry, rh);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now let's initialize compression stream if needed. */
|
||||||
|
stream_id = comp_ctl & 0x03;
|
||||||
|
zs = &client->zlibStream[stream_id];
|
||||||
|
if (!client->zlibStreamActive[stream_id]) {
|
||||||
|
zs->zalloc = Z_NULL;
|
||||||
|
zs->zfree = Z_NULL;
|
||||||
|
zs->opaque = Z_NULL;
|
||||||
|
err = inflateInit(zs);
|
||||||
|
if (err != Z_OK) {
|
||||||
|
if (zs->msg != NULL)
|
||||||
|
rfbClientLog("InflateInit error: %s.\n", zs->msg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
client->zlibStreamActive[stream_id] = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read, decode and draw actual pixel data in a loop. */
|
||||||
|
|
||||||
|
bufferSize = RFB_BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC;
|
||||||
|
if (rowSize > bufferSize) {
|
||||||
|
/* Should be impossible when RFB_BUFFER_SIZE >= 16384 */
|
||||||
|
rfbClientLog("Internal error: incorrect buffer size.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rowsProcessed = 0;
|
||||||
|
extraBytes = 0;
|
||||||
|
|
||||||
|
while (compressedLen > 0) {
|
||||||
|
if (compressedLen > ZLIB_BUFFER_SIZE)
|
||||||
|
portionLen = ZLIB_BUFFER_SIZE;
|
||||||
|
else
|
||||||
|
portionLen = compressedLen;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char*)client->zlib_buffer, portionLen))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
compressedLen -= portionLen;
|
||||||
|
|
||||||
|
zs->next_in = (Bytef *)client->zlib_buffer;
|
||||||
|
zs->avail_in = portionLen;
|
||||||
|
|
||||||
|
do {
|
||||||
|
zs->next_out = (Bytef *)&client->buffer[extraBytes];
|
||||||
|
zs->avail_out = bufferSize - extraBytes;
|
||||||
|
|
||||||
|
err = inflate(zs, Z_SYNC_FLUSH);
|
||||||
|
if (err == Z_BUF_ERROR) /* Input exhausted -- no problem. */
|
||||||
|
break;
|
||||||
|
if (err != Z_OK && err != Z_STREAM_END) {
|
||||||
|
if (zs->msg != NULL) {
|
||||||
|
rfbClientLog("Inflate error: %s.\n", zs->msg);
|
||||||
|
} else {
|
||||||
|
rfbClientLog("Inflate error: %d.\n", err);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
numRows = (bufferSize - zs->avail_out) / rowSize;
|
||||||
|
|
||||||
|
filterFn(client, rx, ry+rowsProcessed, numRows);
|
||||||
|
|
||||||
|
extraBytes = bufferSize - zs->avail_out - numRows * rowSize;
|
||||||
|
if (extraBytes > 0)
|
||||||
|
memcpy(client->buffer, &client->buffer[numRows * rowSize], extraBytes);
|
||||||
|
|
||||||
|
rowsProcessed += numRows;
|
||||||
|
}
|
||||||
|
while (zs->avail_out == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rowsProcessed != rh) {
|
||||||
|
rfbClientLog("Incorrect number of scan lines after decompression.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Filter stuff.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
InitFilterCopyBPP (rfbClient* client, int rw, int rh)
|
||||||
|
{
|
||||||
|
client->rectWidth = rw;
|
||||||
|
|
||||||
|
#if BPP == 32
|
||||||
|
if (client->format.depth == 24 && client->format.redMax == 0xFF &&
|
||||||
|
client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) {
|
||||||
|
client->cutZeros = TRUE;
|
||||||
|
return 24;
|
||||||
|
} else {
|
||||||
|
client->cutZeros = FALSE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return BPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
FilterCopyBPP (rfbClient* client, int srcx, int srcy, int numRows)
|
||||||
|
{
|
||||||
|
CARDBPP *dst =
|
||||||
|
(CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
|
||||||
|
int y;
|
||||||
|
|
||||||
|
#if BPP == 32
|
||||||
|
int x;
|
||||||
|
|
||||||
|
if (client->cutZeros) {
|
||||||
|
for (y = 0; y < numRows; y++) {
|
||||||
|
for (x = 0; x < client->rectWidth; x++) {
|
||||||
|
dst[y*client->width+x] =
|
||||||
|
RGB24_TO_PIXEL32(client->buffer[(y*client->rectWidth+x)*3],
|
||||||
|
client->buffer[(y*client->rectWidth+x)*3+1],
|
||||||
|
client->buffer[(y*client->rectWidth+x)*3+2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (y = 0; y < numRows; y++)
|
||||||
|
memcpy (&dst[y*client->width],
|
||||||
|
&client->buffer[y * client->rectWidth * (BPP / 8)],
|
||||||
|
client->rectWidth * (BPP / 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
InitFilterGradientBPP (rfbClient* client, int rw, int rh)
|
||||||
|
{
|
||||||
|
int bits;
|
||||||
|
|
||||||
|
bits = InitFilterCopyBPP(client, rw, rh);
|
||||||
|
if (client->cutZeros)
|
||||||
|
memset(client->tightPrevRow, 0, rw * 3);
|
||||||
|
else
|
||||||
|
memset(client->tightPrevRow, 0, rw * 3 * sizeof(uint16_t));
|
||||||
|
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if BPP == 32
|
||||||
|
|
||||||
|
static void
|
||||||
|
FilterGradient24 (rfbClient* client, int srcx, int srcy, int numRows)
|
||||||
|
{
|
||||||
|
CARDBPP *dst =
|
||||||
|
(CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
|
||||||
|
int x, y, c;
|
||||||
|
uint8_t thisRow[2048*3];
|
||||||
|
uint8_t pix[3];
|
||||||
|
int est[3];
|
||||||
|
|
||||||
|
for (y = 0; y < numRows; y++) {
|
||||||
|
|
||||||
|
/* First pixel in a row */
|
||||||
|
for (c = 0; c < 3; c++) {
|
||||||
|
pix[c] = client->tightPrevRow[c] + client->buffer[y*client->rectWidth*3+c];
|
||||||
|
thisRow[c] = pix[c];
|
||||||
|
}
|
||||||
|
dst[y*client->width] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
|
||||||
|
|
||||||
|
/* Remaining pixels of a row */
|
||||||
|
for (x = 1; x < client->rectWidth; x++) {
|
||||||
|
for (c = 0; c < 3; c++) {
|
||||||
|
est[c] = (int)client->tightPrevRow[x*3+c] + (int)pix[c] -
|
||||||
|
(int)client->tightPrevRow[(x-1)*3+c];
|
||||||
|
if (est[c] > 0xFF) {
|
||||||
|
est[c] = 0xFF;
|
||||||
|
} else if (est[c] < 0x00) {
|
||||||
|
est[c] = 0x00;
|
||||||
|
}
|
||||||
|
pix[c] = (uint8_t)est[c] + client->buffer[(y*client->rectWidth+x)*3+c];
|
||||||
|
thisRow[x*3+c] = pix[c];
|
||||||
|
}
|
||||||
|
dst[y*client->width+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(client->tightPrevRow, thisRow, client->rectWidth * 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
FilterGradientBPP (rfbClient* client, int srcx, int srcy, int numRows)
|
||||||
|
{
|
||||||
|
CARDBPP *dst =
|
||||||
|
(CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
|
||||||
|
int x, y, c;
|
||||||
|
CARDBPP *src = (CARDBPP *)client->buffer;
|
||||||
|
uint16_t *thatRow = (uint16_t *)client->tightPrevRow;
|
||||||
|
uint16_t thisRow[2048*3];
|
||||||
|
uint16_t pix[3];
|
||||||
|
uint16_t max[3];
|
||||||
|
int shift[3];
|
||||||
|
int est[3];
|
||||||
|
|
||||||
|
#if BPP == 32
|
||||||
|
if (client->cutZeros) {
|
||||||
|
FilterGradient24(client, srcx, srcy, numRows);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
max[0] = client->format.redMax;
|
||||||
|
max[1] = client->format.greenMax;
|
||||||
|
max[2] = client->format.blueMax;
|
||||||
|
|
||||||
|
shift[0] = client->format.redShift;
|
||||||
|
shift[1] = client->format.greenShift;
|
||||||
|
shift[2] = client->format.blueShift;
|
||||||
|
|
||||||
|
for (y = 0; y < numRows; y++) {
|
||||||
|
|
||||||
|
/* First pixel in a row */
|
||||||
|
for (c = 0; c < 3; c++) {
|
||||||
|
pix[c] = (uint16_t)(((src[y*client->rectWidth] >> shift[c]) + thatRow[c]) & max[c]);
|
||||||
|
thisRow[c] = pix[c];
|
||||||
|
}
|
||||||
|
dst[y*client->width] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
|
||||||
|
|
||||||
|
/* Remaining pixels of a row */
|
||||||
|
for (x = 1; x < client->rectWidth; x++) {
|
||||||
|
for (c = 0; c < 3; c++) {
|
||||||
|
est[c] = (int)thatRow[x*3+c] + (int)pix[c] - (int)thatRow[(x-1)*3+c];
|
||||||
|
if (est[c] > (int)max[c]) {
|
||||||
|
est[c] = (int)max[c];
|
||||||
|
} else if (est[c] < 0) {
|
||||||
|
est[c] = 0;
|
||||||
|
}
|
||||||
|
pix[c] = (uint16_t)(((src[y*client->rectWidth+x] >> shift[c]) + est[c]) & max[c]);
|
||||||
|
thisRow[x*3+c] = pix[c];
|
||||||
|
}
|
||||||
|
dst[y*client->width+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
|
||||||
|
}
|
||||||
|
memcpy(thatRow, thisRow, client->rectWidth * 3 * sizeof(uint16_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
InitFilterPaletteBPP (rfbClient* client, int rw, int rh)
|
||||||
|
{
|
||||||
|
uint8_t numColors;
|
||||||
|
#if BPP == 32
|
||||||
|
int i;
|
||||||
|
CARDBPP *palette = (CARDBPP *)client->tightPalette;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
client->rectWidth = rw;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char*)&numColors, 1))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
client->rectColors = (int)numColors;
|
||||||
|
if (++client->rectColors < 2)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#if BPP == 32
|
||||||
|
if (client->format.depth == 24 && client->format.redMax == 0xFF &&
|
||||||
|
client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) {
|
||||||
|
if (!ReadFromRFBServer(client, (char*)&client->tightPalette, client->rectColors * 3))
|
||||||
|
return 0;
|
||||||
|
for (i = client->rectColors - 1; i >= 0; i--) {
|
||||||
|
palette[i] = RGB24_TO_PIXEL32(client->tightPalette[i*3],
|
||||||
|
client->tightPalette[i*3+1],
|
||||||
|
client->tightPalette[i*3+2]);
|
||||||
|
}
|
||||||
|
return (client->rectColors == 2) ? 1 : 8;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char*)&client->tightPalette, client->rectColors * (BPP / 8)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (client->rectColors == 2) ? 1 : 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
FilterPaletteBPP (rfbClient* client, int srcx, int srcy, int numRows)
|
||||||
|
{
|
||||||
|
int x, y, b, w;
|
||||||
|
CARDBPP *dst =
|
||||||
|
(CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
|
||||||
|
uint8_t *src = (uint8_t *)client->buffer;
|
||||||
|
CARDBPP *palette = (CARDBPP *)client->tightPalette;
|
||||||
|
|
||||||
|
if (client->rectColors == 2) {
|
||||||
|
w = (client->rectWidth + 7) / 8;
|
||||||
|
for (y = 0; y < numRows; y++) {
|
||||||
|
for (x = 0; x < client->rectWidth / 8; x++) {
|
||||||
|
for (b = 7; b >= 0; b--)
|
||||||
|
dst[y*client->width+x*8+7-b] = palette[src[y*w+x] >> b & 1];
|
||||||
|
}
|
||||||
|
for (b = 7; b >= 8 - client->rectWidth % 8; b--) {
|
||||||
|
dst[y*client->width+x*8+7-b] = palette[src[y*w+x] >> b & 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (y = 0; y < numRows; y++)
|
||||||
|
for (x = 0; x < client->rectWidth; x++)
|
||||||
|
dst[y*client->width+x] = palette[(int)src[y*client->rectWidth+x]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if BPP != 8
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* JPEG decompression.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h)
|
||||||
|
{
|
||||||
|
int compressedLen;
|
||||||
|
uint8_t *compressedData, *dst;
|
||||||
|
int pixelSize, pitch, flags = 0;
|
||||||
|
|
||||||
|
compressedLen = (int)ReadCompactLen(client);
|
||||||
|
if (compressedLen <= 0) {
|
||||||
|
rfbClientLog("Incorrect data received from the server.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
compressedData = malloc(compressedLen);
|
||||||
|
if (compressedData == NULL) {
|
||||||
|
rfbClientLog("Memory allocation error.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char*)compressedData, compressedLen)) {
|
||||||
|
free(compressedData);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(client->GotJpeg != NULL)
|
||||||
|
return client->GotJpeg(client, compressedData, compressedLen, x, y, w, h);
|
||||||
|
|
||||||
|
if (!client->tjhnd) {
|
||||||
|
if ((client->tjhnd = tjInitDecompress()) == NULL) {
|
||||||
|
rfbClientLog("TurboJPEG error: %s\n", tjGetErrorStr());
|
||||||
|
free(compressedData);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if BPP == 16
|
||||||
|
flags = 0;
|
||||||
|
pixelSize = 3;
|
||||||
|
pitch = w * pixelSize;
|
||||||
|
dst = (uint8_t *)client->buffer;
|
||||||
|
#else
|
||||||
|
if (client->format.bigEndian) flags |= TJ_ALPHAFIRST;
|
||||||
|
if (client->format.redShift == 16 && client->format.blueShift == 0)
|
||||||
|
flags |= TJ_BGR;
|
||||||
|
if (client->format.bigEndian) flags ^= TJ_BGR;
|
||||||
|
pixelSize = BPP / 8;
|
||||||
|
pitch = client->width * pixelSize;
|
||||||
|
dst = &client->frameBuffer[y * pitch + x * pixelSize];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (tjDecompress(client->tjhnd, compressedData, (unsigned long)compressedLen,
|
||||||
|
dst, w, pitch, h, pixelSize, flags)==-1) {
|
||||||
|
rfbClientLog("TurboJPEG error: %s\n", tjGetErrorStr());
|
||||||
|
free(compressedData);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(compressedData);
|
||||||
|
|
||||||
|
#if BPP == 16
|
||||||
|
pixelSize = BPP / 8;
|
||||||
|
pitch = client->width * pixelSize;
|
||||||
|
dst = &client->frameBuffer[y * pitch + x * pixelSize];
|
||||||
|
{
|
||||||
|
CARDBPP *dst16=(CARDBPP *)dst, *dst2;
|
||||||
|
char *src = client->buffer;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (j = 0; j < h; j++) {
|
||||||
|
for (i = 0, dst2 = dst16; i < w; i++, dst2++, src += 3) {
|
||||||
|
*dst2 = RGB24_TO_PIXEL(BPP, src[0], src[1], src[2]);
|
||||||
|
}
|
||||||
|
dst16 += client->width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static long
|
||||||
|
ReadCompactLen (rfbClient* client)
|
||||||
|
{
|
||||||
|
long len;
|
||||||
|
uint8_t b;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&b, 1))
|
||||||
|
return -1;
|
||||||
|
len = (int)b & 0x7F;
|
||||||
|
if (b & 0x80) {
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&b, 1))
|
||||||
|
return -1;
|
||||||
|
len |= ((int)b & 0x7F) << 7;
|
||||||
|
if (b & 0x80) {
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&b, 1))
|
||||||
|
return -1;
|
||||||
|
len |= ((int)b & 0xFF) << 14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef CARDBPP
|
||||||
|
|
||||||
|
/* LIBVNCSERVER_HAVE_LIBZ and LIBVNCSERVER_HAVE_LIBJPEG */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,299 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Wiki Wang <wikiwang@live.com>. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* trle.c - handle trle encoding.
|
||||||
|
*
|
||||||
|
* This file shouldn't be compiled directly. It is included multiple times by
|
||||||
|
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||||
|
* each value of BPP, this file defines a function which handles a trle
|
||||||
|
* encoded rectangle with BPP bits per pixel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REALBPP
|
||||||
|
#define REALBPP BPP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(UNCOMP) || UNCOMP == 0
|
||||||
|
#define HandleTRLE CONCAT2E(HandleTRLE, REALBPP)
|
||||||
|
#elif UNCOMP > 0
|
||||||
|
#define HandleTRLE CONCAT3E(HandleTRLE, REALBPP, Down)
|
||||||
|
#else
|
||||||
|
#define HandleTRLE CONCAT3E(HandleTRLE, REALBPP, Up)
|
||||||
|
#endif
|
||||||
|
#define CARDBPP CONCAT3E(uint, BPP, _t)
|
||||||
|
#define CARDREALBPP CONCAT3E(uint, REALBPP, _t)
|
||||||
|
|
||||||
|
#if REALBPP != BPP && defined(UNCOMP) && UNCOMP != 0
|
||||||
|
#if UNCOMP > 0
|
||||||
|
#define UncompressCPixel(pointer) ((*(CARDBPP *)pointer) >> UNCOMP)
|
||||||
|
#else
|
||||||
|
#define UncompressCPixel(pointer) ((*(CARDBPP *)pointer) << (-(UNCOMP)))
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define UncompressCPixel(pointer) (*(CARDBPP *)pointer)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static rfbBool HandleTRLE(rfbClient *client, int rx, int ry, int rw, int rh) {
|
||||||
|
int x, y, w, h;
|
||||||
|
uint8_t type, last_type = 0;
|
||||||
|
int min_buffer_size = 16 * 16 * (REALBPP / 8) * 2;
|
||||||
|
uint8_t *buffer;
|
||||||
|
CARDBPP palette[128];
|
||||||
|
int bpp = 0, mask = 0, divider = 0;
|
||||||
|
CARDBPP color = 0;
|
||||||
|
|
||||||
|
/* First make sure we have a large enough raw buffer to hold the
|
||||||
|
* decompressed data. In practice, with a fixed REALBPP, fixed frame
|
||||||
|
* buffer size and the first update containing the entire frame
|
||||||
|
* buffer, this buffer allocation should only happen once, on the
|
||||||
|
* first update.
|
||||||
|
*/
|
||||||
|
if (client->raw_buffer_size < min_buffer_size) {
|
||||||
|
|
||||||
|
if (client->raw_buffer != NULL) {
|
||||||
|
|
||||||
|
free(client->raw_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
client->raw_buffer_size = min_buffer_size;
|
||||||
|
client->raw_buffer = (char *)malloc(client->raw_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("Update %d %d %d %d\n", rx, ry, rw, rh);
|
||||||
|
|
||||||
|
for (y = ry; y < ry + rh; y += 16) {
|
||||||
|
for (x = rx; x < rx + rw; x += 16) {
|
||||||
|
w = h = 16;
|
||||||
|
if (rx + rw - x < 16)
|
||||||
|
w = rx + rw - x;
|
||||||
|
if (ry + rh - y < 16)
|
||||||
|
h = ry + rh - y;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)(&type), 1))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
buffer = (uint8_t*)(client->raw_buffer);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 0: {
|
||||||
|
if (!ReadFromRFBServer(client, (char *)buffer, w * h * REALBPP / 8))
|
||||||
|
return FALSE;
|
||||||
|
#if REALBPP != BPP
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (j = y * client->width; j < (y + h) * client->width;
|
||||||
|
j += client->width)
|
||||||
|
for (i = x; i < x + w; i++, buffer += REALBPP / 8)
|
||||||
|
((CARDBPP *)client->frameBuffer)[j + i] = UncompressCPixel(buffer);
|
||||||
|
#else
|
||||||
|
client->GotBitmap(client, buffer, x, y, w, h);
|
||||||
|
#endif
|
||||||
|
type = last_type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
if (!ReadFromRFBServer(client, (char *)buffer, REALBPP / 8))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
color = UncompressCPixel(buffer);
|
||||||
|
|
||||||
|
client->GotFillRect(client, x, y, w, h, color);
|
||||||
|
|
||||||
|
last_type = type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case_127:
|
||||||
|
case 127:
|
||||||
|
switch (last_type) {
|
||||||
|
case 0:
|
||||||
|
return FALSE;
|
||||||
|
case 1:
|
||||||
|
client->GotFillRect(client, x, y, w, h, color);
|
||||||
|
type = last_type;
|
||||||
|
break;
|
||||||
|
case 128:
|
||||||
|
return FALSE;
|
||||||
|
default:
|
||||||
|
if (last_type >= 130) {
|
||||||
|
last_type = last_type & 0x7f;
|
||||||
|
|
||||||
|
bpp = (last_type > 4 ? (last_type > 16 ? 8 : 4)
|
||||||
|
: (last_type > 2 ? 2 : 1)),
|
||||||
|
mask = (1 << bpp) - 1, divider = (8 / bpp);
|
||||||
|
}
|
||||||
|
if (last_type <= 16) {
|
||||||
|
int i, j, shift;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char*)buffer,
|
||||||
|
(w + divider - 1) / divider * h))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* read palettized pixels */
|
||||||
|
for (j = y * client->width; j < (y + h) * client->width;
|
||||||
|
j += client->width) {
|
||||||
|
for (i = x, shift = 8 - bpp; i < x + w; i++) {
|
||||||
|
((CARDBPP *)client->frameBuffer)[j + i] =
|
||||||
|
palette[((*buffer) >> shift) & mask];
|
||||||
|
shift -= bpp;
|
||||||
|
if (shift < 0) {
|
||||||
|
shift = 8 - bpp;
|
||||||
|
buffer++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shift < 8 - bpp)
|
||||||
|
buffer++;
|
||||||
|
|
||||||
|
type = last_type;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 128: {
|
||||||
|
int i = 0, j = 0;
|
||||||
|
while (j < h) {
|
||||||
|
int color, length, buffer_pos = 0;
|
||||||
|
/* read color */
|
||||||
|
if (!ReadFromRFBServer(client, (char*)buffer, REALBPP / 8 + 1))
|
||||||
|
return FALSE;
|
||||||
|
color = UncompressCPixel(buffer);
|
||||||
|
buffer += REALBPP / 8;
|
||||||
|
buffer_pos += REALBPP / 8;
|
||||||
|
/* read run length */
|
||||||
|
length = 1;
|
||||||
|
while (*buffer == 0xff && buffer_pos < client->raw_buffer_size-1) {
|
||||||
|
if (!ReadFromRFBServer(client, (char*)buffer + 1, 1))
|
||||||
|
return FALSE;
|
||||||
|
length += *buffer;
|
||||||
|
buffer++;
|
||||||
|
buffer_pos++;
|
||||||
|
}
|
||||||
|
length += *buffer;
|
||||||
|
buffer++;
|
||||||
|
while (j < h && length > 0) {
|
||||||
|
((CARDBPP *)client->frameBuffer)[(y + j) * client->width + x + i] =
|
||||||
|
color;
|
||||||
|
length--;
|
||||||
|
i++;
|
||||||
|
if (i >= w) {
|
||||||
|
i = 0;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (length > 0)
|
||||||
|
rfbClientLog("Warning: possible TRLE corruption\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
type = last_type;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case_129:
|
||||||
|
case 129: {
|
||||||
|
int i, j;
|
||||||
|
/* read palettized pixels */
|
||||||
|
i = j = 0;
|
||||||
|
while (j < h) {
|
||||||
|
int color, length, buffer_pos = 0;
|
||||||
|
/* read color */
|
||||||
|
if (!ReadFromRFBServer(client, (char *)buffer, 1))
|
||||||
|
return FALSE;
|
||||||
|
color = palette[(*buffer) & 0x7f];
|
||||||
|
length = 1;
|
||||||
|
if (*buffer & 0x80) {
|
||||||
|
if (!ReadFromRFBServer(client, (char *)buffer + 1, 1))
|
||||||
|
return FALSE;
|
||||||
|
buffer++;
|
||||||
|
buffer_pos++;
|
||||||
|
/* read run length */
|
||||||
|
while (*buffer == 0xff && buffer_pos < client->raw_buffer_size-1) {
|
||||||
|
if (!ReadFromRFBServer(client, (char *)buffer + 1, 1))
|
||||||
|
return FALSE;
|
||||||
|
length += *buffer;
|
||||||
|
buffer++;
|
||||||
|
buffer_pos++;
|
||||||
|
}
|
||||||
|
length += *buffer;
|
||||||
|
}
|
||||||
|
buffer++;
|
||||||
|
while (j < h && length > 0) {
|
||||||
|
((CARDBPP *)client->frameBuffer)[(y + j) * client->width + x + i] =
|
||||||
|
color;
|
||||||
|
length--;
|
||||||
|
i++;
|
||||||
|
if (i >= w) {
|
||||||
|
i = 0;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (length > 0)
|
||||||
|
rfbClientLog("Warning: possible TRLE corruption\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == 129) {
|
||||||
|
type = last_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if (type <= 16) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
bpp = (type > 4 ? 4 : (type > 2 ? 2 : 1)),
|
||||||
|
mask = (1 << bpp) - 1, divider = (8 / bpp);
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)buffer, type * REALBPP / 8))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* read palette */
|
||||||
|
for (i = 0; i < type; i++, buffer += REALBPP / 8)
|
||||||
|
palette[i] = UncompressCPixel(buffer);
|
||||||
|
|
||||||
|
last_type = type;
|
||||||
|
goto case_127;
|
||||||
|
} else if (type >= 130) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)buffer, (type - 128) * REALBPP / 8))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* read palette */
|
||||||
|
for (i = 0; i < type - 128; i++, buffer += REALBPP / 8)
|
||||||
|
palette[i] = UncompressCPixel(buffer);
|
||||||
|
|
||||||
|
last_type = type;
|
||||||
|
goto case_129;
|
||||||
|
} else
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
last_type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CARDBPP
|
||||||
|
#undef CARDREALBPP
|
||||||
|
#undef HandleTRLE
|
||||||
|
#undef UncompressCPixel
|
||||||
|
#undef REALBPP
|
||||||
|
#undef UNCOMP
|
|
@ -0,0 +1,224 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
|
||||||
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ultrazip.c - handle ultrazip encoding.
|
||||||
|
*
|
||||||
|
* This file shouldn't be compiled directly. It is included multiple times by
|
||||||
|
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||||
|
* each value of BPP, this file defines a function which handles an zlib
|
||||||
|
* encoded rectangle with BPP bits per pixel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define HandleUltraZipBPP CONCAT2E(HandleUltraZip,BPP)
|
||||||
|
#define HandleUltraBPP CONCAT2E(HandleUltra,BPP)
|
||||||
|
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
HandleUltraBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||||
|
{
|
||||||
|
rfbZlibHeader hdr;
|
||||||
|
int toRead=0;
|
||||||
|
int inflateResult=0;
|
||||||
|
lzo_uint uncompressedBytes = (( rw * rh ) * ( BPP / 8 ));
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbZlibHeader))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
toRead = rfbClientSwap32IfLE(hdr.nBytes);
|
||||||
|
if (toRead==0) return TRUE;
|
||||||
|
|
||||||
|
if (toRead < 0) {
|
||||||
|
rfbClientErr("ultra error: remote sent negative payload size\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uncompressedBytes==0)
|
||||||
|
{
|
||||||
|
rfbClientLog("ultra error: rectangle has 0 uncomressed bytes ((%dw * %dh) * (%d / 8))\n", rw, rh, BPP);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First make sure we have a large enough raw buffer to hold the
|
||||||
|
* decompressed data. In practice, with a fixed BPP, fixed frame
|
||||||
|
* buffer size and the first update containing the entire frame
|
||||||
|
* buffer, this buffer allocation should only happen once, on the
|
||||||
|
* first update.
|
||||||
|
*/
|
||||||
|
if ( client->raw_buffer_size < (int)uncompressedBytes) {
|
||||||
|
if ( client->raw_buffer != NULL ) {
|
||||||
|
free( client->raw_buffer );
|
||||||
|
}
|
||||||
|
client->raw_buffer_size = uncompressedBytes;
|
||||||
|
/* buffer needs to be aligned on 4-byte boundaries */
|
||||||
|
if ((client->raw_buffer_size % 4)!=0)
|
||||||
|
client->raw_buffer_size += (4-(client->raw_buffer_size % 4));
|
||||||
|
client->raw_buffer = (char*) malloc( client->raw_buffer_size );
|
||||||
|
if(client->raw_buffer == NULL)
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate enough space to store the incoming compressed packet */
|
||||||
|
if ( client->ultra_buffer_size < toRead ) {
|
||||||
|
if ( client->ultra_buffer != NULL ) {
|
||||||
|
free( client->ultra_buffer );
|
||||||
|
}
|
||||||
|
client->ultra_buffer_size = toRead;
|
||||||
|
/* buffer needs to be aligned on 4-byte boundaries */
|
||||||
|
if ((client->ultra_buffer_size % 4)!=0)
|
||||||
|
client->ultra_buffer_size += (4-(client->ultra_buffer_size % 4));
|
||||||
|
client->ultra_buffer = (char*) malloc( client->ultra_buffer_size );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill the buffer, obtaining data from the server. */
|
||||||
|
if (!ReadFromRFBServer(client, client->ultra_buffer, toRead))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* uncompress the data */
|
||||||
|
uncompressedBytes = client->raw_buffer_size;
|
||||||
|
inflateResult = lzo1x_decompress_safe(
|
||||||
|
(lzo_byte *)client->ultra_buffer, toRead,
|
||||||
|
(lzo_byte *)client->raw_buffer, (lzo_uintp) &uncompressedBytes,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Note that uncompressedBytes will be 0 on output overrun */
|
||||||
|
if ((rw * rh * (BPP / 8)) != uncompressedBytes)
|
||||||
|
rfbClientLog("Ultra decompressed unexpected amount of data (%d != %d)\n", (rw * rh * (BPP / 8)), uncompressedBytes);
|
||||||
|
|
||||||
|
/* Put the uncompressed contents of the update on the screen. */
|
||||||
|
if ( inflateResult == LZO_E_OK )
|
||||||
|
{
|
||||||
|
client->GotBitmap(client, (unsigned char *)client->raw_buffer, rx, ry, rw, rh);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rfbClientLog("ultra decompress returned error: %d\n",
|
||||||
|
inflateResult);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* UltraZip is like rre in that it is composed of subrects */
|
||||||
|
static rfbBool
|
||||||
|
HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||||
|
{
|
||||||
|
rfbZlibHeader hdr;
|
||||||
|
int i=0;
|
||||||
|
int toRead=0;
|
||||||
|
int inflateResult=0;
|
||||||
|
unsigned char *ptr=NULL;
|
||||||
|
lzo_uint uncompressedBytes = ry + (rw * 65535);
|
||||||
|
unsigned int numCacheRects = rx;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbZlibHeader))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
toRead = rfbClientSwap32IfLE(hdr.nBytes);
|
||||||
|
|
||||||
|
if (toRead==0) return TRUE;
|
||||||
|
|
||||||
|
if (toRead < 0) {
|
||||||
|
rfbClientErr("ultrazip error: remote sent negative payload size\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uncompressedBytes==0)
|
||||||
|
{
|
||||||
|
rfbClientLog("ultrazip error: rectangle has 0 uncomressed bytes (%dy + (%dw * 65535)) (%d rectangles)\n", ry, rw, rx);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First make sure we have a large enough raw buffer to hold the
|
||||||
|
* decompressed data. In practice, with a fixed BPP, fixed frame
|
||||||
|
* buffer size and the first update containing the entire frame
|
||||||
|
* buffer, this buffer allocation should only happen once, on the
|
||||||
|
* first update.
|
||||||
|
*/
|
||||||
|
if ( client->raw_buffer_size < (int)(uncompressedBytes + 500)) {
|
||||||
|
if ( client->raw_buffer != NULL ) {
|
||||||
|
free( client->raw_buffer );
|
||||||
|
}
|
||||||
|
client->raw_buffer_size = uncompressedBytes + 500;
|
||||||
|
/* buffer needs to be aligned on 4-byte boundaries */
|
||||||
|
if ((client->raw_buffer_size % 4)!=0)
|
||||||
|
client->raw_buffer_size += (4-(client->raw_buffer_size % 4));
|
||||||
|
client->raw_buffer = (char*) malloc( client->raw_buffer_size );
|
||||||
|
if(client->raw_buffer == NULL)
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* allocate enough space to store the incoming compressed packet */
|
||||||
|
if ( client->ultra_buffer_size < toRead ) {
|
||||||
|
if ( client->ultra_buffer != NULL ) {
|
||||||
|
free( client->ultra_buffer );
|
||||||
|
}
|
||||||
|
client->ultra_buffer_size = toRead;
|
||||||
|
client->ultra_buffer = (char*) malloc( client->ultra_buffer_size );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill the buffer, obtaining data from the server. */
|
||||||
|
if (!ReadFromRFBServer(client, client->ultra_buffer, toRead))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* uncompress the data */
|
||||||
|
uncompressedBytes = client->raw_buffer_size;
|
||||||
|
inflateResult = lzo1x_decompress_safe(
|
||||||
|
(lzo_byte *)client->ultra_buffer, toRead,
|
||||||
|
(lzo_byte *)client->raw_buffer, &uncompressedBytes, NULL);
|
||||||
|
if ( inflateResult != LZO_E_OK )
|
||||||
|
{
|
||||||
|
rfbClientLog("ultra decompress returned error: %d\n",
|
||||||
|
inflateResult);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Put the uncompressed contents of the update on the screen. */
|
||||||
|
ptr = (unsigned char *)client->raw_buffer;
|
||||||
|
for (i=0; i<numCacheRects; i++)
|
||||||
|
{
|
||||||
|
unsigned short sx, sy, sw, sh;
|
||||||
|
unsigned int se;
|
||||||
|
|
||||||
|
memcpy((char *)&sx, ptr, 2); ptr += 2;
|
||||||
|
memcpy((char *)&sy, ptr, 2); ptr += 2;
|
||||||
|
memcpy((char *)&sw, ptr, 2); ptr += 2;
|
||||||
|
memcpy((char *)&sh, ptr, 2); ptr += 2;
|
||||||
|
memcpy((char *)&se, ptr, 4); ptr += 4;
|
||||||
|
|
||||||
|
sx = rfbClientSwap16IfLE(sx);
|
||||||
|
sy = rfbClientSwap16IfLE(sy);
|
||||||
|
sw = rfbClientSwap16IfLE(sw);
|
||||||
|
sh = rfbClientSwap16IfLE(sh);
|
||||||
|
se = rfbClientSwap32IfLE(se);
|
||||||
|
|
||||||
|
if (se == rfbEncodingRaw)
|
||||||
|
{
|
||||||
|
client->GotBitmap(client, (unsigned char *)ptr, sx, sy, sw, sh);
|
||||||
|
ptr += ((sw * sh) * (BPP / 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CARDBPP
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* vncauth.c - Functions for VNC password management and authentication.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rfbproto.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "crypto.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
/* libvncclient does not need this */
|
||||||
|
#ifndef rfbEncryptBytes
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use a fixed key to store passwords, since we assume that our local
|
||||||
|
* file system is secure but nonetheless don't want to store passwords
|
||||||
|
* as plaintext.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static unsigned char fixedkey[8] = {23,82,107,6,35,78,88,7};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encrypt a password and store it in a file. Returns 0 if successful,
|
||||||
|
* 1 if the file could not be written.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
rfbEncryptAndStorePasswd(char *passwd, char *fname)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
unsigned int i;
|
||||||
|
unsigned char encryptedPasswd[8];
|
||||||
|
int out_len;
|
||||||
|
|
||||||
|
if ((fp = fopen(fname,"w")) == NULL) return 1;
|
||||||
|
|
||||||
|
fchmod(fileno(fp), S_IRUSR|S_IWUSR);
|
||||||
|
|
||||||
|
/* pad password with nulls */
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (i < strlen(passwd)) {
|
||||||
|
encryptedPasswd[i] = passwd[i];
|
||||||
|
} else {
|
||||||
|
encryptedPasswd[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do encryption in-place - this way we overwrite our copy of the plaintext
|
||||||
|
password */
|
||||||
|
encrypt_rfbdes(encryptedPasswd, &out_len, fixedkey, encryptedPasswd, sizeof(encryptedPasswd));
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
putc(encryptedPasswd[i], fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decrypt a password from a file. Returns a pointer to a newly allocated
|
||||||
|
* string containing the password or a null pointer if the password could
|
||||||
|
* not be retrieved for some reason.
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *
|
||||||
|
rfbDecryptPasswdFromFile(char *fname)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
int i, ch;
|
||||||
|
unsigned char *passwd = (unsigned char *)malloc(9);
|
||||||
|
int out_len;
|
||||||
|
|
||||||
|
if (!passwd || (fp = fopen(fname,"r")) == NULL) {
|
||||||
|
free(passwd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
ch = getc(fp);
|
||||||
|
if (ch == EOF) {
|
||||||
|
fclose(fp);
|
||||||
|
free(passwd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
passwd[i] = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if(!decrypt_rfbdes(passwd, &out_len, fixedkey, passwd, 8))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
passwd[8] = 0;
|
||||||
|
|
||||||
|
return (char *)passwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate CHALLENGESIZE random bytes for use in challenge-response
|
||||||
|
* authentication.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
rfbRandomBytes(unsigned char *bytes)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
static rfbBool s_srandom_called = FALSE;
|
||||||
|
|
||||||
|
if (!s_srandom_called) {
|
||||||
|
srandom((unsigned int)time(NULL) ^ (unsigned int)getpid());
|
||||||
|
s_srandom_called = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < CHALLENGESIZE; i++) {
|
||||||
|
bytes[i] = (unsigned char)(random() & 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encrypt CHALLENGESIZE bytes in memory using a password.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
rfbEncryptBytes(unsigned char *bytes, char *passwd)
|
||||||
|
{
|
||||||
|
unsigned char key[8];
|
||||||
|
unsigned int i;
|
||||||
|
int out_len;
|
||||||
|
|
||||||
|
/* key is simply password padded with nulls */
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (i < strlen(passwd)) {
|
||||||
|
key[i] = passwd[i];
|
||||||
|
} else {
|
||||||
|
key[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypt_rfbdes(bytes, &out_len, key, bytes, CHALLENGESIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rfbEncryptBytes2(unsigned char *where, const int length, unsigned char *key) {
|
||||||
|
int i, j, out_len;
|
||||||
|
for (i = 0; i< 8; i++)
|
||||||
|
where[i] ^= key[i];
|
||||||
|
encrypt_rfbdes(where, &out_len, key, where, 8);
|
||||||
|
for (i = 8; i < length; i += 8) {
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
where[i + j] ^= where[i + j - 8];
|
||||||
|
}
|
||||||
|
encrypt_rfbdes(where + i, &out_len, key, where + i, 8);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
|
||||||
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||||
|
|
||||||
|
/*
|
||||||
|
* zlib.c - handle zlib encoding.
|
||||||
|
*
|
||||||
|
* This file shouldn't be compiled directly. It is included multiple times by
|
||||||
|
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||||
|
* each value of BPP, this file defines a function which handles an zlib
|
||||||
|
* encoded rectangle with BPP bits per pixel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define HandleZlibBPP CONCAT2E(HandleZlib,BPP)
|
||||||
|
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
HandleZlibBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||||
|
{
|
||||||
|
rfbZlibHeader hdr;
|
||||||
|
int remaining;
|
||||||
|
int inflateResult;
|
||||||
|
int toRead;
|
||||||
|
|
||||||
|
/* First make sure we have a large enough raw buffer to hold the
|
||||||
|
* decompressed data. In practice, with a fixed BPP, fixed frame
|
||||||
|
* buffer size and the first update containing the entire frame
|
||||||
|
* buffer, this buffer allocation should only happen once, on the
|
||||||
|
* first update.
|
||||||
|
*/
|
||||||
|
if ( client->raw_buffer_size < (( rw * rh ) * ( BPP / 8 ))) {
|
||||||
|
|
||||||
|
if ( client->raw_buffer != NULL ) {
|
||||||
|
|
||||||
|
free( client->raw_buffer );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
client->raw_buffer_size = (( rw * rh ) * ( BPP / 8 ));
|
||||||
|
client->raw_buffer = (char*) malloc( client->raw_buffer_size );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbZlibHeader))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
remaining = rfbClientSwap32IfLE(hdr.nBytes);
|
||||||
|
|
||||||
|
/* Need to initialize the decompressor state. */
|
||||||
|
client->decompStream.next_in = ( Bytef * )client->buffer;
|
||||||
|
client->decompStream.avail_in = 0;
|
||||||
|
client->decompStream.next_out = ( Bytef * )client->raw_buffer;
|
||||||
|
client->decompStream.avail_out = client->raw_buffer_size;
|
||||||
|
client->decompStream.data_type = Z_BINARY;
|
||||||
|
|
||||||
|
/* Initialize the decompression stream structures on the first invocation. */
|
||||||
|
if ( client->decompStreamInited == FALSE ) {
|
||||||
|
|
||||||
|
inflateResult = inflateInit( &client->decompStream );
|
||||||
|
|
||||||
|
if ( inflateResult != Z_OK ) {
|
||||||
|
rfbClientLog(
|
||||||
|
"inflateInit returned error: %d, msg: %s\n",
|
||||||
|
inflateResult,
|
||||||
|
client->decompStream.msg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->decompStreamInited = TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inflateResult = Z_OK;
|
||||||
|
|
||||||
|
/* Process buffer full of data until no more to process, or
|
||||||
|
* some type of inflater error, or Z_STREAM_END.
|
||||||
|
*/
|
||||||
|
while (( remaining > 0 ) &&
|
||||||
|
( inflateResult == Z_OK )) {
|
||||||
|
|
||||||
|
if ( remaining > RFB_BUFFER_SIZE ) {
|
||||||
|
toRead = RFB_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toRead = remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill the buffer, obtaining data from the server. */
|
||||||
|
if (!ReadFromRFBServer(client, client->buffer,toRead))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
client->decompStream.next_in = ( Bytef * )client->buffer;
|
||||||
|
client->decompStream.avail_in = toRead;
|
||||||
|
|
||||||
|
/* Need to uncompress buffer full. */
|
||||||
|
inflateResult = inflate( &client->decompStream, Z_SYNC_FLUSH );
|
||||||
|
|
||||||
|
/* We never supply a dictionary for compression. */
|
||||||
|
if ( inflateResult == Z_NEED_DICT ) {
|
||||||
|
rfbClientLog("zlib inflate needs a dictionary!\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if ( inflateResult < 0 ) {
|
||||||
|
rfbClientLog(
|
||||||
|
"zlib inflate returned error: %d, msg: %s\n",
|
||||||
|
inflateResult,
|
||||||
|
client->decompStream.msg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Result buffer allocated to be at least large enough. We should
|
||||||
|
* never run out of space!
|
||||||
|
*/
|
||||||
|
if (( client->decompStream.avail_in > 0 ) &&
|
||||||
|
( client->decompStream.avail_out <= 0 )) {
|
||||||
|
rfbClientLog("zlib inflate ran out of space!\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining -= toRead;
|
||||||
|
|
||||||
|
} /* while ( remaining > 0 ) */
|
||||||
|
|
||||||
|
if ( inflateResult == Z_OK ) {
|
||||||
|
|
||||||
|
/* Put the uncompressed contents of the update on the screen. */
|
||||||
|
client->GotBitmap(client, (uint8_t *)client->raw_buffer, rx, ry, rw, rh);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
rfbClientLog(
|
||||||
|
"zlib inflate returned error: %d, msg: %s\n",
|
||||||
|
inflateResult,
|
||||||
|
client->decompStream.msg);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CARDBPP
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,427 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2005 Johannes E. Schindelin. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||||
|
|
||||||
|
/*
|
||||||
|
* zrle.c - handle zrle encoding.
|
||||||
|
*
|
||||||
|
* This file shouldn't be compiled directly. It is included multiple times by
|
||||||
|
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||||
|
* each value of BPP, this file defines a function which handles an zrle
|
||||||
|
* encoded rectangle with BPP bits per pixel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REALBPP
|
||||||
|
#define REALBPP BPP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(UNCOMP) || UNCOMP==0
|
||||||
|
#define HandleZRLE CONCAT2E(HandleZRLE,REALBPP)
|
||||||
|
#define HandleZRLETile CONCAT2E(HandleZRLETile,REALBPP)
|
||||||
|
#elif UNCOMP>0
|
||||||
|
#define HandleZRLE CONCAT3E(HandleZRLE,REALBPP,Down)
|
||||||
|
#define HandleZRLETile CONCAT3E(HandleZRLETile,REALBPP,Down)
|
||||||
|
#else
|
||||||
|
#define HandleZRLE CONCAT3E(HandleZRLE,REALBPP,Up)
|
||||||
|
#define HandleZRLETile CONCAT3E(HandleZRLETile,REALBPP,Up)
|
||||||
|
#endif
|
||||||
|
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||||
|
#define CARDREALBPP CONCAT3E(uint,REALBPP,_t)
|
||||||
|
|
||||||
|
#define ENDIAN_LITTLE 0
|
||||||
|
#define ENDIAN_BIG 1
|
||||||
|
#define ENDIAN_NO 2
|
||||||
|
#define ZYWRLE_ENDIAN ENDIAN_LITTLE
|
||||||
|
#undef END_FIX
|
||||||
|
#if ZYWRLE_ENDIAN == ENDIAN_LITTLE
|
||||||
|
# define END_FIX LE
|
||||||
|
#elif ZYWRLE_ENDIAN == ENDIAN_BIG
|
||||||
|
# define END_FIX BE
|
||||||
|
#else
|
||||||
|
# define END_FIX NE
|
||||||
|
#endif
|
||||||
|
#define __RFB_CONCAT3E(a,b,c) CONCAT3E(a,b,c)
|
||||||
|
#define __RFB_CONCAT2E(a,b) CONCAT2E(a,b)
|
||||||
|
#undef CPIXEL
|
||||||
|
#if REALBPP != BPP
|
||||||
|
#if UNCOMP == 0
|
||||||
|
#define CPIXEL REALBPP
|
||||||
|
#elif UNCOMP>0
|
||||||
|
#define CPIXEL CONCAT2E(REALBPP,Down)
|
||||||
|
#else
|
||||||
|
#define CPIXEL CONCAT2E(REALBPP,Up)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#define PIXEL_T __RFB_CONCAT3E(uint,BPP,_t)
|
||||||
|
#if BPP!=8
|
||||||
|
#define ZYWRLE_DECODE 1
|
||||||
|
#include "zywrletemplate.c"
|
||||||
|
#endif
|
||||||
|
#undef CPIXEL
|
||||||
|
|
||||||
|
static int HandleZRLETile(rfbClient* client,
|
||||||
|
uint8_t* buffer,size_t buffer_length,
|
||||||
|
int x,int y,int w,int h);
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
HandleZRLE (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||||
|
{
|
||||||
|
rfbZRLEHeader header;
|
||||||
|
int remaining;
|
||||||
|
int inflateResult;
|
||||||
|
int toRead;
|
||||||
|
int min_buffer_size = rw * rh * (REALBPP / 8) * 2;
|
||||||
|
|
||||||
|
/* First make sure we have a large enough raw buffer to hold the
|
||||||
|
* decompressed data. In practice, with a fixed REALBPP, fixed frame
|
||||||
|
* buffer size and the first update containing the entire frame
|
||||||
|
* buffer, this buffer allocation should only happen once, on the
|
||||||
|
* first update.
|
||||||
|
*/
|
||||||
|
if ( client->raw_buffer_size < min_buffer_size) {
|
||||||
|
|
||||||
|
if ( client->raw_buffer != NULL ) {
|
||||||
|
|
||||||
|
free( client->raw_buffer );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
client->raw_buffer_size = min_buffer_size;
|
||||||
|
client->raw_buffer = (char*) malloc( client->raw_buffer_size );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&header, sz_rfbZRLEHeader))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
remaining = rfbClientSwap32IfLE(header.length);
|
||||||
|
|
||||||
|
/* Need to initialize the decompressor state. */
|
||||||
|
client->decompStream.next_in = ( Bytef * )client->buffer;
|
||||||
|
client->decompStream.avail_in = 0;
|
||||||
|
client->decompStream.next_out = ( Bytef * )client->raw_buffer;
|
||||||
|
client->decompStream.avail_out = client->raw_buffer_size;
|
||||||
|
client->decompStream.data_type = Z_BINARY;
|
||||||
|
|
||||||
|
/* Initialize the decompression stream structures on the first invocation. */
|
||||||
|
if ( client->decompStreamInited == FALSE ) {
|
||||||
|
|
||||||
|
inflateResult = inflateInit( &client->decompStream );
|
||||||
|
|
||||||
|
if ( inflateResult != Z_OK ) {
|
||||||
|
rfbClientLog(
|
||||||
|
"inflateInit returned error: %d, msg: %s\n",
|
||||||
|
inflateResult,
|
||||||
|
client->decompStream.msg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->decompStreamInited = TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inflateResult = Z_OK;
|
||||||
|
|
||||||
|
/* Process buffer full of data until no more to process, or
|
||||||
|
* some type of inflater error, or Z_STREAM_END.
|
||||||
|
*/
|
||||||
|
while (( remaining > 0 ) &&
|
||||||
|
( inflateResult == Z_OK )) {
|
||||||
|
|
||||||
|
if ( remaining > RFB_BUFFER_SIZE ) {
|
||||||
|
toRead = RFB_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toRead = remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill the buffer, obtaining data from the server. */
|
||||||
|
if (!ReadFromRFBServer(client, client->buffer,toRead))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
client->decompStream.next_in = ( Bytef * )client->buffer;
|
||||||
|
client->decompStream.avail_in = toRead;
|
||||||
|
|
||||||
|
/* Need to uncompress buffer full. */
|
||||||
|
inflateResult = inflate( &client->decompStream, Z_SYNC_FLUSH );
|
||||||
|
|
||||||
|
/* We never supply a dictionary for compression. */
|
||||||
|
if ( inflateResult == Z_NEED_DICT ) {
|
||||||
|
rfbClientLog("zlib inflate needs a dictionary!\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if ( inflateResult < 0 ) {
|
||||||
|
rfbClientLog(
|
||||||
|
"zlib inflate returned error: %d, msg: %s\n",
|
||||||
|
inflateResult,
|
||||||
|
client->decompStream.msg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Result buffer allocated to be at least large enough. We should
|
||||||
|
* never run out of space!
|
||||||
|
*/
|
||||||
|
if (( client->decompStream.avail_in > 0 ) &&
|
||||||
|
( client->decompStream.avail_out <= 0 )) {
|
||||||
|
rfbClientLog("zlib inflate ran out of space!\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining -= toRead;
|
||||||
|
|
||||||
|
} /* while ( remaining > 0 ) */
|
||||||
|
|
||||||
|
if ( inflateResult == Z_OK ) {
|
||||||
|
char* buf=client->raw_buffer;
|
||||||
|
int i,j;
|
||||||
|
|
||||||
|
remaining = client->raw_buffer_size-client->decompStream.avail_out;
|
||||||
|
|
||||||
|
for(j=0; j<rh; j+=rfbZRLETileHeight)
|
||||||
|
for(i=0; i<rw; i+=rfbZRLETileWidth) {
|
||||||
|
int subWidth=(i+rfbZRLETileWidth>rw)?rw-i:rfbZRLETileWidth;
|
||||||
|
int subHeight=(j+rfbZRLETileHeight>rh)?rh-j:rfbZRLETileHeight;
|
||||||
|
int result=HandleZRLETile(client,(uint8_t *)buf,remaining,rx+i,ry+j,subWidth,subHeight);
|
||||||
|
|
||||||
|
if(result<0) {
|
||||||
|
rfbClientLog("ZRLE decoding failed (%d)\n",result);
|
||||||
|
return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf+=result;
|
||||||
|
remaining-=result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
rfbClientLog(
|
||||||
|
"zlib inflate returned error: %d, msg: %s\n",
|
||||||
|
inflateResult,
|
||||||
|
client->decompStream.msg);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if REALBPP!=BPP && defined(UNCOMP) && UNCOMP!=0
|
||||||
|
#if UNCOMP>0
|
||||||
|
#define UncompressCPixel(pointer) ((*(CARDBPP*)pointer)>>UNCOMP)
|
||||||
|
#else
|
||||||
|
#define UncompressCPixel(pointer) ((*(CARDBPP*)pointer)<<(-(UNCOMP)))
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define UncompressCPixel(pointer) (*(CARDBPP*)pointer)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int HandleZRLETile(rfbClient* client,
|
||||||
|
uint8_t* buffer,size_t buffer_length,
|
||||||
|
int x,int y,int w,int h) {
|
||||||
|
uint8_t* buffer_copy = buffer;
|
||||||
|
uint8_t* buffer_end = buffer+buffer_length;
|
||||||
|
uint8_t type;
|
||||||
|
#if BPP!=8
|
||||||
|
uint8_t zywrle_level = (client->appData.qualityLevel & 0x80) ?
|
||||||
|
0 : (3 - client->appData.qualityLevel / 3);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(buffer_length<1)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
type = *buffer;
|
||||||
|
buffer++;
|
||||||
|
{
|
||||||
|
if( type == 0 ) /* raw */
|
||||||
|
#if BPP!=8
|
||||||
|
if( zywrle_level > 0 ){
|
||||||
|
CARDBPP* pFrame = (CARDBPP*)client->frameBuffer + y*client->width+x;
|
||||||
|
int ret;
|
||||||
|
client->appData.qualityLevel |= 0x80;
|
||||||
|
ret = HandleZRLETile(client, buffer, buffer_end-buffer, x, y, w, h);
|
||||||
|
client->appData.qualityLevel &= 0x7F;
|
||||||
|
if( ret < 0 ){
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ZYWRLE_SYNTHESIZE( pFrame, pFrame, w, h, client->width, zywrle_level, (int*)client->zlib_buffer );
|
||||||
|
buffer += ret;
|
||||||
|
}else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if REALBPP!=BPP
|
||||||
|
int i,j;
|
||||||
|
|
||||||
|
if(1+w*h*REALBPP/8>buffer_length) {
|
||||||
|
rfbClientLog("expected %d bytes, got only %d (%dx%d)\n",1+w*h*REALBPP/8,buffer_length,w,h);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(j=y*client->width; j<(y+h)*client->width; j+=client->width)
|
||||||
|
for(i=x; i<x+w; i++,buffer+=REALBPP/8)
|
||||||
|
((CARDBPP*)client->frameBuffer)[j+i] = UncompressCPixel(buffer);
|
||||||
|
#else
|
||||||
|
client->GotBitmap(client, buffer, x, y, w, h);
|
||||||
|
buffer+=w*h*REALBPP/8;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if( type == 1 ) /* solid */
|
||||||
|
{
|
||||||
|
CARDBPP color = UncompressCPixel(buffer);
|
||||||
|
|
||||||
|
if(1+REALBPP/8>buffer_length)
|
||||||
|
return -4;
|
||||||
|
|
||||||
|
client->GotFillRect(client, x, y, w, h, color);
|
||||||
|
|
||||||
|
buffer+=REALBPP/8;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if( type <= 127 ) /* packed Palette */
|
||||||
|
{
|
||||||
|
CARDBPP palette[128];
|
||||||
|
int i,j,shift,
|
||||||
|
bpp=(type>4?(type>16?8:4):(type>2?2:1)),
|
||||||
|
mask=(1<<bpp)-1,
|
||||||
|
divider=(8/bpp);
|
||||||
|
|
||||||
|
if(1+type*REALBPP/8+((w+divider-1)/divider)*h>buffer_length)
|
||||||
|
return -5;
|
||||||
|
|
||||||
|
/* read palette */
|
||||||
|
for(i=0; i<type; i++,buffer+=REALBPP/8)
|
||||||
|
palette[i] = UncompressCPixel(buffer);
|
||||||
|
|
||||||
|
/* read palettized pixels */
|
||||||
|
for(j=y*client->width; j<(y+h)*client->width; j+=client->width) {
|
||||||
|
for(i=x,shift=8-bpp; i<x+w; i++) {
|
||||||
|
((CARDBPP*)client->frameBuffer)[j+i] = palette[((*buffer)>>shift)&mask];
|
||||||
|
shift-=bpp;
|
||||||
|
if(shift<0) {
|
||||||
|
shift=8-bpp;
|
||||||
|
buffer++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(shift<8-bpp)
|
||||||
|
buffer++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/* case 17 ... 127: not used, but valid */
|
||||||
|
else if( type == 128 ) /* plain RLE */
|
||||||
|
{
|
||||||
|
int i=0,j=0;
|
||||||
|
while(j<h) {
|
||||||
|
int color,length;
|
||||||
|
/* read color */
|
||||||
|
if(buffer+REALBPP/8+1>buffer_end)
|
||||||
|
return -7;
|
||||||
|
color = UncompressCPixel(buffer);
|
||||||
|
buffer+=REALBPP/8;
|
||||||
|
/* read run length */
|
||||||
|
length=1;
|
||||||
|
while(*buffer==0xff) {
|
||||||
|
if(buffer+1>=buffer_end)
|
||||||
|
return -8;
|
||||||
|
length+=*buffer;
|
||||||
|
buffer++;
|
||||||
|
}
|
||||||
|
length+=*buffer;
|
||||||
|
buffer++;
|
||||||
|
while(j<h && length>0) {
|
||||||
|
((CARDBPP*)client->frameBuffer)[(y+j)*client->width+x+i] = color;
|
||||||
|
length--;
|
||||||
|
i++;
|
||||||
|
if(i>=w) {
|
||||||
|
i=0;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(length>0)
|
||||||
|
rfbClientLog("Warning: possible ZRLE corruption\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if( type == 129 ) /* unused */
|
||||||
|
{
|
||||||
|
return -8;
|
||||||
|
}
|
||||||
|
else if( type >= 130 ) /* palette RLE */
|
||||||
|
{
|
||||||
|
CARDBPP palette[128];
|
||||||
|
int i,j;
|
||||||
|
|
||||||
|
if(2+(type-128)*REALBPP/8>buffer_length)
|
||||||
|
return -9;
|
||||||
|
|
||||||
|
/* read palette */
|
||||||
|
for(i=0; i<type-128; i++,buffer+=REALBPP/8)
|
||||||
|
palette[i] = UncompressCPixel(buffer);
|
||||||
|
/* read palettized pixels */
|
||||||
|
i=j=0;
|
||||||
|
while(j<h) {
|
||||||
|
int color,length;
|
||||||
|
/* read color */
|
||||||
|
if(buffer>=buffer_end)
|
||||||
|
return -10;
|
||||||
|
color = palette[(*buffer)&0x7f];
|
||||||
|
length=1;
|
||||||
|
if(*buffer&0x80) {
|
||||||
|
if(buffer+1>=buffer_end)
|
||||||
|
return -11;
|
||||||
|
buffer++;
|
||||||
|
/* read run length */
|
||||||
|
while(*buffer==0xff) {
|
||||||
|
if(buffer+1>=buffer_end)
|
||||||
|
return -8;
|
||||||
|
length+=*buffer;
|
||||||
|
buffer++;
|
||||||
|
}
|
||||||
|
length+=*buffer;
|
||||||
|
}
|
||||||
|
buffer++;
|
||||||
|
while(j<h && length>0) {
|
||||||
|
((CARDBPP*)client->frameBuffer)[(y+j)*client->width+x+i] = color;
|
||||||
|
length--;
|
||||||
|
i++;
|
||||||
|
if(i>=w) {
|
||||||
|
i=0;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(length>0)
|
||||||
|
rfbClientLog("Warning: possible ZRLE corruption\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer-buffer_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CARDBPP
|
||||||
|
#undef CARDREALBPP
|
||||||
|
#undef HandleZRLE
|
||||||
|
#undef HandleZRLETile
|
||||||
|
#undef UncompressCPixel
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef UNCOMP
|
||||||
|
#undef REALBPP
|
|
@ -0,0 +1,824 @@
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* *
|
||||||
|
* THIS FILE IS PART OF THE 'ZYWRLE' VNC CODEC SOURCE CODE. *
|
||||||
|
* *
|
||||||
|
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
|
||||||
|
* GOVERNED BY A FOLLOWING BSD-STYLE SOURCE LICENSE. *
|
||||||
|
* PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
|
||||||
|
* *
|
||||||
|
* THE 'ZYWRLE' VNC CODEC SOURCE CODE IS (C) COPYRIGHT 2006 *
|
||||||
|
* BY Hitachi Systems & Services, Ltd. *
|
||||||
|
* (Noriaki Yamazaki, Research & Development Center) * *
|
||||||
|
* *
|
||||||
|
********************************************************************
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
- Neither the name of the Hitachi Systems & Services, Ltd. nor
|
||||||
|
the names of its contributors may be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
|
||||||
|
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
/* Change Log:
|
||||||
|
V0.02 : 2008/02/04 : Fix mis encode/decode when width != scanline
|
||||||
|
(Thanks Johannes Schindelin, author of LibVNC
|
||||||
|
Server/Client)
|
||||||
|
V0.01 : 2007/02/06 : Initial release
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* #define ZYWRLE_ENCODE */
|
||||||
|
/* #define ZYWRLE_DECODE */
|
||||||
|
#define ZYWRLE_QUANTIZE
|
||||||
|
|
||||||
|
/*
|
||||||
|
[References]
|
||||||
|
PLHarr:
|
||||||
|
Senecal, J. G., P. Lindstrom, M. A. Duchaineau, and K. I. Joy, "An Improved N-Bit to N-Bit Reversible Haar-Like Transform," Pacific Graphics 2004, October 2004, pp. 371-380.
|
||||||
|
EZW:
|
||||||
|
Shapiro, JM: Embedded Image Coding Using Zerotrees of Wavelet Coefficients, IEEE Trans. Signal. Process., Vol.41, pp.3445-3462 (1993).
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Template Macro stuffs. */
|
||||||
|
#undef ZYWRLE_ANALYZE
|
||||||
|
#undef ZYWRLE_SYNTHESIZE
|
||||||
|
#define ZYWRLE_ANALYZE __RFB_CONCAT3E(zywrleAnalyze,BPP,END_FIX)
|
||||||
|
#define ZYWRLE_SYNTHESIZE __RFB_CONCAT3E(zywrleSynthesize,BPP,END_FIX)
|
||||||
|
|
||||||
|
#define ZYWRLE_RGBYUV __RFB_CONCAT3E(zywrleRGBYUV,BPP,END_FIX)
|
||||||
|
#define ZYWRLE_YUVRGB __RFB_CONCAT3E(zywrleYUVRGB,BPP,END_FIX)
|
||||||
|
#define ZYWRLE_YMASK __RFB_CONCAT2E(ZYWRLE_YMASK,BPP)
|
||||||
|
#define ZYWRLE_UVMASK __RFB_CONCAT2E(ZYWRLE_UVMASK,BPP)
|
||||||
|
#define ZYWRLE_LOAD_PIXEL __RFB_CONCAT2E(ZYWRLE_LOAD_PIXEL,BPP)
|
||||||
|
#define ZYWRLE_SAVE_PIXEL __RFB_CONCAT2E(ZYWRLE_SAVE_PIXEL,BPP)
|
||||||
|
|
||||||
|
/* Packing/Unpacking pixel stuffs.
|
||||||
|
Endian conversion stuffs. */
|
||||||
|
#undef S_0
|
||||||
|
#undef S_1
|
||||||
|
#undef L_0
|
||||||
|
#undef L_1
|
||||||
|
#undef L_2
|
||||||
|
#if ZYWRLE_ENDIAN == ENDIAN_BIG
|
||||||
|
# define S_0 1
|
||||||
|
# define S_1 0
|
||||||
|
# define L_0 3
|
||||||
|
# define L_1 2
|
||||||
|
# define L_2 1
|
||||||
|
#else
|
||||||
|
# define S_0 0
|
||||||
|
# define S_1 1
|
||||||
|
# define L_0 0
|
||||||
|
# define L_1 1
|
||||||
|
# define L_2 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Load/Save pixel stuffs. */
|
||||||
|
#define ZYWRLE_YMASK15 0xFFFFFFF8
|
||||||
|
#define ZYWRLE_UVMASK15 0xFFFFFFF8
|
||||||
|
#define ZYWRLE_LOAD_PIXEL15(pSrc,R,G,B) { \
|
||||||
|
R = (((unsigned char*)pSrc)[S_1]<< 1)& 0xF8; \
|
||||||
|
G = ((((unsigned char*)pSrc)[S_1]<< 6)|(((unsigned char*)pSrc)[S_0]>> 2))& 0xF8; \
|
||||||
|
B = (((unsigned char*)pSrc)[S_0]<< 3)& 0xF8; \
|
||||||
|
}
|
||||||
|
#define ZYWRLE_SAVE_PIXEL15(pDst,R,G,B) { \
|
||||||
|
R &= 0xF8; \
|
||||||
|
G &= 0xF8; \
|
||||||
|
B &= 0xF8; \
|
||||||
|
((unsigned char*)pDst)[S_1] = (unsigned char)( (R>>1)|(G>>6) ); \
|
||||||
|
((unsigned char*)pDst)[S_0] = (unsigned char)(((B>>3)|(G<<2))& 0xFF); \
|
||||||
|
}
|
||||||
|
#define ZYWRLE_YMASK16 0xFFFFFFFC
|
||||||
|
#define ZYWRLE_UVMASK16 0xFFFFFFF8
|
||||||
|
#define ZYWRLE_LOAD_PIXEL16(pSrc,R,G,B) { \
|
||||||
|
R = ((unsigned char*)pSrc)[S_1] & 0xF8; \
|
||||||
|
G = ((((unsigned char*)pSrc)[S_1]<< 5)|(((unsigned char*)pSrc)[S_0]>> 3))& 0xFC; \
|
||||||
|
B = (((unsigned char*)pSrc)[S_0]<< 3)& 0xF8; \
|
||||||
|
}
|
||||||
|
#define ZYWRLE_SAVE_PIXEL16(pDst,R,G,B) { \
|
||||||
|
R &= 0xF8; \
|
||||||
|
G &= 0xFC; \
|
||||||
|
B &= 0xF8; \
|
||||||
|
((unsigned char*)pDst)[S_1] = (unsigned char)( R |(G>>5) ); \
|
||||||
|
((unsigned char*)pDst)[S_0] = (unsigned char)(((B>>3)|(G<<3))& 0xFF); \
|
||||||
|
}
|
||||||
|
#define ZYWRLE_YMASK32 0xFFFFFFFF
|
||||||
|
#define ZYWRLE_UVMASK32 0xFFFFFFFF
|
||||||
|
#define ZYWRLE_LOAD_PIXEL32(pSrc,R,G,B) { \
|
||||||
|
R = ((unsigned char*)pSrc)[L_2]; \
|
||||||
|
G = ((unsigned char*)pSrc)[L_1]; \
|
||||||
|
B = ((unsigned char*)pSrc)[L_0]; \
|
||||||
|
}
|
||||||
|
#define ZYWRLE_SAVE_PIXEL32(pDst,R,G,B) { \
|
||||||
|
((unsigned char*)pDst)[L_2] = (unsigned char)R; \
|
||||||
|
((unsigned char*)pDst)[L_1] = (unsigned char)G; \
|
||||||
|
((unsigned char*)pDst)[L_0] = (unsigned char)B; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef ZYWRLE_ONCE
|
||||||
|
#define ZYWRLE_ONCE
|
||||||
|
|
||||||
|
#ifndef __STRICT_ANSI__
|
||||||
|
# define InlineX inline
|
||||||
|
#else
|
||||||
|
# define InlineX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ZYWRLE_ENCODE
|
||||||
|
/* Tables for Coefficients filtering. */
|
||||||
|
# ifndef ZYWRLE_QUANTIZE
|
||||||
|
/* Type A:lower bit omitting of EZW style. */
|
||||||
|
const static unsigned int zywrleParam[3][3]={
|
||||||
|
{0x0000F000,0x00000000,0x00000000},
|
||||||
|
{0x0000C000,0x00F0F0F0,0x00000000},
|
||||||
|
{0x0000C000,0x00C0C0C0,0x00F0F0F0},
|
||||||
|
/* {0x0000FF00,0x00000000,0x00000000},
|
||||||
|
{0x0000FF00,0x00FFFFFF,0x00000000},
|
||||||
|
{0x0000FF00,0x00FFFFFF,0x00FFFFFF}, */
|
||||||
|
};
|
||||||
|
# else
|
||||||
|
/* Type B:Non liner quantization filter. */
|
||||||
|
static const signed char zywrleConv[4][256]={
|
||||||
|
{ /* bi=5, bo=5 r=0.0:PSNR=24.849 */
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
},
|
||||||
|
{ /* bi=5, bo=5 r=2.0:PSNR=74.031 */
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 32,
|
||||||
|
32, 32, 32, 32, 32, 32, 32, 32,
|
||||||
|
32, 32, 32, 32, 32, 32, 32, 32,
|
||||||
|
48, 48, 48, 48, 48, 48, 48, 48,
|
||||||
|
48, 48, 48, 56, 56, 56, 56, 56,
|
||||||
|
56, 56, 56, 56, 64, 64, 64, 64,
|
||||||
|
64, 64, 64, 64, 72, 72, 72, 72,
|
||||||
|
72, 72, 72, 72, 80, 80, 80, 80,
|
||||||
|
80, 80, 88, 88, 88, 88, 88, 88,
|
||||||
|
88, 88, 88, 88, 88, 88, 96, 96,
|
||||||
|
96, 96, 96, 104, 104, 104, 104, 104,
|
||||||
|
104, 104, 104, 104, 104, 112, 112, 112,
|
||||||
|
112, 112, 112, 112, 112, 112, 120, 120,
|
||||||
|
120, 120, 120, 120, 120, 120, 120, 120,
|
||||||
|
0, -120, -120, -120, -120, -120, -120, -120,
|
||||||
|
-120, -120, -120, -112, -112, -112, -112, -112,
|
||||||
|
-112, -112, -112, -112, -104, -104, -104, -104,
|
||||||
|
-104, -104, -104, -104, -104, -104, -96, -96,
|
||||||
|
-96, -96, -96, -88, -88, -88, -88, -88,
|
||||||
|
-88, -88, -88, -88, -88, -88, -88, -80,
|
||||||
|
-80, -80, -80, -80, -80, -72, -72, -72,
|
||||||
|
-72, -72, -72, -72, -72, -64, -64, -64,
|
||||||
|
-64, -64, -64, -64, -64, -56, -56, -56,
|
||||||
|
-56, -56, -56, -56, -56, -56, -48, -48,
|
||||||
|
-48, -48, -48, -48, -48, -48, -48, -48,
|
||||||
|
-48, -32, -32, -32, -32, -32, -32, -32,
|
||||||
|
-32, -32, -32, -32, -32, -32, -32, -32,
|
||||||
|
-32, -32, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
},
|
||||||
|
{ /* bi=5, bo=4 r=2.0:PSNR=64.441 */
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
48, 48, 48, 48, 48, 48, 48, 48,
|
||||||
|
48, 48, 48, 48, 48, 48, 48, 48,
|
||||||
|
48, 48, 48, 48, 48, 48, 48, 48,
|
||||||
|
64, 64, 64, 64, 64, 64, 64, 64,
|
||||||
|
64, 64, 64, 64, 64, 64, 64, 64,
|
||||||
|
80, 80, 80, 80, 80, 80, 80, 80,
|
||||||
|
80, 80, 80, 80, 80, 88, 88, 88,
|
||||||
|
88, 88, 88, 88, 88, 88, 88, 88,
|
||||||
|
104, 104, 104, 104, 104, 104, 104, 104,
|
||||||
|
104, 104, 104, 112, 112, 112, 112, 112,
|
||||||
|
112, 112, 112, 112, 120, 120, 120, 120,
|
||||||
|
120, 120, 120, 120, 120, 120, 120, 120,
|
||||||
|
0, -120, -120, -120, -120, -120, -120, -120,
|
||||||
|
-120, -120, -120, -120, -120, -112, -112, -112,
|
||||||
|
-112, -112, -112, -112, -112, -112, -104, -104,
|
||||||
|
-104, -104, -104, -104, -104, -104, -104, -104,
|
||||||
|
-104, -88, -88, -88, -88, -88, -88, -88,
|
||||||
|
-88, -88, -88, -88, -80, -80, -80, -80,
|
||||||
|
-80, -80, -80, -80, -80, -80, -80, -80,
|
||||||
|
-80, -64, -64, -64, -64, -64, -64, -64,
|
||||||
|
-64, -64, -64, -64, -64, -64, -64, -64,
|
||||||
|
-64, -48, -48, -48, -48, -48, -48, -48,
|
||||||
|
-48, -48, -48, -48, -48, -48, -48, -48,
|
||||||
|
-48, -48, -48, -48, -48, -48, -48, -48,
|
||||||
|
-48, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
},
|
||||||
|
{ /* bi=5, bo=2 r=2.0:PSNR=43.175 */
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
88, 88, 88, 88, 88, 88, 88, 88,
|
||||||
|
88, 88, 88, 88, 88, 88, 88, 88,
|
||||||
|
88, 88, 88, 88, 88, 88, 88, 88,
|
||||||
|
88, 88, 88, 88, 88, 88, 88, 88,
|
||||||
|
88, 88, 88, 88, 88, 88, 88, 88,
|
||||||
|
88, 88, 88, 88, 88, 88, 88, 88,
|
||||||
|
88, 88, 88, 88, 88, 88, 88, 88,
|
||||||
|
88, 88, 88, 88, 88, 88, 88, 88,
|
||||||
|
0, -88, -88, -88, -88, -88, -88, -88,
|
||||||
|
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||||
|
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||||
|
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||||
|
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||||
|
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||||
|
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||||
|
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||||
|
-88, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const static signed char* zywrleParam[3][3][3]={
|
||||||
|
{{zywrleConv[0],zywrleConv[2],zywrleConv[0]},{zywrleConv[0],zywrleConv[0],zywrleConv[0]},{zywrleConv[0],zywrleConv[0],zywrleConv[0]}},
|
||||||
|
{{zywrleConv[0],zywrleConv[3],zywrleConv[0]},{zywrleConv[1],zywrleConv[1],zywrleConv[1]},{zywrleConv[0],zywrleConv[0],zywrleConv[0]}},
|
||||||
|
{{zywrleConv[0],zywrleConv[3],zywrleConv[0]},{zywrleConv[2],zywrleConv[2],zywrleConv[2]},{zywrleConv[1],zywrleConv[1],zywrleConv[1]}},
|
||||||
|
};
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static InlineX void Harr(signed char* pX0, signed char* pX1)
|
||||||
|
{
|
||||||
|
/* Piecewise-Linear Harr(PLHarr) */
|
||||||
|
int X0 = (int)*pX0, X1 = (int)*pX1;
|
||||||
|
int orgX0 = X0, orgX1 = X1;
|
||||||
|
if ((X0 ^ X1) & 0x80) {
|
||||||
|
/* differ sign */
|
||||||
|
X1 += X0;
|
||||||
|
if (((X1^orgX1)&0x80)==0) {
|
||||||
|
/* |X1| > |X0| */
|
||||||
|
X0 -= X1; /* H = -B */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* same sign */
|
||||||
|
X0 -= X1;
|
||||||
|
if (((X0 ^ orgX0) & 0x80) == 0) {
|
||||||
|
/* |X0| > |X1| */
|
||||||
|
X1 += X0; /* L = A */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*pX0 = (signed char)X1;
|
||||||
|
*pX1 = (signed char)X0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
1D-Wavelet transform.
|
||||||
|
|
||||||
|
In coefficients array, the famous 'pyramid' decomposition is well used.
|
||||||
|
|
||||||
|
1D Model:
|
||||||
|
|L0L0L0L0|L0L0L0L0|H0H0H0H0|H0H0H0H0| : level 0
|
||||||
|
|L1L1L1L1|H1H1H1H1|H0H0H0H0|H0H0H0H0| : level 1
|
||||||
|
|
||||||
|
But this method needs line buffer because H/L is different position from X0/X1.
|
||||||
|
So, I used 'interleave' decomposition instead of it.
|
||||||
|
|
||||||
|
1D Model:
|
||||||
|
|L0H0L0H0|L0H0L0H0|L0H0L0H0|L0H0L0H0| : level 0
|
||||||
|
|L1H0H1H0|L1H0H1H0|L1H0H1H0|L1H0H1H0| : level 1
|
||||||
|
|
||||||
|
In this method, H/L and X0/X1 is always same position.
|
||||||
|
This lead us to more speed and less memory.
|
||||||
|
Of cause, the result of both method is quite same
|
||||||
|
because its only difference is that coefficient position.
|
||||||
|
*/
|
||||||
|
static InlineX void WaveletLevel(int* data, int size, int l, int SkipPixel)
|
||||||
|
{
|
||||||
|
int s, ofs;
|
||||||
|
signed char* pX0;
|
||||||
|
signed char* end;
|
||||||
|
|
||||||
|
pX0 = (signed char*)data;
|
||||||
|
s = (8<<l)*SkipPixel;
|
||||||
|
end = pX0+(size>>(l+1))*s;
|
||||||
|
s -= 2;
|
||||||
|
ofs = (4<<l)*SkipPixel;
|
||||||
|
while (pX0 < end) {
|
||||||
|
Harr(pX0, pX0+ofs);
|
||||||
|
pX0++;
|
||||||
|
Harr(pX0, pX0+ofs);
|
||||||
|
pX0++;
|
||||||
|
Harr(pX0, pX0+ofs);
|
||||||
|
pX0 += s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#define InvWaveletLevel(d,s,l,pix) WaveletLevel(d,s,l,pix)
|
||||||
|
|
||||||
|
#ifdef ZYWRLE_ENCODE
|
||||||
|
# ifndef ZYWRLE_QUANTIZE
|
||||||
|
/* Type A:lower bit omitting of EZW style. */
|
||||||
|
static InlineX void FilterWaveletSquare(int* pBuf, int width, int height, int level, int l)
|
||||||
|
{
|
||||||
|
int r, s;
|
||||||
|
int x, y;
|
||||||
|
int* pH;
|
||||||
|
const unsigned int* pM;
|
||||||
|
|
||||||
|
pM = &(zywrleParam[level-1][l]);
|
||||||
|
s = 2<<l;
|
||||||
|
for (r = 1; r < 4; r++) {
|
||||||
|
pH = pBuf;
|
||||||
|
if (r & 0x01)
|
||||||
|
pH += s>>1;
|
||||||
|
if (r & 0x02)
|
||||||
|
pH += (s>>1)*width;
|
||||||
|
for (y = 0; y < height / s; y++) {
|
||||||
|
for (x = 0; x < width / s; x++) {
|
||||||
|
/*
|
||||||
|
these are same following code.
|
||||||
|
pH[x] = pH[x] / (~pM[x]+1) * (~pM[x]+1);
|
||||||
|
( round pH[x] with pM[x] bit )
|
||||||
|
'&' operator isn't 'round' but is 'floor'.
|
||||||
|
So, we must offset when pH[x] is negative.
|
||||||
|
*/
|
||||||
|
if (((signed char*)pH)[0] & 0x80)
|
||||||
|
((signed char*)pH)[0] += ~((signed char*)pM)[0];
|
||||||
|
if (((signed char*)pH)[1] & 0x80)
|
||||||
|
((signed char*)pH)[1] += ~((signed char*)pM)[1];
|
||||||
|
if (((signed char*)pH)[2] & 0x80)
|
||||||
|
((signed char*)pH)[2] += ~((signed char*)pM)[2];
|
||||||
|
*pH &= *pM;
|
||||||
|
pH += s;
|
||||||
|
}
|
||||||
|
pH += (s-1)*width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
/*
|
||||||
|
Type B:Non liner quantization filter.
|
||||||
|
|
||||||
|
Coefficients have Gaussian curve and smaller value which is
|
||||||
|
large part of coefficients isn't more important than larger value.
|
||||||
|
So, I use filter of Non liner quantize/dequantize table.
|
||||||
|
In general, Non liner quantize formula is explained as following.
|
||||||
|
|
||||||
|
y=f(x) = sign(x)*round( ((abs(x)/(2^7))^ r )* 2^(bo-1) )*2^(8-bo)
|
||||||
|
x=f-1(y) = sign(y)*round( ((abs(y)/(2^7))^(1/r))* 2^(bi-1) )*2^(8-bi)
|
||||||
|
( r:power coefficient bi:effective MSB in input bo:effective MSB in output )
|
||||||
|
|
||||||
|
r < 1.0 : Smaller value is more important than larger value.
|
||||||
|
r > 1.0 : Larger value is more important than smaller value.
|
||||||
|
r = 1.0 : Liner quantization which is same with EZW style.
|
||||||
|
|
||||||
|
r = 0.75 is famous non liner quantization used in MP3 audio codec.
|
||||||
|
In contrast to audio data, larger value is important in wavelet coefficients.
|
||||||
|
So, I select r = 2.0 table( quantize is x^2, dequantize sqrt(x) ).
|
||||||
|
|
||||||
|
As compared with EZW style liner quantization, this filter tended to be
|
||||||
|
more sharp edge and be more compression rate but be more blocking noise and be less quality.
|
||||||
|
Especially, the surface of graphic objects has distinguishable noise in middle quality mode.
|
||||||
|
|
||||||
|
We need only quantized-dequantized(filtered) value rather than quantized value itself
|
||||||
|
because all values are packed or palette-lized in later ZRLE section.
|
||||||
|
This lead us not to need to modify client decoder when we change
|
||||||
|
the filtering procedure in future.
|
||||||
|
Client only decodes coefficients given by encoder.
|
||||||
|
*/
|
||||||
|
static InlineX void FilterWaveletSquare(int* pBuf, int width, int height, int level, int l)
|
||||||
|
{
|
||||||
|
int r, s;
|
||||||
|
int x, y;
|
||||||
|
int* pH;
|
||||||
|
const signed char** pM;
|
||||||
|
|
||||||
|
pM = zywrleParam[level-1][l];
|
||||||
|
s = 2<<l;
|
||||||
|
for (r = 1; r < 4; r++) {
|
||||||
|
pH = pBuf;
|
||||||
|
if (r & 0x01)
|
||||||
|
pH += s>>1;
|
||||||
|
if (r & 0x02)
|
||||||
|
pH += (s>>1)*width;
|
||||||
|
for (y = 0; y < height / s; y++) {
|
||||||
|
for (x = 0; x < width / s; x++) {
|
||||||
|
((signed char*)pH)[0] = pM[0][((unsigned char*)pH)[0]];
|
||||||
|
((signed char*)pH)[1] = pM[1][((unsigned char*)pH)[1]];
|
||||||
|
((signed char*)pH)[2] = pM[2][((unsigned char*)pH)[2]];
|
||||||
|
pH += s;
|
||||||
|
}
|
||||||
|
pH += (s-1)*width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
static InlineX void Wavelet(int* pBuf, int width, int height, int level)
|
||||||
|
{
|
||||||
|
int l, s;
|
||||||
|
int* pTop;
|
||||||
|
int* pEnd;
|
||||||
|
|
||||||
|
for (l = 0; l < level; l++) {
|
||||||
|
pTop = pBuf;
|
||||||
|
pEnd = pBuf+height*width;
|
||||||
|
s = width<<l;
|
||||||
|
while (pTop < pEnd) {
|
||||||
|
WaveletLevel(pTop, width, l, 1);
|
||||||
|
pTop += s;
|
||||||
|
}
|
||||||
|
pTop = pBuf;
|
||||||
|
pEnd = pBuf+width;
|
||||||
|
s = 1<<l;
|
||||||
|
while (pTop < pEnd) {
|
||||||
|
WaveletLevel(pTop, height,l, width);
|
||||||
|
pTop += s;
|
||||||
|
}
|
||||||
|
FilterWaveletSquare(pBuf, width, height, level, l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef ZYWRLE_DECODE
|
||||||
|
static InlineX void InvWavelet(int* pBuf, int width, int height, int level)
|
||||||
|
{
|
||||||
|
int l, s;
|
||||||
|
int* pTop;
|
||||||
|
int* pEnd;
|
||||||
|
|
||||||
|
for (l = level - 1; l >= 0; l--) {
|
||||||
|
pTop = pBuf;
|
||||||
|
pEnd = pBuf+width;
|
||||||
|
s = 1<<l;
|
||||||
|
while (pTop < pEnd) {
|
||||||
|
InvWaveletLevel(pTop, height,l, width);
|
||||||
|
pTop += s;
|
||||||
|
}
|
||||||
|
pTop = pBuf;
|
||||||
|
pEnd = pBuf+height*width;
|
||||||
|
s = width<<l;
|
||||||
|
while (pTop < pEnd) {
|
||||||
|
InvWaveletLevel(pTop, width, l, 1);
|
||||||
|
pTop += s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Load/Save coefficients stuffs.
|
||||||
|
Coefficients manages as 24 bits little-endian pixel. */
|
||||||
|
#define ZYWRLE_LOAD_COEFF(pSrc,R,G,B) { \
|
||||||
|
R = ((signed char*)pSrc)[2]; \
|
||||||
|
G = ((signed char*)pSrc)[1]; \
|
||||||
|
B = ((signed char*)pSrc)[0]; \
|
||||||
|
}
|
||||||
|
#define ZYWRLE_SAVE_COEFF(pDst,R,G,B) { \
|
||||||
|
((signed char*)pDst)[2] = (signed char)R; \
|
||||||
|
((signed char*)pDst)[1] = (signed char)G; \
|
||||||
|
((signed char*)pDst)[0] = (signed char)B; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RGB <=> YUV conversion stuffs.
|
||||||
|
YUV coversion is explained as following formula in strict meaning:
|
||||||
|
Y = 0.299R + 0.587G + 0.114B ( 0<=Y<=255)
|
||||||
|
U = -0.169R - 0.331G + 0.500B (-128<=U<=127)
|
||||||
|
V = 0.500R - 0.419G - 0.081B (-128<=V<=127)
|
||||||
|
|
||||||
|
I use simple conversion RCT(reversible color transform) which is described
|
||||||
|
in JPEG-2000 specification.
|
||||||
|
Y = (R + 2G + B)/4 ( 0<=Y<=255)
|
||||||
|
U = B-G (-256<=U<=255)
|
||||||
|
V = R-G (-256<=V<=255)
|
||||||
|
*/
|
||||||
|
#define ROUND(x) (((x)<0)?0:(((x)>255)?255:(x)))
|
||||||
|
/* RCT is N-bit RGB to N-bit Y and N+1-bit UV.
|
||||||
|
For make Same N-bit, UV is lossy.
|
||||||
|
More exact PLHarr, we reduce to odd range(-127<=x<=127). */
|
||||||
|
#define ZYWRLE_RGBYUV1(R,G,B,Y,U,V,ymask,uvmask) { \
|
||||||
|
Y = (R+(G<<1)+B)>>2; \
|
||||||
|
U = B-G; \
|
||||||
|
V = R-G; \
|
||||||
|
Y -= 128; \
|
||||||
|
U >>= 1; \
|
||||||
|
V >>= 1; \
|
||||||
|
Y &= ymask; \
|
||||||
|
U &= uvmask; \
|
||||||
|
V &= uvmask; \
|
||||||
|
if (Y == -128) \
|
||||||
|
Y += (0xFFFFFFFF-ymask+1); \
|
||||||
|
if (U == -128) \
|
||||||
|
U += (0xFFFFFFFF-uvmask+1); \
|
||||||
|
if (V == -128) \
|
||||||
|
V += (0xFFFFFFFF-uvmask+1); \
|
||||||
|
}
|
||||||
|
#define ZYWRLE_YUVRGB1(R,G,B,Y,U,V) { \
|
||||||
|
Y += 128; \
|
||||||
|
U <<= 1; \
|
||||||
|
V <<= 1; \
|
||||||
|
G = Y-((U+V)>>2); \
|
||||||
|
B = U+G; \
|
||||||
|
R = V+G; \
|
||||||
|
G = ROUND(G); \
|
||||||
|
B = ROUND(B); \
|
||||||
|
R = ROUND(R); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
coefficient packing/unpacking stuffs.
|
||||||
|
Wavelet transform makes 4 sub coefficient image from 1 original image.
|
||||||
|
|
||||||
|
model with pyramid decomposition:
|
||||||
|
+------+------+
|
||||||
|
| | |
|
||||||
|
| L | Hx |
|
||||||
|
| | |
|
||||||
|
+------+------+
|
||||||
|
| | |
|
||||||
|
| H | Hxy |
|
||||||
|
| | |
|
||||||
|
+------+------+
|
||||||
|
|
||||||
|
So, we must transfer each sub images individually in strict meaning.
|
||||||
|
But at least ZRLE meaning, following one decompositon image is same as
|
||||||
|
avobe individual sub image. I use this format.
|
||||||
|
(Strictly saying, transfer order is reverse(Hxy->Hy->Hx->L)
|
||||||
|
for simplified procedure for any wavelet level.)
|
||||||
|
|
||||||
|
+------+------+
|
||||||
|
| L |
|
||||||
|
+------+------+
|
||||||
|
| Hx |
|
||||||
|
+------+------+
|
||||||
|
| Hy |
|
||||||
|
+------+------+
|
||||||
|
| Hxy |
|
||||||
|
+------+------+
|
||||||
|
*/
|
||||||
|
#define INC_PTR(data) \
|
||||||
|
data++; \
|
||||||
|
if( data-pData >= (w+uw) ){ \
|
||||||
|
data += scanline-(w+uw); \
|
||||||
|
pData = data; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZYWRLE_TRANSFER_COEFF(pBuf,data,r,w,h,scanline,level,TRANS) \
|
||||||
|
pH = pBuf; \
|
||||||
|
s = 2<<level; \
|
||||||
|
if (r & 0x01) \
|
||||||
|
pH += s>>1; \
|
||||||
|
if (r & 0x02) \
|
||||||
|
pH += (s>>1)*w; \
|
||||||
|
pEnd = pH+h*w; \
|
||||||
|
while (pH < pEnd) { \
|
||||||
|
pLine = pH+w; \
|
||||||
|
while (pH < pLine) { \
|
||||||
|
TRANS \
|
||||||
|
INC_PTR(data) \
|
||||||
|
pH += s; \
|
||||||
|
} \
|
||||||
|
pH += (s-1)*w; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZYWRLE_PACK_COEFF(pBuf,data,r,width,height,scanline,level) \
|
||||||
|
ZYWRLE_TRANSFER_COEFF(pBuf,data,r,width,height,scanline,level,ZYWRLE_LOAD_COEFF(pH,R,G,B);ZYWRLE_SAVE_PIXEL(data,R,G,B);)
|
||||||
|
|
||||||
|
#define ZYWRLE_UNPACK_COEFF(pBuf,data,r,width,height,scanline,level) \
|
||||||
|
ZYWRLE_TRANSFER_COEFF(pBuf,data,r,width,height,scanline,level,ZYWRLE_LOAD_PIXEL(data,R,G,B);ZYWRLE_SAVE_COEFF(pH,R,G,B);)
|
||||||
|
|
||||||
|
#define ZYWRLE_SAVE_UNALIGN(data,TRANS) \
|
||||||
|
pTop = pBuf+w*h; \
|
||||||
|
pEnd = pBuf + (w+uw)*(h+uh); \
|
||||||
|
while (pTop < pEnd) { \
|
||||||
|
TRANS \
|
||||||
|
INC_PTR(data) \
|
||||||
|
pTop++; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZYWRLE_LOAD_UNALIGN(data,TRANS) \
|
||||||
|
pTop = pBuf+w*h; \
|
||||||
|
if (uw) { \
|
||||||
|
pData= data + w; \
|
||||||
|
pEnd = (int*)(pData+ h*scanline); \
|
||||||
|
while (pData < (PIXEL_T*)pEnd) { \
|
||||||
|
pLine = (int*)(pData + uw); \
|
||||||
|
while (pData < (PIXEL_T*)pLine) { \
|
||||||
|
TRANS \
|
||||||
|
pData++; \
|
||||||
|
pTop++; \
|
||||||
|
} \
|
||||||
|
pData += scanline-uw; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (uh) { \
|
||||||
|
pData= data + h*scanline; \
|
||||||
|
pEnd = (int*)(pData+ uh*scanline); \
|
||||||
|
while (pData < (PIXEL_T*)pEnd) { \
|
||||||
|
pLine = (int*)(pData + w); \
|
||||||
|
while (pData < (PIXEL_T*)pLine) { \
|
||||||
|
TRANS \
|
||||||
|
pData++; \
|
||||||
|
pTop++; \
|
||||||
|
} \
|
||||||
|
pData += scanline-w; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (uw && uh) { \
|
||||||
|
pData= data + w+ h*scanline; \
|
||||||
|
pEnd = (int*)(pData+ uh*scanline); \
|
||||||
|
while (pData < (PIXEL_T*)pEnd) { \
|
||||||
|
pLine = (int*)(pData + uw); \
|
||||||
|
while (pData < (PIXEL_T*)pLine) { \
|
||||||
|
TRANS \
|
||||||
|
pData++; \
|
||||||
|
pTop++; \
|
||||||
|
} \
|
||||||
|
pData += scanline-uw; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
static InlineX void zywrleCalcSize(int* pW, int* pH, int level)
|
||||||
|
{
|
||||||
|
*pW &= ~((1<<level)-1);
|
||||||
|
*pH &= ~((1<<level)-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ZYWRLE_ONCE */
|
||||||
|
|
||||||
|
#ifndef CPIXEL
|
||||||
|
#ifdef ZYWRLE_ENCODE
|
||||||
|
static InlineX void ZYWRLE_RGBYUV(int* pBuf, PIXEL_T* data, int width, int height, int scanline)
|
||||||
|
{
|
||||||
|
int R, G, B;
|
||||||
|
int Y, U, V;
|
||||||
|
int* pLine;
|
||||||
|
int* pEnd;
|
||||||
|
pEnd = pBuf+height*width;
|
||||||
|
while (pBuf < pEnd) {
|
||||||
|
pLine = pBuf+width;
|
||||||
|
while (pBuf < pLine) {
|
||||||
|
ZYWRLE_LOAD_PIXEL(data,R,G,B);
|
||||||
|
ZYWRLE_RGBYUV1(R,G,B,Y,U,V,ZYWRLE_YMASK,ZYWRLE_UVMASK);
|
||||||
|
ZYWRLE_SAVE_COEFF(pBuf,V,Y,U);
|
||||||
|
pBuf++;
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
data += scanline-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef ZYWRLE_DECODE
|
||||||
|
static InlineX void ZYWRLE_YUVRGB(int* pBuf, PIXEL_T* data, int width, int height, int scanline) {
|
||||||
|
int R, G, B;
|
||||||
|
int Y, U, V;
|
||||||
|
int* pLine;
|
||||||
|
int* pEnd;
|
||||||
|
pEnd = pBuf+height*width;
|
||||||
|
while (pBuf < pEnd) {
|
||||||
|
pLine = pBuf+width;
|
||||||
|
while (pBuf < pLine) {
|
||||||
|
ZYWRLE_LOAD_COEFF(pBuf,V,Y,U);
|
||||||
|
ZYWRLE_YUVRGB1(R,G,B,Y,U,V);
|
||||||
|
ZYWRLE_SAVE_PIXEL(data,R,G,B);
|
||||||
|
pBuf++;
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
data += scanline-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ZYWRLE_ENCODE
|
||||||
|
PIXEL_T* ZYWRLE_ANALYZE(PIXEL_T* dst, PIXEL_T* src, int w, int h, int scanline, int level, int* pBuf) {
|
||||||
|
int l;
|
||||||
|
int uw = w;
|
||||||
|
int uh = h;
|
||||||
|
int* pTop;
|
||||||
|
int* pEnd;
|
||||||
|
int* pLine;
|
||||||
|
PIXEL_T* pData;
|
||||||
|
int R, G, B;
|
||||||
|
int s;
|
||||||
|
int* pH;
|
||||||
|
|
||||||
|
zywrleCalcSize(&w, &h, level);
|
||||||
|
if (w == 0 || h == 0)
|
||||||
|
return NULL;
|
||||||
|
uw -= w;
|
||||||
|
uh -= h;
|
||||||
|
|
||||||
|
pData = dst;
|
||||||
|
ZYWRLE_LOAD_UNALIGN(src,*(PIXEL_T*)pTop=*pData;)
|
||||||
|
ZYWRLE_RGBYUV(pBuf, src, w, h, scanline);
|
||||||
|
Wavelet(pBuf, w, h, level);
|
||||||
|
for (l = 0; l < level; l++) {
|
||||||
|
ZYWRLE_PACK_COEFF(pBuf, dst, 3, w, h, scanline, l);
|
||||||
|
ZYWRLE_PACK_COEFF(pBuf, dst, 2, w, h, scanline, l);
|
||||||
|
ZYWRLE_PACK_COEFF(pBuf, dst, 1, w, h, scanline, l);
|
||||||
|
if (l == level - 1) {
|
||||||
|
ZYWRLE_PACK_COEFF(pBuf, dst, 0, w, h, scanline, l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ZYWRLE_SAVE_UNALIGN(dst,*dst=*(PIXEL_T*)pTop;)
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef ZYWRLE_DECODE
|
||||||
|
PIXEL_T* ZYWRLE_SYNTHESIZE(PIXEL_T* dst, PIXEL_T* src, int w, int h, int scanline, int level, int* pBuf)
|
||||||
|
{
|
||||||
|
int l;
|
||||||
|
int uw = w;
|
||||||
|
int uh = h;
|
||||||
|
int* pTop;
|
||||||
|
int* pEnd;
|
||||||
|
int* pLine;
|
||||||
|
PIXEL_T* pData;
|
||||||
|
int R, G, B;
|
||||||
|
int s;
|
||||||
|
int* pH;
|
||||||
|
|
||||||
|
zywrleCalcSize(&w, &h, level);
|
||||||
|
if (w == 0 || h == 0)
|
||||||
|
return NULL;
|
||||||
|
uw -= w;
|
||||||
|
uh -= h;
|
||||||
|
|
||||||
|
pData = src;
|
||||||
|
for (l = 0; l < level; l++) {
|
||||||
|
ZYWRLE_UNPACK_COEFF(pBuf, src, 3, w, h, scanline, l);
|
||||||
|
ZYWRLE_UNPACK_COEFF(pBuf, src, 2, w, h, scanline, l);
|
||||||
|
ZYWRLE_UNPACK_COEFF(pBuf, src, 1, w, h, scanline, l);
|
||||||
|
if (l == level - 1) {
|
||||||
|
ZYWRLE_UNPACK_COEFF(pBuf, src, 0, w, h, scanline, l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ZYWRLE_SAVE_UNALIGN(src,*(PIXEL_T*)pTop=*src;)
|
||||||
|
InvWavelet(pBuf, w, h, level);
|
||||||
|
ZYWRLE_YUVRGB(pBuf, dst, w, h, scanline);
|
||||||
|
ZYWRLE_LOAD_UNALIGN(dst,*pData=*(PIXEL_T*)pTop;)
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* CPIXEL */
|
||||||
|
|
||||||
|
#undef ZYWRLE_RGBYUV
|
||||||
|
#undef ZYWRLE_YUVRGB
|
||||||
|
#undef ZYWRLE_LOAD_PIXEL
|
||||||
|
#undef ZYWRLE_SAVE_PIXEL
|
613
src/main.c
613
src/main.c
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Andri Yngvason
|
* Copyright (c) 2020 - 2022 Andri Yngvason
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -24,10 +24,13 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <aml.h>
|
#include <aml.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "pixman.h"
|
#include "pixman.h"
|
||||||
#include "xdg-shell.h"
|
#include "xdg-shell.h"
|
||||||
|
@ -36,13 +39,21 @@
|
||||||
#include "pointer.h"
|
#include "pointer.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
#include "vnc.h"
|
#include "vnc.h"
|
||||||
|
#include "pixels.h"
|
||||||
|
#include "region.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "renderer.h"
|
||||||
|
#include "renderer-egl.h"
|
||||||
|
#include "linux-dmabuf-unstable-v1.h"
|
||||||
|
#include "time-util.h"
|
||||||
|
#include "output.h"
|
||||||
|
#include "data-control.h"
|
||||||
|
|
||||||
struct buffer {
|
#define CANARY_TICK_PERIOD INT64_C(100000) // us
|
||||||
int width, height, stride;
|
#define CANARY_LETHALITY_LEVEL INT64_C(8000) // us
|
||||||
size_t size;
|
|
||||||
enum wl_shm_format format;
|
struct point {
|
||||||
struct wl_buffer* wl_buffer;
|
double x, y;
|
||||||
void* pixels;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct window {
|
struct window {
|
||||||
|
@ -50,20 +61,41 @@ struct window {
|
||||||
struct xdg_surface* xdg_surface;
|
struct xdg_surface* xdg_surface;
|
||||||
struct xdg_toplevel* xdg_toplevel;
|
struct xdg_toplevel* xdg_toplevel;
|
||||||
|
|
||||||
struct buffer* buffer;
|
struct buffer* buffers[3];
|
||||||
|
struct buffer* back_buffer;
|
||||||
|
int buffer_index;
|
||||||
|
|
||||||
|
struct pixman_region16 current_damage;
|
||||||
|
|
||||||
|
struct vnc_client* vnc;
|
||||||
|
void* vnc_fb;
|
||||||
|
|
||||||
|
bool is_frame_committed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void register_frame_callback(void);
|
||||||
|
|
||||||
static struct wl_display* wl_display;
|
static struct wl_display* wl_display;
|
||||||
static struct wl_registry* wl_registry;
|
static struct wl_registry* wl_registry;
|
||||||
struct wl_compositor* wl_compositor = NULL;
|
struct wl_compositor* wl_compositor = NULL;
|
||||||
struct wl_shm* wl_shm = NULL;
|
struct wl_shm* wl_shm = NULL;
|
||||||
|
struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1 = NULL;
|
||||||
|
struct gbm_device* gbm_device = NULL;
|
||||||
static struct xdg_wm_base* xdg_wm_base;
|
static struct xdg_wm_base* xdg_wm_base;
|
||||||
static struct wl_list seats;
|
static struct wl_list seats;
|
||||||
|
static struct wl_list outputs;
|
||||||
struct pointer_collection* pointers;
|
struct pointer_collection* pointers;
|
||||||
struct keyboard_collection* keyboards;
|
struct keyboard_collection* keyboards;
|
||||||
|
static int drm_fd = -1;
|
||||||
|
static uint64_t last_canary_tick;
|
||||||
|
static struct data_control* data_control;
|
||||||
|
static struct zwlr_data_control_manager_v1 *manager;
|
||||||
|
static struct vnc_client* vnc;
|
||||||
|
|
||||||
static enum wl_shm_format wl_shm_format;
|
static bool have_egl = false;
|
||||||
static bool have_format = false;
|
|
||||||
|
static uint32_t shm_format = DRM_FORMAT_INVALID;
|
||||||
|
static uint32_t dmabuf_format = DRM_FORMAT_INVALID;
|
||||||
|
|
||||||
static bool do_run = true;
|
static bool do_run = true;
|
||||||
|
|
||||||
|
@ -86,6 +118,9 @@ static void on_seat_capability_change(struct seat* seat)
|
||||||
struct wl_keyboard* wl_keyboard =
|
struct wl_keyboard* wl_keyboard =
|
||||||
wl_seat_get_keyboard(seat->wl_seat);
|
wl_seat_get_keyboard(seat->wl_seat);
|
||||||
keyboard_collection_add_wl_keyboard(keyboards, wl_keyboard);
|
keyboard_collection_add_wl_keyboard(keyboards, wl_keyboard);
|
||||||
|
|
||||||
|
data_control = malloc(sizeof(data_control));
|
||||||
|
data_control_init(data_control, seat, manager);
|
||||||
} else {
|
} else {
|
||||||
// TODO Remove
|
// TODO Remove
|
||||||
}
|
}
|
||||||
|
@ -101,6 +136,9 @@ static void registry_add(void* data, struct wl_registry* registry, uint32_t id,
|
||||||
xdg_wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1);
|
xdg_wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1);
|
||||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||||
wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
|
wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
|
||||||
|
} else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) {
|
||||||
|
zwp_linux_dmabuf_v1 = wl_registry_bind(registry, id,
|
||||||
|
&zwp_linux_dmabuf_v1_interface, 2);
|
||||||
} else if (strcmp(interface, "wl_seat") == 0) {
|
} else if (strcmp(interface, "wl_seat") == 0) {
|
||||||
struct wl_seat* wl_seat;
|
struct wl_seat* wl_seat;
|
||||||
wl_seat = wl_registry_bind(registry, id, &wl_seat_interface, 5);
|
wl_seat = wl_registry_bind(registry, id, &wl_seat_interface, 5);
|
||||||
|
@ -113,6 +151,19 @@ static void registry_add(void* data, struct wl_registry* registry, uint32_t id,
|
||||||
|
|
||||||
wl_list_insert(&seats, &seat->link);
|
wl_list_insert(&seats, &seat->link);
|
||||||
seat->on_capability_change = on_seat_capability_change;
|
seat->on_capability_change = on_seat_capability_change;
|
||||||
|
} else if (strcmp(interface, "wl_output") == 0) {
|
||||||
|
struct wl_output* wl_output;
|
||||||
|
wl_output = wl_registry_bind(registry, id, &wl_output_interface, 2);
|
||||||
|
|
||||||
|
struct output* output = output_new(wl_output, id);
|
||||||
|
if (!output) {
|
||||||
|
wl_output_destroy(wl_output);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_insert(&outputs, &output->link);
|
||||||
|
} else if (strcmp(interface, zwlr_data_control_manager_v1_interface.name) == 0) {
|
||||||
|
manager = wl_registry_bind(registry, id, &zwlr_data_control_manager_v1_interface, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,77 +181,26 @@ static const struct wl_registry_listener registry_listener = {
|
||||||
.global_remove = registry_remove,
|
.global_remove = registry_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct buffer* buffer_create(int width, int height, int stride,
|
static void handle_shm_format(void* data, struct wl_shm* shm, uint32_t format)
|
||||||
enum wl_shm_format format)
|
|
||||||
{
|
|
||||||
struct buffer* self = calloc(1, sizeof(*self));
|
|
||||||
if (!self)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
self->width = width;
|
|
||||||
self->height = height;
|
|
||||||
self->stride = stride;
|
|
||||||
self->format = format;
|
|
||||||
|
|
||||||
self->size = height * stride;
|
|
||||||
int fd = shm_alloc_fd(self->size);
|
|
||||||
if (fd < 0)
|
|
||||||
goto failure;
|
|
||||||
|
|
||||||
self->pixels = mmap(NULL, self->size, PROT_READ | PROT_WRITE,
|
|
||||||
MAP_SHARED, fd, 0);
|
|
||||||
if (!self->pixels)
|
|
||||||
goto mmap_failure;
|
|
||||||
|
|
||||||
struct wl_shm_pool* pool = wl_shm_create_pool(wl_shm, fd, self->size);
|
|
||||||
if (!pool)
|
|
||||||
goto pool_failure;
|
|
||||||
|
|
||||||
self->wl_buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
|
|
||||||
stride, format);
|
|
||||||
wl_shm_pool_destroy(pool);
|
|
||||||
if (!self->wl_buffer)
|
|
||||||
goto shm_failure;
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
return self;
|
|
||||||
|
|
||||||
shm_failure:
|
|
||||||
pool_failure:
|
|
||||||
munmap(self->pixels, self->size);
|
|
||||||
mmap_failure:
|
|
||||||
close(fd);
|
|
||||||
failure:
|
|
||||||
free(self);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void buffer_destroy(struct buffer* self)
|
|
||||||
{
|
|
||||||
wl_buffer_destroy(self->wl_buffer);
|
|
||||||
munmap(self->pixels, self->size);
|
|
||||||
free(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void shm_format(void* data, struct wl_shm* shm, uint32_t format)
|
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)wl_shm;
|
(void)wl_shm;
|
||||||
|
|
||||||
if (have_format)
|
if (shm_format != DRM_FORMAT_INVALID)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (format) {
|
uint32_t drm_format = drm_format_from_wl_shm(format);
|
||||||
case WL_SHM_FORMAT_XRGB8888:
|
|
||||||
wl_shm_format = format;
|
switch (drm_format) {
|
||||||
have_format = true;
|
case DRM_FORMAT_XRGB8888:
|
||||||
|
shm_format = drm_format;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support more formats
|
// TODO: Support more formats
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_shm_listener shm_listener = {
|
static const struct wl_shm_listener shm_listener = {
|
||||||
.format = shm_format,
|
.format = handle_shm_format,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void xdg_wm_base_ping(void* data, struct xdg_wm_base* shell,
|
static void xdg_wm_base_ping(void* data, struct xdg_wm_base* shell,
|
||||||
|
@ -213,6 +213,26 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
||||||
.ping = xdg_wm_base_ping,
|
.ping = xdg_wm_base_ping,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void handle_dmabuf_format(void *data,
|
||||||
|
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
(void)zwp_linux_dmabuf;
|
||||||
|
|
||||||
|
if (dmabuf_format != DRM_FORMAT_INVALID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case DRM_FORMAT_XRGB8888:
|
||||||
|
case DRM_FORMAT_XBGR8888:
|
||||||
|
dmabuf_format = format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
|
||||||
|
.format = handle_dmabuf_format,
|
||||||
|
};
|
||||||
|
|
||||||
void on_wayland_event(void* obj)
|
void on_wayland_event(void* obj)
|
||||||
{
|
{
|
||||||
int rc = wl_display_prepare_read(wl_display);
|
int rc = wl_display_prepare_read(wl_display);
|
||||||
|
@ -263,7 +283,88 @@ static int init_signal_handler(void)
|
||||||
|
|
||||||
static void window_attach(struct window* w, int x, int y)
|
static void window_attach(struct window* w, int x, int y)
|
||||||
{
|
{
|
||||||
wl_surface_attach(w->wl_surface, w->buffer->wl_buffer, x, y);
|
w->back_buffer->is_attached = true;
|
||||||
|
wl_surface_attach(w->wl_surface, w->back_buffer->wl_buffer, x, y);
|
||||||
|
wl_surface_set_buffer_scale(window->wl_surface,
|
||||||
|
window->back_buffer->scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct point surface_coord_to_buffer_coord(double x, double y)
|
||||||
|
{
|
||||||
|
double scale = output_list_get_max_scale(&outputs);
|
||||||
|
|
||||||
|
struct point result = {
|
||||||
|
.x = round(x * scale),
|
||||||
|
.y = round(y * scale),
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct point buffer_coord_to_surface_coord(double x, double y)
|
||||||
|
{
|
||||||
|
double scale = output_list_get_max_scale(&outputs);
|
||||||
|
|
||||||
|
struct point result = {
|
||||||
|
.x = x / scale,
|
||||||
|
.y = y / scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void window_calculate_transform(struct window* w, double* scale,
|
||||||
|
int* x_pos, int* y_pos)
|
||||||
|
{
|
||||||
|
double src_width = vnc_client_get_width(w->vnc);
|
||||||
|
double src_height = vnc_client_get_height(w->vnc);
|
||||||
|
double dst_width = w->back_buffer->width;
|
||||||
|
double dst_height = w->back_buffer->height;
|
||||||
|
|
||||||
|
double hratio = (double)dst_width / (double)src_width;
|
||||||
|
double vratio = (double)dst_height / (double)src_height;
|
||||||
|
*scale = fmin(hratio, vratio);
|
||||||
|
|
||||||
|
if (hratio < vratio + 0.01 && hratio > vratio - 0.01) {
|
||||||
|
*x_pos = 0;
|
||||||
|
*y_pos = 0;
|
||||||
|
} else if (hratio < vratio) {
|
||||||
|
*x_pos = 0;
|
||||||
|
*y_pos = round(dst_height / 2.0 - *scale * src_height / 2.0);
|
||||||
|
} else {
|
||||||
|
*x_pos = round(dst_width / 2.0 - *scale * src_width / 2.0);
|
||||||
|
*y_pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void window_transfer_pixels(struct window* w)
|
||||||
|
{
|
||||||
|
double scale;
|
||||||
|
int x_pos, y_pos;
|
||||||
|
window_calculate_transform(w, &scale, &x_pos, &y_pos);
|
||||||
|
|
||||||
|
if (w->vnc->n_av_frames != 0) {
|
||||||
|
assert(have_egl);
|
||||||
|
|
||||||
|
render_av_frames_egl(w->back_buffer, w->vnc->av_frames,
|
||||||
|
w->vnc->n_av_frames, scale, x_pos, y_pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct image image = {
|
||||||
|
.pixels = w->vnc_fb,
|
||||||
|
.width = vnc_client_get_width(w->vnc),
|
||||||
|
.height = vnc_client_get_height(w->vnc),
|
||||||
|
.stride = vnc_client_get_stride(w->vnc),
|
||||||
|
// TODO: Get the format from the vnc module
|
||||||
|
.format = w->back_buffer->format,
|
||||||
|
.damage = &w->current_damage,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (have_egl)
|
||||||
|
render_image_egl(w->back_buffer, &image, scale, x_pos, y_pos);
|
||||||
|
else
|
||||||
|
render_image(w->back_buffer, &image, scale, x_pos, y_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void window_commit(struct window* w)
|
static void window_commit(struct window* w)
|
||||||
|
@ -271,6 +372,12 @@ static void window_commit(struct window* w)
|
||||||
wl_surface_commit(w->wl_surface);
|
wl_surface_commit(w->wl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void window_swap(struct window* w)
|
||||||
|
{
|
||||||
|
w->buffer_index = (w->buffer_index + 1) % 3;
|
||||||
|
w->back_buffer = w->buffers[w->buffer_index];
|
||||||
|
}
|
||||||
|
|
||||||
static void window_damage(struct window* w, int x, int y, int width, int height)
|
static void window_damage(struct window* w, int x, int y, int width, int height)
|
||||||
{
|
{
|
||||||
wl_surface_damage(w->wl_surface, x, y, width, height);
|
wl_surface_damage(w->wl_surface, x, y, width, height);
|
||||||
|
@ -293,10 +400,36 @@ static const struct xdg_surface_listener xdg_surface_listener = {
|
||||||
.configure = xdg_surface_configure,
|
.configure = xdg_surface_configure,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void window_resize(struct window* w, int width, int height, int scale)
|
||||||
|
{
|
||||||
|
if (width == 0 || height == 0 || scale == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (w->back_buffer && w->back_buffer->width == width &&
|
||||||
|
w->back_buffer->height == height &&
|
||||||
|
w->back_buffer->scale == scale)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
buffer_destroy(w->buffers[i]);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
w->buffers[i] = have_egl
|
||||||
|
? buffer_create_dmabuf(scale * width, scale * height,
|
||||||
|
dmabuf_format)
|
||||||
|
: buffer_create_shm(scale * width, scale * height,
|
||||||
|
scale * 4 * width, shm_format);
|
||||||
|
w->buffers[i]->scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
w->back_buffer = w->buffers[0];
|
||||||
|
}
|
||||||
|
|
||||||
static void xdg_toplevel_configure(void* data, struct xdg_toplevel* toplevel,
|
static void xdg_toplevel_configure(void* data, struct xdg_toplevel* toplevel,
|
||||||
int32_t width, int32_t height, struct wl_array* state)
|
int32_t width, int32_t height, struct wl_array* state)
|
||||||
{
|
{
|
||||||
// What to do here?
|
int32_t scale = output_list_get_max_scale(&outputs);
|
||||||
|
window_resize(data, width, height, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xdg_toplevel_close(void* data, struct xdg_toplevel* toplevel)
|
static void xdg_toplevel_close(void* data, struct xdg_toplevel* toplevel)
|
||||||
|
@ -315,6 +448,8 @@ static struct window* window_create(const char* app_id, const char* title)
|
||||||
if (!w)
|
if (!w)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
pixman_region_init(&w->current_damage);
|
||||||
|
|
||||||
w->wl_surface = wl_compositor_create_surface(wl_compositor);
|
w->wl_surface = wl_compositor_create_surface(wl_compositor);
|
||||||
if (!w->wl_surface)
|
if (!w->wl_surface)
|
||||||
goto wl_surface_failure;
|
goto wl_surface_failure;
|
||||||
|
@ -348,12 +483,14 @@ wl_surface_failure:
|
||||||
|
|
||||||
static void window_destroy(struct window* w)
|
static void window_destroy(struct window* w)
|
||||||
{
|
{
|
||||||
if (w->buffer)
|
for (int i = 0; i < 3; ++i)
|
||||||
buffer_destroy(w->buffer);
|
buffer_destroy(w->buffers[i]);
|
||||||
|
|
||||||
|
free(w->vnc_fb);
|
||||||
xdg_toplevel_destroy(w->xdg_toplevel);
|
xdg_toplevel_destroy(w->xdg_toplevel);
|
||||||
xdg_surface_destroy(w->xdg_surface);
|
xdg_surface_destroy(w->xdg_surface);
|
||||||
wl_surface_destroy(w->wl_surface);
|
wl_surface_destroy(w->wl_surface);
|
||||||
|
pixman_region_fini(&w->current_damage);
|
||||||
free(w);
|
free(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,8 +499,16 @@ void on_pointer_event(struct pointer_collection* collection,
|
||||||
{
|
{
|
||||||
struct vnc_client* client = collection->userdata;
|
struct vnc_client* client = collection->userdata;
|
||||||
|
|
||||||
int x = wl_fixed_to_int(pointer->x);
|
double scale;
|
||||||
int y = wl_fixed_to_int(pointer->y);
|
int x_pos, y_pos;
|
||||||
|
window_calculate_transform(window, &scale, &x_pos, &y_pos);
|
||||||
|
|
||||||
|
struct point coord = surface_coord_to_buffer_coord(
|
||||||
|
wl_fixed_to_double(pointer->x),
|
||||||
|
wl_fixed_to_double(pointer->y));
|
||||||
|
|
||||||
|
int x = round((coord.x - (double)x_pos) / scale);
|
||||||
|
int y = round((coord.y - (double)y_pos) / scale);
|
||||||
|
|
||||||
enum pointer_button_mask pressed = pointer->pressed;
|
enum pointer_button_mask pressed = pointer->pressed;
|
||||||
int vertical_steps = pointer->vertical_scroll_steps;
|
int vertical_steps = pointer->vertical_scroll_steps;
|
||||||
|
@ -378,14 +523,14 @@ void on_pointer_event(struct pointer_collection* collection,
|
||||||
if (vertical_steps < 0) {
|
if (vertical_steps < 0) {
|
||||||
vertical_steps *= -1;
|
vertical_steps *= -1;
|
||||||
scroll_mask |= POINTER_SCROLL_UP;
|
scroll_mask |= POINTER_SCROLL_UP;
|
||||||
} else {
|
} else if (vertical_steps > 0) {
|
||||||
scroll_mask |= POINTER_SCROLL_DOWN;
|
scroll_mask |= POINTER_SCROLL_DOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (horizontal_steps < 0) {
|
if (horizontal_steps < 0) {
|
||||||
horizontal_steps *= -1;
|
horizontal_steps *= -1;
|
||||||
scroll_mask |= POINTER_SCROLL_LEFT;
|
scroll_mask |= POINTER_SCROLL_LEFT;
|
||||||
} else {
|
} else if (horizontal_steps > 0) {
|
||||||
scroll_mask |= POINTER_SCROLL_RIGHT;
|
scroll_mask |= POINTER_SCROLL_RIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,33 +561,51 @@ void on_keyboard_event(struct keyboard_collection* collection,
|
||||||
|
|
||||||
int on_vnc_client_alloc_fb(struct vnc_client* client)
|
int on_vnc_client_alloc_fb(struct vnc_client* client)
|
||||||
{
|
{
|
||||||
assert(!window); // TODO: Support resizing
|
|
||||||
|
|
||||||
int width = vnc_client_get_width(client);
|
int width = vnc_client_get_width(client);
|
||||||
int height = vnc_client_get_height(client);
|
int height = vnc_client_get_height(client);
|
||||||
int stride = vnc_client_get_stride(client);
|
int stride = vnc_client_get_stride(client);
|
||||||
|
|
||||||
|
if (!window) {
|
||||||
window = window_create(app_id, vnc_client_get_desktop_name(client));
|
window = window_create(app_id, vnc_client_get_desktop_name(client));
|
||||||
if (!window)
|
window->vnc = client;
|
||||||
return -1;
|
|
||||||
|
|
||||||
window->buffer = buffer_create(width, height, stride, wl_shm_format);
|
int32_t scale = output_list_get_max_scale(&outputs);
|
||||||
vnc_client_set_fb(client, window->buffer->pixels);
|
window_resize(window, width, height, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(window->vnc_fb);
|
||||||
|
window->vnc_fb = malloc(height * stride);
|
||||||
|
assert(window->vnc_fb);
|
||||||
|
|
||||||
|
vnc_client_set_fb(client, window->vnc_fb);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_vnc_client_update_fb(struct vnc_client* client)
|
static void get_frame_damage(struct vnc_client* client,
|
||||||
|
struct pixman_region16* damage)
|
||||||
{
|
{
|
||||||
if (!pixman_region_not_empty(&client->damage))
|
pixman_region_union(damage, damage, &client->damage);
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: Make sure that the buffer is released at this point, or make
|
for (int i = 0; i < client->n_av_frames; ++i) {
|
||||||
// this a side-buffer and copy damaged regions into double buffers.
|
const struct vnc_av_frame* frame = client->av_frames[i];
|
||||||
window_attach(window, 0, 0);
|
|
||||||
|
|
||||||
|
pixman_region_union_rect(damage, damage, frame->x, frame->y,
|
||||||
|
frame->width, frame->height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void apply_buffer_damage(struct pixman_region16* damage)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
pixman_region_union(&window->buffers[i]->damage,
|
||||||
|
&window->buffers[i]->damage, damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void window_damage_region(struct window* w,
|
||||||
|
struct pixman_region16* damage)
|
||||||
|
{
|
||||||
int n_rects = 0;
|
int n_rects = 0;
|
||||||
struct pixman_box16* box = pixman_region_rectangles(&client->damage,
|
struct pixman_box16* box = pixman_region_rectangles(damage, &n_rects);
|
||||||
&n_rects);
|
|
||||||
|
|
||||||
for (int i = 0; i < n_rects; ++i) {
|
for (int i = 0; i < n_rects; ++i) {
|
||||||
int x = box[i].x1;
|
int x = box[i].x1;
|
||||||
|
@ -450,10 +613,83 @@ void on_vnc_client_update_fb(struct vnc_client* client)
|
||||||
int width = box[i].x2 - x;
|
int width = box[i].x2 - x;
|
||||||
int height = box[i].y2 - y;
|
int height = box[i].y2 - y;
|
||||||
|
|
||||||
window_damage(window, x, y, width, height);
|
window_damage(w, x, y, width, height);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void render_from_vnc(void)
|
||||||
|
{
|
||||||
|
if (!pixman_region_not_empty(&window->current_damage) &&
|
||||||
|
window->vnc->n_av_frames == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (window->is_frame_committed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (window->back_buffer->is_attached)
|
||||||
|
fprintf(stderr, "Oops, back-buffer is still attached.\n");
|
||||||
|
|
||||||
|
window_attach(window, 0, 0);
|
||||||
|
|
||||||
|
double scale;
|
||||||
|
int x_pos, y_pos;
|
||||||
|
window_calculate_transform(window, &scale, &x_pos, &y_pos);
|
||||||
|
|
||||||
|
struct pixman_region16 damage_scaled = { 0 }, buffer_damage = { 0 },
|
||||||
|
surface_damage = { 0 };
|
||||||
|
region_scale(&damage_scaled, &window->current_damage, scale);
|
||||||
|
region_translate(&buffer_damage, &damage_scaled, x_pos, y_pos);
|
||||||
|
pixman_region_clear(&damage_scaled);
|
||||||
|
|
||||||
|
double output_scale = output_list_get_max_scale(&outputs);
|
||||||
|
struct point scoord = buffer_coord_to_surface_coord(x_pos, y_pos);
|
||||||
|
region_scale(&damage_scaled, &window->current_damage,
|
||||||
|
scale / output_scale);
|
||||||
|
region_translate(&surface_damage, &damage_scaled, scoord.x, scoord.y);
|
||||||
|
pixman_region_fini(&damage_scaled);
|
||||||
|
|
||||||
|
apply_buffer_damage(&buffer_damage);
|
||||||
|
window_damage_region(window, &surface_damage);
|
||||||
|
|
||||||
|
pixman_region_fini(&surface_damage);
|
||||||
|
pixman_region_fini(&buffer_damage);
|
||||||
|
|
||||||
|
window_transfer_pixels(window);
|
||||||
|
|
||||||
|
window->is_frame_committed = true;
|
||||||
|
register_frame_callback();
|
||||||
|
|
||||||
window_commit(window);
|
window_commit(window);
|
||||||
|
window_swap(window);
|
||||||
|
|
||||||
|
pixman_region_clear(&window->current_damage);
|
||||||
|
vnc_client_clear_av_frames(window->vnc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_vnc_client_update_fb(struct vnc_client* client)
|
||||||
|
{
|
||||||
|
get_frame_damage(window->vnc, &window->current_damage);
|
||||||
|
render_from_vnc();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_frame_callback(void* data, struct wl_callback* callback,
|
||||||
|
uint32_t time)
|
||||||
|
{
|
||||||
|
wl_callback_destroy(callback);
|
||||||
|
window->is_frame_committed = false;
|
||||||
|
|
||||||
|
if (!window->vnc->is_updating)
|
||||||
|
render_from_vnc();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_callback_listener frame_listener = {
|
||||||
|
.done = handle_frame_callback
|
||||||
|
};
|
||||||
|
|
||||||
|
static void register_frame_callback(void)
|
||||||
|
{
|
||||||
|
struct wl_callback* callback = wl_surface_frame(window->wl_surface);
|
||||||
|
wl_callback_add_listener(callback, &frame_listener, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_vnc_client_event(void* obj)
|
void on_vnc_client_event(void* obj)
|
||||||
|
@ -477,6 +713,129 @@ int init_vnc_client_handler(struct vnc_client* client)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int find_render_node(char *node, size_t maxlen) {
|
||||||
|
bool r = -1;
|
||||||
|
drmDevice *devices[64];
|
||||||
|
|
||||||
|
int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0]));
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
drmDevice *dev = devices[i];
|
||||||
|
if (!(dev->available_nodes & (1 << DRM_NODE_RENDER)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
strncpy(node, dev->nodes[DRM_NODE_RENDER], maxlen);
|
||||||
|
node[maxlen - 1] = '\0';
|
||||||
|
r = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmFreeDevices(devices, n);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_gbm_device(void)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
char render_node[256];
|
||||||
|
rc = find_render_node(render_node, sizeof(render_node));
|
||||||
|
if (rc < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
drm_fd = open(render_node, O_RDWR);
|
||||||
|
if (drm_fd < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
gbm_device = gbm_create_device(drm_fd);
|
||||||
|
if (!gbm_device) {
|
||||||
|
close(drm_fd);
|
||||||
|
drm_fd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_egl_renderer(void)
|
||||||
|
{
|
||||||
|
if (!zwp_linux_dmabuf_v1) {
|
||||||
|
printf("Missing linux-dmabuf-unstable-v1. Using software rendering.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
zwp_linux_dmabuf_v1_add_listener(zwp_linux_dmabuf_v1,
|
||||||
|
&dmabuf_listener, NULL);
|
||||||
|
wl_display_roundtrip(wl_display);
|
||||||
|
|
||||||
|
if (dmabuf_format == DRM_FORMAT_INVALID) {
|
||||||
|
printf("No supported dmabuf pixel format found. Using software rendering.\n");
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_gbm_device() < 0) {
|
||||||
|
printf("Failed to find render node. Using software rendering.\n");
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (egl_init() < 0) {
|
||||||
|
printf("Failed initialise EGL. Using software rendering.\n");
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Using EGL for rendering...\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
if (zwp_linux_dmabuf_v1) {
|
||||||
|
zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf_v1);
|
||||||
|
zwp_linux_dmabuf_v1 = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_canary_tick(void* obj)
|
||||||
|
{
|
||||||
|
(void)obj;
|
||||||
|
|
||||||
|
uint64_t t = gettime_us();
|
||||||
|
int64_t dt = t - last_canary_tick;
|
||||||
|
last_canary_tick = t;
|
||||||
|
|
||||||
|
int64_t delay = dt - CANARY_TICK_PERIOD;
|
||||||
|
|
||||||
|
// Early ticks are just a result of late ticks...
|
||||||
|
if (delay < CANARY_LETHALITY_LEVEL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fprintf(stderr, "WARNING: Long delays observed (%"PRIi64"). Something is blocking the main loop\n",
|
||||||
|
delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_canary_ticker(void)
|
||||||
|
{
|
||||||
|
last_canary_tick = gettime_us();
|
||||||
|
|
||||||
|
struct aml* aml = aml_get_default();
|
||||||
|
struct aml_ticker* ticker = aml_ticker_new(CANARY_TICK_PERIOD,
|
||||||
|
on_canary_tick, NULL, NULL);
|
||||||
|
aml_start(aml, ticker);
|
||||||
|
aml_unref(ticker);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vnc_send_clipboard(char* text, size_t size) {
|
||||||
|
vnc_client_send_cut_text(vnc, text, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_main_loop_once(void)
|
||||||
|
{
|
||||||
|
struct aml* aml = aml_get_default();
|
||||||
|
wl_display_flush(wl_display);
|
||||||
|
aml_poll(aml, -1);
|
||||||
|
aml_dispatch(aml);
|
||||||
|
}
|
||||||
|
|
||||||
static int usage(int r)
|
static int usage(int r)
|
||||||
{
|
{
|
||||||
fprintf(r ? stderr : stdout, "\
|
fprintf(r ? stderr : stdout, "\
|
||||||
|
@ -486,10 +845,11 @@ Usage: wlvncc <address> [port]\n\
|
||||||
-c,--compression Compression level (0 - 9).\n\
|
-c,--compression Compression level (0 - 9).\n\
|
||||||
-e,--encodings=<list> Set allowed encodings, comma separated list.\n\
|
-e,--encodings=<list> Set allowed encodings, comma separated list.\n\
|
||||||
Supported values: tight, zrle, ultra, copyrect,\n\
|
Supported values: tight, zrle, ultra, copyrect,\n\
|
||||||
hextile, zlib, corre, rre, raw.\n\
|
hextile, zlib, corre, rre, raw, open-h264.\n\
|
||||||
-h,--help Get help.\n\
|
-h,--help Get help.\n\
|
||||||
-n,--hide-cursor Hide the client-side cursor.\n\
|
-n,--hide-cursor Hide the client-side cursor.\n\
|
||||||
-q,--quality Quality level (0 - 9).\n\
|
-q,--quality Quality level (0 - 9).\n\
|
||||||
|
-s,--use-sw-renderer Use software rendering.\n\
|
||||||
\n\
|
\n\
|
||||||
");
|
");
|
||||||
return r;
|
return r;
|
||||||
|
@ -503,7 +863,8 @@ int main(int argc, char* argv[])
|
||||||
const char* encodings = NULL;
|
const char* encodings = NULL;
|
||||||
int quality = -1;
|
int quality = -1;
|
||||||
int compression = -1;
|
int compression = -1;
|
||||||
static const char* shortopts = "a:q:c:e:hn";
|
static const char* shortopts = "a:q:c:e:hns";
|
||||||
|
bool use_sw_renderer = false;
|
||||||
|
|
||||||
static const struct option longopts[] = {
|
static const struct option longopts[] = {
|
||||||
{ "app-id", required_argument, NULL, 'a' },
|
{ "app-id", required_argument, NULL, 'a' },
|
||||||
|
@ -512,6 +873,7 @@ int main(int argc, char* argv[])
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
{ "quality", required_argument, NULL, 'q' },
|
{ "quality", required_argument, NULL, 'q' },
|
||||||
{ "hide-cursor", no_argument, NULL, 'n' },
|
{ "hide-cursor", no_argument, NULL, 'n' },
|
||||||
|
{ "use-sw-renderer", no_argument, NULL, 's' },
|
||||||
{ NULL, 0, NULL, 0 }
|
{ NULL, 0, NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -536,6 +898,9 @@ int main(int argc, char* argv[])
|
||||||
case 'n':
|
case 'n':
|
||||||
cursor_type = POINTER_CURSOR_NONE;
|
cursor_type = POINTER_CURSOR_NONE;
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
use_sw_renderer = true;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
return usage(0);
|
return usage(0);
|
||||||
default:
|
default:
|
||||||
|
@ -553,6 +918,11 @@ int main(int argc, char* argv[])
|
||||||
if (n_args >= 2)
|
if (n_args >= 2)
|
||||||
port = atoi(argv[optind + 1]);
|
port = atoi(argv[optind + 1]);
|
||||||
|
|
||||||
|
if (aml_unstable_abi_version != AML_UNSTABLE_API) {
|
||||||
|
fprintf(stderr, "libaml is incompatible with current build of wlvncc!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
struct aml* aml = aml_new();
|
struct aml* aml = aml_new();
|
||||||
if (!aml)
|
if (!aml)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -563,8 +933,10 @@ int main(int argc, char* argv[])
|
||||||
goto signal_handler_failure;
|
goto signal_handler_failure;
|
||||||
|
|
||||||
wl_display = wl_display_connect(NULL);
|
wl_display = wl_display_connect(NULL);
|
||||||
if (!wl_display)
|
if (!wl_display) {
|
||||||
|
fprintf(stderr, "Failed to connect to local wayland display\n");
|
||||||
goto display_failure;
|
goto display_failure;
|
||||||
|
}
|
||||||
|
|
||||||
if (init_wayland_event_handler() < 0)
|
if (init_wayland_event_handler() < 0)
|
||||||
goto event_handler_failure;
|
goto event_handler_failure;
|
||||||
|
@ -586,6 +958,7 @@ int main(int argc, char* argv[])
|
||||||
goto registry_failure;
|
goto registry_failure;
|
||||||
|
|
||||||
wl_list_init(&seats);
|
wl_list_init(&seats);
|
||||||
|
wl_list_init(&outputs);
|
||||||
|
|
||||||
wl_registry_add_listener(wl_registry, ®istry_listener, wl_display);
|
wl_registry_add_listener(wl_registry, ®istry_listener, wl_display);
|
||||||
wl_display_roundtrip(wl_display);
|
wl_display_roundtrip(wl_display);
|
||||||
|
@ -595,23 +968,39 @@ int main(int argc, char* argv[])
|
||||||
assert(xdg_wm_base);
|
assert(xdg_wm_base);
|
||||||
|
|
||||||
wl_shm_add_listener(wl_shm, &shm_listener, NULL);
|
wl_shm_add_listener(wl_shm, &shm_listener, NULL);
|
||||||
|
|
||||||
xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL);
|
xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL);
|
||||||
|
|
||||||
|
if (!use_sw_renderer)
|
||||||
|
have_egl = init_egl_renderer() == 0;
|
||||||
|
|
||||||
wl_display_roundtrip(wl_display);
|
wl_display_roundtrip(wl_display);
|
||||||
wl_display_roundtrip(wl_display);
|
wl_display_roundtrip(wl_display);
|
||||||
|
|
||||||
struct vnc_client* vnc = vnc_client_create();
|
vnc = vnc_client_create(data_control);
|
||||||
if (!vnc)
|
if (!vnc)
|
||||||
goto vnc_failure;
|
goto vnc_failure;
|
||||||
|
|
||||||
vnc->alloc_fb = on_vnc_client_alloc_fb;
|
vnc->alloc_fb = on_vnc_client_alloc_fb;
|
||||||
vnc->update_fb = on_vnc_client_update_fb;
|
vnc->update_fb = on_vnc_client_update_fb;
|
||||||
|
data_control->vnc_write_clipboard = vnc_send_clipboard;
|
||||||
|
|
||||||
if (vnc_client_set_pixel_format(vnc, wl_shm_format) < 0) {
|
if (vnc_client_set_pixel_format(vnc, shm_format) < 0) {
|
||||||
fprintf(stderr, "Unsupported pixel format\n");
|
fprintf(stderr, "Unsupported pixel format\n");
|
||||||
goto vnc_setup_failure;
|
goto vnc_setup_failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encodings)
|
if (encodings) {
|
||||||
|
if (!have_egl && strstr(encodings, "open-h264")) {
|
||||||
|
fprintf(stderr, "Open H.264 encoding won't work without EGL\n");
|
||||||
|
goto vnc_setup_failure;
|
||||||
|
}
|
||||||
|
} else if (have_egl) {
|
||||||
|
encodings = "open-h264,tight,zrle,ultra,copyrect,hextile,zlib"
|
||||||
|
",corre,rre,raw";
|
||||||
|
} else {
|
||||||
|
encodings = "tight,zrle,ultra,copyrect,hextile,zlib,corre,rre,raw";
|
||||||
|
}
|
||||||
vnc_client_set_encodings(vnc, encodings);
|
vnc_client_set_encodings(vnc, encodings);
|
||||||
|
|
||||||
if (quality >= 0)
|
if (quality >= 0)
|
||||||
|
@ -628,16 +1017,20 @@ int main(int argc, char* argv[])
|
||||||
if (init_vnc_client_handler(vnc) < 0)
|
if (init_vnc_client_handler(vnc) < 0)
|
||||||
goto vnc_setup_failure;
|
goto vnc_setup_failure;
|
||||||
|
|
||||||
|
if (vnc_client_init(vnc) < 0) {
|
||||||
|
fprintf(stderr, "Failed to connect to server\n");
|
||||||
|
goto vnc_setup_failure;
|
||||||
|
}
|
||||||
|
|
||||||
pointers->userdata = vnc;
|
pointers->userdata = vnc;
|
||||||
keyboards->userdata = vnc;
|
keyboards->userdata = vnc;
|
||||||
|
|
||||||
wl_display_dispatch(wl_display);
|
wl_display_dispatch(wl_display);
|
||||||
|
|
||||||
while (do_run) {
|
create_canary_ticker();
|
||||||
wl_display_flush(wl_display);
|
|
||||||
aml_poll(aml, -1);
|
while (do_run)
|
||||||
aml_dispatch(aml);
|
run_main_loop_once();
|
||||||
}
|
|
||||||
|
|
||||||
rc = 0;
|
rc = 0;
|
||||||
if (window)
|
if (window)
|
||||||
|
@ -645,10 +1038,18 @@ int main(int argc, char* argv[])
|
||||||
vnc_setup_failure:
|
vnc_setup_failure:
|
||||||
vnc_client_destroy(vnc);
|
vnc_client_destroy(vnc);
|
||||||
vnc_failure:
|
vnc_failure:
|
||||||
|
output_list_destroy(&outputs);
|
||||||
seat_list_destroy(&seats);
|
seat_list_destroy(&seats);
|
||||||
wl_compositor_destroy(wl_compositor);
|
wl_compositor_destroy(wl_compositor);
|
||||||
wl_shm_destroy(wl_shm);
|
wl_shm_destroy(wl_shm);
|
||||||
xdg_wm_base_destroy(xdg_wm_base);
|
xdg_wm_base_destroy(xdg_wm_base);
|
||||||
|
egl_finish();
|
||||||
|
if (zwp_linux_dmabuf_v1)
|
||||||
|
zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf_v1);
|
||||||
|
if (gbm_device)
|
||||||
|
gbm_device_destroy(gbm_device);
|
||||||
|
if (drm_fd >= 0)
|
||||||
|
close(drm_fd);
|
||||||
|
|
||||||
wl_registry_destroy(wl_registry);
|
wl_registry_destroy(wl_registry);
|
||||||
registry_failure:
|
registry_failure:
|
||||||
|
@ -662,5 +1063,9 @@ display_failure:
|
||||||
signal_handler_failure:
|
signal_handler_failure:
|
||||||
aml_unref(aml);
|
aml_unref(aml);
|
||||||
printf("Exiting...\n");
|
printf("Exiting...\n");
|
||||||
|
|
||||||
|
// @TODO this will throw an segfault (can't determine proxy version, but why?)
|
||||||
|
if (data_control)
|
||||||
|
data_control_destroy(data_control);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,286 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "open-h264.h"
|
||||||
|
#include "rfbclient.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
|
||||||
|
enum open_h264_flags {
|
||||||
|
OPEN_H264_RESET_CONTEXT = 1 << 0,
|
||||||
|
OPEN_H264_RESET_ALL_CONTEXTS = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct open_h264_msg_head {
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t flags;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct open_h264_context {
|
||||||
|
rfbRectangle rect;
|
||||||
|
|
||||||
|
AVCodecParserContext* parser;
|
||||||
|
AVCodecContext* codec_ctx;
|
||||||
|
AVBufferRef* hwctx_ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct open_h264 {
|
||||||
|
rfbClient* client;
|
||||||
|
|
||||||
|
struct open_h264_context* contexts[OPEN_H264_MAX_CONTEXTS];
|
||||||
|
int n_contexts;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool are_rects_equal(const rfbRectangle* a, const rfbRectangle* b)
|
||||||
|
{
|
||||||
|
return memcmp(a, b, sizeof(*a)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_context_index(const struct open_h264* self,
|
||||||
|
const rfbRectangle* rect)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < self->n_contexts; ++i)
|
||||||
|
if (are_rects_equal(&self->contexts[i]->rect, rect))
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct open_h264_context* find_context(
|
||||||
|
const struct open_h264* self, const rfbRectangle* rect)
|
||||||
|
{
|
||||||
|
int i = find_context_index(self, rect);
|
||||||
|
return i >= 0 ? self->contexts[i] : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct open_h264_context* open_h264_context_create(
|
||||||
|
struct open_h264* self, const rfbRectangle* rect)
|
||||||
|
{
|
||||||
|
if (self->n_contexts >= OPEN_H264_MAX_CONTEXTS)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
struct open_h264_context* context = calloc(1, sizeof(*context));
|
||||||
|
if (!context)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memcpy(&context->rect, rect, sizeof(context->rect));
|
||||||
|
|
||||||
|
const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||||
|
if (!codec)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
context->parser = av_parser_init(codec->id);
|
||||||
|
if (!context->parser)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
context->codec_ctx = avcodec_alloc_context3(codec);
|
||||||
|
if (!context->codec_ctx)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
if (av_hwdevice_ctx_create(&context->hwctx_ref, AV_HWDEVICE_TYPE_VAAPI,
|
||||||
|
NULL, NULL, 0) != 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
context->codec_ctx->hw_device_ctx = av_buffer_ref(context->hwctx_ref);
|
||||||
|
|
||||||
|
if (avcodec_open2(context->codec_ctx, codec, NULL) != 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
self->contexts[self->n_contexts++] = context;
|
||||||
|
return context;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
av_buffer_unref(&context->hwctx_ref);
|
||||||
|
avcodec_free_context(&context->codec_ctx);
|
||||||
|
av_parser_close(context->parser);
|
||||||
|
free(context);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void open_h264_context_destroy(struct open_h264_context* context)
|
||||||
|
{
|
||||||
|
av_buffer_unref(&context->hwctx_ref);
|
||||||
|
avcodec_free_context(&context->codec_ctx);
|
||||||
|
av_parser_close(context->parser);
|
||||||
|
free(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct open_h264_context* get_context(struct open_h264* self,
|
||||||
|
const rfbRectangle* rect)
|
||||||
|
{
|
||||||
|
struct open_h264_context* context = find_context(self, rect);
|
||||||
|
return context ? context : open_h264_context_create(self, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void reset_context(struct open_h264* self,
|
||||||
|
const rfbRectangle* rect)
|
||||||
|
{
|
||||||
|
int i = find_context_index(self, rect);
|
||||||
|
if (i < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
open_h264_context_destroy(self->contexts[i]);
|
||||||
|
--self->n_contexts;
|
||||||
|
|
||||||
|
memmove(&self->contexts[i], &self->contexts[i + 1],
|
||||||
|
self->n_contexts - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_all_contexts(struct open_h264* self)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < self->n_contexts; ++i) {
|
||||||
|
open_h264_context_destroy(self->contexts[i]);
|
||||||
|
self->contexts[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct open_h264* open_h264_create(rfbClient* client)
|
||||||
|
{
|
||||||
|
// Use this to enable debug logs
|
||||||
|
// av_log_set_level(AV_LOG_DEBUG);
|
||||||
|
|
||||||
|
struct open_h264* self = calloc(1, sizeof(*self));
|
||||||
|
if (!self)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
self->client = client;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_h264_destroy(struct open_h264* self)
|
||||||
|
{
|
||||||
|
if (!self)
|
||||||
|
return;
|
||||||
|
|
||||||
|
reset_all_contexts(self);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool decode_frame(struct open_h264_context* context, AVFrame* frame,
|
||||||
|
AVPacket* packet)
|
||||||
|
{
|
||||||
|
av_frame_unref(frame);
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = avcodec_send_packet(context->codec_ctx, packet);
|
||||||
|
if (rc < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct AVFrame* vaapi_frame = av_frame_alloc();
|
||||||
|
if (!vaapi_frame)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rc = avcodec_receive_frame(context->codec_ctx, vaapi_frame);
|
||||||
|
if (rc < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
frame->format = AV_PIX_FMT_DRM_PRIME;
|
||||||
|
|
||||||
|
rc = av_hwframe_map(frame, vaapi_frame, AV_HWFRAME_MAP_DIRECT);
|
||||||
|
if (rc < 0) {
|
||||||
|
av_frame_free(&vaapi_frame);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_frame_copy_props(frame, vaapi_frame);
|
||||||
|
av_frame_free(&vaapi_frame);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_elementary_stream(struct open_h264_context* context,
|
||||||
|
AVPacket* packet, const uint8_t* src, uint32_t length)
|
||||||
|
{
|
||||||
|
return av_parser_parse2(context->parser, context->codec_ctx,
|
||||||
|
&packet->data, &packet->size, src, length,
|
||||||
|
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AVFrame* open_h264_decode_rect(struct open_h264* self,
|
||||||
|
rfbFramebufferUpdateRectHeader* msg)
|
||||||
|
{
|
||||||
|
bool have_frame = false;
|
||||||
|
|
||||||
|
struct open_h264_msg_head head = { 0 };
|
||||||
|
if (!ReadFromRFBServer(self->client, (char*)&head, sizeof(head)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
uint32_t length = ntohl(head.length);
|
||||||
|
enum open_h264_flags flags = ntohl(head.flags);
|
||||||
|
|
||||||
|
if (flags & OPEN_H264_RESET_ALL_CONTEXTS) {
|
||||||
|
reset_all_contexts(self);
|
||||||
|
} else if (flags & OPEN_H264_RESET_CONTEXT) {
|
||||||
|
reset_context(self, &msg->r);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct open_h264_context* context = get_context(self, &msg->r);
|
||||||
|
if (!context)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char* data = calloc(1, length + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||||
|
if (!data)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
AVFrame* frame = av_frame_alloc();
|
||||||
|
if (!frame)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
AVPacket* packet = av_packet_alloc();
|
||||||
|
if (!packet)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(self->client, data, length))
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
uint8_t* dp = (uint8_t*)data;
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
int rc = parse_elementary_stream(context, packet, dp, length);
|
||||||
|
if (rc < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
dp += rc;
|
||||||
|
length -= rc;
|
||||||
|
|
||||||
|
// The h264 elementary stream doesn't have end-markers, so the
|
||||||
|
// parser doesn't know where the frame ends. This flushes it:
|
||||||
|
if (packet->size == 0 && length == 0) {
|
||||||
|
int rc = parse_elementary_stream(context, packet, dp,
|
||||||
|
length);
|
||||||
|
if (rc < 0)
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get multiple frames per rect, there's no point in
|
||||||
|
// rendering them all, so we just return the last one.
|
||||||
|
if (packet->size != 0)
|
||||||
|
have_frame = decode_frame(context, frame, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
failure:
|
||||||
|
av_packet_free(&packet);
|
||||||
|
if (!have_frame)
|
||||||
|
av_frame_unref(frame);
|
||||||
|
free(data);
|
||||||
|
return have_frame ? frame : NULL;
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 - 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "output.h"
|
||||||
|
|
||||||
|
static void output_handle_geometry(void* data, struct wl_output* wl_output,
|
||||||
|
int32_t x, int32_t y, int32_t phys_width, int32_t phys_height,
|
||||||
|
int32_t subpixel, const char* make, const char* model,
|
||||||
|
int32_t transform)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handle_mode(void* data, struct wl_output* wl_output,
|
||||||
|
uint32_t flags, int32_t width, int32_t height, int32_t refresh)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handle_done(void* data, struct wl_output* wl_output)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handle_scale(void* data, struct wl_output* wl_output,
|
||||||
|
int32_t factor)
|
||||||
|
{
|
||||||
|
struct output* output = data;
|
||||||
|
output->scale = factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_output_listener output_listener = {
|
||||||
|
.geometry = output_handle_geometry,
|
||||||
|
.mode = output_handle_mode,
|
||||||
|
.done = output_handle_done,
|
||||||
|
.scale = output_handle_scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
void output_destroy(struct output* output)
|
||||||
|
{
|
||||||
|
wl_output_destroy(output->wl_output);
|
||||||
|
free(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_list_destroy(struct wl_list* list)
|
||||||
|
{
|
||||||
|
struct output* output;
|
||||||
|
struct output* tmp;
|
||||||
|
|
||||||
|
wl_list_for_each_safe(output, tmp, list, link) {
|
||||||
|
wl_list_remove(&output->link);
|
||||||
|
output_destroy(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t output_list_get_max_scale(const struct wl_list* list)
|
||||||
|
{
|
||||||
|
struct output* output;
|
||||||
|
|
||||||
|
int32_t scale = 1;
|
||||||
|
|
||||||
|
wl_list_for_each(output, list, link)
|
||||||
|
if (output->scale > scale)
|
||||||
|
scale = output->scale;
|
||||||
|
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct output* output_new(struct wl_output* wl_output, uint32_t id)
|
||||||
|
{
|
||||||
|
struct output* output = calloc(1, sizeof(*output));
|
||||||
|
if (!output)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
output->wl_output = wl_output;
|
||||||
|
output->id = id;
|
||||||
|
|
||||||
|
wl_output_add_listener(output->wl_output, &output_listener,
|
||||||
|
output);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct output* output_find_by_id(struct wl_list* list, uint32_t id)
|
||||||
|
{
|
||||||
|
struct output* output;
|
||||||
|
|
||||||
|
wl_list_for_each(output, list, link)
|
||||||
|
if (output->id == id)
|
||||||
|
return output;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libdrm/drm_fourcc.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
enum wl_shm_format drm_format_to_wl_shm(uint32_t in)
|
||||||
|
{
|
||||||
|
assert(!(in & DRM_FORMAT_BIG_ENDIAN));
|
||||||
|
|
||||||
|
switch (in) {
|
||||||
|
case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888;
|
||||||
|
case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888;
|
||||||
|
}
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t drm_format_from_wl_shm(enum wl_shm_format in)
|
||||||
|
{
|
||||||
|
switch (in) {
|
||||||
|
case WL_SHM_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888;
|
||||||
|
case WL_SHM_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool drm_format_to_pixman_fmt(pixman_format_code_t* dst, uint32_t src)
|
||||||
|
{
|
||||||
|
#define LOWER_R r
|
||||||
|
#define LOWER_G g
|
||||||
|
#define LOWER_B b
|
||||||
|
#define LOWER_A a
|
||||||
|
#define LOWER_X x
|
||||||
|
#define LOWER_
|
||||||
|
#define LOWER(x) LOWER_##x
|
||||||
|
|
||||||
|
#define CONCAT_(a, b) a ## b
|
||||||
|
#define CONCAT(a, b) CONCAT_(a, b)
|
||||||
|
|
||||||
|
#define FMT_DRM(x, y, z, v, a, b, c, d) DRM_FORMAT_##x##y##z##v##a##b##c##d
|
||||||
|
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
#define FMT_PIXMAN(x, y, z, v, a, b, c, d) \
|
||||||
|
CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(\
|
||||||
|
PIXMAN_, LOWER(x)), a), LOWER(y)), b), LOWER(z)), c), LOWER(v)), d)
|
||||||
|
#else
|
||||||
|
#define FMT_PIXMAN(x, y, z, v, a, b, c, d) \
|
||||||
|
CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(CONCAT(\
|
||||||
|
PIXMAN_, LOWER(v)), d), LOWER(z)), c), LOWER(y)), b), LOWER(x)), a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (src) {
|
||||||
|
#define X(...) \
|
||||||
|
case FMT_DRM(__VA_ARGS__): *dst = FMT_PIXMAN(__VA_ARGS__); break
|
||||||
|
|
||||||
|
/* 32 bits */
|
||||||
|
X(A,R,G,B,8,8,8,8);
|
||||||
|
X(A,B,G,R,8,8,8,8);
|
||||||
|
X(X,R,G,B,8,8,8,8);
|
||||||
|
X(X,B,G,R,8,8,8,8);
|
||||||
|
X(R,G,B,A,8,8,8,8);
|
||||||
|
X(B,G,R,A,8,8,8,8);
|
||||||
|
X(R,G,B,X,8,8,8,8);
|
||||||
|
X(B,G,R,X,8,8,8,8);
|
||||||
|
|
||||||
|
/* 24 bits */
|
||||||
|
X(R,G,B,,8,8,8,);
|
||||||
|
X(B,G,R,,8,8,8,);
|
||||||
|
|
||||||
|
/* 16 bits */
|
||||||
|
X(R,G,B,,5,6,5,);
|
||||||
|
X(B,G,R,,5,6,5,);
|
||||||
|
|
||||||
|
/* These are incompatible on big endian */
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
X(A,R,G,B,2,10,10,10);
|
||||||
|
X(X,R,G,B,2,10,10,10);
|
||||||
|
X(A,B,G,R,2,10,10,10);
|
||||||
|
X(X,B,G,R,2,10,10,10);
|
||||||
|
X(A,R,G,B,1,5,5,5);
|
||||||
|
X(A,B,G,R,1,5,5,5);
|
||||||
|
X(X,R,G,B,1,5,5,5);
|
||||||
|
X(X,B,G,R,1,5,5,5);
|
||||||
|
X(A,R,G,B,4,4,4,4);
|
||||||
|
X(A,B,G,R,4,4,4,4);
|
||||||
|
X(X,R,G,B,4,4,4,4);
|
||||||
|
X(X,B,G,R,4,4,4,4);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
|
||||||
|
void region_scale(struct pixman_region16* dst, struct pixman_region16* src,
|
||||||
|
double scale)
|
||||||
|
{
|
||||||
|
if (scale == 1.0) {
|
||||||
|
pixman_region_copy(dst, src);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixman_region_fini(dst);
|
||||||
|
pixman_region_init(dst);
|
||||||
|
|
||||||
|
int n_rects = 0;
|
||||||
|
pixman_box16_t* rects = pixman_region_rectangles(src, &n_rects);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_rects; ++i) {
|
||||||
|
pixman_box16_t* r = &rects[i];
|
||||||
|
|
||||||
|
int x1 = floor((double)r->x1 * scale);
|
||||||
|
int x2 = ceil((double)r->x2 * scale);
|
||||||
|
int y1 = floor((double)r->y1 * scale);
|
||||||
|
int y2 = ceil((double)r->y2 * scale);
|
||||||
|
|
||||||
|
pixman_region_union_rect(dst, dst, x1, y1, x2 - x1, y2 - y1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void region_translate(struct pixman_region16* dst, struct pixman_region16* src,
|
||||||
|
int x, int y)
|
||||||
|
{
|
||||||
|
if (x == 0 && y == 0) {
|
||||||
|
pixman_region_copy(dst, src);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixman_region_fini(dst);
|
||||||
|
pixman_region_init(dst);
|
||||||
|
|
||||||
|
int n_rects = 0;
|
||||||
|
pixman_box16_t* rects = pixman_region_rectangles(src, &n_rects);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_rects; ++i) {
|
||||||
|
pixman_box16_t* r = &rects[i];
|
||||||
|
|
||||||
|
int x1 = r->x1 + x;
|
||||||
|
int x2 = r->x2 + x;
|
||||||
|
int y1 = r->y1 + y;
|
||||||
|
int y2 = r->y2 + y;
|
||||||
|
|
||||||
|
pixman_region_union_rect(dst, dst, x1, y1, x2 - x1, y2 - y1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,544 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "renderer.h"
|
||||||
|
#include "renderer-egl.h"
|
||||||
|
#include "vnc.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
|
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <GLES2/gl2ext.h>
|
||||||
|
|
||||||
|
#include <libavutil/frame.h>
|
||||||
|
#include <libavutil/hwcontext_drm.h>
|
||||||
|
|
||||||
|
#define XSTR(s) STR(s)
|
||||||
|
#define STR(s) #s
|
||||||
|
|
||||||
|
#define EGL_EXTENSION_LIST \
|
||||||
|
X(PFNEGLGETPLATFORMDISPLAYEXTPROC, eglGetPlatformDisplayEXT) \
|
||||||
|
X(PFNEGLCREATEIMAGEKHRPROC, eglCreateImageKHR) \
|
||||||
|
X(PFNEGLDESTROYIMAGEKHRPROC, eglDestroyImageKHR) \
|
||||||
|
|
||||||
|
#define GL_EXTENSION_LIST \
|
||||||
|
X(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC, glEGLImageTargetTexture2DOES) \
|
||||||
|
X(PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC, glEGLImageTargetRenderbufferStorageOES) \
|
||||||
|
|
||||||
|
#define X(t, n) static t n;
|
||||||
|
EGL_EXTENSION_LIST
|
||||||
|
GL_EXTENSION_LIST
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ATTR_INDEX_POS = 0,
|
||||||
|
ATTR_INDEX_TEXTURE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fbo_info {
|
||||||
|
GLuint fbo;
|
||||||
|
GLuint rbo;
|
||||||
|
int width, height;
|
||||||
|
};
|
||||||
|
|
||||||
|
static EGLDisplay egl_display = EGL_NO_DISPLAY;
|
||||||
|
static EGLContext egl_context = EGL_NO_CONTEXT;
|
||||||
|
|
||||||
|
static GLuint shader_program = 0;
|
||||||
|
static GLuint shader_program_ext = 0;
|
||||||
|
static GLuint texture = 0;
|
||||||
|
|
||||||
|
static const char *vertex_shader_src =
|
||||||
|
"attribute vec2 pos;\n"
|
||||||
|
"attribute vec2 texture;\n"
|
||||||
|
"varying vec2 v_texture;\n"
|
||||||
|
"void main() {\n"
|
||||||
|
" v_texture = vec2(texture.s, 1.0 - texture.t);\n"
|
||||||
|
" gl_Position = vec4(pos, 0.0, 1.0);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static const char *fragment_shader_src =
|
||||||
|
"precision mediump float;\n"
|
||||||
|
"uniform sampler2D u_tex;\n"
|
||||||
|
"varying vec2 v_texture;\n"
|
||||||
|
"void main() {\n"
|
||||||
|
" vec4 colour = texture2D(u_tex, v_texture);\n"
|
||||||
|
" gl_FragColor = vec4(colour.rgb, 1.0);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static const char *fragment_shader_ext_src =
|
||||||
|
"#extension GL_OES_EGL_image_external: require\n\n"
|
||||||
|
"precision mediump float;\n"
|
||||||
|
"uniform samplerExternalOES u_tex;\n"
|
||||||
|
"varying vec2 v_texture;\n"
|
||||||
|
"void main() {\n"
|
||||||
|
" gl_FragColor = texture2D(u_tex, v_texture);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
struct {
|
||||||
|
GLuint u_tex;
|
||||||
|
} uniforms;
|
||||||
|
|
||||||
|
static int egl_load_egl_ext(void)
|
||||||
|
{
|
||||||
|
#define X(t, n) \
|
||||||
|
n = (t)eglGetProcAddress(XSTR(n)); \
|
||||||
|
if (!n) \
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
EGL_EXTENSION_LIST
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int egl_load_gl_ext(void)
|
||||||
|
{
|
||||||
|
#define X(t, n) \
|
||||||
|
n = (t)eglGetProcAddress(XSTR(n)); \
|
||||||
|
if (!n) \
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
GL_EXTENSION_LIST
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compile_shaders(const char* vert_src, const char* frag_src)
|
||||||
|
{
|
||||||
|
GLuint vert = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource(vert, 1, &vert_src, NULL);
|
||||||
|
glCompileShader(vert);
|
||||||
|
|
||||||
|
GLint is_compiled = 0;
|
||||||
|
glGetShaderiv(vert, GL_COMPILE_STATUS, &is_compiled);
|
||||||
|
assert(is_compiled);
|
||||||
|
|
||||||
|
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(frag, 1, &frag_src, NULL);
|
||||||
|
glCompileShader(frag);
|
||||||
|
glGetShaderiv(frag, GL_COMPILE_STATUS, &is_compiled);
|
||||||
|
assert(is_compiled);
|
||||||
|
|
||||||
|
int prog = glCreateProgram();
|
||||||
|
|
||||||
|
glAttachShader(prog, vert);
|
||||||
|
glAttachShader(prog, frag);
|
||||||
|
|
||||||
|
glBindAttribLocation(prog, ATTR_INDEX_POS, "pos");
|
||||||
|
glBindAttribLocation(prog, ATTR_INDEX_TEXTURE, "texture");
|
||||||
|
|
||||||
|
glLinkProgram(prog);
|
||||||
|
|
||||||
|
glDeleteShader(vert);
|
||||||
|
glDeleteShader(frag);
|
||||||
|
|
||||||
|
GLint is_linked = 0;
|
||||||
|
glGetProgramiv(prog, GL_LINK_STATUS, &is_linked);
|
||||||
|
assert(is_linked);
|
||||||
|
|
||||||
|
uniforms.u_tex = glGetUniformLocation(prog, "u_tex");
|
||||||
|
|
||||||
|
return prog;
|
||||||
|
}
|
||||||
|
|
||||||
|
int egl_init(void)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
rc = eglBindAPI(EGL_OPENGL_ES_API);
|
||||||
|
if (!rc)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (egl_load_egl_ext() < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
egl_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA,
|
||||||
|
EGL_DEFAULT_DISPLAY, NULL);
|
||||||
|
if (egl_display == EGL_NO_DISPLAY)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
rc = eglInitialize(egl_display, NULL, NULL);
|
||||||
|
if (!rc)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
static const EGLint attribs[] = {
|
||||||
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
egl_context = eglCreateContext(egl_display, EGL_NO_CONFIG_KHR,
|
||||||
|
EGL_NO_CONTEXT, attribs);
|
||||||
|
if (egl_context == EGL_NO_CONTEXT)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
if (!eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||||
|
egl_context))
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
if (egl_load_gl_ext() < 0)
|
||||||
|
goto failure;
|
||||||
|
|
||||||
|
shader_program = compile_shaders(vertex_shader_src,
|
||||||
|
fragment_shader_src);
|
||||||
|
shader_program_ext = compile_shaders(vertex_shader_src,
|
||||||
|
fragment_shader_ext_src);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
eglDestroyContext(egl_display, egl_context);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void egl_finish(void)
|
||||||
|
{
|
||||||
|
if (texture)
|
||||||
|
glDeleteTextures(1, &texture);
|
||||||
|
if (shader_program_ext)
|
||||||
|
glDeleteProgram(shader_program_ext);
|
||||||
|
if (shader_program)
|
||||||
|
glDeleteProgram(shader_program);
|
||||||
|
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||||
|
EGL_NO_CONTEXT);
|
||||||
|
eglDestroyContext(egl_display, egl_context);
|
||||||
|
eglTerminate(egl_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void append_attr(EGLint* dst, int* i, EGLint name, EGLint value)
|
||||||
|
{
|
||||||
|
dst[*i] = name;
|
||||||
|
i[0] += 1;
|
||||||
|
dst[*i] = value;
|
||||||
|
i[0] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fbo_from_gbm_bo(struct fbo_info* dst, struct gbm_bo* bo)
|
||||||
|
{
|
||||||
|
memset(dst, 0, sizeof(*dst));
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
EGLint attr[128];
|
||||||
|
|
||||||
|
// Won't do multi-planar...
|
||||||
|
int n_planes = gbm_bo_get_plane_count(bo);
|
||||||
|
assert(n_planes == 1);
|
||||||
|
|
||||||
|
int width = gbm_bo_get_width(bo);
|
||||||
|
int height = gbm_bo_get_height(bo);
|
||||||
|
|
||||||
|
append_attr(attr, &index, EGL_WIDTH, width);
|
||||||
|
append_attr(attr, &index, EGL_HEIGHT, height);
|
||||||
|
append_attr(attr, &index, EGL_LINUX_DRM_FOURCC_EXT,
|
||||||
|
gbm_bo_get_format(bo));
|
||||||
|
|
||||||
|
int fd = gbm_bo_get_fd(bo);
|
||||||
|
|
||||||
|
// Plane 0:
|
||||||
|
uint64_t mod = gbm_bo_get_modifier(bo);
|
||||||
|
uint32_t mod_hi = mod >> 32;
|
||||||
|
uint32_t mod_lo = mod & 0xffffffff;
|
||||||
|
|
||||||
|
append_attr(attr, &index, EGL_DMA_BUF_PLANE0_FD_EXT, fd);
|
||||||
|
append_attr(attr, &index, EGL_DMA_BUF_PLANE0_OFFSET_EXT,
|
||||||
|
gbm_bo_get_offset(bo, 0));
|
||||||
|
append_attr(attr, &index, EGL_DMA_BUF_PLANE0_PITCH_EXT,
|
||||||
|
gbm_bo_get_stride(bo));
|
||||||
|
append_attr(attr, &index, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, mod_lo);
|
||||||
|
append_attr(attr, &index, EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, mod_hi);
|
||||||
|
|
||||||
|
attr[index++] = EGL_NONE;
|
||||||
|
|
||||||
|
EGLImageKHR image = eglCreateImageKHR(egl_display, EGL_NO_CONTEXT,
|
||||||
|
EGL_LINUX_DMA_BUF_EXT, NULL, attr);
|
||||||
|
assert(image != EGL_NO_IMAGE_KHR);
|
||||||
|
|
||||||
|
GLuint rbo = 0;
|
||||||
|
glGenRenderbuffers(1, &rbo);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
||||||
|
glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, image);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||||
|
|
||||||
|
GLuint fbo = 0;
|
||||||
|
glGenFramebuffers(1, &fbo);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||||
|
GL_RENDERBUFFER, rbo);
|
||||||
|
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
assert(status == GL_FRAMEBUFFER_COMPLETE);
|
||||||
|
|
||||||
|
dst->fbo = fbo;
|
||||||
|
dst->rbo = rbo;
|
||||||
|
dst->width = width;
|
||||||
|
dst->height = height;
|
||||||
|
|
||||||
|
eglDestroyImageKHR(egl_display, image);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define X(lc, uc) \
|
||||||
|
static EGLint plane_ ## lc ## _key(int plane) \
|
||||||
|
{ \
|
||||||
|
switch (plane) { \
|
||||||
|
case 0: return EGL_DMA_BUF_PLANE0_ ## uc ## _EXT; \
|
||||||
|
case 1: return EGL_DMA_BUF_PLANE1_ ## uc ## _EXT; \
|
||||||
|
case 2: return EGL_DMA_BUF_PLANE2_ ## uc ## _EXT; \
|
||||||
|
case 3: return EGL_DMA_BUF_PLANE3_ ## uc ## _EXT; \
|
||||||
|
} \
|
||||||
|
return EGL_NONE; \
|
||||||
|
}
|
||||||
|
|
||||||
|
X(fd, FD);
|
||||||
|
X(offset, OFFSET);
|
||||||
|
X(pitch, PITCH);
|
||||||
|
X(modifier_lo, MODIFIER_LO);
|
||||||
|
X(modifier_hi, MODIFIER_HI);
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
static void dmabuf_attr_append_planes(EGLint* dst, int* index,
|
||||||
|
struct AVDRMFrameDescriptor* desc)
|
||||||
|
{
|
||||||
|
struct AVDRMPlaneDescriptor *plane;
|
||||||
|
struct AVDRMObjectDescriptor *obj;
|
||||||
|
|
||||||
|
for (int i = 0; i < desc->nb_layers; ++i) {
|
||||||
|
assert(desc->layers[i].nb_planes == 1);
|
||||||
|
|
||||||
|
plane = &desc->layers[i].planes[0];
|
||||||
|
obj = &desc->objects[plane->object_index];
|
||||||
|
|
||||||
|
append_attr(dst, index, plane_fd_key(i), obj->fd);
|
||||||
|
append_attr(dst, index, plane_offset_key(i), plane->offset);
|
||||||
|
append_attr(dst, index, plane_pitch_key(i), plane->pitch);
|
||||||
|
append_attr(dst, index, plane_modifier_lo_key(i),
|
||||||
|
obj->format_modifier);
|
||||||
|
append_attr(dst, index, plane_modifier_hi_key(i),
|
||||||
|
obj->format_modifier >> 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLuint texture_from_av_frame(const struct AVFrame* frame)
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
EGLint attr[128];
|
||||||
|
|
||||||
|
AVDRMFrameDescriptor *desc = (void*)frame->data[0];
|
||||||
|
|
||||||
|
append_attr(attr, &index, EGL_WIDTH, frame->width);
|
||||||
|
append_attr(attr, &index, EGL_HEIGHT, frame->height);
|
||||||
|
append_attr(attr, &index, EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_NV12);
|
||||||
|
append_attr(attr, &index, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE);
|
||||||
|
dmabuf_attr_append_planes(attr, &index, desc);
|
||||||
|
attr[index++] = EGL_NONE;
|
||||||
|
|
||||||
|
EGLImageKHR image = eglCreateImageKHR(egl_display, EGL_NO_CONTEXT,
|
||||||
|
EGL_LINUX_DMA_BUF_EXT, NULL, attr);
|
||||||
|
assert(image != EGL_NO_IMAGE_KHR);
|
||||||
|
|
||||||
|
GLuint tex = 0;
|
||||||
|
glGenTextures(1, &tex);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
|
||||||
|
|
||||||
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
|
||||||
|
|
||||||
|
eglDestroyImageKHR(egl_display, image);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gl_draw(void)
|
||||||
|
{
|
||||||
|
static const GLfloat s_vertices[4][2] = {
|
||||||
|
{ -1.0, 1.0 },
|
||||||
|
{ 1.0, 1.0 },
|
||||||
|
{ -1.0, -1.0 },
|
||||||
|
{ 1.0, -1.0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const GLfloat s_positions[4][2] = {
|
||||||
|
{ 0, 0 },
|
||||||
|
{ 1, 0 },
|
||||||
|
{ 0, 1 },
|
||||||
|
{ 1, 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
glVertexAttribPointer(ATTR_INDEX_POS, 2, GL_FLOAT, GL_FALSE, 0,
|
||||||
|
s_vertices);
|
||||||
|
glVertexAttribPointer(ATTR_INDEX_TEXTURE, 2, GL_FLOAT, GL_FALSE, 0,
|
||||||
|
s_positions);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(ATTR_INDEX_POS);
|
||||||
|
glEnableVertexAttribArray(ATTR_INDEX_TEXTURE);
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
|
||||||
|
glDisableVertexAttribArray(ATTR_INDEX_TEXTURE);
|
||||||
|
glDisableVertexAttribArray(ATTR_INDEX_POS);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum gl_format_from_drm(uint32_t format)
|
||||||
|
{
|
||||||
|
switch (format) {
|
||||||
|
case DRM_FORMAT_ARGB8888:
|
||||||
|
case DRM_FORMAT_XRGB8888:
|
||||||
|
return GL_BGRA_EXT;
|
||||||
|
case DRM_FORMAT_ABGR8888:
|
||||||
|
case DRM_FORMAT_XBGR8888:
|
||||||
|
return GL_RGBA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void import_image_with_damage(const struct image* src,
|
||||||
|
struct pixman_region16* damage)
|
||||||
|
{
|
||||||
|
GLenum fmt = gl_format_from_drm(src->format);
|
||||||
|
|
||||||
|
int n_rects = 0;
|
||||||
|
struct pixman_box16* rects =
|
||||||
|
pixman_region_rectangles(damage, &n_rects);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_rects; ++i) {
|
||||||
|
int x = rects[i].x1;
|
||||||
|
int y = rects[i].y1;
|
||||||
|
int width = rects[i].x2 - x;
|
||||||
|
int height = rects[i].y2 - y;
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, x);
|
||||||
|
glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, y);
|
||||||
|
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, fmt,
|
||||||
|
GL_UNSIGNED_BYTE, src->pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
|
||||||
|
glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_image_egl(struct buffer* dst, const struct image* src,
|
||||||
|
double scale, int x_pos, int y_pos)
|
||||||
|
{
|
||||||
|
struct fbo_info fbo;
|
||||||
|
fbo_from_gbm_bo(&fbo, dst->bo);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo.fbo);
|
||||||
|
|
||||||
|
bool is_new_texture = !texture;
|
||||||
|
|
||||||
|
if (!texture)
|
||||||
|
glGenTextures(1, &texture);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, src->stride / 4);
|
||||||
|
|
||||||
|
if (is_new_texture) {
|
||||||
|
GLenum fmt = gl_format_from_drm(src->format);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, fmt, src->width, src->height, 0,
|
||||||
|
fmt, GL_UNSIGNED_BYTE, src->pixels);
|
||||||
|
} else {
|
||||||
|
import_image_with_damage(src,
|
||||||
|
(struct pixman_region16*)src->damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
|
||||||
|
|
||||||
|
int width = round((double)src->width * scale);
|
||||||
|
int height = round((double)src->height * scale);
|
||||||
|
glViewport(x_pos, y_pos, width, height);
|
||||||
|
|
||||||
|
glUseProgram(shader_program);
|
||||||
|
|
||||||
|
struct pixman_box16* ext = pixman_region_extents(&dst->damage);
|
||||||
|
glScissor(ext->x1, ext->y1, ext->x2 - ext->x1, ext->y2 - ext->y1);
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
gl_draw();
|
||||||
|
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
glFlush();
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
glDeleteFramebuffers(1, &fbo.fbo);
|
||||||
|
glDeleteRenderbuffers(1, &fbo.rbo);
|
||||||
|
|
||||||
|
pixman_region_clear(&dst->damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_av_frames_egl(struct buffer* dst, struct vnc_av_frame** src,
|
||||||
|
int n_av_frames, double scale, int x_pos, int y_pos)
|
||||||
|
{
|
||||||
|
struct fbo_info fbo;
|
||||||
|
fbo_from_gbm_bo(&fbo, dst->bo);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo.fbo);
|
||||||
|
|
||||||
|
struct pixman_box16* ext = pixman_region_extents(&dst->damage);
|
||||||
|
glScissor(ext->x1, ext->y1, ext->x2 - ext->x1, ext->y2 - ext->y1);
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
glUseProgram(shader_program_ext);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_av_frames; ++i) {
|
||||||
|
const struct vnc_av_frame* frame = src[i];
|
||||||
|
|
||||||
|
int width = round((double)frame->width * scale);
|
||||||
|
int height = round((double)frame->height * scale);
|
||||||
|
glViewport(x_pos + frame->x, y_pos + frame->y, width, height);
|
||||||
|
|
||||||
|
GLuint tex = texture_from_av_frame(frame->frame);
|
||||||
|
glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
|
||||||
|
|
||||||
|
gl_draw();
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
glFlush();
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glDeleteFramebuffers(1, &fbo.fbo);
|
||||||
|
glDeleteRenderbuffers(1, &fbo.rbo);
|
||||||
|
|
||||||
|
pixman_region_clear(&dst->damage);
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "renderer.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "pixels.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <pixman.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
void render_image(struct buffer* dst, const struct image* src, double scale,
|
||||||
|
int x_pos, int y_pos)
|
||||||
|
{
|
||||||
|
bool ok __attribute__((unused));
|
||||||
|
|
||||||
|
pixman_format_code_t dst_fmt = 0;
|
||||||
|
ok = drm_format_to_pixman_fmt(&dst_fmt, dst->format);
|
||||||
|
assert(ok);
|
||||||
|
|
||||||
|
pixman_format_code_t src_fmt = 0;
|
||||||
|
ok = drm_format_to_pixman_fmt(&src_fmt, src->format);
|
||||||
|
assert(ok);
|
||||||
|
|
||||||
|
pixman_image_t* dstimg = pixman_image_create_bits_no_clear(dst_fmt,
|
||||||
|
dst->width, dst->height, dst->pixels, dst->stride);
|
||||||
|
|
||||||
|
pixman_image_t* srcimg = pixman_image_create_bits_no_clear(src_fmt,
|
||||||
|
src->width, src->height, src->pixels, src->stride);
|
||||||
|
|
||||||
|
pixman_fixed_t src_scale = pixman_double_to_fixed(1.0 / scale);
|
||||||
|
|
||||||
|
pixman_transform_t xform;
|
||||||
|
pixman_transform_init_scale(&xform, src_scale, src_scale);
|
||||||
|
pixman_image_set_transform(srcimg, &xform);
|
||||||
|
|
||||||
|
pixman_image_set_clip_region(dstimg, &dst->damage);
|
||||||
|
|
||||||
|
pixman_image_composite(PIXMAN_OP_OVER, srcimg, NULL, dstimg,
|
||||||
|
0, 0,
|
||||||
|
0, 0,
|
||||||
|
x_pos, y_pos,
|
||||||
|
dst->width, dst->height);
|
||||||
|
|
||||||
|
pixman_image_unref(srcimg);
|
||||||
|
pixman_image_unref(dstimg);
|
||||||
|
|
||||||
|
pixman_region_clear(&dst->damage);
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,557 @@
|
||||||
|
/*
|
||||||
|
* The software in this file is derived from the vncconnection.c source file
|
||||||
|
* from the GTK VNC Widget with modifications by S. Waterman <simon.waterman@zynstra.com>
|
||||||
|
* for compatibility with libvncserver. The copyright and license
|
||||||
|
* statements below apply only to this source file and to no other parts of the
|
||||||
|
* libvncserver library.
|
||||||
|
*
|
||||||
|
* GTK VNC Widget
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
|
||||||
|
* Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.0 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sasl.c - functions to deal with client side of the SASL protocol.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include "rfbclient.h"
|
||||||
|
|
||||||
|
#include "sockets.h"
|
||||||
|
|
||||||
|
#include "sasl.h"
|
||||||
|
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NB, keep in sync with similar method in qemud/remote.c
|
||||||
|
*/
|
||||||
|
static char *vnc_connection_addr_to_string(char *host, int port)
|
||||||
|
{
|
||||||
|
char * buf = (char *)malloc(strlen(host) + 7);
|
||||||
|
if (buf) {
|
||||||
|
sprintf(buf, "%s;%hu", host, (unsigned short)port);
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int log_func(void *context,
|
||||||
|
int level,
|
||||||
|
const char *message)
|
||||||
|
{
|
||||||
|
rfbClientLog("SASL: %s\n", message);
|
||||||
|
|
||||||
|
return SASL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int user_callback_adapt(void *context,
|
||||||
|
int id,
|
||||||
|
const char **result,
|
||||||
|
unsigned *len)
|
||||||
|
{
|
||||||
|
rfbClient* client = (rfbClient *)context;
|
||||||
|
|
||||||
|
if (id != SASL_CB_AUTHNAME) {
|
||||||
|
rfbClientLog("Unrecognized SASL callback ID %d\n", id);
|
||||||
|
return SASL_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client->GetUser) {
|
||||||
|
rfbClientLog("Client user callback not found\n");
|
||||||
|
return SASL_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*result = client->GetUser(client);
|
||||||
|
|
||||||
|
if (! *result) return SASL_FAIL;
|
||||||
|
/**len = strlen(*result);*/
|
||||||
|
return SASL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int password_callback_adapt(sasl_conn_t *conn,
|
||||||
|
void * context,
|
||||||
|
int id,
|
||||||
|
sasl_secret_t **secret)
|
||||||
|
{
|
||||||
|
rfbClient* client = (rfbClient *)context;
|
||||||
|
char * password;
|
||||||
|
|
||||||
|
if (id != SASL_CB_PASS) {
|
||||||
|
rfbClientLog("Unrecognized SASL callback ID %d\n", id);
|
||||||
|
return SASL_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->saslSecret) { /* If we've already got it just return it. */
|
||||||
|
*secret = client->saslSecret;
|
||||||
|
return SASL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client->GetPassword) {
|
||||||
|
rfbClientLog("Client password callback not found\n");
|
||||||
|
return SASL_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
password = client->GetPassword(client);
|
||||||
|
|
||||||
|
if (! password) return SASL_FAIL;
|
||||||
|
|
||||||
|
sasl_secret_t *lsec = (sasl_secret_t *)malloc(sizeof(sasl_secret_t) + strlen(password));
|
||||||
|
if (!lsec) {
|
||||||
|
rfbClientLog("Could not allocate sasl_secret_t\n");
|
||||||
|
return SASL_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy((char *)lsec->data, password);
|
||||||
|
lsec->len = strlen(password);
|
||||||
|
client->saslSecret = lsec;
|
||||||
|
*secret = lsec;
|
||||||
|
|
||||||
|
/* Clear client password */
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < lsec->len; i++) {
|
||||||
|
password[i] = '\0';
|
||||||
|
}
|
||||||
|
free(password);
|
||||||
|
|
||||||
|
return SASL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SASL_MAX_MECHLIST_LEN 300
|
||||||
|
#define SASL_MAX_DATA_LEN (1024 * 1024)
|
||||||
|
|
||||||
|
/* Perform the SASL authentication process
|
||||||
|
*/
|
||||||
|
rfbBool
|
||||||
|
HandleSASLAuth(rfbClient *client)
|
||||||
|
{
|
||||||
|
sasl_conn_t *saslconn = NULL;
|
||||||
|
sasl_security_properties_t secprops;
|
||||||
|
const char *clientout;
|
||||||
|
char *serverin = NULL;
|
||||||
|
unsigned int clientoutlen, serverinlen;
|
||||||
|
int err, complete = 0;
|
||||||
|
char *localAddr = NULL, *remoteAddr = NULL;
|
||||||
|
const void *val;
|
||||||
|
sasl_ssf_t ssf;
|
||||||
|
sasl_callback_t saslcb[] = {
|
||||||
|
{SASL_CB_LOG, (void *)log_func, NULL},
|
||||||
|
{SASL_CB_AUTHNAME, client->GetUser ? (void *)user_callback_adapt : NULL, client},
|
||||||
|
{SASL_CB_PASS, client->GetPassword ? (void *)password_callback_adapt : NULL, client},
|
||||||
|
{ .id = 0 },
|
||||||
|
};
|
||||||
|
sasl_interact_t *interact = NULL;
|
||||||
|
uint32_t mechlistlen;
|
||||||
|
char *mechlist;
|
||||||
|
char *wantmech;
|
||||||
|
const char *mechname;
|
||||||
|
|
||||||
|
client->saslconn = NULL;
|
||||||
|
|
||||||
|
/* Sets up the SASL library as a whole */
|
||||||
|
err = sasl_client_init(NULL);
|
||||||
|
rfbClientLog("Client initialize SASL authentication %d\n", err);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
rfbClientLog("failed to initialize SASL library: %d (%s)\n",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get local address in form IPADDR:PORT */
|
||||||
|
struct sockaddr_storage localAddress;
|
||||||
|
socklen_t addressLength = sizeof(localAddress);
|
||||||
|
char buf[INET6_ADDRSTRLEN];
|
||||||
|
int port;
|
||||||
|
|
||||||
|
if (getsockname(client->sock, (struct sockaddr*)&localAddress, &addressLength)) {
|
||||||
|
rfbClientLog("failed to get local address\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localAddress.ss_family == AF_INET) {
|
||||||
|
struct sockaddr_in *sa_in = (struct sockaddr_in*)&localAddress;
|
||||||
|
inet_ntop(AF_INET, &(sa_in->sin_addr), buf, INET_ADDRSTRLEN);
|
||||||
|
port = ntohs(sa_in->sin_port);
|
||||||
|
localAddr = vnc_connection_addr_to_string(buf, port);
|
||||||
|
} else if (localAddress.ss_family == AF_INET6) {
|
||||||
|
struct sockaddr_in6 *sa_in = (struct sockaddr_in6*)&localAddress;
|
||||||
|
inet_ntop(AF_INET6, &(sa_in->sin6_addr), buf, INET6_ADDRSTRLEN);
|
||||||
|
port = ntohs(sa_in->sin6_port);
|
||||||
|
localAddr = vnc_connection_addr_to_string(buf, port);
|
||||||
|
} else {
|
||||||
|
rfbClientLog("failed to get local address\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get remote address in form IPADDR:PORT */
|
||||||
|
remoteAddr = vnc_connection_addr_to_string(client->serverHost, client->serverPort);
|
||||||
|
|
||||||
|
rfbClientLog("Client SASL new host:'%s' local:'%s' remote:'%s'\n", client->serverHost, localAddr, remoteAddr);
|
||||||
|
|
||||||
|
/* Setup a handle for being a client */
|
||||||
|
err = sasl_client_new("vnc",
|
||||||
|
client->serverHost,
|
||||||
|
localAddr,
|
||||||
|
remoteAddr,
|
||||||
|
saslcb,
|
||||||
|
SASL_SUCCESS_DATA,
|
||||||
|
&saslconn);
|
||||||
|
free(localAddr);
|
||||||
|
free(remoteAddr);
|
||||||
|
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
rfbClientLog("Failed to create SASL client context: %d (%s)\n",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize some connection props we care about */
|
||||||
|
if (client->tlsSession) {
|
||||||
|
if (!(ssf = (sasl_ssf_t)GetTLSCipherBits(client))) {
|
||||||
|
rfbClientLog("%s", "invalid cipher size for TLS session\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("Setting external SSF %d\n", ssf);
|
||||||
|
err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
rfbClientLog("cannot set external SSF %d (%s)\n",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (&secprops, 0, sizeof secprops);
|
||||||
|
/* If we've got TLS, we don't care about SSF */
|
||||||
|
secprops.min_ssf = client->tlsSession ? 0 : 56; /* Equiv to DES supported by all Kerberos */
|
||||||
|
secprops.max_ssf = client->tlsSession ? 0 : 100000; /* Very strong ! AES == 256 */
|
||||||
|
secprops.maxbufsize = 100000;
|
||||||
|
/* If we're not TLS, then forbid any anonymous or trivially crackable auth */
|
||||||
|
secprops.security_flags = client->tlsSession ? 0 :
|
||||||
|
SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
|
||||||
|
|
||||||
|
err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
rfbClientLog("cannot set security props %d (%s)\n",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the supported mechanisms from the server */
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&mechlistlen, 4)) {
|
||||||
|
rfbClientLog("failed to read mechlistlen\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
mechlistlen = rfbClientSwap32IfLE(mechlistlen);
|
||||||
|
rfbClientLog("mechlistlen is %d\n", mechlistlen);
|
||||||
|
if (mechlistlen > SASL_MAX_MECHLIST_LEN) {
|
||||||
|
rfbClientLog("mechlistlen %d too long\n", mechlistlen);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
mechlist = malloc(mechlistlen+1);
|
||||||
|
if (!mechlist || !ReadFromRFBServer(client, mechlist, mechlistlen)) {
|
||||||
|
free(mechlist);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
mechlist[mechlistlen] = '\0';
|
||||||
|
|
||||||
|
/* Allow the client to influence the mechanism selected. */
|
||||||
|
if (client->GetSASLMechanism) {
|
||||||
|
wantmech = client->GetSASLMechanism(client, mechlist);
|
||||||
|
|
||||||
|
if (wantmech && *wantmech != 0) {
|
||||||
|
if (strstr(mechlist, wantmech) == NULL) {
|
||||||
|
rfbClientLog("Client requested SASL mechanism %s not supported by server\n",
|
||||||
|
wantmech);
|
||||||
|
free(mechlist);
|
||||||
|
free(wantmech);
|
||||||
|
goto error;
|
||||||
|
} else {
|
||||||
|
free(mechlist);
|
||||||
|
mechlist = wantmech;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("Client start negotiation mechlist '%s'\n", mechlist);
|
||||||
|
|
||||||
|
/* Start the auth negotiation on the client end first */
|
||||||
|
err = sasl_client_start(saslconn,
|
||||||
|
mechlist,
|
||||||
|
&interact,
|
||||||
|
&clientout,
|
||||||
|
&clientoutlen,
|
||||||
|
&mechname);
|
||||||
|
if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
|
||||||
|
rfbClientLog("Failed to start SASL negotiation: %d (%s)\n",
|
||||||
|
err, sasl_errdetail(saslconn));
|
||||||
|
free(mechlist);
|
||||||
|
mechlist = NULL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Need to gather some credentials from the client */
|
||||||
|
if (err == SASL_INTERACT) {
|
||||||
|
rfbClientLog("User interaction required but not currently supported\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("Server start negotiation with mech %s. Data %d bytes %p '%s'\n",
|
||||||
|
mechname, clientoutlen, clientout, clientout);
|
||||||
|
|
||||||
|
if (clientoutlen > SASL_MAX_DATA_LEN) {
|
||||||
|
rfbClientLog("SASL negotiation data too long: %d bytes\n",
|
||||||
|
clientoutlen);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send back the chosen mechname */
|
||||||
|
uint32_t mechnamelen = rfbClientSwap32IfLE(strlen(mechname));
|
||||||
|
if (!WriteToRFBServer(client, (char *)&mechnamelen, 4)) goto error;
|
||||||
|
if (!WriteToRFBServer(client, (char *)mechname, strlen(mechname))) goto error;
|
||||||
|
|
||||||
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
||||||
|
if (clientout) {
|
||||||
|
uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1);
|
||||||
|
if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error;
|
||||||
|
if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error;
|
||||||
|
} else {
|
||||||
|
uint32_t temp = 0;
|
||||||
|
if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("%s", "Getting sever start negotiation reply\n");
|
||||||
|
/* Read the 'START' message reply from server */
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error;
|
||||||
|
serverinlen = rfbClientSwap32IfLE(serverinlen);
|
||||||
|
|
||||||
|
if (serverinlen > SASL_MAX_DATA_LEN) {
|
||||||
|
rfbClientLog("SASL negotiation data too long: %d bytes\n",
|
||||||
|
serverinlen);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
||||||
|
if (serverinlen) {
|
||||||
|
serverin = malloc(serverinlen);
|
||||||
|
if (!serverin || !ReadFromRFBServer(client, serverin, serverinlen)) goto error;
|
||||||
|
serverin[serverinlen-1] = '\0';
|
||||||
|
serverinlen--;
|
||||||
|
} else {
|
||||||
|
serverin = NULL;
|
||||||
|
}
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error;
|
||||||
|
|
||||||
|
rfbClientLog("Client start result complete: %d. Data %d bytes %p '%s'\n",
|
||||||
|
complete, serverinlen, serverin, serverin);
|
||||||
|
|
||||||
|
/* Loop-the-loop...
|
||||||
|
* Even if the server has completed, the client must *always* do at least one step
|
||||||
|
* in this loop to verify the server isn't lying about something. Mutual auth */
|
||||||
|
for (;;) {
|
||||||
|
err = sasl_client_step(saslconn,
|
||||||
|
serverin,
|
||||||
|
serverinlen,
|
||||||
|
&interact,
|
||||||
|
&clientout,
|
||||||
|
&clientoutlen);
|
||||||
|
if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
|
||||||
|
rfbClientLog("Failed SASL step: %d (%s)\n",
|
||||||
|
err, sasl_errdetail(saslconn));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Need to gather some credentials from the client */
|
||||||
|
if (err == SASL_INTERACT) {
|
||||||
|
rfbClientLog("User interaction required but not currently supported\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverin) {
|
||||||
|
free(serverin);
|
||||||
|
serverin = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("Client step result %d. Data %d bytes %p '%s'\n", err, clientoutlen, clientout, clientout);
|
||||||
|
|
||||||
|
/* Previous server call showed completion & we're now locally complete too */
|
||||||
|
if (complete && err == SASL_OK)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Not done, prepare to talk with the server for another iteration */
|
||||||
|
|
||||||
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
||||||
|
if (clientout) {
|
||||||
|
uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1);
|
||||||
|
if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error;
|
||||||
|
if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error;
|
||||||
|
} else {
|
||||||
|
uint32_t temp = 0;
|
||||||
|
if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("Server step with %d bytes %p\n", clientoutlen, clientout);
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error;
|
||||||
|
serverinlen = rfbClientSwap32IfLE(serverinlen);
|
||||||
|
|
||||||
|
if (serverinlen > SASL_MAX_DATA_LEN) {
|
||||||
|
rfbClientLog("SASL negotiation data too long: %d bytes\n",
|
||||||
|
serverinlen);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
||||||
|
if (serverinlen) {
|
||||||
|
serverin = malloc(serverinlen);
|
||||||
|
if (!serverin || !ReadFromRFBServer(client, serverin, serverinlen)) goto error;
|
||||||
|
serverin[serverinlen-1] = '\0';
|
||||||
|
serverinlen--;
|
||||||
|
} else {
|
||||||
|
serverin = NULL;
|
||||||
|
}
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error;
|
||||||
|
|
||||||
|
rfbClientLog("Client step result complete: %d. Data %d bytes %p '%s'\n",
|
||||||
|
complete, serverinlen, serverin, serverin);
|
||||||
|
|
||||||
|
/* This server call shows complete, and earlier client step was OK */
|
||||||
|
if (complete && err == SASL_OK) {
|
||||||
|
free(serverin);
|
||||||
|
serverin = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for suitable SSF if non-TLS */
|
||||||
|
if (!client->tlsSession) {
|
||||||
|
err = sasl_getprop(saslconn, SASL_SSF, &val);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
rfbClientLog("cannot query SASL ssf on connection %d (%s)\n",
|
||||||
|
err, sasl_errstring(err, NULL, NULL));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
ssf = *(const int *)val;
|
||||||
|
rfbClientLog("SASL SSF value %d\n", ssf);
|
||||||
|
if (ssf < 56) { /* 56 == DES level, good for Kerberos */
|
||||||
|
rfbClientLog("negotiation SSF %d was not strong enough\n", ssf);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("%s", "SASL authentication complete\n");
|
||||||
|
|
||||||
|
uint32_t result;
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&result, 4)) {
|
||||||
|
rfbClientLog("Failed to read authentication result\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
result = rfbClientSwap32IfLE(result);
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
|
rfbClientLog("Authentication failure\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
rfbClientLog("Authentication successful - switching to SSF\n");
|
||||||
|
|
||||||
|
/* This must come *after* check-auth-result, because the former
|
||||||
|
* is defined to be sent unencrypted, and setting saslconn turns
|
||||||
|
* on the SSF layer encryption processing */
|
||||||
|
client->saslconn = saslconn;
|
||||||
|
|
||||||
|
/* Clear SASL secret from memory if set - it'll be free'd on dispose */
|
||||||
|
if (client->saslSecret) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < client->saslSecret->len; i++)
|
||||||
|
client->saslSecret->data[i] = '\0';
|
||||||
|
client->saslSecret->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (client->saslSecret) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < client->saslSecret->len; i++)
|
||||||
|
client->saslSecret->data[i] = '\0';
|
||||||
|
client->saslSecret->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saslconn)
|
||||||
|
sasl_dispose(&saslconn);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ReadFromSASL(rfbClient* client, char *out, unsigned int n)
|
||||||
|
{
|
||||||
|
size_t want;
|
||||||
|
|
||||||
|
if (client->saslDecoded == NULL) {
|
||||||
|
char *encoded;
|
||||||
|
int encodedLen;
|
||||||
|
int err, ret;
|
||||||
|
|
||||||
|
encodedLen = 8192;
|
||||||
|
encoded = (char *)malloc(encodedLen);
|
||||||
|
if (!encoded) {
|
||||||
|
errno = EIO;
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
ret = read(client->sock, encoded, encodedLen);
|
||||||
|
if (ret < 0) {
|
||||||
|
free(encoded);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (ret == 0) {
|
||||||
|
free(encoded);
|
||||||
|
errno = EIO;
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sasl_decode(client->saslconn, encoded, ret,
|
||||||
|
&client->saslDecoded, &client->saslDecodedLength);
|
||||||
|
free(encoded);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
rfbClientLog("Failed to decode SASL data %s\n",
|
||||||
|
sasl_errstring(err, NULL, NULL));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
client->saslDecodedOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
want = client->saslDecodedLength - client->saslDecodedOffset;
|
||||||
|
if (want > n)
|
||||||
|
want = n;
|
||||||
|
|
||||||
|
memcpy(out,
|
||||||
|
client->saslDecoded + client->saslDecodedOffset,
|
||||||
|
want);
|
||||||
|
client->saslDecodedOffset += want;
|
||||||
|
if (client->saslDecodedOffset == client->saslDecodedLength) {
|
||||||
|
client->saslDecodedLength = client->saslDecodedOffset = 0;
|
||||||
|
client->saslDecoded = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!want) {
|
||||||
|
errno = EAGAIN;
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return want;
|
||||||
|
}
|
|
@ -0,0 +1,378 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 Andri Yngvasin <andri@yngvason.is>
|
||||||
|
* Copyright (C) 2011-2012 Christian Beier <dontmind@freeshell.org>
|
||||||
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sockets.c - functions to deal with sockets.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include "rfbclient.h"
|
||||||
|
#include "sockets.h"
|
||||||
|
#include "tls.h"
|
||||||
|
#include "sasl.h"
|
||||||
|
|
||||||
|
void run_main_loop_once(void);
|
||||||
|
|
||||||
|
rfbBool errorMessageOnReadFailure = TRUE;
|
||||||
|
|
||||||
|
rfbBool ReadToBuffer(rfbClient* client) {
|
||||||
|
if (client->buffered == RFB_BUF_SIZE)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
ssize_t size;
|
||||||
|
|
||||||
|
#if defined(LIBVNCSERVER_HAVE_GNUTLS) || defined(LIBVNCSERVER_HAVE_LIBSSL)
|
||||||
|
if (client->tlsSession) {
|
||||||
|
size = ReadFromTLS(client, client->buf + client->buffered,
|
||||||
|
RFB_BUF_SIZE - client->buffered);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
if (client->saslconn) {
|
||||||
|
size = ReadFromSASL(client, client->buf + client->buffered,
|
||||||
|
RFB_BUF_SIZE - client->buffered);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
size = recv(client->sock, client->buf + client->buffered,
|
||||||
|
RFB_BUF_SIZE - client->buffered, MSG_DONTWAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (size > 0)
|
||||||
|
client->buffered += size;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbBool ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
|
||||||
|
{
|
||||||
|
if (!out)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
while (n != 0) {
|
||||||
|
while (n != 0 && client->buffered == 0) {
|
||||||
|
run_main_loop_once();
|
||||||
|
if (!ReadToBuffer(client))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int size = MIN(client->buffered, n);
|
||||||
|
memcpy(out, client->buf, size);
|
||||||
|
|
||||||
|
client->buffered -= size;
|
||||||
|
memmove(client->buf, client->buf + size, client->buffered);
|
||||||
|
|
||||||
|
out += size;
|
||||||
|
n -= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write an exact number of bytes, and don't return until you've sent them.
|
||||||
|
*/
|
||||||
|
rfbBool
|
||||||
|
WriteToRFBServer(rfbClient* client, const char *buf, unsigned int n)
|
||||||
|
{
|
||||||
|
struct pollfd fds;
|
||||||
|
int i = 0;
|
||||||
|
int j;
|
||||||
|
const char *obuf = buf;
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
const char *output;
|
||||||
|
unsigned int outputlen;
|
||||||
|
int err;
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
||||||
|
if (client->tlsSession) {
|
||||||
|
/* WriteToTLS() will guarantee either everything is written, or error/eof returns */
|
||||||
|
i = WriteToTLS(client, buf, n);
|
||||||
|
if (i <= 0) return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
if (client->saslconn) {
|
||||||
|
err = sasl_encode(client->saslconn,
|
||||||
|
buf, n,
|
||||||
|
&output, &outputlen);
|
||||||
|
if (err != SASL_OK) {
|
||||||
|
rfbClientLog("Failed to encode SASL data %s",
|
||||||
|
sasl_errstring(err, NULL, NULL));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
obuf = output;
|
||||||
|
n = outputlen;
|
||||||
|
}
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
||||||
|
// TODO: Dispatch events while waiting
|
||||||
|
while (i < n) {
|
||||||
|
j = write(client->sock, obuf + i, (n - i));
|
||||||
|
if (j > 0) {
|
||||||
|
i += j;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j == 0) {
|
||||||
|
rfbClientLog("write failed\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno != EWOULDBLOCK && errno != EAGAIN) {
|
||||||
|
rfbClientErr("write\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fds.fd = client->sock;
|
||||||
|
fds.events = POLLOUT;
|
||||||
|
if (poll(&fds, 1, -1) <= 0) {
|
||||||
|
rfbClientErr("poll\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool WaitForConnected(int socket, unsigned int secs)
|
||||||
|
{
|
||||||
|
struct pollfd fds = {
|
||||||
|
.fd = socket,
|
||||||
|
.events = POLLIN | POLLOUT | POLLERR | POLLHUP,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (poll(&fds, 1, secs * 1000) != 1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
int so_error = 0;
|
||||||
|
socklen_t len = sizeof(so_error);
|
||||||
|
getsockopt(socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
|
||||||
|
|
||||||
|
return so_error == 0 ? TRUE : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbSocket
|
||||||
|
ConnectClientToTcpAddr6(const char *hostname, int port)
|
||||||
|
{
|
||||||
|
rfbSocket sock = ConnectClientToTcpAddr6WithTimeout(hostname, port, DEFAULT_CONNECT_TIMEOUT);
|
||||||
|
/* put socket back into blocking mode for compatibility reasons */
|
||||||
|
if (sock != RFB_INVALID_SOCKET) {
|
||||||
|
SetBlocking(sock);
|
||||||
|
}
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbSocket
|
||||||
|
ConnectClientToTcpAddr6WithTimeout(const char *hostname, int port, unsigned int timeout)
|
||||||
|
{
|
||||||
|
rfbSocket sock;
|
||||||
|
int n;
|
||||||
|
struct addrinfo hints, *res, *ressave;
|
||||||
|
char port_s[10];
|
||||||
|
int one = 1;
|
||||||
|
|
||||||
|
snprintf(port_s, 10, "%d", port);
|
||||||
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
if ((n = getaddrinfo(strcmp(hostname,"") == 0 ? "localhost": hostname, port_s, &hints, &res)))
|
||||||
|
{
|
||||||
|
rfbClientErr("ConnectClientToTcpAddr6: getaddrinfo (%s)\n", gai_strerror(n));
|
||||||
|
return RFB_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
ressave = res;
|
||||||
|
sock = RFB_INVALID_SOCKET;
|
||||||
|
while (res)
|
||||||
|
{
|
||||||
|
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||||
|
if (sock != RFB_INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
if (SetNonBlocking(sock)) {
|
||||||
|
if (connect(sock, res->ai_addr, res->ai_addrlen) == 0) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if ((errno == EWOULDBLOCK || errno == EINPROGRESS) && WaitForConnected(sock, timeout))
|
||||||
|
break;
|
||||||
|
rfbCloseSocket(sock);
|
||||||
|
sock = RFB_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rfbCloseSocket(sock);
|
||||||
|
sock = RFB_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = res->ai_next;
|
||||||
|
}
|
||||||
|
freeaddrinfo(ressave);
|
||||||
|
|
||||||
|
if (sock == RFB_INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
rfbClientErr("ConnectClientToTcpAddr6: connect\n");
|
||||||
|
return RFB_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
|
||||||
|
(char *)&one, sizeof(one)) < 0) {
|
||||||
|
rfbClientErr("ConnectToTcpAddr: setsockopt\n");
|
||||||
|
rfbCloseSocket(sock);
|
||||||
|
return RFB_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbSocket
|
||||||
|
ConnectClientToUnixSock(const char *sockFile)
|
||||||
|
{
|
||||||
|
rfbSocket sock = ConnectClientToUnixSockWithTimeout(sockFile, DEFAULT_CONNECT_TIMEOUT);
|
||||||
|
/* put socket back into blocking mode for compatibility reasons */
|
||||||
|
if (sock != RFB_INVALID_SOCKET) {
|
||||||
|
SetBlocking(sock);
|
||||||
|
}
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbSocket
|
||||||
|
ConnectClientToUnixSockWithTimeout(const char *sockFile, unsigned int timeout)
|
||||||
|
{
|
||||||
|
rfbSocket sock;
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
if(strlen(sockFile) + 1 > sizeof(addr.sun_path)) {
|
||||||
|
rfbClientErr("ConnectToUnixSock: socket file name too long\n");
|
||||||
|
return RFB_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
strcpy(addr.sun_path, sockFile);
|
||||||
|
|
||||||
|
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (sock == RFB_INVALID_SOCKET) {
|
||||||
|
rfbClientErr("ConnectToUnixSock: socket (%s)\n",strerror(errno));
|
||||||
|
return RFB_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetNonBlocking(sock))
|
||||||
|
return RFB_INVALID_SOCKET;
|
||||||
|
|
||||||
|
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr.sun_family) + strlen(addr.sun_path)) < 0 &&
|
||||||
|
!(errno == EINPROGRESS && WaitForConnected(sock, timeout))) {
|
||||||
|
rfbClientErr("ConnectToUnixSock: connect\n");
|
||||||
|
rfbCloseSocket(sock);
|
||||||
|
return RFB_INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SetNonBlocking sets a socket into non-blocking mode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
rfbBool
|
||||||
|
SetNonBlocking(rfbSocket sock)
|
||||||
|
{
|
||||||
|
int flags = fcntl(sock, F_GETFL);
|
||||||
|
if(flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||||
|
rfbClientErr("Setting socket to non-blocking failed: %s\n",strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rfbBool SetBlocking(rfbSocket sock)
|
||||||
|
{
|
||||||
|
int flags = fcntl(sock, F_GETFL);
|
||||||
|
if(flags < 0 || fcntl(sock, F_SETFL, flags & ~O_NONBLOCK) < 0) {
|
||||||
|
rfbClientErr("Setting socket to blocking failed: %s\n",strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SetDSCP sets a socket's IP QoS parameters aka Differentiated Services Code Point field
|
||||||
|
*/
|
||||||
|
|
||||||
|
rfbBool
|
||||||
|
SetDSCP(rfbSocket sock, int dscp)
|
||||||
|
{
|
||||||
|
int level, cmd;
|
||||||
|
struct sockaddr addr;
|
||||||
|
socklen_t addrlen = sizeof(addr);
|
||||||
|
|
||||||
|
if(getsockname(sock, &addr, &addrlen) != 0) {
|
||||||
|
rfbClientErr("Setting socket QoS failed while getting socket address: %s\n",strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(addr.sa_family)
|
||||||
|
{
|
||||||
|
case AF_INET6:
|
||||||
|
level = IPPROTO_IPV6;
|
||||||
|
cmd = IPV6_TCLASS;
|
||||||
|
break;
|
||||||
|
case AF_INET:
|
||||||
|
level = IPPROTO_IP;
|
||||||
|
cmd = IP_TOS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rfbClientErr("Setting socket QoS failed: Not bound to IP address");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(setsockopt(sock, level, cmd, (void*)&dscp, sizeof(dscp)) != 0) {
|
||||||
|
rfbClientErr("Setting socket QoS failed: %s\n", strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test if the other end of a socket is on the same machine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
rfbBool
|
||||||
|
SameMachine(rfbSocket sock)
|
||||||
|
{
|
||||||
|
struct sockaddr_in peeraddr, myaddr;
|
||||||
|
socklen_t addrlen = sizeof(struct sockaddr_in);
|
||||||
|
|
||||||
|
getpeername(sock, (struct sockaddr *)&peeraddr, &addrlen);
|
||||||
|
getsockname(sock, (struct sockaddr *)&myaddr, &addrlen);
|
||||||
|
|
||||||
|
return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr);
|
||||||
|
}
|
|
@ -0,0 +1,587 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 Vic Lee.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rfbclient.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
#include <gnutls/x509.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
static const char *rfbTLSPriority = "NORMAL:+DHE-DSS:+RSA:+DHE-RSA";
|
||||||
|
static const char *rfbAnonTLSPriority= "NORMAL:+ANON-DH";
|
||||||
|
|
||||||
|
#define DH_BITS 1024
|
||||||
|
static gnutls_dh_params_t rfbDHParams;
|
||||||
|
|
||||||
|
static rfbBool rfbTLSInitialized = FALSE;
|
||||||
|
|
||||||
|
static int
|
||||||
|
verify_certificate_callback (gnutls_session_t session)
|
||||||
|
{
|
||||||
|
unsigned int status;
|
||||||
|
const gnutls_datum_t *cert_list;
|
||||||
|
unsigned int cert_list_size;
|
||||||
|
int ret;
|
||||||
|
gnutls_x509_crt_t cert;
|
||||||
|
rfbClient *sptr;
|
||||||
|
char *hostname;
|
||||||
|
|
||||||
|
sptr = (rfbClient *)gnutls_session_get_ptr(session);
|
||||||
|
if (!sptr) {
|
||||||
|
rfbClientLog("Failed to validate certificate - missing client data\n");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname = sptr->serverHost;
|
||||||
|
if (!hostname) {
|
||||||
|
rfbClientLog("No server hostname found for client\n");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This verification function uses the trusted CAs in the credentials
|
||||||
|
* structure. So you must have installed one or more CA certificates.
|
||||||
|
*/
|
||||||
|
ret = gnutls_certificate_verify_peers2 (session, &status);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog ("Certificate validation call failed\n");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status & GNUTLS_CERT_INVALID)
|
||||||
|
rfbClientLog("The certificate is not trusted.\n");
|
||||||
|
|
||||||
|
if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
|
||||||
|
rfbClientLog("The certificate hasn't got a known issuer.\n");
|
||||||
|
|
||||||
|
if (status & GNUTLS_CERT_REVOKED)
|
||||||
|
rfbClientLog("The certificate has been revoked.\n");
|
||||||
|
|
||||||
|
if (status & GNUTLS_CERT_EXPIRED)
|
||||||
|
rfbClientLog("The certificate has expired\n");
|
||||||
|
|
||||||
|
if (status & GNUTLS_CERT_NOT_ACTIVATED)
|
||||||
|
rfbClientLog("The certificate is not yet activated\n");
|
||||||
|
|
||||||
|
if (status)
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
|
||||||
|
/* Up to here the process is the same for X.509 certificates and
|
||||||
|
* OpenPGP keys. From now on X.509 certificates are assumed. This can
|
||||||
|
* be easily extended to work with openpgp keys as well.
|
||||||
|
*/
|
||||||
|
if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) {
|
||||||
|
rfbClientLog("The certificate was not X509\n");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gnutls_x509_crt_init (&cert) < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Error initialising certificate structure\n");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
|
||||||
|
if (cert_list == NULL)
|
||||||
|
{
|
||||||
|
rfbClientLog("No certificate was found!\n");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Error parsing certificate\n");
|
||||||
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hostname verification does NOT work
|
||||||
|
//if (!gnutls_x509_crt_check_hostname (cert, hostname))
|
||||||
|
// {
|
||||||
|
// rfbClientLog("The certificate's owner does not match hostname '%s'\n",
|
||||||
|
// hostname);
|
||||||
|
// return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
// }
|
||||||
|
|
||||||
|
gnutls_x509_crt_deinit (cert);
|
||||||
|
|
||||||
|
/* notify gnutls to continue handshake normally */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
InitializeTLS(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (rfbTLSInitialized) return TRUE;
|
||||||
|
if ((ret = gnutls_global_init()) < 0 ||
|
||||||
|
(ret = gnutls_dh_params_init(&rfbDHParams)) < 0 ||
|
||||||
|
(ret = gnutls_dh_params_generate2(rfbDHParams, DH_BITS)) < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Failed to initialized GnuTLS: %s.\n", gnutls_strerror(ret));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
rfbClientLog("GnuTLS version %s initialized.\n", gnutls_check_version(NULL));
|
||||||
|
rfbTLSInitialized = TRUE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
PushTLS(gnutls_transport_ptr_t transport, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
rfbClient *client = (rfbClient*)transport;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
ret = write(client->sock, data, len);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
PullTLS(gnutls_transport_ptr_t transport, void *data, size_t len)
|
||||||
|
{
|
||||||
|
rfbClient *client = (rfbClient*)transport;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
ret = read(client->sock, data, len);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
InitializeTLSSession(rfbClient* client, rfbBool anonTLS)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
if (client->tlsSession) return TRUE;
|
||||||
|
|
||||||
|
if ((ret = gnutls_init((gnutls_session_t*)&client->tlsSession, GNUTLS_CLIENT)) < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Failed to initialized TLS session: %s.\n", gnutls_strerror(ret));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = gnutls_priority_set_direct((gnutls_session_t)client->tlsSession,
|
||||||
|
anonTLS ? rfbAnonTLSPriority : rfbTLSPriority, &p)) < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Warning: Failed to set TLS priority: %s (%s).\n", gnutls_strerror(ret), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
gnutls_transport_set_ptr((gnutls_session_t)client->tlsSession, (gnutls_transport_ptr_t)client);
|
||||||
|
gnutls_transport_set_push_function((gnutls_session_t)client->tlsSession, PushTLS);
|
||||||
|
gnutls_transport_set_pull_function((gnutls_session_t)client->tlsSession, PullTLS);
|
||||||
|
|
||||||
|
INIT_MUTEX(client->tlsRwMutex);
|
||||||
|
|
||||||
|
rfbClientLog("TLS session initialized.\n");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
SetTLSAnonCredential(rfbClient* client)
|
||||||
|
{
|
||||||
|
gnutls_anon_client_credentials_t anonCred;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if ((ret = gnutls_anon_allocate_client_credentials(&anonCred)) < 0 ||
|
||||||
|
(ret = gnutls_credentials_set((gnutls_session_t)client->tlsSession, GNUTLS_CRD_ANON, anonCred)) < 0)
|
||||||
|
{
|
||||||
|
FreeTLS(client);
|
||||||
|
rfbClientLog("Failed to create anonymous credentials: %s", gnutls_strerror(ret));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
rfbClientLog("TLS anonymous credential created.\n");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
HandshakeTLS(rfbClient* client)
|
||||||
|
{
|
||||||
|
int timeout = 15;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
while (timeout > 0 && (ret = gnutls_handshake((gnutls_session_t)client->tlsSession)) < 0)
|
||||||
|
{
|
||||||
|
if (!gnutls_error_is_fatal(ret))
|
||||||
|
{
|
||||||
|
rfbClientLog("TLS handshake blocking.\n");
|
||||||
|
sleep(1);
|
||||||
|
timeout--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rfbClientLog("TLS handshake failed: %s.\n", gnutls_strerror(ret));
|
||||||
|
|
||||||
|
FreeTLS(client);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout <= 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("TLS handshake timeout.\n");
|
||||||
|
FreeTLS(client);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("TLS handshake done.\n");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VeNCrypt sub auth. 1 byte auth count, followed by count * 4 byte integers */
|
||||||
|
static rfbBool
|
||||||
|
ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
|
||||||
|
{
|
||||||
|
uint8_t count=0;
|
||||||
|
uint8_t loop=0;
|
||||||
|
uint8_t flag=0;
|
||||||
|
uint32_t tAuth[256], t;
|
||||||
|
char buf1[500],buf2[10];
|
||||||
|
uint32_t authScheme;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE;
|
||||||
|
|
||||||
|
if (count==0)
|
||||||
|
{
|
||||||
|
rfbClientLog("List of security types is ZERO. Giving up.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("We have %d security types to read\n", count);
|
||||||
|
authScheme=0;
|
||||||
|
/* now, we have a list of available security types to read ( uint8_t[] ) */
|
||||||
|
for (loop=0;loop<count;loop++)
|
||||||
|
{
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
|
||||||
|
t=rfbClientSwap32IfLE(tAuth[loop]);
|
||||||
|
rfbClientLog("%d) Received security type %d\n", loop, t);
|
||||||
|
if (flag) continue;
|
||||||
|
if (t==rfbVeNCryptTLSNone ||
|
||||||
|
t==rfbVeNCryptTLSVNC ||
|
||||||
|
t==rfbVeNCryptTLSPlain ||
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
t==rfbVeNCryptTLSSASL ||
|
||||||
|
t==rfbVeNCryptX509SASL ||
|
||||||
|
#endif /*LIBVNCSERVER_HAVE_SASL */
|
||||||
|
t==rfbVeNCryptX509None ||
|
||||||
|
t==rfbVeNCryptX509VNC ||
|
||||||
|
t==rfbVeNCryptX509Plain)
|
||||||
|
{
|
||||||
|
flag++;
|
||||||
|
authScheme=t;
|
||||||
|
rfbClientLog("Selecting security type %d (%d/%d in the list)\n", authScheme, loop, count);
|
||||||
|
/* send back 4 bytes (in original byte order!) indicating which security type to use */
|
||||||
|
if (!WriteToRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
|
||||||
|
}
|
||||||
|
tAuth[loop]=t;
|
||||||
|
}
|
||||||
|
if (authScheme==0)
|
||||||
|
{
|
||||||
|
memset(buf1, 0, sizeof(buf1));
|
||||||
|
for (loop=0;loop<count;loop++)
|
||||||
|
{
|
||||||
|
if (strlen(buf1)>=sizeof(buf1)-1) break;
|
||||||
|
snprintf(buf2, sizeof(buf2), (loop>0 ? ", %d" : "%d"), (int)tAuth[loop]);
|
||||||
|
strncat(buf1, buf2, sizeof(buf1)-strlen(buf1)-1);
|
||||||
|
}
|
||||||
|
rfbClientLog("Unknown VeNCrypt authentication scheme from VNC server: %s\n",
|
||||||
|
buf1);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
*result = authScheme;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
FreeX509Credential(rfbCredential *cred)
|
||||||
|
{
|
||||||
|
if (cred->x509Credential.x509CACertFile) free(cred->x509Credential.x509CACertFile);
|
||||||
|
if (cred->x509Credential.x509CACrlFile) free(cred->x509Credential.x509CACrlFile);
|
||||||
|
if (cred->x509Credential.x509ClientCertFile) free(cred->x509Credential.x509ClientCertFile);
|
||||||
|
if (cred->x509Credential.x509ClientKeyFile) free(cred->x509Credential.x509ClientKeyFile);
|
||||||
|
free(cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gnutls_certificate_credentials_t
|
||||||
|
CreateX509CertCredential(rfbCredential *cred)
|
||||||
|
{
|
||||||
|
// Use this to enable debug logs
|
||||||
|
//gnutls_global_set_log_level(GNUTLS_DEBUG_LEVEL);
|
||||||
|
|
||||||
|
gnutls_certificate_credentials_t x509_cred;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!cred->x509Credential.x509CACertFile)
|
||||||
|
{
|
||||||
|
rfbClientLog("No CA certificate provided.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Cannot allocate credentials: %s.\n", gnutls_strerror(ret));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
|
||||||
|
cred->x509Credential.x509CACertFile, GNUTLS_X509_FMT_PEM)) < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Cannot load CA credentials: %s.\n", gnutls_strerror(ret));
|
||||||
|
gnutls_certificate_free_credentials (x509_cred);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cred->x509Credential.x509ClientCertFile && cred->x509Credential.x509ClientKeyFile)
|
||||||
|
{
|
||||||
|
if ((ret = gnutls_certificate_set_x509_key_file(x509_cred,
|
||||||
|
cred->x509Credential.x509ClientCertFile, cred->x509Credential.x509ClientKeyFile,
|
||||||
|
GNUTLS_X509_FMT_PEM)) < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Cannot load client certificate or key: %s.\n", gnutls_strerror(ret));
|
||||||
|
gnutls_certificate_free_credentials (x509_cred);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
rfbClientLog("No client certificate or key provided.\n");
|
||||||
|
}
|
||||||
|
if (cred->x509Credential.x509CACrlFile)
|
||||||
|
{
|
||||||
|
if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
|
||||||
|
cred->x509Credential.x509CACrlFile, GNUTLS_X509_FMT_PEM)) < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Cannot load CRL: %s.\n", gnutls_strerror(ret));
|
||||||
|
gnutls_certificate_free_credentials (x509_cred);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
rfbClientLog("No CRL provided.\n");
|
||||||
|
}
|
||||||
|
gnutls_certificate_set_dh_params (x509_cred, rfbDHParams);
|
||||||
|
return x509_cred;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rfbBool
|
||||||
|
HandleAnonTLSAuth(rfbClient* client)
|
||||||
|
{
|
||||||
|
if (!InitializeTLS() || !InitializeTLSSession(client, TRUE)) return FALSE;
|
||||||
|
|
||||||
|
if (!SetTLSAnonCredential(client)) return FALSE;
|
||||||
|
|
||||||
|
if (!HandshakeTLS(client)) return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbBool
|
||||||
|
HandleVeNCryptAuth(rfbClient* client)
|
||||||
|
{
|
||||||
|
uint8_t major, minor, status;
|
||||||
|
uint32_t authScheme;
|
||||||
|
rfbBool anonTLS;
|
||||||
|
gnutls_certificate_credentials_t x509_cred = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!InitializeTLS()) return FALSE;
|
||||||
|
|
||||||
|
/* Read VeNCrypt version */
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&major, 1) ||
|
||||||
|
!ReadFromRFBServer(client, (char *)&minor, 1))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
rfbClientLog("Got VeNCrypt version %d.%d from server.\n", (int)major, (int)minor);
|
||||||
|
|
||||||
|
if (major != 0 && minor != 2)
|
||||||
|
{
|
||||||
|
rfbClientLog("Unsupported VeNCrypt version.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WriteToRFBServer(client, (char *)&major, 1) ||
|
||||||
|
!WriteToRFBServer(client, (char *)&minor, 1) ||
|
||||||
|
!ReadFromRFBServer(client, (char *)&status, 1))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Server refused VeNCrypt version %d.%d.\n", (int)major, (int)minor);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReadVeNCryptSecurityType(client, &authScheme)) return FALSE;
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&status, 1) || status != 1)
|
||||||
|
{
|
||||||
|
rfbClientLog("Server refused VeNCrypt authentication %d (%d).\n", authScheme, (int)status);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
client->subAuthScheme = authScheme;
|
||||||
|
|
||||||
|
/* Some VeNCrypt security types are anonymous TLS, others are X509 */
|
||||||
|
switch (authScheme)
|
||||||
|
{
|
||||||
|
case rfbVeNCryptTLSNone:
|
||||||
|
case rfbVeNCryptTLSVNC:
|
||||||
|
case rfbVeNCryptTLSPlain:
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
case rfbVeNCryptTLSSASL:
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
anonTLS = TRUE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
anonTLS = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get X509 Credentials if it's not anonymous */
|
||||||
|
if (!anonTLS)
|
||||||
|
{
|
||||||
|
rfbCredential *cred;
|
||||||
|
|
||||||
|
if (!client->GetCredential)
|
||||||
|
{
|
||||||
|
rfbClientLog("GetCredential callback is not set.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cred = client->GetCredential(client, rfbCredentialTypeX509);
|
||||||
|
if (!cred)
|
||||||
|
{
|
||||||
|
rfbClientLog("Reading credential failed\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
x509_cred = CreateX509CertCredential(cred);
|
||||||
|
FreeX509Credential(cred);
|
||||||
|
if (!x509_cred) return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start up the TLS session */
|
||||||
|
if (!InitializeTLSSession(client, anonTLS)) return FALSE;
|
||||||
|
|
||||||
|
if (anonTLS)
|
||||||
|
{
|
||||||
|
if (!SetTLSAnonCredential(client)) return FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Set the certificate verification callback. */
|
||||||
|
gnutls_certificate_set_verify_function (x509_cred, verify_certificate_callback);
|
||||||
|
gnutls_session_set_ptr ((gnutls_session_t)client->tlsSession, (void *)client);
|
||||||
|
|
||||||
|
if ((ret = gnutls_credentials_set((gnutls_session_t)client->tlsSession, GNUTLS_CRD_CERTIFICATE, x509_cred)) < 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Cannot set x509 credential: %s.\n", gnutls_strerror(ret));
|
||||||
|
FreeTLS(client);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HandshakeTLS(client)) return FALSE;
|
||||||
|
|
||||||
|
/* We are done here. The caller should continue with client->subAuthScheme
|
||||||
|
* to do actual sub authentication.
|
||||||
|
*/
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ReadFromTLS(rfbClient* client, char *out, unsigned int n)
|
||||||
|
{
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
LOCK(client->tlsRwMutex);
|
||||||
|
ret = gnutls_record_recv((gnutls_session_t)client->tlsSession, out, n);
|
||||||
|
UNLOCK(client->tlsRwMutex);
|
||||||
|
|
||||||
|
if (ret >= 0) return ret;
|
||||||
|
if (ret == GNUTLS_E_REHANDSHAKE || ret == GNUTLS_E_AGAIN)
|
||||||
|
{
|
||||||
|
errno = EAGAIN;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
rfbClientLog("Error reading from TLS: %s.\n", gnutls_strerror(ret));
|
||||||
|
errno = EINTR;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
WriteToTLS(rfbClient* client, const char *buf, unsigned int n)
|
||||||
|
{
|
||||||
|
unsigned int offset = 0;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
while (offset < n)
|
||||||
|
{
|
||||||
|
LOCK(client->tlsRwMutex);
|
||||||
|
ret = gnutls_record_send((gnutls_session_t)client->tlsSession, buf+offset, (size_t)(n-offset));
|
||||||
|
UNLOCK(client->tlsRwMutex);
|
||||||
|
|
||||||
|
if (ret == 0) continue;
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) continue;
|
||||||
|
rfbClientLog("Error writing to TLS: %s.\n", gnutls_strerror(ret));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
offset += (unsigned int)ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTLS(rfbClient* client)
|
||||||
|
{
|
||||||
|
if (client->tlsSession)
|
||||||
|
{
|
||||||
|
gnutls_deinit((gnutls_session_t)client->tlsSession);
|
||||||
|
client->tlsSession = NULL;
|
||||||
|
TINI_MUTEX(client->tlsRwMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
int
|
||||||
|
GetTLSCipherBits(rfbClient* client)
|
||||||
|
{
|
||||||
|
gnutls_cipher_algorithm_t cipher = gnutls_cipher_get((gnutls_session_t)client->tlsSession);
|
||||||
|
|
||||||
|
return gnutls_cipher_get_key_size(cipher) * 8;
|
||||||
|
}
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Christian Beier.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rfbclient.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
rfbBool HandleAnonTLSAuth(rfbClient* client)
|
||||||
|
{
|
||||||
|
rfbClientLog("TLS is not supported.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rfbBool HandleVeNCryptAuth(rfbClient* client)
|
||||||
|
{
|
||||||
|
rfbClientLog("TLS is not supported.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ReadFromTLS(rfbClient* client, char *out, unsigned int n)
|
||||||
|
{
|
||||||
|
rfbClientLog("TLS is not supported.\n");
|
||||||
|
errno = EINTR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int WriteToTLS(rfbClient* client, const char *buf, unsigned int n)
|
||||||
|
{
|
||||||
|
rfbClientLog("TLS is not supported.\n");
|
||||||
|
errno = EINTR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FreeTLS(rfbClient* client)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
int
|
||||||
|
GetTLSCipherBits(rfbClient* client)
|
||||||
|
{
|
||||||
|
rfbClientLog("TLS is not supported.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
|
@ -0,0 +1,686 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Philip Van Hoof <philip@codeminded.be>
|
||||||
|
* Copyright (C) 2009 Vic Lee.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rfbclient.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static rfbBool rfbTLSInitialized = FALSE;
|
||||||
|
|
||||||
|
// Locking callbacks are only initialized if we have mutex support.
|
||||||
|
#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD)
|
||||||
|
|
||||||
|
static MUTEX(*mutex_buf) = NULL;
|
||||||
|
|
||||||
|
struct CRYPTO_dynlock_value {
|
||||||
|
MUTEX(mutex);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void locking_function(int mode, int n, const char *file, int line)
|
||||||
|
{
|
||||||
|
if (mode & CRYPTO_LOCK)
|
||||||
|
LOCK(mutex_buf[n]);
|
||||||
|
else
|
||||||
|
UNLOCK(mutex_buf[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long id_function(void)
|
||||||
|
{
|
||||||
|
return ((unsigned long) CURRENT_THREAD_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct CRYPTO_dynlock_value *dyn_create_function(const char *file, int line)
|
||||||
|
{
|
||||||
|
struct CRYPTO_dynlock_value *value;
|
||||||
|
|
||||||
|
value = (struct CRYPTO_dynlock_value *)
|
||||||
|
malloc(sizeof(struct CRYPTO_dynlock_value));
|
||||||
|
if (!value)
|
||||||
|
goto err;
|
||||||
|
INIT_MUTEX(value->mutex);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
|
||||||
|
err:
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dyn_lock_function (int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
|
||||||
|
{
|
||||||
|
if (mode & CRYPTO_LOCK)
|
||||||
|
LOCK(l->mutex);
|
||||||
|
else
|
||||||
|
UNLOCK(l->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
dyn_destroy_function(struct CRYPTO_dynlock_value *l, const char *file, int line)
|
||||||
|
{
|
||||||
|
TINI_MUTEX(l->mutex);
|
||||||
|
free(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool InitLockingCb()
|
||||||
|
{
|
||||||
|
mutex_buf = malloc(CRYPTO_num_locks() * MUTEX_SIZE);
|
||||||
|
if (mutex_buf == NULL) {
|
||||||
|
rfbClientLog("Failed to initialized OpenSSL: memory.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < CRYPTO_num_locks(); i++)
|
||||||
|
INIT_MUTEX(mutex_buf[i]);
|
||||||
|
|
||||||
|
CRYPTO_set_locking_callback(locking_function);
|
||||||
|
CRYPTO_set_id_callback(id_function);
|
||||||
|
CRYPTO_set_dynlock_create_callback(dyn_create_function);
|
||||||
|
CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
|
||||||
|
CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
//If mutex support is not available, locking initialization is a no-op.
|
||||||
|
static rfbBool InitLockingCb() { return TRUE; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int
|
||||||
|
ssl_error_to_errno (int ssl_error)
|
||||||
|
{
|
||||||
|
switch (ssl_error) {
|
||||||
|
case SSL_ERROR_NONE:
|
||||||
|
return 0;
|
||||||
|
case SSL_ERROR_ZERO_RETURN:
|
||||||
|
/* this one does not map well at all */
|
||||||
|
//d(printf ("ssl_errno: SSL_ERROR_ZERO_RETURN\n"));
|
||||||
|
return EINVAL;
|
||||||
|
case SSL_ERROR_WANT_READ: /* non-fatal; retry */
|
||||||
|
case SSL_ERROR_WANT_WRITE: /* non-fatal; retry */
|
||||||
|
//d(printf ("ssl_errno: SSL_ERROR_WANT_[READ,WRITE]\n"));
|
||||||
|
return EAGAIN;
|
||||||
|
case SSL_ERROR_SYSCALL:
|
||||||
|
//d(printf ("ssl_errno: SSL_ERROR_SYSCALL\n"));
|
||||||
|
return EINTR;
|
||||||
|
case SSL_ERROR_SSL:
|
||||||
|
//d(printf ("ssl_errno: SSL_ERROR_SSL <-- very useful error...riiiiight\n"));
|
||||||
|
return EINTR;
|
||||||
|
default:
|
||||||
|
//d(printf ("ssl_errno: default error\n"));
|
||||||
|
return EINTR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
InitializeTLS(void)
|
||||||
|
{
|
||||||
|
if (rfbTLSInitialized)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
if (!InitLockingCb())
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
SSL_load_error_strings();
|
||||||
|
SSLeay_add_ssl_algorithms();
|
||||||
|
RAND_load_file("/dev/urandom", 1024);
|
||||||
|
|
||||||
|
rfbClientLog("OpenSSL version %s initialized.\n", SSLeay_version(SSLEAY_VERSION));
|
||||||
|
rfbTLSInitialized = TRUE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sock_read_ready(SSL *ssl, uint32_t ms)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
|
||||||
|
FD_SET(SSL_get_fd(ssl), &fds);
|
||||||
|
|
||||||
|
tv.tv_sec = ms / 1000;
|
||||||
|
tv.tv_usec = (ms % 1000) * 1000;
|
||||||
|
|
||||||
|
r = select (SSL_get_fd(ssl) + 1, &fds, NULL, NULL, &tv);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wait_for_data(SSL *ssl, int ret, int timeout)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
int retval = 1;
|
||||||
|
|
||||||
|
err = SSL_get_error(ssl, ret);
|
||||||
|
|
||||||
|
switch(err)
|
||||||
|
{
|
||||||
|
case SSL_ERROR_WANT_READ:
|
||||||
|
case SSL_ERROR_WANT_WRITE:
|
||||||
|
ret = sock_read_ready(ssl, timeout*1000);
|
||||||
|
|
||||||
|
if (ret == -1) {
|
||||||
|
retval = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
retval = 3;
|
||||||
|
long verify_res = SSL_get_verify_result(ssl);
|
||||||
|
if (verify_res != X509_V_OK)
|
||||||
|
rfbClientLog("Could not verify server certificate: %s.\n",
|
||||||
|
X509_verify_cert_error_string(verify_res));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERR_clear_error();
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
load_crls_from_file(char *file, SSL_CTX *ssl_ctx)
|
||||||
|
{
|
||||||
|
X509_STORE *st;
|
||||||
|
int i;
|
||||||
|
int count = 0;
|
||||||
|
BIO *bio;
|
||||||
|
STACK_OF(X509_INFO) *xis = NULL;
|
||||||
|
X509_INFO *xi;
|
||||||
|
|
||||||
|
st = SSL_CTX_get_cert_store(ssl_ctx);
|
||||||
|
|
||||||
|
bio = BIO_new_file(file, "r");
|
||||||
|
if (bio == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
xis = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
|
||||||
|
BIO_free(bio);
|
||||||
|
|
||||||
|
for (i = 0; i < sk_X509_INFO_num(xis); i++)
|
||||||
|
{
|
||||||
|
xi = sk_X509_INFO_value(xis, i);
|
||||||
|
if (xi->crl)
|
||||||
|
{
|
||||||
|
X509_STORE_add_crl(st, xi->crl);
|
||||||
|
xi->crl = NULL;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sk_X509_INFO_pop_free(xis, X509_INFO_free);
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
return TRUE;
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SSL *
|
||||||
|
open_ssl_connection (rfbClient *client, int sockfd, rfbBool anonTLS, rfbCredential *cred)
|
||||||
|
{
|
||||||
|
SSL_CTX *ssl_ctx = NULL;
|
||||||
|
SSL *ssl = NULL;
|
||||||
|
int n, finished = 0;
|
||||||
|
X509_VERIFY_PARAM *param;
|
||||||
|
uint8_t verify_crls;
|
||||||
|
|
||||||
|
if (!(ssl_ctx = SSL_CTX_new(SSLv23_client_method())))
|
||||||
|
{
|
||||||
|
rfbClientLog("Could not create new SSL context.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
param = X509_VERIFY_PARAM_new();
|
||||||
|
|
||||||
|
/* Setup verification if not anonymous */
|
||||||
|
if (!anonTLS)
|
||||||
|
{
|
||||||
|
verify_crls = cred->x509Credential.x509CrlVerifyMode;
|
||||||
|
if (cred->x509Credential.x509CACertFile)
|
||||||
|
{
|
||||||
|
if (!SSL_CTX_load_verify_locations(ssl_ctx, cred->x509Credential.x509CACertFile, NULL))
|
||||||
|
{
|
||||||
|
rfbClientLog("Failed to load CA certificate from %s.\n",
|
||||||
|
cred->x509Credential.x509CACertFile);
|
||||||
|
goto error_free_ctx;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rfbClientLog("Using default paths for certificate verification.\n");
|
||||||
|
SSL_CTX_set_default_verify_paths (ssl_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cred->x509Credential.x509CACrlFile)
|
||||||
|
{
|
||||||
|
if (!load_crls_from_file(cred->x509Credential.x509CACrlFile, ssl_ctx))
|
||||||
|
{
|
||||||
|
rfbClientLog("CRLs could not be loaded.\n");
|
||||||
|
goto error_free_ctx;
|
||||||
|
}
|
||||||
|
if (verify_crls == rfbX509CrlVerifyNone) verify_crls = rfbX509CrlVerifyAll;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cred->x509Credential.x509ClientCertFile && cred->x509Credential.x509ClientKeyFile)
|
||||||
|
{
|
||||||
|
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cred->x509Credential.x509ClientCertFile) != 1)
|
||||||
|
{
|
||||||
|
rfbClientLog("Client certificate could not be loaded.\n");
|
||||||
|
goto error_free_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, cred->x509Credential.x509ClientKeyFile,
|
||||||
|
SSL_FILETYPE_PEM) != 1)
|
||||||
|
{
|
||||||
|
rfbClientLog("Client private key could not be loaded.\n");
|
||||||
|
goto error_free_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SSL_CTX_check_private_key(ssl_ctx) == 0) {
|
||||||
|
rfbClientLog("Client certificate and private key do not match.\n");
|
||||||
|
goto error_free_ctx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||||
|
|
||||||
|
if (verify_crls == rfbX509CrlVerifyClient)
|
||||||
|
X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
|
||||||
|
else if (verify_crls == rfbX509CrlVerifyAll)
|
||||||
|
X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
|
||||||
|
|
||||||
|
if(!X509_VERIFY_PARAM_set1_host(param, client->serverHost, strlen(client->serverHost)))
|
||||||
|
{
|
||||||
|
rfbClientLog("Could not set server name for verification.\n");
|
||||||
|
goto error_free_ctx;
|
||||||
|
}
|
||||||
|
SSL_CTX_set1_param(ssl_ctx, param);
|
||||||
|
SSL_CTX_set_cipher_list(ssl_ctx, "ALL");
|
||||||
|
} else { /* anonTLS here */
|
||||||
|
/* Need anonymous ciphers for anonTLS, see https://github.com/LibVNC/libvncserver/issues/347#issuecomment-597477103 */
|
||||||
|
SSL_CTX_set_cipher_list(ssl_ctx, "aNULL");
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined LIBRESSL_VERSION_NUMBER
|
||||||
|
/*
|
||||||
|
See https://www.openssl.org/docs/man1.1.0/man3/SSL_set_security_level.html
|
||||||
|
Not specifying 0 here makes LibVNCClient fail connecting to some servers.
|
||||||
|
*/
|
||||||
|
SSL_CTX_set_security_level(ssl_ctx, 0);
|
||||||
|
/*
|
||||||
|
Specifying a maximum protocol version of 1.2 gets us ADH cipher on OpenSSL 1.1.x,
|
||||||
|
see https://github.com/LibVNC/libvncserver/issues/347#issuecomment-597974313
|
||||||
|
*/
|
||||||
|
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_2_VERSION);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(ssl = SSL_new (ssl_ctx)))
|
||||||
|
{
|
||||||
|
rfbClientLog("Could not create a new SSL session.\n");
|
||||||
|
goto error_free_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_set_fd (ssl, sockfd);
|
||||||
|
SSL_CTX_set_app_data (ssl_ctx, client);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
n = SSL_connect(ssl);
|
||||||
|
|
||||||
|
if (n != 1)
|
||||||
|
{
|
||||||
|
if (wait_for_data(ssl, n, 1) != 1)
|
||||||
|
{
|
||||||
|
finished = 1;
|
||||||
|
SSL_shutdown(ssl);
|
||||||
|
|
||||||
|
goto error_free_ssl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while( n != 1 && finished != 1 );
|
||||||
|
|
||||||
|
X509_VERIFY_PARAM_free(param);
|
||||||
|
return ssl;
|
||||||
|
|
||||||
|
error_free_ssl:
|
||||||
|
SSL_free(ssl);
|
||||||
|
|
||||||
|
error_free_ctx:
|
||||||
|
X509_VERIFY_PARAM_free(param);
|
||||||
|
SSL_CTX_free(ssl_ctx);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
InitializeTLSSession(rfbClient* client, rfbBool anonTLS, rfbCredential *cred)
|
||||||
|
{
|
||||||
|
if (client->tlsSession) return TRUE;
|
||||||
|
|
||||||
|
client->tlsSession = open_ssl_connection (client, client->sock, anonTLS, cred);
|
||||||
|
|
||||||
|
if (!client->tlsSession)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
INIT_MUTEX(client->tlsRwMutex);
|
||||||
|
|
||||||
|
rfbClientLog("TLS session initialized.\n");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool
|
||||||
|
HandshakeTLS(rfbClient* client)
|
||||||
|
{
|
||||||
|
int timeout = 15;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
while (timeout > 0 && (ret = SSL_do_handshake(client->tlsSession)) < 0)
|
||||||
|
{
|
||||||
|
if (ret != -1)
|
||||||
|
{
|
||||||
|
rfbClientLog("TLS handshake blocking.\n");
|
||||||
|
sleep(1);
|
||||||
|
timeout--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rfbClientLog("TLS handshake failed.\n");
|
||||||
|
|
||||||
|
FreeTLS(client);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout <= 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("TLS handshake timeout.\n");
|
||||||
|
FreeTLS(client);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("TLS handshake done.\n");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VeNCrypt sub auth. 1 byte auth count, followed by count * 4 byte integers */
|
||||||
|
static rfbBool
|
||||||
|
ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
|
||||||
|
{
|
||||||
|
uint8_t count=0;
|
||||||
|
uint8_t loop=0;
|
||||||
|
uint8_t flag=0;
|
||||||
|
uint32_t tAuth[256], t;
|
||||||
|
char buf1[500],buf2[10];
|
||||||
|
uint32_t authScheme;
|
||||||
|
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE;
|
||||||
|
|
||||||
|
if (count==0)
|
||||||
|
{
|
||||||
|
rfbClientLog("List of security types is ZERO. Giving up.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClientLog("We have %d security types to read\n", count);
|
||||||
|
authScheme=0;
|
||||||
|
/* now, we have a list of available security types to read ( uint8_t[] ) */
|
||||||
|
for (loop=0;loop<count;loop++)
|
||||||
|
{
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
|
||||||
|
t=rfbClientSwap32IfLE(tAuth[loop]);
|
||||||
|
rfbClientLog("%d) Received security type %d\n", loop, t);
|
||||||
|
if (flag) continue;
|
||||||
|
if (t==rfbVeNCryptTLSNone ||
|
||||||
|
t==rfbVeNCryptTLSVNC ||
|
||||||
|
t==rfbVeNCryptTLSPlain ||
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
t==rfbVeNCryptTLSSASL ||
|
||||||
|
t==rfbVeNCryptX509SASL ||
|
||||||
|
#endif /*LIBVNCSERVER_HAVE_SASL */
|
||||||
|
t==rfbVeNCryptX509None ||
|
||||||
|
t==rfbVeNCryptX509VNC ||
|
||||||
|
t==rfbVeNCryptX509Plain)
|
||||||
|
{
|
||||||
|
flag++;
|
||||||
|
authScheme=t;
|
||||||
|
rfbClientLog("Selecting security type %d (%d/%d in the list)\n", authScheme, loop, count);
|
||||||
|
/* send back 4 bytes (in original byte order!) indicating which security type to use */
|
||||||
|
if (!WriteToRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
|
||||||
|
}
|
||||||
|
tAuth[loop]=t;
|
||||||
|
}
|
||||||
|
if (authScheme==0)
|
||||||
|
{
|
||||||
|
memset(buf1, 0, sizeof(buf1));
|
||||||
|
for (loop=0;loop<count;loop++)
|
||||||
|
{
|
||||||
|
if (strlen(buf1)>=sizeof(buf1)-1) break;
|
||||||
|
snprintf(buf2, sizeof(buf2), (loop>0 ? ", %d" : "%d"), (int)tAuth[loop]);
|
||||||
|
strncat(buf1, buf2, sizeof(buf1)-strlen(buf1)-1);
|
||||||
|
}
|
||||||
|
rfbClientLog("Unknown VeNCrypt authentication scheme from VNC server: %s\n",
|
||||||
|
buf1);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
*result = authScheme;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbBool
|
||||||
|
HandleAnonTLSAuth(rfbClient* client)
|
||||||
|
{
|
||||||
|
if (!InitializeTLS() || !InitializeTLSSession(client, TRUE, NULL)) return FALSE;
|
||||||
|
|
||||||
|
if (!HandshakeTLS(client)) return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
FreeX509Credential(rfbCredential *cred)
|
||||||
|
{
|
||||||
|
if (cred->x509Credential.x509CACertFile) free(cred->x509Credential.x509CACertFile);
|
||||||
|
if (cred->x509Credential.x509CACrlFile) free(cred->x509Credential.x509CACrlFile);
|
||||||
|
if (cred->x509Credential.x509ClientCertFile) free(cred->x509Credential.x509ClientCertFile);
|
||||||
|
if (cred->x509Credential.x509ClientKeyFile) free(cred->x509Credential.x509ClientKeyFile);
|
||||||
|
free(cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbBool
|
||||||
|
HandleVeNCryptAuth(rfbClient* client)
|
||||||
|
{
|
||||||
|
uint8_t major, minor, status;
|
||||||
|
uint32_t authScheme;
|
||||||
|
rfbBool anonTLS;
|
||||||
|
rfbCredential *cred = NULL;
|
||||||
|
rfbBool result = TRUE;
|
||||||
|
|
||||||
|
if (!InitializeTLS()) return FALSE;
|
||||||
|
|
||||||
|
/* Read VeNCrypt version */
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&major, 1) ||
|
||||||
|
!ReadFromRFBServer(client, (char *)&minor, 1))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
rfbClientLog("Got VeNCrypt version %d.%d from server.\n", (int)major, (int)minor);
|
||||||
|
|
||||||
|
if (major != 0 && minor != 2)
|
||||||
|
{
|
||||||
|
rfbClientLog("Unsupported VeNCrypt version.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WriteToRFBServer(client, (char *)&major, 1) ||
|
||||||
|
!WriteToRFBServer(client, (char *)&minor, 1) ||
|
||||||
|
!ReadFromRFBServer(client, (char *)&status, 1))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != 0)
|
||||||
|
{
|
||||||
|
rfbClientLog("Server refused VeNCrypt version %d.%d.\n", (int)major, (int)minor);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReadVeNCryptSecurityType(client, &authScheme)) return FALSE;
|
||||||
|
if (!ReadFromRFBServer(client, (char *)&status, 1) || status != 1)
|
||||||
|
{
|
||||||
|
rfbClientLog("Server refused VeNCrypt authentication %d (%d).\n", authScheme, (int)status);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
client->subAuthScheme = authScheme;
|
||||||
|
|
||||||
|
/* Some VeNCrypt security types are anonymous TLS, others are X509 */
|
||||||
|
switch (authScheme)
|
||||||
|
{
|
||||||
|
case rfbVeNCryptTLSNone:
|
||||||
|
case rfbVeNCryptTLSVNC:
|
||||||
|
case rfbVeNCryptTLSPlain:
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
case rfbVeNCryptTLSSASL:
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
anonTLS = TRUE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
anonTLS = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get X509 Credentials if it's not anonymous */
|
||||||
|
if (!anonTLS)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!client->GetCredential)
|
||||||
|
{
|
||||||
|
rfbClientLog("GetCredential callback is not set.\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cred = client->GetCredential(client, rfbCredentialTypeX509);
|
||||||
|
if (!cred)
|
||||||
|
{
|
||||||
|
rfbClientLog("Reading credential failed\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start up the TLS session */
|
||||||
|
if (!InitializeTLSSession(client, anonTLS, cred)) result = FALSE;
|
||||||
|
|
||||||
|
if (!HandshakeTLS(client)) result = FALSE;
|
||||||
|
|
||||||
|
/* We are done here. The caller should continue with client->subAuthScheme
|
||||||
|
* to do actual sub authentication.
|
||||||
|
*/
|
||||||
|
if (cred) FreeX509Credential(cred);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ReadFromTLS(rfbClient* client, char *out, unsigned int n)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int ssl_error = SSL_ERROR_NONE;
|
||||||
|
|
||||||
|
LOCK(client->tlsRwMutex);
|
||||||
|
ret = SSL_read (client->tlsSession, out, n);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
ssl_error = SSL_get_error(client->tlsSession, ret);
|
||||||
|
UNLOCK(client->tlsRwMutex);
|
||||||
|
|
||||||
|
if (ret >= 0)
|
||||||
|
return ret;
|
||||||
|
else {
|
||||||
|
errno = ssl_error_to_errno(ssl_error);
|
||||||
|
if (errno != EAGAIN) {
|
||||||
|
rfbClientLog("Error reading from TLS: -.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
WriteToTLS(rfbClient* client, const char *buf, unsigned int n)
|
||||||
|
{
|
||||||
|
unsigned int offset = 0;
|
||||||
|
int ret = 0;
|
||||||
|
int ssl_error = SSL_ERROR_NONE;
|
||||||
|
|
||||||
|
while (offset < n)
|
||||||
|
{
|
||||||
|
LOCK(client->tlsRwMutex);
|
||||||
|
ret = SSL_write (client->tlsSession, buf + offset, (size_t)(n-offset));
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
ssl_error = SSL_get_error (client->tlsSession, ret);
|
||||||
|
UNLOCK(client->tlsRwMutex);
|
||||||
|
|
||||||
|
if (ret == 0) continue;
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
errno = ssl_error_to_errno(ssl_error);
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) continue;
|
||||||
|
rfbClientLog("Error writing to TLS: -\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
offset += (unsigned int)ret;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTLS(rfbClient* client)
|
||||||
|
{
|
||||||
|
if (client->tlsSession)
|
||||||
|
{
|
||||||
|
SSL_free(client->tlsSession);
|
||||||
|
client->tlsSession = NULL;
|
||||||
|
TINI_MUTEX(client->tlsRwMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
int GetTLSCipherBits(rfbClient* client)
|
||||||
|
{
|
||||||
|
SSL *ssl = (SSL *)(client->tlsSession);
|
||||||
|
|
||||||
|
const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
|
||||||
|
|
||||||
|
return SSL_CIPHER_get_bits(cipher, NULL);
|
||||||
|
}
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
|
@ -0,0 +1,858 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C)2009-2012 D. R. Commander. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* TurboJPEG/OSS: this implements the TurboJPEG API using libjpeg-turbo */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifndef JCS_EXTENSIONS
|
||||||
|
#define JPEG_INTERNAL_OPTIONS
|
||||||
|
#endif
|
||||||
|
#include <jpeglib.h>
|
||||||
|
#include <jerror.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include "./turbojpeg.h"
|
||||||
|
|
||||||
|
#define PAD(v, p) ((v+(p)-1)&(~((p)-1)))
|
||||||
|
|
||||||
|
#define CSTATE_START 100
|
||||||
|
#define DSTATE_START 200
|
||||||
|
#define MEMZERO(ptr, size) memset(ptr, 0, size)
|
||||||
|
|
||||||
|
#ifndef min
|
||||||
|
#define min(a,b) ((a)<(b)?(a):(b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef max
|
||||||
|
#define max(a,b) ((a)>(b)?(a):(b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Error handling (based on example in example.c) */
|
||||||
|
|
||||||
|
static char errStr[JMSG_LENGTH_MAX]="No error";
|
||||||
|
|
||||||
|
struct my_error_mgr
|
||||||
|
{
|
||||||
|
struct jpeg_error_mgr pub;
|
||||||
|
jmp_buf setjmp_buffer;
|
||||||
|
};
|
||||||
|
typedef struct my_error_mgr *my_error_ptr;
|
||||||
|
|
||||||
|
static void my_error_exit(j_common_ptr cinfo)
|
||||||
|
{
|
||||||
|
my_error_ptr myerr=(my_error_ptr)cinfo->err;
|
||||||
|
(*cinfo->err->output_message)(cinfo);
|
||||||
|
longjmp(myerr->setjmp_buffer, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Based on output_message() in jerror.c */
|
||||||
|
|
||||||
|
static void my_output_message(j_common_ptr cinfo)
|
||||||
|
{
|
||||||
|
(*cinfo->err->format_message)(cinfo, errStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Global structures, macros, etc. */
|
||||||
|
|
||||||
|
enum {COMPRESS=1, DECOMPRESS=2};
|
||||||
|
|
||||||
|
typedef struct _tjinstance
|
||||||
|
{
|
||||||
|
struct jpeg_compress_struct cinfo;
|
||||||
|
struct jpeg_decompress_struct dinfo;
|
||||||
|
struct jpeg_destination_mgr jdst;
|
||||||
|
struct jpeg_source_mgr jsrc;
|
||||||
|
struct my_error_mgr jerr;
|
||||||
|
int init;
|
||||||
|
} tjinstance;
|
||||||
|
|
||||||
|
static const int pixelsize[TJ_NUMSAMP]={3, 3, 3, 1, 3};
|
||||||
|
|
||||||
|
#define NUMSF 4
|
||||||
|
static const tjscalingfactor sf[NUMSF]={
|
||||||
|
{1, 1},
|
||||||
|
{1, 2},
|
||||||
|
{1, 4},
|
||||||
|
{1, 8}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define _throw(m) {snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
|
||||||
|
retval=-1; goto bailout;}
|
||||||
|
#define getinstance(handle) tjinstance *this=(tjinstance *)handle; \
|
||||||
|
j_compress_ptr cinfo=NULL; j_decompress_ptr dinfo=NULL; \
|
||||||
|
(void) cinfo; (void) dinfo; /* silence warnings */ \
|
||||||
|
if(!this) {snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
|
||||||
|
return -1;} \
|
||||||
|
cinfo=&this->cinfo; dinfo=&this->dinfo;
|
||||||
|
|
||||||
|
static int getPixelFormat(int pixelSize, int flags)
|
||||||
|
{
|
||||||
|
if(pixelSize==1) return TJPF_GRAY;
|
||||||
|
if(pixelSize==3)
|
||||||
|
{
|
||||||
|
if(flags&TJ_BGR) return TJPF_BGR;
|
||||||
|
else return TJPF_RGB;
|
||||||
|
}
|
||||||
|
if(pixelSize==4)
|
||||||
|
{
|
||||||
|
if(flags&TJ_ALPHAFIRST)
|
||||||
|
{
|
||||||
|
if(flags&TJ_BGR) return TJPF_XBGR;
|
||||||
|
else return TJPF_XRGB;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(flags&TJ_BGR) return TJPF_BGRX;
|
||||||
|
else return TJPF_RGBX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setCompDefaults(struct jpeg_compress_struct *cinfo,
|
||||||
|
int pixelFormat, int subsamp, int jpegQual)
|
||||||
|
{
|
||||||
|
int retval=0;
|
||||||
|
|
||||||
|
switch(pixelFormat)
|
||||||
|
{
|
||||||
|
case TJPF_GRAY:
|
||||||
|
cinfo->in_color_space=JCS_GRAYSCALE; break;
|
||||||
|
#if JCS_EXTENSIONS==1
|
||||||
|
case TJPF_RGB:
|
||||||
|
cinfo->in_color_space=JCS_EXT_RGB; break;
|
||||||
|
case TJPF_BGR:
|
||||||
|
cinfo->in_color_space=JCS_EXT_BGR; break;
|
||||||
|
case TJPF_RGBX:
|
||||||
|
case TJPF_RGBA:
|
||||||
|
cinfo->in_color_space=JCS_EXT_RGBX; break;
|
||||||
|
case TJPF_BGRX:
|
||||||
|
case TJPF_BGRA:
|
||||||
|
cinfo->in_color_space=JCS_EXT_BGRX; break;
|
||||||
|
case TJPF_XRGB:
|
||||||
|
case TJPF_ARGB:
|
||||||
|
cinfo->in_color_space=JCS_EXT_XRGB; break;
|
||||||
|
case TJPF_XBGR:
|
||||||
|
case TJPF_ABGR:
|
||||||
|
cinfo->in_color_space=JCS_EXT_XBGR; break;
|
||||||
|
#else
|
||||||
|
case TJPF_RGB:
|
||||||
|
case TJPF_BGR:
|
||||||
|
case TJPF_RGBX:
|
||||||
|
case TJPF_BGRX:
|
||||||
|
case TJPF_XRGB:
|
||||||
|
case TJPF_XBGR:
|
||||||
|
case TJPF_RGBA:
|
||||||
|
case TJPF_BGRA:
|
||||||
|
case TJPF_ARGB:
|
||||||
|
case TJPF_ABGR:
|
||||||
|
cinfo->in_color_space=JCS_RGB; pixelFormat=TJPF_RGB;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
cinfo->input_components=tjPixelSize[pixelFormat];
|
||||||
|
jpeg_set_defaults(cinfo);
|
||||||
|
if(jpegQual>=0)
|
||||||
|
{
|
||||||
|
jpeg_set_quality(cinfo, jpegQual, TRUE);
|
||||||
|
if(jpegQual>=96) cinfo->dct_method=JDCT_ISLOW;
|
||||||
|
else cinfo->dct_method=JDCT_FASTEST;
|
||||||
|
}
|
||||||
|
if(subsamp==TJSAMP_GRAY)
|
||||||
|
jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
|
||||||
|
else
|
||||||
|
jpeg_set_colorspace(cinfo, JCS_YCbCr);
|
||||||
|
|
||||||
|
cinfo->comp_info[0].h_samp_factor=tjMCUWidth[subsamp]/8;
|
||||||
|
cinfo->comp_info[1].h_samp_factor=1;
|
||||||
|
cinfo->comp_info[2].h_samp_factor=1;
|
||||||
|
cinfo->comp_info[0].v_samp_factor=tjMCUHeight[subsamp]/8;
|
||||||
|
cinfo->comp_info[1].v_samp_factor=1;
|
||||||
|
cinfo->comp_info[2].v_samp_factor=1;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setDecompDefaults(struct jpeg_decompress_struct *dinfo,
|
||||||
|
int pixelFormat)
|
||||||
|
{
|
||||||
|
int retval=0;
|
||||||
|
|
||||||
|
switch(pixelFormat)
|
||||||
|
{
|
||||||
|
case TJPF_GRAY:
|
||||||
|
dinfo->out_color_space=JCS_GRAYSCALE; break;
|
||||||
|
#if JCS_EXTENSIONS==1
|
||||||
|
case TJPF_RGB:
|
||||||
|
dinfo->out_color_space=JCS_EXT_RGB; break;
|
||||||
|
case TJPF_BGR:
|
||||||
|
dinfo->out_color_space=JCS_EXT_BGR; break;
|
||||||
|
case TJPF_RGBX:
|
||||||
|
dinfo->out_color_space=JCS_EXT_RGBX; break;
|
||||||
|
case TJPF_BGRX:
|
||||||
|
dinfo->out_color_space=JCS_EXT_BGRX; break;
|
||||||
|
case TJPF_XRGB:
|
||||||
|
dinfo->out_color_space=JCS_EXT_XRGB; break;
|
||||||
|
case TJPF_XBGR:
|
||||||
|
dinfo->out_color_space=JCS_EXT_XBGR; break;
|
||||||
|
#if JCS_ALPHA_EXTENSIONS==1
|
||||||
|
case TJPF_RGBA:
|
||||||
|
dinfo->out_color_space=JCS_EXT_RGBA; break;
|
||||||
|
case TJPF_BGRA:
|
||||||
|
dinfo->out_color_space=JCS_EXT_BGRA; break;
|
||||||
|
case TJPF_ARGB:
|
||||||
|
dinfo->out_color_space=JCS_EXT_ARGB; break;
|
||||||
|
case TJPF_ABGR:
|
||||||
|
dinfo->out_color_space=JCS_EXT_ABGR; break;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
case TJPF_RGB:
|
||||||
|
case TJPF_BGR:
|
||||||
|
case TJPF_RGBX:
|
||||||
|
case TJPF_BGRX:
|
||||||
|
case TJPF_XRGB:
|
||||||
|
case TJPF_XBGR:
|
||||||
|
case TJPF_RGBA:
|
||||||
|
case TJPF_BGRA:
|
||||||
|
case TJPF_ARGB:
|
||||||
|
case TJPF_ABGR:
|
||||||
|
dinfo->out_color_space=JCS_RGB; break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
_throw("Unsupported pixel format");
|
||||||
|
}
|
||||||
|
|
||||||
|
bailout:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int getSubsamp(j_decompress_ptr dinfo)
|
||||||
|
{
|
||||||
|
int retval=-1, i, k;
|
||||||
|
for(i=0; i<NUMSUBOPT; i++)
|
||||||
|
{
|
||||||
|
if(dinfo->num_components==pixelsize[i])
|
||||||
|
{
|
||||||
|
if(dinfo->comp_info[0].h_samp_factor==tjMCUWidth[i]/8
|
||||||
|
&& dinfo->comp_info[0].v_samp_factor==tjMCUHeight[i]/8)
|
||||||
|
{
|
||||||
|
int match=0;
|
||||||
|
for(k=1; k<dinfo->num_components; k++)
|
||||||
|
{
|
||||||
|
if(dinfo->comp_info[k].h_samp_factor==1
|
||||||
|
&& dinfo->comp_info[k].v_samp_factor==1)
|
||||||
|
match++;
|
||||||
|
}
|
||||||
|
if(match==dinfo->num_components-1)
|
||||||
|
{
|
||||||
|
retval=i; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef JCS_EXTENSIONS
|
||||||
|
|
||||||
|
/* Conversion functions to emulate the colorspace extensions. This allows the
|
||||||
|
TurboJPEG wrapper to be used with libjpeg */
|
||||||
|
|
||||||
|
#define TORGB(PS, ROFFSET, GOFFSET, BOFFSET) { \
|
||||||
|
int rowPad=pitch-width*PS; \
|
||||||
|
while(height--) \
|
||||||
|
{ \
|
||||||
|
unsigned char *endOfRow=src+width*PS; \
|
||||||
|
while(src<endOfRow) \
|
||||||
|
{ \
|
||||||
|
dst[RGB_RED]=src[ROFFSET]; \
|
||||||
|
dst[RGB_GREEN]=src[GOFFSET]; \
|
||||||
|
dst[RGB_BLUE]=src[BOFFSET]; \
|
||||||
|
dst+=RGB_PIXELSIZE; src+=PS; \
|
||||||
|
} \
|
||||||
|
src+=rowPad; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *toRGB(unsigned char *src, int width, int pitch,
|
||||||
|
int height, int pixelFormat, unsigned char *dst)
|
||||||
|
{
|
||||||
|
unsigned char *retval=src;
|
||||||
|
switch(pixelFormat)
|
||||||
|
{
|
||||||
|
case TJPF_RGB:
|
||||||
|
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=3
|
||||||
|
retval=dst; TORGB(3, 0, 1, 2);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_BGR:
|
||||||
|
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=3
|
||||||
|
retval=dst; TORGB(3, 2, 1, 0);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_RGBX:
|
||||||
|
case TJPF_RGBA:
|
||||||
|
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
|
||||||
|
retval=dst; TORGB(4, 0, 1, 2);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_BGRX:
|
||||||
|
case TJPF_BGRA:
|
||||||
|
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
|
||||||
|
retval=dst; TORGB(4, 2, 1, 0);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_XRGB:
|
||||||
|
case TJPF_ARGB:
|
||||||
|
#if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
|
||||||
|
retval=dst; TORGB(4, 1, 2, 3);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_XBGR:
|
||||||
|
case TJPF_ABGR:
|
||||||
|
#if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
|
||||||
|
retval=dst; TORGB(4, 3, 2, 1);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FROMRGB(PS, ROFFSET, GOFFSET, BOFFSET, SETALPHA) { \
|
||||||
|
int rowPad=pitch-width*PS; \
|
||||||
|
while(height--) \
|
||||||
|
{ \
|
||||||
|
unsigned char *endOfRow=dst+width*PS; \
|
||||||
|
while(dst<endOfRow) \
|
||||||
|
{ \
|
||||||
|
dst[ROFFSET]=src[RGB_RED]; \
|
||||||
|
dst[GOFFSET]=src[RGB_GREEN]; \
|
||||||
|
dst[BOFFSET]=src[RGB_BLUE]; \
|
||||||
|
SETALPHA \
|
||||||
|
dst+=PS; src+=RGB_PIXELSIZE; \
|
||||||
|
} \
|
||||||
|
dst+=rowPad; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fromRGB(unsigned char *src, unsigned char *dst, int width,
|
||||||
|
int pitch, int height, int pixelFormat)
|
||||||
|
{
|
||||||
|
switch(pixelFormat)
|
||||||
|
{
|
||||||
|
case TJPF_RGB:
|
||||||
|
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=3
|
||||||
|
FROMRGB(3, 0, 1, 2,);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_BGR:
|
||||||
|
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=3
|
||||||
|
FROMRGB(3, 2, 1, 0,);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_RGBX:
|
||||||
|
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
|
||||||
|
FROMRGB(4, 0, 1, 2,);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_RGBA:
|
||||||
|
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
|
||||||
|
FROMRGB(4, 0, 1, 2, dst[3]=0xFF;);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_BGRX:
|
||||||
|
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
|
||||||
|
FROMRGB(4, 2, 1, 0,);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_BGRA:
|
||||||
|
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
|
||||||
|
FROMRGB(4, 2, 1, 0, dst[3]=0xFF;); return;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_XRGB:
|
||||||
|
#if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
|
||||||
|
FROMRGB(4, 1, 2, 3,); return;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_ARGB:
|
||||||
|
#if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
|
||||||
|
FROMRGB(4, 1, 2, 3, dst[0]=0xFF;); return;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_XBGR:
|
||||||
|
#if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
|
||||||
|
FROMRGB(4, 3, 2, 1,); return;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TJPF_ABGR:
|
||||||
|
#if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
|
||||||
|
FROMRGB(4, 3, 2, 1, dst[0]=0xFF;); return;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* General API functions */
|
||||||
|
|
||||||
|
DLLEXPORT char* DLLCALL tjGetErrorStr(void)
|
||||||
|
{
|
||||||
|
return errStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DLLEXPORT int DLLCALL tjDestroy(tjhandle handle)
|
||||||
|
{
|
||||||
|
getinstance(handle);
|
||||||
|
if(setjmp(this->jerr.setjmp_buffer)) return -1;
|
||||||
|
if(this->init&COMPRESS) jpeg_destroy_compress(cinfo);
|
||||||
|
if(this->init&DECOMPRESS) jpeg_destroy_decompress(dinfo);
|
||||||
|
free(this);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Compressor */
|
||||||
|
|
||||||
|
static boolean empty_output_buffer(j_compress_ptr cinfo)
|
||||||
|
{
|
||||||
|
ERREXIT(cinfo, JERR_BUFFER_SIZE);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dst_noop(j_compress_ptr cinfo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static tjhandle _tjInitCompress(tjinstance *this)
|
||||||
|
{
|
||||||
|
/* This is also straight out of example.c */
|
||||||
|
this->cinfo.err=jpeg_std_error(&this->jerr.pub);
|
||||||
|
this->jerr.pub.error_exit=my_error_exit;
|
||||||
|
this->jerr.pub.output_message=my_output_message;
|
||||||
|
|
||||||
|
if(setjmp(this->jerr.setjmp_buffer))
|
||||||
|
{
|
||||||
|
/* If we get here, the JPEG code has signaled an error. */
|
||||||
|
if(this) free(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_create_compress(&this->cinfo);
|
||||||
|
this->cinfo.dest=&this->jdst;
|
||||||
|
this->jdst.init_destination=dst_noop;
|
||||||
|
this->jdst.empty_output_buffer=empty_output_buffer;
|
||||||
|
this->jdst.term_destination=dst_noop;
|
||||||
|
|
||||||
|
this->init|=COMPRESS;
|
||||||
|
return (tjhandle)this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT tjhandle DLLCALL tjInitCompress(void)
|
||||||
|
{
|
||||||
|
tjinstance *this=NULL;
|
||||||
|
if((this=(tjinstance *)malloc(sizeof(tjinstance)))==NULL)
|
||||||
|
{
|
||||||
|
snprintf(errStr, JMSG_LENGTH_MAX,
|
||||||
|
"tjInitCompress(): Memory allocation failure");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
MEMZERO(this, sizeof(tjinstance));
|
||||||
|
return _tjInitCompress(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DLLEXPORT unsigned long DLLCALL tjBufSize(int width, int height,
|
||||||
|
int jpegSubsamp)
|
||||||
|
{
|
||||||
|
unsigned long retval=0; int mcuw, mcuh, chromasf;
|
||||||
|
if(width<1 || height<1 || jpegSubsamp<0 || jpegSubsamp>=NUMSUBOPT)
|
||||||
|
_throw("tjBufSize(): Invalid argument");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This allows for rare corner cases in which a JPEG image can actually be
|
||||||
|
* larger than the uncompressed input (we wouldn't mention it if it hadn't
|
||||||
|
* happened before.)
|
||||||
|
*/
|
||||||
|
mcuw=tjMCUWidth[jpegSubsamp];
|
||||||
|
mcuh=tjMCUHeight[jpegSubsamp];
|
||||||
|
chromasf=jpegSubsamp==TJSAMP_GRAY? 0: 4*64/(mcuw*mcuh);
|
||||||
|
retval=PAD(width, mcuw) * PAD(height, mcuh) * (2 + chromasf) + 2048;
|
||||||
|
|
||||||
|
bailout:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height)
|
||||||
|
{
|
||||||
|
unsigned long retval=0;
|
||||||
|
if(width<1 || height<1)
|
||||||
|
_throw("TJBUFSIZE(): Invalid argument");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This allows for rare corner cases in which a JPEG image can actually be
|
||||||
|
* larger than the uncompressed input (we wouldn't mention it if it hadn't
|
||||||
|
* happened before.)
|
||||||
|
*/
|
||||||
|
retval=PAD(width, 16) * PAD(height, 16) * 6 + 2048;
|
||||||
|
|
||||||
|
bailout:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, unsigned char *srcBuf,
|
||||||
|
int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf,
|
||||||
|
unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags)
|
||||||
|
{
|
||||||
|
int i, retval=0; JSAMPROW *row_pointer=NULL;
|
||||||
|
#ifndef JCS_EXTENSIONS
|
||||||
|
unsigned char *rgbBuf=NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
getinstance(handle)
|
||||||
|
if((this->init&COMPRESS)==0)
|
||||||
|
_throw("tjCompress2(): Instance has not been initialized for compression");
|
||||||
|
|
||||||
|
if(srcBuf==NULL || width<=0 || pitch<0 || height<=0 || pixelFormat<0
|
||||||
|
|| pixelFormat>=TJ_NUMPF || jpegBuf==NULL || jpegSize==NULL
|
||||||
|
|| jpegSubsamp<0 || jpegSubsamp>=NUMSUBOPT || jpegQual<0 || jpegQual>100)
|
||||||
|
_throw("tjCompress2(): Invalid argument");
|
||||||
|
|
||||||
|
if(setjmp(this->jerr.setjmp_buffer))
|
||||||
|
{
|
||||||
|
/* If we get here, the JPEG code has signaled an error. */
|
||||||
|
retval=-1;
|
||||||
|
goto bailout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pitch==0) pitch=width*tjPixelSize[pixelFormat];
|
||||||
|
|
||||||
|
#ifndef JCS_EXTENSIONS
|
||||||
|
if(pixelFormat!=TJPF_GRAY)
|
||||||
|
{
|
||||||
|
rgbBuf=(unsigned char *)malloc(width*height*RGB_PIXELSIZE);
|
||||||
|
if(!rgbBuf) _throw("tjCompress2(): Memory allocation failure");
|
||||||
|
srcBuf=toRGB(srcBuf, width, pitch, height, pixelFormat, rgbBuf);
|
||||||
|
pitch=width*RGB_PIXELSIZE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
cinfo->image_width=width;
|
||||||
|
cinfo->image_height=height;
|
||||||
|
|
||||||
|
if(flags&TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
|
||||||
|
else if(flags&TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
|
||||||
|
else if(flags&TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
|
||||||
|
|
||||||
|
if(setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual)==-1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
this->jdst.next_output_byte=*jpegBuf;
|
||||||
|
this->jdst.free_in_buffer=tjBufSize(width, height, jpegSubsamp);
|
||||||
|
|
||||||
|
jpeg_start_compress(cinfo, TRUE);
|
||||||
|
if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL)
|
||||||
|
_throw("tjCompress2(): Memory allocation failure");
|
||||||
|
for(i=0; i<height; i++)
|
||||||
|
{
|
||||||
|
if(flags&TJFLAG_BOTTOMUP) row_pointer[i]=&srcBuf[(height-i-1)*pitch];
|
||||||
|
else row_pointer[i]=&srcBuf[i*pitch];
|
||||||
|
}
|
||||||
|
while(cinfo->next_scanline<cinfo->image_height)
|
||||||
|
{
|
||||||
|
jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
|
||||||
|
cinfo->image_height-cinfo->next_scanline);
|
||||||
|
}
|
||||||
|
jpeg_finish_compress(cinfo);
|
||||||
|
*jpegSize=tjBufSize(width, height, jpegSubsamp)
|
||||||
|
-(unsigned long)(this->jdst.free_in_buffer);
|
||||||
|
|
||||||
|
bailout:
|
||||||
|
if(cinfo->global_state>CSTATE_START) jpeg_abort_compress(cinfo);
|
||||||
|
#ifndef JCS_EXTENSIONS
|
||||||
|
if(rgbBuf) free(rgbBuf);
|
||||||
|
#endif
|
||||||
|
if(row_pointer) free(row_pointer);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT int DLLCALL tjCompress(tjhandle handle, unsigned char *srcBuf,
|
||||||
|
int width, int pitch, int height, int pixelSize, unsigned char *jpegBuf,
|
||||||
|
unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags)
|
||||||
|
{
|
||||||
|
int retval=0; unsigned long size = 0;
|
||||||
|
retval=tjCompress2(handle, srcBuf, width, pitch, height,
|
||||||
|
getPixelFormat(pixelSize, flags), &jpegBuf, &size, jpegSubsamp, jpegQual,
|
||||||
|
flags);
|
||||||
|
*jpegSize=size;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Decompressor */
|
||||||
|
|
||||||
|
static boolean fill_input_buffer(j_decompress_ptr dinfo)
|
||||||
|
{
|
||||||
|
ERREXIT(dinfo, JERR_BUFFER_SIZE);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void skip_input_data(j_decompress_ptr dinfo, long num_bytes)
|
||||||
|
{
|
||||||
|
dinfo->src->next_input_byte += (size_t) num_bytes;
|
||||||
|
dinfo->src->bytes_in_buffer -= (size_t) num_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void src_noop(j_decompress_ptr dinfo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static tjhandle _tjInitDecompress(tjinstance *this)
|
||||||
|
{
|
||||||
|
/* This is also straight out of example.c */
|
||||||
|
this->dinfo.err=jpeg_std_error(&this->jerr.pub);
|
||||||
|
this->jerr.pub.error_exit=my_error_exit;
|
||||||
|
this->jerr.pub.output_message=my_output_message;
|
||||||
|
|
||||||
|
if(setjmp(this->jerr.setjmp_buffer))
|
||||||
|
{
|
||||||
|
/* If we get here, the JPEG code has signaled an error. */
|
||||||
|
if(this) free(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_create_decompress(&this->dinfo);
|
||||||
|
this->dinfo.src=&this->jsrc;
|
||||||
|
this->jsrc.init_source=src_noop;
|
||||||
|
this->jsrc.fill_input_buffer=fill_input_buffer;
|
||||||
|
this->jsrc.skip_input_data=skip_input_data;
|
||||||
|
this->jsrc.resync_to_restart=jpeg_resync_to_restart;
|
||||||
|
this->jsrc.term_source=src_noop;
|
||||||
|
|
||||||
|
this->init|=DECOMPRESS;
|
||||||
|
return (tjhandle)this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT tjhandle DLLCALL tjInitDecompress(void)
|
||||||
|
{
|
||||||
|
tjinstance *this;
|
||||||
|
if((this=(tjinstance *)malloc(sizeof(tjinstance)))==NULL)
|
||||||
|
{
|
||||||
|
snprintf(errStr, JMSG_LENGTH_MAX,
|
||||||
|
"tjInitDecompress(): Memory allocation failure");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
MEMZERO(this, sizeof(tjinstance));
|
||||||
|
return _tjInitDecompress(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle handle,
|
||||||
|
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height,
|
||||||
|
int *jpegSubsamp)
|
||||||
|
{
|
||||||
|
int retval=0;
|
||||||
|
|
||||||
|
getinstance(handle);
|
||||||
|
if((this->init&DECOMPRESS)==0)
|
||||||
|
_throw("tjDecompressHeader2(): Instance has not been initialized for decompression");
|
||||||
|
|
||||||
|
if(jpegBuf==NULL || jpegSize<=0 || width==NULL || height==NULL
|
||||||
|
|| jpegSubsamp==NULL)
|
||||||
|
_throw("tjDecompressHeader2(): Invalid argument");
|
||||||
|
|
||||||
|
if(setjmp(this->jerr.setjmp_buffer))
|
||||||
|
{
|
||||||
|
/* If we get here, the JPEG code has signaled an error. */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->jsrc.bytes_in_buffer=jpegSize;
|
||||||
|
this->jsrc.next_input_byte=jpegBuf;
|
||||||
|
jpeg_read_header(dinfo, TRUE);
|
||||||
|
|
||||||
|
*width=dinfo->image_width;
|
||||||
|
*height=dinfo->image_height;
|
||||||
|
*jpegSubsamp=getSubsamp(dinfo);
|
||||||
|
|
||||||
|
jpeg_abort_decompress(dinfo);
|
||||||
|
|
||||||
|
if(*jpegSubsamp<0)
|
||||||
|
_throw("tjDecompressHeader2(): Could not determine subsampling type for JPEG image");
|
||||||
|
if(*width<1 || *height<1)
|
||||||
|
_throw("tjDecompressHeader2(): Invalid data returned in header");
|
||||||
|
|
||||||
|
bailout:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle handle,
|
||||||
|
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height)
|
||||||
|
{
|
||||||
|
int jpegSubsamp;
|
||||||
|
return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
|
||||||
|
&jpegSubsamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DLLEXPORT tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors)
|
||||||
|
{
|
||||||
|
if(numscalingfactors==NULL)
|
||||||
|
{
|
||||||
|
snprintf(errStr, JMSG_LENGTH_MAX,
|
||||||
|
"tjGetScalingFactors(): Invalid argument");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*numscalingfactors=NUMSF;
|
||||||
|
return (tjscalingfactor *)sf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle, unsigned char *jpegBuf,
|
||||||
|
unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch,
|
||||||
|
int height, int pixelFormat, int flags)
|
||||||
|
{
|
||||||
|
int i, retval=0; JSAMPROW *row_pointer=NULL;
|
||||||
|
int jpegwidth, jpegheight, scaledw, scaledh;
|
||||||
|
#ifndef JCS_EXTENSIONS
|
||||||
|
unsigned char *rgbBuf=NULL;
|
||||||
|
unsigned char *_dstBuf=NULL; int _pitch=0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
getinstance(handle);
|
||||||
|
if((this->init&DECOMPRESS)==0)
|
||||||
|
_throw("tjDecompress2(): Instance has not been initialized for decompression");
|
||||||
|
|
||||||
|
if(jpegBuf==NULL || jpegSize<=0 || dstBuf==NULL || width<0 || pitch<0
|
||||||
|
|| height<0 || pixelFormat<0 || pixelFormat>=TJ_NUMPF)
|
||||||
|
_throw("tjDecompress2(): Invalid argument");
|
||||||
|
|
||||||
|
if(flags&TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
|
||||||
|
else if(flags&TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
|
||||||
|
else if(flags&TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
|
||||||
|
|
||||||
|
if(setjmp(this->jerr.setjmp_buffer))
|
||||||
|
{
|
||||||
|
/* If we get here, the JPEG code has signaled an error. */
|
||||||
|
retval=-1;
|
||||||
|
goto bailout;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->jsrc.bytes_in_buffer=jpegSize;
|
||||||
|
this->jsrc.next_input_byte=jpegBuf;
|
||||||
|
jpeg_read_header(dinfo, TRUE);
|
||||||
|
if(setDecompDefaults(dinfo, pixelFormat)==-1)
|
||||||
|
{
|
||||||
|
retval=-1; goto bailout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flags&TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling=FALSE;
|
||||||
|
|
||||||
|
jpegwidth=dinfo->image_width; jpegheight=dinfo->image_height;
|
||||||
|
if(width==0) width=jpegwidth;
|
||||||
|
if(height==0) height=jpegheight;
|
||||||
|
for(i=0; i<NUMSF; i++)
|
||||||
|
{
|
||||||
|
scaledw=TJSCALED(jpegwidth, sf[i]);
|
||||||
|
scaledh=TJSCALED(jpegheight, sf[i]);
|
||||||
|
if(scaledw<=width && scaledh<=height)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(scaledw>width || scaledh>height)
|
||||||
|
_throw("tjDecompress2(): Could not scale down to desired image dimensions");
|
||||||
|
#ifndef JCS_EXTENSIONS
|
||||||
|
width=scaledw; height=scaledh;
|
||||||
|
#endif
|
||||||
|
dinfo->scale_num=sf[i].num;
|
||||||
|
dinfo->scale_denom=sf[i].denom;
|
||||||
|
|
||||||
|
jpeg_start_decompress(dinfo);
|
||||||
|
if(pitch==0) pitch=dinfo->output_width*tjPixelSize[pixelFormat];
|
||||||
|
|
||||||
|
#ifndef JCS_EXTENSIONS
|
||||||
|
if(pixelFormat!=TJPF_GRAY &&
|
||||||
|
(RGB_RED!=tjRedOffset[pixelFormat] ||
|
||||||
|
RGB_GREEN!=tjGreenOffset[pixelFormat] ||
|
||||||
|
RGB_BLUE!=tjBlueOffset[pixelFormat] ||
|
||||||
|
RGB_PIXELSIZE!=tjPixelSize[pixelFormat]))
|
||||||
|
{
|
||||||
|
rgbBuf=(unsigned char *)malloc(width*height*3);
|
||||||
|
if(!rgbBuf) _throw("tjDecompress2(): Memory allocation failure");
|
||||||
|
_pitch=pitch; pitch=width*3;
|
||||||
|
_dstBuf=dstBuf; dstBuf=rgbBuf;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)
|
||||||
|
*dinfo->output_height))==NULL)
|
||||||
|
_throw("tjDecompress2(): Memory allocation failure");
|
||||||
|
for(i=0; i<(int)dinfo->output_height; i++)
|
||||||
|
{
|
||||||
|
if(flags&TJFLAG_BOTTOMUP)
|
||||||
|
row_pointer[i]=&dstBuf[(dinfo->output_height-i-1)*pitch];
|
||||||
|
else row_pointer[i]=&dstBuf[i*pitch];
|
||||||
|
}
|
||||||
|
while(dinfo->output_scanline<dinfo->output_height)
|
||||||
|
{
|
||||||
|
jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
|
||||||
|
dinfo->output_height-dinfo->output_scanline);
|
||||||
|
}
|
||||||
|
jpeg_finish_decompress(dinfo);
|
||||||
|
|
||||||
|
#ifndef JCS_EXTENSIONS
|
||||||
|
fromRGB(rgbBuf, _dstBuf, width, _pitch, height, pixelFormat);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bailout:
|
||||||
|
if(dinfo->global_state>DSTATE_START) jpeg_abort_decompress(dinfo);
|
||||||
|
#ifndef JCS_EXTENSIONS
|
||||||
|
if(rgbBuf) free(rgbBuf);
|
||||||
|
#endif
|
||||||
|
if(row_pointer) free(row_pointer);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT int DLLCALL tjDecompress(tjhandle handle, unsigned char *jpegBuf,
|
||||||
|
unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch,
|
||||||
|
int height, int pixelSize, int flags)
|
||||||
|
{
|
||||||
|
return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
|
||||||
|
height, getPixelFormat(pixelSize, flags), flags);
|
||||||
|
}
|
282
src/vnc.c
282
src/vnc.c
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Andri Yngvason
|
* Copyright (c) 2020 - 2022 Andri Yngvason
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and/or distribute this software for any
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -19,15 +19,50 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <pixman.h>
|
#include <pixman.h>
|
||||||
#include <rfb/rfbclient.h>
|
#include <libdrm/drm_fourcc.h>
|
||||||
#include <wayland-client.h>
|
#include <libavutil/frame.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <data-control.h>
|
||||||
|
|
||||||
|
#include "rfbclient.h"
|
||||||
#include "vnc.h"
|
#include "vnc.h"
|
||||||
|
#include "open-h264.h"
|
||||||
|
#include "usdt.h"
|
||||||
|
|
||||||
|
#define RFB_ENCODING_OPEN_H264 50
|
||||||
|
#define RFB_ENCODING_PTS -1000
|
||||||
|
|
||||||
|
#define NO_PTS UINT64_MAX
|
||||||
|
|
||||||
extern const unsigned short code_map_linux_to_qnum[];
|
extern const unsigned short code_map_linux_to_qnum[];
|
||||||
extern const unsigned int code_map_linux_to_qnum_len;
|
extern const unsigned int code_map_linux_to_qnum_len;
|
||||||
|
|
||||||
|
static uint64_t vnc_client_htonll(uint64_t x)
|
||||||
|
{
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
return __builtin_bswap64(x);
|
||||||
|
#else
|
||||||
|
return x;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool vnc_client_lock_handler(struct vnc_client* self)
|
||||||
|
{
|
||||||
|
if (self->handler_lock)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
self->handler_lock = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vnc_client_unlock_handler(struct vnc_client* self)
|
||||||
|
{
|
||||||
|
assert(self->handler_lock);
|
||||||
|
self->handler_lock = false;
|
||||||
|
}
|
||||||
|
|
||||||
static rfbBool vnc_client_alloc_fb(rfbClient* client)
|
static rfbBool vnc_client_alloc_fb(rfbClient* client)
|
||||||
{
|
{
|
||||||
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
||||||
|
@ -42,22 +77,155 @@ static void vnc_client_update_box(rfbClient* client, int x, int y, int width,
|
||||||
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
||||||
|
if (self->current_rect_is_av_frame) {
|
||||||
|
self->current_rect_is_av_frame = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pixman_region_union_rect(&self->damage, &self->damage, x, y, width,
|
pixman_region_union_rect(&self->damage, &self->damage, x, y, width,
|
||||||
height);
|
height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vnc_client_clear_av_frames(struct vnc_client* self)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < self->n_av_frames; ++i) {
|
||||||
|
av_frame_unref(self->av_frames[i]->frame);
|
||||||
|
av_frame_free(&self->av_frames[i]->frame);
|
||||||
|
free(self->av_frames[i]);
|
||||||
|
}
|
||||||
|
self->n_av_frames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vnc_client_start_update(rfbClient* client)
|
||||||
|
{
|
||||||
|
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
self->pts = NO_PTS;
|
||||||
|
pixman_region_clear(&self->damage);
|
||||||
|
vnc_client_clear_av_frames(self);
|
||||||
|
|
||||||
|
self->is_updating = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vnc_client_cancel_update(rfbClient* client)
|
||||||
|
{
|
||||||
|
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
self->is_updating = false;
|
||||||
|
}
|
||||||
|
|
||||||
static void vnc_client_finish_update(rfbClient* client)
|
static void vnc_client_finish_update(rfbClient* client)
|
||||||
{
|
{
|
||||||
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
||||||
self->update_fb(self);
|
DTRACE_PROBE2(wlvncc, vnc_client_finish_update, client, self->pts);
|
||||||
|
|
||||||
pixman_region_clear(&self->damage);
|
self->is_updating = false;
|
||||||
|
|
||||||
|
self->update_fb(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct vnc_client* vnc_client_create(void)
|
static void vnc_client_got_cut_text(rfbClient* client, const char* text,
|
||||||
|
int len)
|
||||||
{
|
{
|
||||||
|
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (self->cut_text)
|
||||||
|
self->cut_text(self, text, len);
|
||||||
|
else {
|
||||||
|
printf("Cut text is not defined!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool vnc_client_handle_open_h264_rect(rfbClient* client,
|
||||||
|
rfbFramebufferUpdateRectHeader* rect_header)
|
||||||
|
{
|
||||||
|
if ((int)rect_header->encoding != RFB_ENCODING_OPEN_H264)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (!self->open_h264)
|
||||||
|
self->open_h264 = open_h264_create(client);
|
||||||
|
|
||||||
|
if (!self->open_h264)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AVFrame* frame = open_h264_decode_rect(self->open_h264, rect_header);
|
||||||
|
if (!frame)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
assert(self->n_av_frames < VNC_CLIENT_MAX_AV_FRAMES);
|
||||||
|
|
||||||
|
struct vnc_av_frame* f = calloc(1, sizeof(*f));
|
||||||
|
if (!f) {
|
||||||
|
av_frame_unref(frame);
|
||||||
|
av_frame_free(&frame);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
f->frame = frame;
|
||||||
|
f->x = rect_header->r.x;
|
||||||
|
f->y = rect_header->r.y;
|
||||||
|
f->width = rect_header->r.w;
|
||||||
|
f->height = rect_header->r.h;
|
||||||
|
|
||||||
|
self->av_frames[self->n_av_frames++] = f;
|
||||||
|
|
||||||
|
self->current_rect_is_av_frame = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vnc_client_init_open_h264(void)
|
||||||
|
{
|
||||||
|
static int encodings[] = { RFB_ENCODING_OPEN_H264, 0 };
|
||||||
|
static rfbClientProtocolExtension ext = {
|
||||||
|
.encodings = encodings,
|
||||||
|
.handleEncoding = vnc_client_handle_open_h264_rect,
|
||||||
|
};
|
||||||
|
rfbClientRegisterExtension(&ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
static rfbBool vnc_client_handle_pts_rect(rfbClient* client,
|
||||||
|
rfbFramebufferUpdateRectHeader* rect_header)
|
||||||
|
{
|
||||||
|
if ((int)rect_header->encoding != RFB_ENCODING_PTS)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
struct vnc_client* self = rfbClientGetClientData(client, NULL);
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
uint64_t pts_msg = 0;
|
||||||
|
if (!ReadFromRFBServer(self->client, (char*)&pts_msg, sizeof(pts_msg)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
self->pts = vnc_client_htonll(pts_msg);
|
||||||
|
|
||||||
|
DTRACE_PROBE1(wlvncc, vnc_client_handle_pts_rect, self->pts);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vnc_client_init_pts_ext(void)
|
||||||
|
{
|
||||||
|
static int encodings[] = { RFB_ENCODING_PTS, 0 };
|
||||||
|
static rfbClientProtocolExtension ext = {
|
||||||
|
.encodings = encodings,
|
||||||
|
.handleEncoding = vnc_client_handle_pts_rect,
|
||||||
|
};
|
||||||
|
rfbClientRegisterExtension(&ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vnc_client* vnc_client_create(struct data_control* data_control)
|
||||||
|
{
|
||||||
|
vnc_client_init_open_h264();
|
||||||
|
vnc_client_init_pts_ext();
|
||||||
|
|
||||||
struct vnc_client* self = calloc(1, sizeof(*self));
|
struct vnc_client* self = calloc(1, sizeof(*self));
|
||||||
if (!self)
|
if (!self)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -75,11 +243,21 @@ struct vnc_client* vnc_client_create(void)
|
||||||
goto failure;
|
goto failure;
|
||||||
|
|
||||||
self->client = client;
|
self->client = client;
|
||||||
|
self->data_control = data_control;
|
||||||
rfbClientSetClientData(client, NULL, self);
|
rfbClientSetClientData(client, NULL, self);
|
||||||
|
|
||||||
client->MallocFrameBuffer = vnc_client_alloc_fb;
|
client->MallocFrameBuffer = vnc_client_alloc_fb;
|
||||||
client->GotFrameBufferUpdate = vnc_client_update_box;
|
client->GotFrameBufferUpdate = vnc_client_update_box;
|
||||||
client->FinishedFrameBufferUpdate = vnc_client_finish_update;
|
client->FinishedFrameBufferUpdate = vnc_client_finish_update;
|
||||||
|
client->StartingFrameBufferUpdate = vnc_client_start_update;
|
||||||
|
client->CancelledFrameBufferUpdate = vnc_client_cancel_update;
|
||||||
|
client->GotXCutText = vnc_client_got_cut_text;
|
||||||
|
self->cut_text = cut_text;
|
||||||
|
|
||||||
|
self->pts = NO_PTS;
|
||||||
|
|
||||||
|
// Handle authentication
|
||||||
|
client->GetCredential = handle_vnc_authentication;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
|
@ -88,8 +266,40 @@ failure:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rfbCredential* handle_vnc_authentication(struct _rfbClient *client, int credentialType) {
|
||||||
|
rfbCredential* creds = (rfbCredential*) malloc(sizeof(rfbCredential));
|
||||||
|
|
||||||
|
if (client->authScheme == rfbVeNCrypt && credentialType == rfbCredentialTypeX509) {
|
||||||
|
char* path = getenv("TLS_CA");
|
||||||
|
rfbClientLog("Using TLS CA certificate from env 'TLS_CA': %s\n", path);
|
||||||
|
|
||||||
|
creds->x509Credential.x509CACertFile = malloc(strlen(path) + 1);
|
||||||
|
strcpy(creds->x509Credential.x509CACertFile, path);
|
||||||
|
creds->x509Credential.x509CrlVerifyMode = rfbX509CrlVerifyAll;
|
||||||
|
} else if (client->authScheme == rfbVeNCrypt && credentialType == rfbCredentialTypeUser) {
|
||||||
|
const* username = getenv("VNC_USERNAME");
|
||||||
|
const* password = getenv("VNC_PASSWORD");
|
||||||
|
rfbClientLog("Using username and password for VNC authentication 'VNC_USERNAME', 'VNC_PASSWORD'\n");
|
||||||
|
|
||||||
|
creds->userCredential.password = malloc(strlen(password) + 1);
|
||||||
|
creds->userCredential.username = malloc(strlen(username) + 1);
|
||||||
|
strcpy(creds->userCredential.password, password);
|
||||||
|
strcpy(creds->userCredential.username, username);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
return creds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cut_text (struct vnc_client* self, const char* text, size_t size) {
|
||||||
|
data_control_to_clipboard(self->data_control, text, size);
|
||||||
|
//printf("Received string FROM vnc_server: %s\n", text);
|
||||||
|
}
|
||||||
|
|
||||||
void vnc_client_destroy(struct vnc_client* self)
|
void vnc_client_destroy(struct vnc_client* self)
|
||||||
{
|
{
|
||||||
|
vnc_client_clear_av_frames(self);
|
||||||
|
open_h264_destroy(self->open_h264);
|
||||||
rfbClientCleanup(self->client);
|
rfbClientCleanup(self->client);
|
||||||
free(self);
|
free(self);
|
||||||
}
|
}
|
||||||
|
@ -98,20 +308,27 @@ int vnc_client_connect(struct vnc_client* self, const char* address, int port)
|
||||||
{
|
{
|
||||||
rfbClient* client = self->client;
|
rfbClient* client = self->client;
|
||||||
|
|
||||||
if (!ConnectToRFBServer(client, address, port))
|
return ConnectToRFBServer(client, address, port) ? 0 : -1;
|
||||||
return -1;
|
}
|
||||||
|
|
||||||
|
int vnc_client_init(struct vnc_client* self)
|
||||||
|
{
|
||||||
|
int rc = -1;
|
||||||
|
rfbClient* client = self->client;
|
||||||
|
|
||||||
|
vnc_client_lock_handler(self);
|
||||||
|
|
||||||
if (!InitialiseRFBConnection(client))
|
if (!InitialiseRFBConnection(client))
|
||||||
return -1;
|
goto failure;
|
||||||
|
|
||||||
client->width = client->si.framebufferWidth;
|
client->width = client->si.framebufferWidth;
|
||||||
client->height = client->si.framebufferHeight;
|
client->height = client->si.framebufferHeight;
|
||||||
|
|
||||||
if (!client->MallocFrameBuffer(client))
|
if (!client->MallocFrameBuffer(client))
|
||||||
return -1;
|
goto failure;
|
||||||
|
|
||||||
if (!SetFormatAndEncodings(client))
|
if (!SetFormatAndEncodings(client))
|
||||||
return -1;
|
goto failure;
|
||||||
|
|
||||||
if (client->updateRect.x < 0) {
|
if (client->updateRect.x < 0) {
|
||||||
client->updateRect.x = client->updateRect.y = 0;
|
client->updateRect.x = client->updateRect.y = 0;
|
||||||
|
@ -123,20 +340,25 @@ int vnc_client_connect(struct vnc_client* self, const char* address, int port)
|
||||||
client->updateRect.x, client->updateRect.y,
|
client->updateRect.x, client->updateRect.y,
|
||||||
client->updateRect.w, client->updateRect.h,
|
client->updateRect.w, client->updateRect.h,
|
||||||
FALSE))
|
FALSE))
|
||||||
return -1;
|
goto failure;
|
||||||
|
|
||||||
return 0;
|
SendIncrementalFramebufferUpdateRequest(client);
|
||||||
|
SendIncrementalFramebufferUpdateRequest(client);
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
failure:
|
||||||
|
vnc_client_unlock_handler(self);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int vnc_client_set_pixel_format(struct vnc_client* self,
|
int vnc_client_set_pixel_format(struct vnc_client* self, uint32_t format)
|
||||||
enum wl_shm_format format)
|
|
||||||
{
|
{
|
||||||
rfbPixelFormat* dst = &self->client->format;
|
rfbPixelFormat* dst = &self->client->format;
|
||||||
int bpp = -1;
|
int bpp = -1;
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case WL_SHM_FORMAT_ARGB8888:
|
case DRM_FORMAT_ARGB8888:
|
||||||
case WL_SHM_FORMAT_XRGB8888:
|
case DRM_FORMAT_XRGB8888:
|
||||||
dst->redShift = 16;
|
dst->redShift = 16;
|
||||||
dst->greenShift = 8;
|
dst->greenShift = 8;
|
||||||
dst->blueShift = 0;
|
dst->blueShift = 0;
|
||||||
|
@ -203,7 +425,21 @@ const char* vnc_client_get_desktop_name(const struct vnc_client* self)
|
||||||
|
|
||||||
int vnc_client_process(struct vnc_client* self)
|
int vnc_client_process(struct vnc_client* self)
|
||||||
{
|
{
|
||||||
return HandleRFBServerMessage(self->client) ? 0 : -1;
|
if (!ReadToBuffer(self->client))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!vnc_client_lock_handler(self))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
while (self->client->buffered > 0) {
|
||||||
|
rc = HandleRFBServerMessage(self->client) ? 0 : -1;
|
||||||
|
if (rc < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vnc_client_unlock_handler(self);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vnc_client_send_pointer_event(struct vnc_client* self, int x, int y,
|
void vnc_client_send_pointer_event(struct vnc_client* self, int x, int y,
|
||||||
|
@ -222,7 +458,8 @@ void vnc_client_send_keyboard_event(struct vnc_client* self, uint32_t symbol,
|
||||||
if (!qnum)
|
if (!qnum)
|
||||||
qnum = code;
|
qnum = code;
|
||||||
|
|
||||||
SendExtendedKeyEvent(self->client, symbol, qnum, is_pressed);
|
if (!SendExtendedKeyEvent(self->client, symbol, qnum, is_pressed))
|
||||||
|
SendKeyEvent(self->client, symbol, is_pressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vnc_client_set_encodings(struct vnc_client* self, const char* encodings)
|
void vnc_client_set_encodings(struct vnc_client* self, const char* encodings)
|
||||||
|
@ -239,3 +476,10 @@ void vnc_client_set_compression_level(struct vnc_client* self, int value)
|
||||||
{
|
{
|
||||||
self->client->appData.compressLevel = value;
|
self->client->appData.compressLevel = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vnc_client_send_cut_text(struct vnc_client* self, const char* text,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
// libvncclient doesn't modify text, so typecast is OK.
|
||||||
|
SendClientCutText(self->client, (char*)text, len);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,393 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This 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
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* vncviewer.c - the Xt-based VNC viewer.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "rfbclient.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
static void Dummy(rfbClient* client) {
|
||||||
|
}
|
||||||
|
static rfbBool DummyPoint(rfbClient* client, int x, int y) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
static void DummyRect(rfbClient* client, int x, int y, int w, int h) {
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
static char* ReadPassword(rfbClient* client) {
|
||||||
|
int i;
|
||||||
|
char* p=calloc(1,9);
|
||||||
|
if (!p) return p;
|
||||||
|
struct termios save,noecho;
|
||||||
|
if(tcgetattr(fileno(stdin),&save)!=0) return p;
|
||||||
|
noecho=save; noecho.c_lflag &= ~ECHO;
|
||||||
|
if(tcsetattr(fileno(stdin),TCSAFLUSH,&noecho)!=0) return p;
|
||||||
|
fprintf(stderr,"Password: ");
|
||||||
|
fflush(stderr);
|
||||||
|
i=0;
|
||||||
|
while(1) {
|
||||||
|
int c=fgetc(stdin);
|
||||||
|
if(c=='\n')
|
||||||
|
break;
|
||||||
|
if(i<8) {
|
||||||
|
p[i]=c;
|
||||||
|
i++;
|
||||||
|
p[i]=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tcsetattr(fileno(stdin),TCSAFLUSH,&save);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
static rfbBool MallocFrameBuffer(rfbClient* client) {
|
||||||
|
uint64_t allocSize;
|
||||||
|
|
||||||
|
if(client->frameBuffer) {
|
||||||
|
free(client->frameBuffer);
|
||||||
|
client->frameBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SECURITY: promote 'width' into uint64_t so that the multiplication does not overflow
|
||||||
|
'width' and 'height' are 16-bit integers per RFB protocol design
|
||||||
|
SIZE_MAX is the maximum value that can fit into size_t
|
||||||
|
*/
|
||||||
|
allocSize = (uint64_t)client->width * client->height * client->format.bitsPerPixel/8;
|
||||||
|
|
||||||
|
if (allocSize >= SIZE_MAX) {
|
||||||
|
rfbClientErr("CRITICAL: cannot allocate frameBuffer, requested size is too large\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->frameBuffer=malloc( (size_t)allocSize );
|
||||||
|
|
||||||
|
if (client->frameBuffer == NULL)
|
||||||
|
rfbClientErr("CRITICAL: frameBuffer allocation failed, requested size too large or not enough memory?\n");
|
||||||
|
|
||||||
|
return client->frameBuffer?TRUE:FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* messages */
|
||||||
|
|
||||||
|
static rfbBool CheckRect(rfbClient* client, int x, int y, int w, int h) {
|
||||||
|
return x + w <= client->width && y + h <= client->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) {
|
||||||
|
int i,j;
|
||||||
|
|
||||||
|
if (client->frameBuffer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CheckRect(client, x, y, w, h)) {
|
||||||
|
rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", x, y, w, h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FILL_RECT(BPP) \
|
||||||
|
for(j=y*client->width;j<(y+h)*client->width;j+=client->width) \
|
||||||
|
for(i=x;i<x+w;i++) \
|
||||||
|
((uint##BPP##_t*)client->frameBuffer)[j+i]=colour;
|
||||||
|
|
||||||
|
switch(client->format.bitsPerPixel) {
|
||||||
|
case 8: FILL_RECT(8); break;
|
||||||
|
case 16: FILL_RECT(16); break;
|
||||||
|
case 32: FILL_RECT(32); break;
|
||||||
|
default:
|
||||||
|
rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CopyRectangle(rfbClient* client, const uint8_t* buffer, int x, int y, int w, int h) {
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (client->frameBuffer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CheckRect(client, x, y, w, h)) {
|
||||||
|
rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", x, y, w, h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define COPY_RECT(BPP) \
|
||||||
|
{ \
|
||||||
|
int rs = w * BPP / 8, rs2 = client->width * BPP / 8; \
|
||||||
|
for (j = ((x * (BPP / 8)) + (y * rs2)); j < (y + h) * rs2; j += rs2) { \
|
||||||
|
memcpy(client->frameBuffer + j, buffer, rs); \
|
||||||
|
buffer += rs; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(client->format.bitsPerPixel) {
|
||||||
|
case 8: COPY_RECT(8); break;
|
||||||
|
case 16: COPY_RECT(16); break;
|
||||||
|
case 32: COPY_RECT(32); break;
|
||||||
|
default:
|
||||||
|
rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: test */
|
||||||
|
static void CopyRectangleFromRectangle(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) {
|
||||||
|
int i,j;
|
||||||
|
|
||||||
|
if (client->frameBuffer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CheckRect(client, src_x, src_y, w, h)) {
|
||||||
|
rfbClientLog("Source rect out of bounds: %dx%d at (%d, %d)\n", src_x, src_y, w, h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CheckRect(client, dest_x, dest_y, w, h)) {
|
||||||
|
rfbClientLog("Dest rect out of bounds: %dx%d at (%d, %d)\n", dest_x, dest_y, w, h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define COPY_RECT_FROM_RECT(BPP) \
|
||||||
|
{ \
|
||||||
|
uint##BPP##_t* _buffer=((uint##BPP##_t*)client->frameBuffer)+(src_y-dest_y)*client->width+src_x-dest_x; \
|
||||||
|
if (dest_y < src_y) { \
|
||||||
|
for(j = dest_y*client->width; j < (dest_y+h)*client->width; j += client->width) { \
|
||||||
|
if (dest_x < src_x) { \
|
||||||
|
for(i = dest_x; i < dest_x+w; i++) { \
|
||||||
|
((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
|
||||||
|
} \
|
||||||
|
} else { \
|
||||||
|
for(i = dest_x+w-1; i >= dest_x; i--) { \
|
||||||
|
((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} else { \
|
||||||
|
for(j = (dest_y+h-1)*client->width; j >= dest_y*client->width; j-=client->width) { \
|
||||||
|
if (dest_x < src_x) { \
|
||||||
|
for(i = dest_x; i < dest_x+w; i++) { \
|
||||||
|
((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
|
||||||
|
} \
|
||||||
|
} else { \
|
||||||
|
for(i = dest_x+w-1; i >= dest_x; i--) { \
|
||||||
|
((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(client->format.bitsPerPixel) {
|
||||||
|
case 8: COPY_RECT_FROM_RECT(8); break;
|
||||||
|
case 16: COPY_RECT_FROM_RECT(16); break;
|
||||||
|
case 32: COPY_RECT_FROM_RECT(32); break;
|
||||||
|
default:
|
||||||
|
rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initAppData(AppData* data) {
|
||||||
|
data->shareDesktop=TRUE;
|
||||||
|
data->viewOnly=FALSE;
|
||||||
|
data->encodingsString="tight zrle ultra copyrect hextile zlib corre rre raw";
|
||||||
|
data->useBGR233=FALSE;
|
||||||
|
data->nColours=0;
|
||||||
|
data->forceOwnCmap=FALSE;
|
||||||
|
data->forceTrueColour=FALSE;
|
||||||
|
data->requestedDepth=0;
|
||||||
|
data->compressLevel=3;
|
||||||
|
data->qualityLevel=5;
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
||||||
|
data->enableJPEG=TRUE;
|
||||||
|
#else
|
||||||
|
data->enableJPEG=FALSE;
|
||||||
|
#endif
|
||||||
|
data->useRemoteCursor=FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
|
||||||
|
int bytesPerPixel) {
|
||||||
|
rfbClient* client=(rfbClient*)calloc(sizeof(rfbClient),1);
|
||||||
|
if(!client) {
|
||||||
|
rfbClientErr("Couldn't allocate client structure!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
initAppData(&client->appData);
|
||||||
|
client->endianTest = 1;
|
||||||
|
client->programName="";
|
||||||
|
client->serverHost=strdup("");
|
||||||
|
client->serverPort=5900;
|
||||||
|
|
||||||
|
client->destHost = NULL;
|
||||||
|
client->destPort = 5900;
|
||||||
|
|
||||||
|
client->connectTimeout = DEFAULT_CONNECT_TIMEOUT;
|
||||||
|
client->readTimeout = DEFAULT_READ_TIMEOUT;
|
||||||
|
|
||||||
|
/* default: use complete frame buffer */
|
||||||
|
client->updateRect.x = -1;
|
||||||
|
|
||||||
|
client->frameBuffer = NULL;
|
||||||
|
client->outputWindow = 0;
|
||||||
|
|
||||||
|
client->format.bitsPerPixel = bytesPerPixel*8;
|
||||||
|
client->format.depth = bitsPerSample*samplesPerPixel;
|
||||||
|
client->appData.requestedDepth=client->format.depth;
|
||||||
|
client->format.bigEndian = *(char *)&client->endianTest?FALSE:TRUE;
|
||||||
|
client->format.trueColour = 1;
|
||||||
|
|
||||||
|
if (client->format.bitsPerPixel == 8) {
|
||||||
|
client->format.redMax = 7;
|
||||||
|
client->format.greenMax = 7;
|
||||||
|
client->format.blueMax = 3;
|
||||||
|
client->format.redShift = 0;
|
||||||
|
client->format.greenShift = 3;
|
||||||
|
client->format.blueShift = 6;
|
||||||
|
} else {
|
||||||
|
client->format.redMax = (1 << bitsPerSample) - 1;
|
||||||
|
client->format.greenMax = (1 << bitsPerSample) - 1;
|
||||||
|
client->format.blueMax = (1 << bitsPerSample) - 1;
|
||||||
|
if(!client->format.bigEndian) {
|
||||||
|
client->format.redShift = 0;
|
||||||
|
client->format.greenShift = bitsPerSample;
|
||||||
|
client->format.blueShift = bitsPerSample * 2;
|
||||||
|
} else {
|
||||||
|
if(client->format.bitsPerPixel==8*3) {
|
||||||
|
client->format.redShift = bitsPerSample*2;
|
||||||
|
client->format.greenShift = bitsPerSample*1;
|
||||||
|
client->format.blueShift = 0;
|
||||||
|
} else {
|
||||||
|
client->format.redShift = bitsPerSample*3;
|
||||||
|
client->format.greenShift = bitsPerSample*2;
|
||||||
|
client->format.blueShift = bitsPerSample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client->bufoutptr=client->buf;
|
||||||
|
client->buffered=0;
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||||
|
client->raw_buffer_size = -1;
|
||||||
|
client->decompStreamInited = FALSE;
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
||||||
|
memset(client->zlibStreamActive,0,sizeof(rfbBool)*4);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
client->HandleCursorPos = DummyPoint;
|
||||||
|
client->SoftCursorLockArea = DummyRect;
|
||||||
|
client->SoftCursorUnlockScreen = Dummy;
|
||||||
|
client->GotFrameBufferUpdate = DummyRect;
|
||||||
|
client->GotCopyRect = CopyRectangleFromRectangle;
|
||||||
|
client->GotFillRect = FillRectangle;
|
||||||
|
client->GotBitmap = CopyRectangle;
|
||||||
|
client->FinishedFrameBufferUpdate = NULL;
|
||||||
|
client->GetPassword = ReadPassword;
|
||||||
|
client->MallocFrameBuffer = MallocFrameBuffer;
|
||||||
|
client->Bell = Dummy;
|
||||||
|
client->CurrentKeyboardLedState = 0;
|
||||||
|
client->HandleKeyboardLedState = (HandleKeyboardLedStateProc)DummyPoint;
|
||||||
|
client->QoS_DSCP = 0;
|
||||||
|
|
||||||
|
client->authScheme = 0;
|
||||||
|
client->subAuthScheme = 0;
|
||||||
|
client->GetCredential = NULL;
|
||||||
|
client->tlsSession = NULL;
|
||||||
|
client->LockWriteToTLS = NULL;
|
||||||
|
client->UnlockWriteToTLS = NULL;
|
||||||
|
client->sock = RFB_INVALID_SOCKET;
|
||||||
|
client->listenSock = RFB_INVALID_SOCKET;
|
||||||
|
client->listenAddress = NULL;
|
||||||
|
client->listen6Sock = RFB_INVALID_SOCKET;
|
||||||
|
client->listen6Address = NULL;
|
||||||
|
client->clientAuthSchemes = NULL;
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
client->GetSASLMechanism = NULL;
|
||||||
|
client->GetUser = NULL;
|
||||||
|
client->saslSecret = NULL;
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
||||||
|
client->requestedResize = FALSE;
|
||||||
|
client->screen.width = 0;
|
||||||
|
client->screen.height = 0;
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rfbClientCleanup(rfbClient* client) {
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for ( i = 0; i < 4; i++ ) {
|
||||||
|
if (client->zlibStreamActive[i] == TRUE ) {
|
||||||
|
if (inflateEnd (&client->zlibStream[i]) != Z_OK &&
|
||||||
|
client->zlibStream[i].msg != NULL)
|
||||||
|
rfbClientLog("inflateEnd: %s\n", client->zlibStream[i].msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( client->decompStreamInited == TRUE ) {
|
||||||
|
if (inflateEnd (&client->decompStream) != Z_OK &&
|
||||||
|
client->decompStream.msg != NULL)
|
||||||
|
rfbClientLog("inflateEnd: %s\n", client->decompStream.msg );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (client->ultra_buffer)
|
||||||
|
free(client->ultra_buffer);
|
||||||
|
|
||||||
|
if (client->raw_buffer)
|
||||||
|
free(client->raw_buffer);
|
||||||
|
|
||||||
|
FreeTLS(client);
|
||||||
|
|
||||||
|
while (client->clientData) {
|
||||||
|
rfbClientData* next = client->clientData->next;
|
||||||
|
free(client->clientData);
|
||||||
|
client->clientData = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(client->vncRec);
|
||||||
|
|
||||||
|
if (client->sock != RFB_INVALID_SOCKET)
|
||||||
|
rfbCloseSocket(client->sock);
|
||||||
|
if (client->listenSock != RFB_INVALID_SOCKET)
|
||||||
|
rfbCloseSocket(client->listenSock);
|
||||||
|
free(client->desktopName);
|
||||||
|
free(client->serverHost);
|
||||||
|
if (client->destHost)
|
||||||
|
free(client->destHost);
|
||||||
|
if (client->clientAuthSchemes)
|
||||||
|
free(client->clientAuthSchemes);
|
||||||
|
|
||||||
|
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||||
|
if (client->saslSecret)
|
||||||
|
free(client->saslSecret);
|
||||||
|
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||||
|
|
||||||
|
free(client);
|
||||||
|
}
|
Loading…
Reference in New Issue