diff --git a/protocols/rdp/.gitignore b/protocols/rdp/.gitignore new file mode 100644 index 00000000..3f725332 --- /dev/null +++ b/protocols/rdp/.gitignore @@ -0,0 +1,36 @@ + +# Object code +*.o +*.so +*.lo +*.la + +# Backup files +*~ + +# Release files +*.tar.gz + +# Files currently being edited by vim or vi +*.swp + +# automake/autoconf +.deps/ +.libs/ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache/ +m4/* +!README +config.guess +config.log +config.status +config.sub +configure +depcomp +install-sh +libtool +ltmain.sh +missing + diff --git a/protocols/rdp/AUTHORS b/protocols/rdp/AUTHORS new file mode 100644 index 00000000..e62be891 --- /dev/null +++ b/protocols/rdp/AUTHORS @@ -0,0 +1,6 @@ +Michael Jumper +Matt Hortman +Jocelyn Delalande +David Lechevalier +Alexandre Devely +Laurent Meunier diff --git a/protocols/rdp/ChangeLog b/protocols/rdp/ChangeLog new file mode 100644 index 00000000..f789140f --- /dev/null +++ b/protocols/rdp/ChangeLog @@ -0,0 +1,49 @@ +2013-05-09 Michael Jumper + + * Merge contributed keymaps for French and German. + +2012-12-25 Michael Jumper + + * Console arguments (fixes #227) + +2012-12-13 Michael Jumper + + * Implement PATBLT fallback (fixes #238) + +2012-11-22 Michael Jumper + + * Add disable-audio option (fixes #221) + +2012-11-02 Michael Jumper + + * Added sound support (fixes #32) + +2012-10-22 Michael Jumper + + * Use guac_client_info to choose optimal size if size not overridden + +2012-08-31 Laurent Meunier + + * Use configured color depth + +2012-08-11 Michael Jumper + + * Fix m4/ autoreconf error + +2012-05-23 David Pham-Van + + * Add SetNull and SetDefault handlers (fixes #148) + +2012-05-04 Michael Jumper + + * Removed Alt-code fallback mode + * Added embedded default mouse pointer + +2012-05-15 Jocelyn Delalande + + * Unicode keyboard events + +2012-05-04 Michael Jumper + + * Initial release + diff --git a/protocols/rdp/LICENSE b/protocols/rdp/LICENSE new file mode 100644 index 00000000..7714141d --- /dev/null +++ b/protocols/rdp/LICENSE @@ -0,0 +1,470 @@ + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] + diff --git a/protocols/rdp/Makefile.am b/protocols/rdp/Makefile.am new file mode 100644 index 00000000..595691f8 --- /dev/null +++ b/protocols/rdp/Makefile.am @@ -0,0 +1,99 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is libguac-client-rdp. +# +# The Initial Developer of the Original Code is +# Michael Jumper. +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +AUTOMAKE_OPTIONS = foreign + +ACLOCAL_AMFLAGS = -I m4 +AM_CFLAGS = -Werror -Wall -Iinclude + +lib_LTLIBRARIES = libguac-client-rdp.la +freerdp_LTLIBRARIES = guac_rdpsnd.la + +libguac_client_rdp_la_SOURCES = \ + $(OGG_SOURCES) \ + src/audio.c \ + src/client.c \ + src/default_pointer.c \ + src/guac_handlers.c \ + src/rdp_bitmap.c \ + src/rdp_cliprdr.c \ + src/rdp_gdi.c \ + src/rdp_glyph.c \ + src/rdp_keymap_base.c \ + src/rdp_keymap.c \ + src/rdp_keymap_de_de.c \ + src/rdp_keymap_failsafe.c \ + src/rdp_keymap_fr_fr.c \ + src/rdp_keymap_en_us.c \ + src/rdp_pointer.c \ + src/wav_encoder.c + +guac_rdpsnd_la_SOURCES = \ + guac_rdpsnd/messages.c \ + guac_rdpsnd/service.c \ + src/audio.c + +noinst_HEADERS = \ + $(OGG_HEADERS) \ + guac_rdpsnd/messages.h \ + guac_rdpsnd/service.h \ + include/audio.h \ + include/client.h \ + include/config.h \ + include/default_pointer.h \ + include/guac_handlers.h \ + include/rdp_bitmap.h \ + include/rdp_cliprdr.h \ + include/rdp_gdi.h \ + include/rdp_glyph.h \ + include/rdp_keymap.h \ + include/rdp_pointer.h \ + include/wav_encoder.h + +# Compile OGG support if available +if ENABLE_OGG + libguac_client_rdp_la_SOURCES += src/ogg_encoder.c + noinst_HEADERS += include/ogg_encoder.h +endif + + +libguac_client_rdp_la_LDFLAGS = -version-info 0:0:0 +guac_rdpsnd_la_LDFLAGS = -module -avoid-version -shared + +freerdpdir = ${libdir}/freerdp/ + +EXTRA_DIST = LICENSE + diff --git a/protocols/rdp/README b/protocols/rdp/README new file mode 100644 index 00000000..84629d07 --- /dev/null +++ b/protocols/rdp/README @@ -0,0 +1,75 @@ + +------------------------------------------------------------ + About this README +------------------------------------------------------------ + +This README is intended to provide quick and to-the-point documentation for +technical users intending to compile parts of Guacamole themselves. + +Distribution-specific packages are available from the files section of the main +project page: + + http://sourceforge.net/projects/guacamole/files/ + +Distribution-specific documentation is provided on the Guacamole wiki: + + http://guac-dev.org/ + + +------------------------------------------------------------ + What is libguac-client-rdp? +------------------------------------------------------------ + +libguac-client-rdp is a protocol support plugin for the Guacamole proxy (guacd) +which provides support for RDP, the proprietary remote desktop protocol used +by Windows Remote Deskop / Terminal Services, via the libfreerdp library. + + +------------------------------------------------------------ + Compiling and installing libguac-client-rdp +------------------------------------------------------------ + +Please note that distribution-specific pre-compiled packages are available from +the files section of the main project site: + + http://sourceforge.net/projects/guacamole/files/ + +libguac-client-rdp is built using the popular GNU Automake, and thus provides +the standard configure script. + +1) Run configure + + $ ./configure + + Assuming all dependencies have been installed, this should succeed without + errors. + +2) Run make + + $ make + + libguac-client-rdp will now compile. + +3) Install (as root) + + # make install + + libguac-client-rdp will install to your /usr/local/lib directory by default. + You can change the install location by using the --prefix option for + configure. + + You will need to run ldconfig (as root) so that guacd can find the library + when needed: + + # ldconfig + + +------------------------------------------------------------ + Reporting problems +------------------------------------------------------------ + +Please report any bugs encountered by opening a new ticket at the Trac system +hosted at: + + http://guac-dev.org/trac/ + diff --git a/protocols/rdp/configure.in b/protocols/rdp/configure.in new file mode 100644 index 00000000..e21c6713 --- /dev/null +++ b/protocols/rdp/configure.in @@ -0,0 +1,86 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is libguac-client-rdp. +# +# The Initial Developer of the Original Code is +# Michael Jumper. +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +AC_INIT(src/client.c) +AM_INIT_AUTOMAKE([libguac-client-rdp], 0.7.4) +AC_CONFIG_MACRO_DIR([m4]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_CC_C99 +AC_PROG_LIBTOOL + +# Checks for libraries. +AC_CHECK_LIB([guac], [guac_client_plugin_open],, AC_MSG_ERROR("libguac must be installed first")) +AC_CHECK_LIB([cairo], [cairo_create],, AC_MSG_ERROR("cairo is required for drawing instructions")) +AC_CHECK_LIB([freerdp-cache], [glyph_cache_register_callbacks],, AC_MSG_ERROR("libfreerdp-cache is required (part of FreeRDP)")) +AC_CHECK_LIB([freerdp-core], [freerdp_new],, AC_MSG_ERROR("libfreerdp-core is required (part of FreeRDP)")) +AC_CHECK_LIB([freerdp-channels], [freerdp_channels_new],, AC_MSG_ERROR("libfreerdp-channels is required (part of FreeRDP)")) +AC_CHECK_LIB([freerdp-utils], [xzalloc],, AC_MSG_ERROR("libfreerdp-utils is required (part of FreeRDP)")) +AC_CHECK_LIB([freerdp-codec], [freerdp_image_convert],, AC_MSG_ERROR("libfreerdp-codec is required (part of FreeRDP)")) +AC_CHECK_LIB([pthread], [pthread_mutex_init],, AC_MSG_ERROR("libpthread is required")) + +# Check for libvorbisenc + +have_vorbisenc=yes +AC_CHECK_HEADER(vorbis/vorbisenc.h,, [have_vorbisenc=no]) +AC_CHECK_LIB([vorbisenc], [vorbis_encode_init],, [have_vorbisenc=no]) +AM_CONDITIONAL([ENABLE_OGG], [test "x${have_vorbisenc}" = "xyes"]) + +if test "x${have_vorbisenc}" = "xno" +then + AC_MSG_WARN([ + -------------------------------------------- + Unable to find libvorbisenc. + Sound will not be encoded with Ogg Vorbis. + --------------------------------------------]) +else + AC_DEFINE([ENABLE_OGG]) +fi + +# Checks for header files. +AC_CHECK_HEADERS([guacamole/client.h guacamole/guacio.h guacamole/protocol.h freerdp/locale/keyboard.h freerdp/kbd/layouts.h]) + +# Check for FreeRDP version-specific features +AC_CHECK_MEMBERS([rdpPointer.SetDefault, rdpPointer.SetNull], + [], [], + [[#include ]]) + +# Checks for library functions. +AC_FUNC_MALLOC + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/protocols/rdp/guac_rdpsnd/messages.c b/protocols/rdp/guac_rdpsnd/messages.c new file mode 100644 index 00000000..1df1747d --- /dev/null +++ b/protocols/rdp/guac_rdpsnd/messages.c @@ -0,0 +1,313 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "audio.h" +#include "service.h" +#include "messages.h" +#include "client.h" + +/* MESSAGE HANDLERS */ + +void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd, + audio_stream* audio, STREAM* input_stream, + guac_rdpsnd_pdu_header* header) { + + int server_format_count; + int server_version; + int i; + + STREAM* output_stream; + int output_body_size; + unsigned char* output_stream_end; + + rdp_guac_client_data* guac_client_data = + (rdp_guac_client_data*) audio->client->data; + + /* Format header */ + stream_seek(input_stream, 14); + stream_read_uint16(input_stream, server_format_count); + stream_seek_uint8(input_stream); + stream_read_uint16(input_stream, server_version); + stream_seek_uint8(input_stream); + + /* Initialize Client Audio Formats and Version PDU */ + output_stream = stream_new(24); + stream_write_uint8(output_stream, SNDC_FORMATS); + stream_write_uint8(output_stream, 0); + + /* Fill in body size later */ + stream_seek_uint16(output_stream); /* offset = 0x02 */ + + /* Flags, volume, and pitch */ + stream_write_uint32(output_stream, TSSNDCAPS_ALIVE); + stream_write_uint32(output_stream, 0); + stream_write_uint32(output_stream, 0); + + /* Datagram port (UDP) */ + stream_write_uint16(output_stream, 0); + + /* Fill in format count later */ + stream_seek_uint16(output_stream); /* offset = 0x12 */ + + /* Version and padding */ + stream_write_uint8(output_stream, 0); + stream_write_uint16(output_stream, 6); + stream_write_uint8(output_stream, 0); + + /* Check each server format, respond if supported */ + for (i=0; i < server_format_count; i++) { + + unsigned char* format_start; + + int format_tag; + int channels; + int rate; + int bps; + int body_size; + + /* Remember position in stream */ + stream_get_mark(input_stream, format_start); + + /* Read format */ + stream_read_uint16(input_stream, format_tag); + stream_read_uint16(input_stream, channels); + stream_read_uint32(input_stream, rate); + stream_seek_uint32(input_stream); + stream_seek_uint16(input_stream); + stream_read_uint16(input_stream, bps); + + /* Skip past extra data */ + stream_read_uint16(input_stream, body_size); + stream_seek(input_stream, body_size); + + /* If PCM, accept */ + if (format_tag == WAVE_FORMAT_PCM) { + + /* If can fit another format, accept it */ + if (rdpsnd->format_count < GUAC_RDP_MAX_FORMATS) { + + /* Add channel */ + int current = rdpsnd->format_count++; + rdpsnd->formats[current].rate = rate; + rdpsnd->formats[current].channels = channels; + rdpsnd->formats[current].bps = bps; + + /* Log format */ + guac_client_log_info(audio->client, + "Accepted format: %i-bit PCM with %i channels at " + "%i Hz", + bps, channels, rate); + + /* Queue format for sending as accepted */ + stream_check_size(output_stream, 18 + body_size); + stream_write(output_stream, format_start, 18 + body_size); + + /* + * BEWARE that using stream_check_size means that any "marks" + * set via stream_set_mark on output_stream are invalid. + */ + + } + + /* Otherwise, log that we dropped one */ + else + guac_client_log_info(audio->client, + "Dropped valid format: %i-bit PCM with %i channels at " + "%i Hz", + bps, channels, rate); + + } + + } + + /* Calculate size of PDU */ + output_body_size = stream_get_length(output_stream) - 4; + stream_get_mark(output_stream, output_stream_end); + + /* Set body size */ + stream_set_pos(output_stream, 0x02); + stream_write_uint16(output_stream, output_body_size); + + /* Set format count */ + stream_set_pos(output_stream, 0x12); + stream_write_uint16(output_stream, rdpsnd->format_count); + + /* Reposition cursor at end (necessary for message send) */ + stream_set_mark(output_stream, output_stream_end); + + /* Send accepted formats */ + pthread_mutex_lock(&(guac_client_data->rdp_lock)); + svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream); + + /* If version greater than 6, must send Quality Mode PDU */ + if (server_version >= 6) { + + /* Always send High Quality for now */ + output_stream = stream_new(8); + stream_write_uint8(output_stream, SNDC_QUALITYMODE); + stream_write_uint8(output_stream, 0); + stream_write_uint16(output_stream, 4); + stream_write_uint16(output_stream, HIGH_QUALITY); + stream_write_uint16(output_stream, 0); + + svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream); + } + + pthread_mutex_unlock(&(guac_client_data->rdp_lock)); + +} + +/* server is getting a feel of the round trip time */ +void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd, + audio_stream* audio, STREAM* input_stream, + guac_rdpsnd_pdu_header* header) { + + int data_size; + STREAM* output_stream; + + rdp_guac_client_data* guac_client_data = + (rdp_guac_client_data*) audio->client->data; + + /* Read timestamp and data size */ + stream_read_uint16(input_stream, rdpsnd->server_timestamp); + stream_read_uint16(input_stream, data_size); + + /* Send training response */ + output_stream = stream_new(8); + stream_write_uint8(output_stream, SNDC_TRAINING); + stream_write_uint8(output_stream, 0); + stream_write_uint16(output_stream, 4); + stream_write_uint16(output_stream, rdpsnd->server_timestamp); + stream_write_uint16(output_stream, data_size); + + pthread_mutex_lock(&(guac_client_data->rdp_lock)); + svc_plugin_send((rdpSvcPlugin*) rdpsnd, output_stream); + pthread_mutex_unlock(&(guac_client_data->rdp_lock)); + +} + +void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd, + audio_stream* audio, STREAM* input_stream, + guac_rdpsnd_pdu_header* header) { + + unsigned char buffer[4]; + int format; + + /* Read wave information */ + stream_read_uint16(input_stream, rdpsnd->server_timestamp); + stream_read_uint16(input_stream, format); + stream_read_uint8(input_stream, rdpsnd->waveinfo_block_number); + stream_seek(input_stream, 3); + stream_read(input_stream, buffer, 4); + + /* + * Size of incoming wave data is equal to the body size field of this + * header, less the size of a WaveInfo PDU (not including the header), + * thus body_size - 12. + */ + rdpsnd->incoming_wave_size = header->body_size - 12; + + /* Read wave in next iteration */ + rdpsnd->next_pdu_is_wave = true; + + /* Init stream with requested format */ + audio_stream_begin(audio, + rdpsnd->formats[format].rate, + rdpsnd->formats[format].channels, + rdpsnd->formats[format].bps); + + /* Write initial 4 bytes of data */ + audio_stream_write_pcm(audio, buffer, 4); + +} + +void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd, + audio_stream* audio, STREAM* input_stream, + guac_rdpsnd_pdu_header* header) { + + rdpSvcPlugin* plugin = (rdpSvcPlugin*)rdpsnd; + + rdp_guac_client_data* guac_client_data = + (rdp_guac_client_data*) audio->client->data; + + /* Wave Confirmation PDU */ + STREAM* output_stream = stream_new(8); + + /* Get wave data */ + unsigned char* buffer = stream_get_head(input_stream) + 4; + + /* Write rest of audio packet */ + audio_stream_write_pcm(audio, buffer, rdpsnd->incoming_wave_size); + audio_stream_end(audio); + + /* Write Wave Confirmation PDU */ + stream_write_uint8(output_stream, SNDC_WAVECONFIRM); + stream_write_uint8(output_stream, 0); + stream_write_uint16(output_stream, 4); + stream_write_uint16(output_stream, rdpsnd->server_timestamp); + stream_write_uint8(output_stream, rdpsnd->waveinfo_block_number); + stream_write_uint8(output_stream, 0); + + /* Send Wave Confirmation PDU */ + pthread_mutex_lock(&(guac_client_data->rdp_lock)); + svc_plugin_send(plugin, output_stream); + pthread_mutex_unlock(&(guac_client_data->rdp_lock)); + + /* We no longer expect to receive wave data */ + rdpsnd->next_pdu_is_wave = false; + +} + +void guac_rdpsnd_close_handler(guac_rdpsndPlugin* rdpsnd, + audio_stream* audio, STREAM* input_stream, + guac_rdpsnd_pdu_header* header) { + + /* STUB: Do nothing for now */ + +} + diff --git a/protocols/rdp/guac_rdpsnd/messages.h b/protocols/rdp/guac_rdpsnd/messages.h new file mode 100644 index 00000000..12c5e717 --- /dev/null +++ b/protocols/rdp/guac_rdpsnd/messages.h @@ -0,0 +1,162 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __GUAC_RDPSND_MESSAGES_H +#define __GUAC_RDPSND_MESSAGES_H + +/* + * PDU Message Types + */ + +/** + * Close PDU + */ +#define SNDC_CLOSE 1 + +/** + * WaveInfo PDU. This PDU is sent just before wave data is sent. + */ +#define SNDC_WAVE 2 + +/** + * Wave Confirm PDU. This PDU is sent in response to the WaveInfo PDU, + * confirming it has been received and played. + */ +#define SNDC_WAVECONFIRM 5 + +/** + * Training PDU. This PDU is sent by the server occasionally and must be + * responded to with another training PDU, similar to Guac's sync message. + */ +#define SNDC_TRAINING 6 + +/** + * Server Audio Formats and Version PDU. This PDU is sent by the server to + * advertise to the client which audio formats are supported. + */ +#define SNDC_FORMATS 7 + +/** + * Quality Mode PDU. This PDU must be sent by the client to select an audio + * quality mode if the server is at least version 6. + */ +#define SNDC_QUALITYMODE 12 + +/* + * Quality Modes + */ + +/** + * Dynamic Quality. The server will choose the audio quality based on its + * perception of latency. + */ +#define DYNAMIC_QUALITY 0x0000 + +/** + * Medium Quality. The server prioritizes bandwidth over quality. + */ +#define MEDIUM_QUALITY 0x0001 + +/** + * High Quality. The server prioritizes quality over bandwidth. + */ +#define HIGH_QUALITY 0x0002 + +/* + * Capabilities + */ +#define TSSNDCAPS_ALIVE 1 + +/* + * Sound Formats + */ +#define WAVE_FORMAT_PCM 1 + +/** + * The header common to all RDPSND PDUs. + */ +typedef struct guac_rdpsnd_pdu_header { + + /** + * The type of message represented by this PDU (SNDC_WAVE, etc.) + */ + int message_type; + + /** + * The size of the remainder of the message. + */ + int body_size; + +} guac_rdpsnd_pdu_header; + +/** + * Handler for the SNDC_FORMATS (Server Audio Formats and Version) PDU. + */ +void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd, + audio_stream* audio, STREAM* input_stream, + guac_rdpsnd_pdu_header* header); + +/** + * Handler for the SNDC_TRAINING (Training) PDU. + */ +void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd, + audio_stream* audio, STREAM* input_stream, + guac_rdpsnd_pdu_header* header); + +/** + * Handler for the SNDC_WAVE (WaveInfo) PDU. + */ +void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd, + audio_stream* audio, STREAM* input_stream, + guac_rdpsnd_pdu_header* header); + +/** + * Handler for the SNDWAV (Wave) PDU which follows any WaveInfo PDU. + */ +void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd, + audio_stream* audio, STREAM* input_stream, + guac_rdpsnd_pdu_header* header); + +/** + * Handler for the SNDC_CLOSE (Close) PDU. + */ +void guac_rdpsnd_close_handler(guac_rdpsndPlugin* rdpsnd, + audio_stream* audio, STREAM* input_stream, + guac_rdpsnd_pdu_header* header); + +#endif + diff --git a/protocols/rdp/guac_rdpsnd/service.c b/protocols/rdp/guac_rdpsnd/service.c new file mode 100644 index 00000000..89569ca5 --- /dev/null +++ b/protocols/rdp/guac_rdpsnd/service.c @@ -0,0 +1,140 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "audio.h" +#include "service.h" +#include "messages.h" + + +/* Define service, associate with "rdpsnd" channel */ + +DEFINE_SVC_PLUGIN(guac_rdpsnd, "rdpsnd", + CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP) + + +/* + * Service Handlers + */ + +void guac_rdpsnd_process_connect(rdpSvcPlugin* plugin) { + + /* Get audio stream from plugin */ + audio_stream* audio = (audio_stream*) + plugin->channel_entry_points.pExtendedData; + + /* Update every 10 ms */ + plugin->interval_ms = 10; + + /* Log that sound has been loaded */ + guac_client_log_info(audio->client, "guac_rdpsnd connected."); + +} + +void guac_rdpsnd_process_terminate(rdpSvcPlugin* plugin) { + xfree(plugin); +} + +void guac_rdpsnd_process_event(rdpSvcPlugin* plugin, RDP_EVENT* event) { + freerdp_event_free(event); +} + +void guac_rdpsnd_process_receive(rdpSvcPlugin* plugin, + STREAM* input_stream) { + + guac_rdpsndPlugin* rdpsnd = (guac_rdpsndPlugin*) plugin; + guac_rdpsnd_pdu_header header; + + /* Get audio stream from plugin */ + audio_stream* audio = (audio_stream*) + plugin->channel_entry_points.pExtendedData; + + /* Read RDPSND PDU header */ + stream_read_uint8(input_stream, header.message_type); + stream_seek_uint8(input_stream); + stream_read_uint16(input_stream, header.body_size); + + /* + * If next PDU is SNDWAVE (due to receiving WaveInfo PDU previously), + * ignore the header and parse as a Wave PDU. + */ + if (rdpsnd->next_pdu_is_wave) { + guac_rdpsnd_wave_handler(rdpsnd, audio, input_stream, &header); + return; + } + + /* Dispatch message to standard handlers */ + switch (header.message_type) { + + /* Server Audio Formats and Version PDU */ + case SNDC_FORMATS: + guac_rdpsnd_formats_handler(rdpsnd, audio, + input_stream, &header); + break; + + /* Training PDU */ + case SNDC_TRAINING: + guac_rdpsnd_training_handler(rdpsnd, audio, + input_stream, &header); + break; + + /* WaveInfo PDU */ + case SNDC_WAVE: + guac_rdpsnd_wave_info_handler(rdpsnd, audio, + input_stream, &header); + break; + + /* Close PDU */ + case SNDC_CLOSE: + guac_rdpsnd_close_handler(rdpsnd, audio, + input_stream, &header); + break; + + } + +} + diff --git a/protocols/rdp/guac_rdpsnd/service.h b/protocols/rdp/guac_rdpsnd/service.h new file mode 100644 index 00000000..d843d7f9 --- /dev/null +++ b/protocols/rdp/guac_rdpsnd/service.h @@ -0,0 +1,146 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __GUAC_RDPSND_SERVICE_H +#define __GUAC_RDPSND_SERVICE_H + +/** + * The maximum number of PCM formats to accept during the initial RDPSND + * handshake with the RDP server. + */ +#define GUAC_RDP_MAX_FORMATS 16 + + +/** + * Abstract representation of a PCM format, including the sample rate, number + * of channels, and bits per sample. + */ +typedef struct guac_pcm_format { + + /** + * The sample rate of this PCM format. + */ + int rate; + + /** + * The number off channels used by this PCM format. This will typically + * be 1 or 2. + */ + int channels; + + /** + * The number of bits per sample within this PCM format. This should be + * either 8 or 16. + */ + int bps; + +} guac_pcm_format; + + +/** + * Structure representing the current state of the Guacamole RDPSND plugin for + * FreeRDP. + */ +typedef struct guac_rdpsndPlugin { + + /** + * The FreeRDP parts of this plugin. This absolutely MUST be first. + * FreeRDP depends on accessing this structure as if it were an instance + * of rdpSvcPlugin. + */ + rdpSvcPlugin plugin; + + /** + * The block number of the last SNDC_WAVE (WaveInfo) PDU received. + */ + int waveinfo_block_number; + + /** + * Whether the next PDU coming is a SNDWAVE (Wave) PDU. Wave PDUs do not + * have headers, and are indicated by the receipt of a WaveInfo PDU. + */ + int next_pdu_is_wave; + + /** + * The size, in bytes, of the wave data in the coming Wave PDU, if any. + */ + int incoming_wave_size; + + /** + * The last received server timestamp. + */ + int server_timestamp; + + /** + * All formats agreed upon by server and client during the initial format + * exchange. All of these formats will be PCM, which is the only format + * guaranteed to be supported (based on the official RDP documentation). + */ + guac_pcm_format formats[GUAC_RDP_MAX_FORMATS]; + + /** + * The total number of formats. + */ + int format_count; + +} guac_rdpsndPlugin; + + +/** + * Handler called when this plugin is loaded by FreeRDP. + */ +void guac_rdpsnd_process_connect(rdpSvcPlugin* plugin); + +/** + * Handler called when this plugin receives data along its designated channel. + */ +void guac_rdpsnd_process_receive(rdpSvcPlugin* plugin, + STREAM* input_stream); + +/** + * Handler called when this plugin is being unloaded. + */ +void guac_rdpsnd_process_terminate(rdpSvcPlugin* plugin); + +/** + * Handler called when this plugin receives an event. For the sake of RDPSND, + * all events will be ignored and simply free'd. + */ +void guac_rdpsnd_process_event(rdpSvcPlugin* plugin, RDP_EVENT* event); + +#endif + diff --git a/protocols/rdp/include/audio.h b/protocols/rdp/include/audio.h new file mode 100644 index 00000000..441856df --- /dev/null +++ b/protocols/rdp/include/audio.h @@ -0,0 +1,212 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __GUAC_TEST_AUDIO_H +#define __GUAC_TEST_AUDIO_H + +#include +#include + +typedef struct audio_stream audio_stream; + +/** + * Handler which is called when the audio stream is opened. + */ +typedef void audio_encoder_begin_handler(audio_stream* audio); + +/** + * Handler which is called when the audio stream is closed. + */ +typedef void audio_encoder_end_handler(audio_stream* audio); + +/** + * Handler which is called when the audio stream is flushed. + */ +typedef void audio_encoder_write_handler(audio_stream* audio, + unsigned char* pcm_data, int length); + +/** + * Arbitrary audio codec encoder. + */ +typedef struct audio_encoder { + + /** + * The mimetype of the audio data encoded by this audio + * encoder. + */ + const char* mimetype; + + /** + * Handler which will be called when the audio stream is opened. + */ + audio_encoder_begin_handler* begin_handler; + + /** + * Handler which will be called when the audio stream is flushed. + */ + audio_encoder_write_handler* write_handler; + + /** + * Handler which will be called when the audio stream is closed. + */ + audio_encoder_end_handler* end_handler; + +} audio_encoder; + +/** + * Basic audio stream. PCM data is added to the stream. When the stream is + * flushed, a write handler receives PCM data packets and, presumably, streams + * them to the guac_stream provided. + */ +struct audio_stream { + + /** + * PCM data buffer, 16-bit samples, 2-channel, 44100 Hz. + */ + unsigned char* pcm_data; + + /** + * Number of bytes in buffer. + */ + int used; + + /** + * Maximum number of bytes in buffer. + */ + int length; + + /** + * Encoded audio data buffer, as written by the encoder. + */ + unsigned char* encoded_data; + + /** + * Number of bytes in the encoded data buffer. + */ + int encoded_data_used; + + /** + * Maximum number of bytes in the encoded data buffer. + */ + int encoded_data_length; + + /** + * Arbitrary codec encoder. When the PCM buffer is flushed, PCM data will + * be sent to this encoder. + */ + audio_encoder* encoder; + + /** + * The client associated with this audio stream. + */ + guac_client* client; + + /** + * The actual stream associated with this audio stream. + */ + guac_stream* stream; + + /** + * The number of samples per second of PCM data sent to this stream. + */ + int rate; + + /** + * The number of audio channels per sample of PCM data. Legal values are + * 1 or 2. + */ + int channels; + + /** + * The number of bits per sample per channel for PCM data. Legal values are + * 8 or 16. + */ + int bps; + + /** + * The number of PCM bytes written since the audio chunk began. + */ + int pcm_bytes_written; + + /** + * Encoder-specific state data. + */ + void* data; + +}; + +/** + * Allocates a new audio stream. + */ +audio_stream* audio_stream_alloc(guac_client* client, + audio_encoder* encoder); + +/** + * Frees the given audio stream. + */ +void audio_stream_free(audio_stream* stream); + +/** + * Begins a new audio stream. + */ +void audio_stream_begin(audio_stream* stream, int rate, int channels, int bps); + +/** + * Ends the current audio stream. + */ +void audio_stream_end(audio_stream* stream); + +/** + * Writes PCM data to the given audio stream. + */ +void audio_stream_write_pcm(audio_stream* stream, + unsigned char* data, int length); + +/** + * Flushes the given audio stream. + */ +void audio_stream_flush(audio_stream* stream); + +/** + * Appends arbitrarily-encoded data to the encoded_data buffer + * within the given audio stream. + */ +void audio_stream_write_encoded(audio_stream* audio, + unsigned char* data, int length); + +#endif + diff --git a/protocols/rdp/include/client.h b/protocols/rdp/include/client.h new file mode 100644 index 00000000..78bbe2ad --- /dev/null +++ b/protocols/rdp/include/client.h @@ -0,0 +1,221 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _GUAC_RDP_CLIENT_H +#define _GUAC_RDP_CLIENT_H + +#include + +#include +#include + +#include + +#include "audio.h" +#include "rdp_keymap.h" + +/** + * The default RDP port. + */ +#define RDP_DEFAULT_PORT 3389 + +/** + * Default screen width, in pixels. + */ +#define RDP_DEFAULT_WIDTH 1024 + +/** + * Default screen height, in pixels. + */ +#define RDP_DEFAULT_HEIGHT 768 + +/** + * Default color depth, in bits. + */ +#define RDP_DEFAULT_DEPTH 16 + +/** + * Client data that will remain accessible through the guac_client. + * This should generally include data commonly used by Guacamole handlers. + */ +typedef struct rdp_guac_client_data { + + /** + * Pointer to the FreeRDP client instance handling the current connection. + */ + freerdp* rdp_inst; + + /** + * The settings structure associated with the FreeRDP client instance + * handling the current connection. + */ + rdpSettings* settings; + + /** + * Button mask containing the OR'd value of all currently pressed buttons. + */ + int mouse_button_mask; + + /** + * Cairo surface which will receive all TRANSPARENT glyphs. + */ + cairo_surface_t* trans_glyph_surface; + + /** + * Cairo surface which will receive all OPAQUE glyphs. + */ + cairo_surface_t* opaque_glyph_surface; + + /** + * The current Cairo surface which will receive all drawn glyphs, + * depending on whether we are currently drawing transparent or + * opaque glyphs. + */ + cairo_surface_t* glyph_surface; + + /** + * Cairo instance for drawing to the current glyph surface. + */ + cairo_t* glyph_cairo; + + /** + * The Guacamole layer that GDI operations should draw to. RDP messages + * exist which change this surface to allow drawing to occur off-screen. + */ + const guac_layer* current_surface; + + /** + * Whether graphical operations are restricted to a specific bounding + * rectangle. + */ + int bounded; + + /** + * The X coordinate of the upper-left corner of the bounding rectangle, + * if any. + */ + int bounds_left; + + /** + * The Y coordinate of the upper-left corner of the bounding rectangle, + * if any. + */ + int bounds_top; + + /** + * The X coordinate of the lower-right corner of the bounding rectangle, + * if any. + */ + int bounds_right; + + /** + * The Y coordinate of the lower-right corner of the bounding rectangle, + * if any. + */ + int bounds_bottom; + + /** + * The keymap to use when translating keysyms into scancodes or sequences + * of scancodes for RDP. + */ + guac_rdp_static_keymap keymap; + + /** + * The state of all keys, based on whether events for pressing/releasing + * particular keysyms have been received. This is necessary in order to + * determine which keys must be released/pressed when a particular + * keysym can only be typed through a sequence of scancodes (such as + * an Alt-code) because the server-side keymap does not support that + * keysym. + */ + guac_rdp_keysym_state_map keysym_state; + + /** + * The current text (NOT Unicode) clipboard contents. + */ + char* clipboard; + + /** + * Whether audio is enabled. + */ + int audio_enabled; + + /** + * Audio output, if any. + */ + audio_stream* audio; + + /** + * Lock which is locked and unlocked for each update. + */ + pthread_mutex_t update_lock; + + /** + * Lock which is locked and unlocked for each RDP message. + */ + pthread_mutex_t rdp_lock; + + pthread_mutexattr_t attributes; + +} rdp_guac_client_data; + +/** + * Client data that will remain accessible through the RDP context. + * This should generally include data commonly used by FreeRDP handlers. + */ +typedef struct rdp_freerdp_context { + + /** + * The parent context. THIS MUST BE THE FIRST ELEMENT. + */ + rdpContext _p; + + /** + * Pointer to the guac_client instance handling the RDP connection with + * this context. + */ + guac_client* client; + + /** + * Color conversion structure to be used to convert RDP images to PNGs. + */ + CLRCONV* clrconv; + +} rdp_freerdp_context; + +#endif + diff --git a/protocols/rdp/include/config.h b/protocols/rdp/include/config.h new file mode 100644 index 00000000..e69de29b diff --git a/protocols/rdp/include/default_pointer.h b/protocols/rdp/include/default_pointer.h new file mode 100644 index 00000000..b13ff488 --- /dev/null +++ b/protocols/rdp/include/default_pointer.h @@ -0,0 +1,76 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _GUAC_RDP_DEFAULT_POINTER_H +#define _GUAC_RDP_DEFAULT_POINTER_H + +#include +#include + +/** + * Width of the embedded mouse cursor graphic. + */ +extern const int guac_rdp_default_pointer_width; + +/** + * Height of the embedded mouse cursor graphic. + */ +extern const int guac_rdp_default_pointer_height; + +/** + * Number of bytes in each row of the embedded mouse cursor graphic. + */ +extern const int guac_rdp_default_pointer_stride; + +/** + * The Cairo grapic format of the mouse cursor graphic. + */ +extern const cairo_format_t guac_rdp_default_pointer_format; + +/** + * Embedded mouse cursor graphic. + */ +extern unsigned char guac_rdp_default_pointer[]; + +/** + * Set the cursor of the remote display to the embedded cursor graphic. + * + * @param client The guac_client to send the cursor to. + */ +void guac_rdp_set_default_pointer(guac_client* client); + +#endif diff --git a/protocols/rdp/include/guac_handlers.h b/protocols/rdp/include/guac_handlers.h new file mode 100644 index 00000000..5e9ccee3 --- /dev/null +++ b/protocols/rdp/include/guac_handlers.h @@ -0,0 +1,50 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _GUAC_RDP_GUAC_HANDLERS_H +#define _GUAC_RDP_GUAC_HANDLERS_H + +#include + +int rdp_guac_client_free_handler(guac_client* client); +int rdp_guac_client_handle_messages(guac_client* client); +int rdp_guac_client_mouse_handler(guac_client* client, int x, int y, int mask); +int rdp_guac_client_key_handler(guac_client* client, int keysym, int pressed); +int rdp_guac_client_clipboard_handler(guac_client* client, char* data); + +#endif + diff --git a/protocols/rdp/include/ogg_encoder.h b/protocols/rdp/include/ogg_encoder.h new file mode 100644 index 00000000..78bfa6e5 --- /dev/null +++ b/protocols/rdp/include/ogg_encoder.h @@ -0,0 +1,67 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __GUAC_OGG_ENCODER_H +#define __GUAC_OGG_ENCODER_H + +#include "audio.h" + +#include + +typedef struct ogg_encoder_state { + + /** + * Ogg state + */ + ogg_stream_state ogg_state; + ogg_page ogg_page; + ogg_packet ogg_packet; + + /** + * Vorbis state + */ + vorbis_info info; + vorbis_comment comment; + vorbis_dsp_state vorbis_state; + vorbis_block vorbis_block; + +} ogg_encoder_state; + +extern audio_encoder* ogg_encoder; + +#endif + diff --git a/protocols/rdp/include/rdp_bitmap.h b/protocols/rdp/include/rdp_bitmap.h new file mode 100644 index 00000000..b4cdd1e0 --- /dev/null +++ b/protocols/rdp/include/rdp_bitmap.h @@ -0,0 +1,71 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _GUAC_RDP_RDP_BITMAP_H +#define _GUAC_RDP_RDP_BITMAP_H + +#include + +#include + +typedef struct guac_rdp_bitmap { + + /** + * FreeRDP bitmap data - MUST GO FIRST. + */ + rdpBitmap bitmap; + + /** + * Guacamole layer containing cached image data. + */ + guac_layer* layer; + + /** + * The number of times a bitmap has been used. + */ + int used; + +} guac_rdp_bitmap; + +void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap); +void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap); +void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap, uint8* data, int width, int height, int bpp, int length, boolean compressed); +void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap); +void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap); +void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, boolean primary); + +#endif diff --git a/protocols/rdp/include/rdp_cliprdr.h b/protocols/rdp/include/rdp_cliprdr.h new file mode 100644 index 00000000..be838c4a --- /dev/null +++ b/protocols/rdp/include/rdp_cliprdr.h @@ -0,0 +1,56 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __GUAC_RDP_RDP_CLIPRDR_H +#define __GUAC_RDP_RDP_CLIPRDR_H + +#include + +void guac_rdp_process_cliprdr_event(guac_client* client, RDP_EVENT* event); +void guac_rdp_process_cb_monitor_ready(guac_client* client, RDP_EVENT* event); + +void guac_rdp_process_cb_format_list(guac_client* client, + RDP_CB_FORMAT_LIST_EVENT* event); + +void guac_rdp_process_cb_data_request(guac_client* client, + RDP_CB_DATA_REQUEST_EVENT* event); + +void guac_rdp_process_cb_data_response(guac_client* client, + RDP_CB_DATA_RESPONSE_EVENT* event); + +#endif + diff --git a/protocols/rdp/include/rdp_gdi.h b/protocols/rdp/include/rdp_gdi.h new file mode 100644 index 00000000..1f68352e --- /dev/null +++ b/protocols/rdp/include/rdp_gdi.h @@ -0,0 +1,56 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _GUAC_RDP_RDP_GDI_H +#define _GUAC_RDP_RDP_GDI_H + +#include +#include + +guac_composite_mode guac_rdp_rop3_transfer_function(guac_client* client, + int rop3); + +void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt); +void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt); +void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt); +void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt); +void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect); +void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette); +void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds); +void guac_rdp_gdi_end_paint(rdpContext* context); + +#endif diff --git a/protocols/rdp/include/rdp_glyph.h b/protocols/rdp/include/rdp_glyph.h new file mode 100644 index 00000000..2c9510a3 --- /dev/null +++ b/protocols/rdp/include/rdp_glyph.h @@ -0,0 +1,67 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _GUAC_RDP_RDP_GLYPH_H +#define _GUAC_RDP_RDP_GLYPH_H + +#include + +#include + +typedef struct guac_rdp_glyph { + + /** + * FreeRDP glyph data - MUST GO FIRST. + */ + rdpGlyph glyph; + + /** + * Cairo surface layer containing cached image data. + */ + cairo_surface_t* surface; + +} guac_rdp_glyph; + +void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph); +void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y); +void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph); +void guac_rdp_glyph_begindraw(rdpContext* context, + int x, int y, int width, int height, uint32 fgcolor, uint32 bgcolor); +void guac_rdp_glyph_enddraw(rdpContext* context, + int x, int y, int width, int height, uint32 fgcolor, uint32 bgcolor); + +#endif diff --git a/protocols/rdp/include/rdp_keymap.h b/protocols/rdp/include/rdp_keymap.h new file mode 100644 index 00000000..fcefdea1 --- /dev/null +++ b/protocols/rdp/include/rdp_keymap.h @@ -0,0 +1,230 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _GUAC_RDP_RDP_KEYMAP_H +#define _GUAC_RDP_RDP_KEYMAP_H + +#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H +#include +#else +#include +#endif + +/** + * Represents a keysym-to-scancode mapping for RDP, with extra information + * about the state of prerequisite keysyms. + */ +typedef struct guac_rdp_keysym_desc { + + /** + * The keysym being mapped. + */ + int keysym; + + /** + * The scancode this keysym maps to. + */ + int scancode; + + /** + * Required RDP-specific flags. + */ + int flags; + + /** + * Null-terminated list of keysyms which must be down for this keysym + * to be properly typed. + */ + const int* set_keysyms; + + /** + * Null-terminated list of keysyms which must be up for this keysym + * to be properly typed. + */ + const int* clear_keysyms; + +} guac_rdp_keysym_desc; + +/** + * Hierarchical keysym mapping + */ +typedef struct guac_rdp_keymap guac_rdp_keymap; +struct guac_rdp_keymap { + + /** + * The parent mapping this map will inherit its initial mapping from. + * Any other mapping information will add to or override the mapping + * inherited from the parent. + */ + const guac_rdp_keymap* parent; + + /** + * Descriptive name of this keymap + */ + const char* name; + + /** + * Null-terminated array of scancode mappings. + */ + const guac_rdp_keysym_desc* mapping; + + /** + * FreeRDP keyboard layout associated with this + * keymap. If this keymap is selected, this layout + * will be requested from the server. + */ + const uint32 freerdp_keyboard_layout; + +}; + +/** + * Static mapping from keysyms to scancodes. + */ +typedef guac_rdp_keysym_desc guac_rdp_static_keymap[0x200][0x100]; + +/** + * Mapping from keysym to current state + */ +typedef int guac_rdp_keysym_state_map[0x200][0x100]; + +/** + * US English keymap. + */ +extern const guac_rdp_keymap guac_rdp_keymap_en_us; + +/** + * German keymap. + */ +extern const guac_rdp_keymap guac_rdp_keymap_de_de; + +/** + * French keymap. + */ +extern const guac_rdp_keymap guac_rdp_keymap_fr_fr; + +/** + * Failsafe (Unicode events for all printable characters) keymap. + */ +extern const guac_rdp_keymap guac_rdp_keymap_failsafe; + +/** + * Common, base keymap for non-printable keys. + */ +extern const guac_rdp_keymap guac_rdp_keymap_base; + +/** + * Simple macro for determing whether a keysym can be stored (or retrieved) + * from any keymap. + */ +#define GUAC_RDP_KEYSYM_STORABLE(keysym) ((keysym) <= 0xFFFF || ((keysym) & 0xFFFF0000) == 0x01000000) + +/** + * Simple macro for referencing the mapped value of a scancode for a given + * keysym. The idea here is that a keysym of the form 0xABCD will map to + * mapping[0xAB][0xCD] while a keysym of the form 0x100ABCD will map to + * mapping[0x1AB][0xCD]. + */ +#define GUAC_RDP_KEYSYM_LOOKUP(keysym_mapping, keysym) ( \ + (keysym_mapping) \ + [(((keysym) & 0xFF00) >> 8) | ((keysym) >> 16)] \ + [(keysym) & 0xFF] \ + ) + +/** + * Keysym string containing only the left "shift" key. + */ +extern const int GUAC_KEYSYMS_SHIFT[]; + +/** + * Keysym string containing both "shift" keys. + */ +extern const int GUAC_KEYSYMS_ALL_SHIFT[]; + +/** + * Keysym string containing only the right "alt" key (AltGr). + */ +extern const int GUAC_KEYSYMS_ALTGR[]; + +/** + * Keysym string containing the right "alt" key (AltGr) and + * left shift. + */ +extern const int GUAC_KEYSYMS_SHIFT_ALTGR[]; + +/** + * Keysym string containing the right "alt" key (AltGr) and + * both shift keys. + */ +extern const int GUAC_KEYSYMS_ALL_SHIFT_ALTGR[]; + +/** + * Keysym string containing only the left "ctrl" key. + */ +extern const int GUAC_KEYSYMS_CTRL[]; + +/** + * Keysym string containing both "ctrl" keys. + */ +extern const int GUAC_KEYSYMS_ALL_CTRL[]; + +/** + * Keysym string containing only the left "alt" key. + */ +extern const int GUAC_KEYSYMS_ALT[]; + +/** + * Keysym string containing both "alt" keys. + */ +extern const int GUAC_KEYSYMS_ALL_ALT[]; + +/** + * Keysym string containing the left "alt" and left "ctrl" keys + */ +extern const int GUAC_KEYSYMS_CTRL_ALT[]; + +/** + * Keysym string containing all modifier keys. + */ +extern const int GUAC_KEYSYMS_ALL_MODIFIERS[]; + +/** + * NULL-terminated array of all keymaps. + */ +extern const guac_rdp_keymap* GUAC_KEYMAPS[]; + +#endif + diff --git a/protocols/rdp/include/rdp_pointer.h b/protocols/rdp/include/rdp_pointer.h new file mode 100644 index 00000000..77316284 --- /dev/null +++ b/protocols/rdp/include/rdp_pointer.h @@ -0,0 +1,66 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David PHAM-VAN Ulteo SAS - http://www.ulteo.com + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _GUAC_RDP_RDP_POINTER_H +#define _GUAC_RDP_RDP_POINTER_H + +#include + +#include + +typedef struct guac_rdp_pointer { + + /** + * FreeRDP pointer data - MUST GO FIRST. + */ + rdpPointer pointer; + + /** + * Guacamole layer containing cached image data. + */ + guac_layer* layer; + +} guac_rdp_pointer; + +void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer); +void guac_rdp_pointer_set(rdpContext* context, rdpPointer* pointer); +void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer); +void guac_rdp_pointer_set_null(rdpContext* context); +void guac_rdp_pointer_set_default(rdpContext* context); + +#endif diff --git a/protocols/rdp/include/wav_encoder.h b/protocols/rdp/include/wav_encoder.h new file mode 100644 index 00000000..ce575ae9 --- /dev/null +++ b/protocols/rdp/include/wav_encoder.h @@ -0,0 +1,144 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __GUAC_WAV_ENCODER_H +#define __GUAC_WAV_ENCODER_H + +#include "audio.h" + +typedef struct wav_encoder_riff_header { + + /** + * The RIFF chunk header, normally the string "RIFF". + */ + unsigned char chunk_id[4]; + + /** + * Size of the entire file, not including chunk_id or chunk_size. + */ + unsigned char chunk_size[4]; + + /** + * The format of this file, normally the string "WAVE". + */ + unsigned char chunk_format[4]; + +} wav_encoder_riff_header; + +typedef struct wav_encoder_fmt_header { + + /** + * ID of this subchunk. For the fmt subchunk, this should be "fmt ". + */ + unsigned char subchunk_id[4]; + + /** + * The size of the rest of this subchunk. For PCM, this will be 16. + */ + unsigned char subchunk_size[4]; + + /** + * Format of this subchunk. For PCM, this will be 1. + */ + unsigned char subchunk_format[2]; + + /** + * The number of channels in the PCM data. + */ + unsigned char subchunk_channels[2]; + + /** + * The sample rate of the PCM data. + */ + unsigned char subchunk_sample_rate[4]; + + /** + * The sample rate of the PCM data in bytes per second. + */ + unsigned char subchunk_byte_rate[4]; + + /** + * The number of bytes per sample. + */ + unsigned char subchunk_block_align[2]; + + /** + * The number of bits per sample. + */ + unsigned char subchunk_bps[2]; + +} wav_encoder_fmt_header; + +typedef struct wav_encoder_state { + + /** + * Arbitrary PCM data available for writing when the overall WAV is + * flushed. + */ + unsigned char* data_buffer; + + /** + * The number of bytes currently present in the data buffer. + */ + int used; + + /** + * The total number of bytes that can be written into the data buffer + * without requiring resizing. + */ + int length; + +} wav_encoder_state; + +typedef struct wav_encoder_data_header { + + /** + * ID of this subchunk. For the data subchunk, this should be "data". + */ + unsigned char subchunk_id[4]; + + /** + * The number of bytes in the PCM data. + */ + unsigned char subchunk_size[4]; + +} wav_encoder_data_header; + +extern audio_encoder* wav_encoder; + +#endif + diff --git a/protocols/rdp/m4/README b/protocols/rdp/m4/README new file mode 100644 index 00000000..c9c9cbc4 --- /dev/null +++ b/protocols/rdp/m4/README @@ -0,0 +1,12 @@ +This file exists such that the m4/ directory will be created when cloning the +git repository. + +The m4/ directory is not directly used by this project, but libtoolize +populates this directory with files, recommending that the directory be +included in the macro search path for aclocal. + +Because autoreconf runs aclocal before libtoolize, this directory will not +exist when autoreconf is run, triggering an error from aclocal. + +Creating this directory (and keeping this file in it as a placeholder) +prevents this error. diff --git a/protocols/rdp/src/audio.c b/protocols/rdp/src/audio.c new file mode 100644 index 00000000..c6c5e0db --- /dev/null +++ b/protocols/rdp/src/audio.c @@ -0,0 +1,179 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include +#include +#include +#include + +#include "audio.h" +#include "client.h" + +audio_stream* audio_stream_alloc(guac_client* client, audio_encoder* encoder) { + + /* Allocate stream */ + audio_stream* audio = (audio_stream*) malloc(sizeof(audio_stream)); + audio->client = client; + + /* Reset buffer stats */ + audio->used = 0; + audio->length = 0x40000; + + audio->encoded_data_used = 0; + audio->encoded_data_length = 0x40000; + + /* Allocate buffers */ + audio->pcm_data = malloc(audio->length); + audio->encoded_data = malloc(audio->encoded_data_length); + + /* Assign encoder */ + audio->encoder = encoder; + audio->stream = guac_client_alloc_stream(client); + + return audio; +} + +void audio_stream_begin(audio_stream* audio, int rate, int channels, int bps) { + + /* Load PCM properties */ + audio->rate = rate; + audio->channels = channels; + audio->bps = bps; + + /* Reset write counter */ + audio->pcm_bytes_written = 0; + + /* Call handler */ + audio->encoder->begin_handler(audio); + +} + +void audio_stream_end(audio_stream* audio) { + + double duration; + + rdp_guac_client_data* data = (rdp_guac_client_data*) audio->client->data; + + /* Flush stream and finish encoding */ + audio_stream_flush(audio); + audio->encoder->end_handler(audio); + + /* Calculate duration of PCM data */ + duration = ((double) (audio->pcm_bytes_written * 1000 * 8)) + / audio->rate / audio->channels / audio->bps; + + pthread_mutex_lock(&(data->update_lock)); + + /* Send audio */ + guac_protocol_send_audio(audio->stream->socket, + 0, audio->encoder->mimetype, + duration, audio->encoded_data, audio->encoded_data_used); + + pthread_mutex_unlock(&(data->update_lock)); + + /* Clear data */ + audio->encoded_data_used = 0; + +} + +void audio_stream_free(audio_stream* audio) { + free(audio->pcm_data); + free(audio); +} + +void audio_stream_write_pcm(audio_stream* audio, + unsigned char* data, int length) { + + /* Update counter */ + audio->pcm_bytes_written += length; + + /* Resize audio buffer if necessary */ + if (length > audio->length) { + + /* Resize to double provided length */ + audio->length = length*2; + audio->pcm_data = realloc(audio->pcm_data, audio->length); + + } + + /* Flush if necessary */ + if (audio->used + length > audio->length) + audio_stream_flush(audio); + + /* Append to buffer */ + memcpy(&(audio->pcm_data[audio->used]), data, length); + audio->used += length; + +} + +void audio_stream_flush(audio_stream* audio) { + + /* If data in buffer */ + if (audio->used != 0) { + + /* Write data */ + audio->encoder->write_handler(audio, + audio->pcm_data, audio->used); + + /* Reset buffer */ + audio->used = 0; + + } + +} + +void audio_stream_write_encoded(audio_stream* audio, + unsigned char* data, int length) { + + /* Resize audio buffer if necessary */ + if (audio->encoded_data_used + length > audio->encoded_data_length) { + + /* Increase to double concatenated size to accomodate */ + audio->encoded_data_length = (audio->encoded_data_length + length)*2; + audio->encoded_data = realloc(audio->encoded_data, + audio->encoded_data_length); + + } + + /* Append to buffer */ + memcpy(&(audio->encoded_data[audio->encoded_data_used]), data, length); + audio->encoded_data_used += length; + +} + diff --git a/protocols/rdp/src/client.c b/protocols/rdp/src/client.c new file mode 100644 index 00000000..c6f14227 --- /dev/null +++ b/protocols/rdp/src/client.c @@ -0,0 +1,602 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Hortman + * David PHAM-VAN Ulteo SAS - http://www.ulteo.com + * Laurent Meunier + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#define _XOPEN_SOURCE 500 + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "audio.h" +#include "wav_encoder.h" + +#ifdef ENABLE_OGG +#include "ogg_encoder.h" +#endif + +#include "client.h" +#include "guac_handlers.h" +#include "rdp_keymap.h" +#include "rdp_bitmap.h" +#include "rdp_glyph.h" +#include "rdp_pointer.h" +#include "rdp_gdi.h" +#include "default_pointer.h" + +/* Client plugin arguments */ +const char* GUAC_CLIENT_ARGS[] = { + "hostname", + "port", + "domain", + "username", + "password", + "width", + "height", + "initial-program", + "color-depth", + "disable-audio", + "console", + "console-audio", + "server-layout", + NULL +}; + +enum RDP_ARGS_IDX { + + IDX_HOSTNAME, + IDX_PORT, + IDX_DOMAIN, + IDX_USERNAME, + IDX_PASSWORD, + IDX_WIDTH, + IDX_HEIGHT, + IDX_INITIAL_PROGRAM, + IDX_COLOR_DEPTH, + IDX_DISABLE_AUDIO, + IDX_CONSOLE, + IDX_CONSOLE_AUDIO, + IDX_SERVER_LAYOUT, + RDP_ARGS_COUNT +}; + +int __guac_receive_channel_data(freerdp* rdp_inst, int channelId, uint8* data, int size, int flags, int total_size) { + return freerdp_channels_data(rdp_inst, channelId, data, size, flags, total_size); +} + +boolean rdp_freerdp_pre_connect(freerdp* instance) { + + rdpContext* context = instance->context; + guac_client* client = ((rdp_freerdp_context*) context)->client; + rdpChannels* channels = context->channels; + rdpBitmap* bitmap; + rdpGlyph* glyph; + rdpPointer* pointer; + rdpPrimaryUpdate* primary; + CLRCONV* clrconv; + int i; + + rdp_guac_client_data* guac_client_data = + (rdp_guac_client_data*) client->data; + + /* Load clipboard plugin */ + if (freerdp_channels_load_plugin(channels, instance->settings, + "cliprdr", NULL)) + guac_client_log_error(client, "Failed to load cliprdr plugin."); + + /* If audio enabled, choose an encoder */ + if (guac_client_data->audio_enabled) { + + /* Choose an encoding */ + for (i=0; client->info.audio_mimetypes[i] != NULL; i++) { + + const char* mimetype = client->info.audio_mimetypes[i]; + +#ifdef ENABLE_OGG + /* If Ogg is supported, done. */ + if (strcmp(mimetype, ogg_encoder->mimetype) == 0) { + guac_client_log_info(client, "Loading Ogg Vorbis encoder."); + guac_client_data->audio = audio_stream_alloc(client, + ogg_encoder); + break; + } +#endif + + /* If wav is supported, done. */ + if (strcmp(mimetype, wav_encoder->mimetype) == 0) { + guac_client_log_info(client, "Loading wav encoder."); + guac_client_data->audio = audio_stream_alloc(client, + wav_encoder); + break; + } + + } + + /* If an encoding is available, load the sound plugin */ + if (guac_client_data->audio != NULL) { + + /* Load sound plugin */ + if (freerdp_channels_load_plugin(channels, instance->settings, + "guac_rdpsnd", guac_client_data->audio)) + guac_client_log_error(client, + "Failed to load guac_rdpsnd plugin."); + + } + else + guac_client_log_info(client, + "No available audio encoding. Sound disabled."); + + } /* end if audio enabled */ + + /* Init color conversion structure */ + clrconv = xnew(CLRCONV); + clrconv->alpha = 1; + clrconv->invert = 0; + clrconv->rgb555 = 0; + clrconv->palette = xnew(rdpPalette); + ((rdp_freerdp_context*) context)->clrconv = clrconv; + + /* Init FreeRDP cache */ + instance->context->cache = cache_new(instance->settings); + + /* Set up bitmap handling */ + bitmap = xnew(rdpBitmap); + bitmap->size = sizeof(guac_rdp_bitmap); + bitmap->New = guac_rdp_bitmap_new; + bitmap->Free = guac_rdp_bitmap_free; + bitmap->Paint = guac_rdp_bitmap_paint; + bitmap->Decompress = guac_rdp_bitmap_decompress; + bitmap->SetSurface = guac_rdp_bitmap_setsurface; + graphics_register_bitmap(context->graphics, bitmap); + xfree(bitmap); + + /* Set up glyph handling */ + glyph = xnew(rdpGlyph); + glyph->size = sizeof(guac_rdp_glyph); + glyph->New = guac_rdp_glyph_new; + glyph->Free = guac_rdp_glyph_free; + glyph->Draw = guac_rdp_glyph_draw; + glyph->BeginDraw = guac_rdp_glyph_begindraw; + glyph->EndDraw = guac_rdp_glyph_enddraw; + graphics_register_glyph(context->graphics, glyph); + xfree(glyph); + + /* Set up pointer handling */ + pointer = xnew(rdpPointer); + pointer->size = sizeof(guac_rdp_pointer); + pointer->New = guac_rdp_pointer_new; + pointer->Free = guac_rdp_pointer_free; + pointer->Set = guac_rdp_pointer_set; +#ifdef HAVE_RDPPOINTER_SETNULL + pointer->SetNull = guac_rdp_pointer_set_null; +#endif +#ifdef HAVE_RDPPOINTER_SETDEFAULT + pointer->SetDefault = guac_rdp_pointer_set_default; +#endif + graphics_register_pointer(context->graphics, pointer); + xfree(pointer); + + /* Set up GDI */ + instance->update->EndPaint = guac_rdp_gdi_end_paint; + instance->update->Palette = guac_rdp_gdi_palette_update; + instance->update->SetBounds = guac_rdp_gdi_set_bounds; + + primary = instance->update->primary; + primary->DstBlt = guac_rdp_gdi_dstblt; + primary->PatBlt = guac_rdp_gdi_patblt; + primary->ScrBlt = guac_rdp_gdi_scrblt; + primary->MemBlt = guac_rdp_gdi_memblt; + primary->OpaqueRect = guac_rdp_gdi_opaquerect; + + pointer_cache_register_callbacks(instance->update); + glyph_cache_register_callbacks(instance->update); + brush_cache_register_callbacks(instance->update); + bitmap_cache_register_callbacks(instance->update); + offscreen_cache_register_callbacks(instance->update); + palette_cache_register_callbacks(instance->update); + + /* Init channels (pre-connect) */ + if (freerdp_channels_pre_connect(channels, instance)) { + guac_protocol_send_error(client->socket, "Error initializing RDP client channel manager"); + guac_socket_flush(client->socket); + return false; + } + + return true; + +} + +boolean rdp_freerdp_post_connect(freerdp* instance) { + + rdpContext* context = instance->context; + guac_client* client = ((rdp_freerdp_context*) context)->client; + rdpChannels* channels = instance->context->channels; + + /* Init channels (post-connect) */ + if (freerdp_channels_post_connect(channels, instance)) { + guac_protocol_send_error(client->socket, "Error initializing RDP client channel manager"); + guac_socket_flush(client->socket); + return false; + } + + /* Client handlers */ + client->free_handler = rdp_guac_client_free_handler; + client->handle_messages = rdp_guac_client_handle_messages; + client->mouse_handler = rdp_guac_client_mouse_handler; + client->key_handler = rdp_guac_client_key_handler; + client->clipboard_handler = rdp_guac_client_clipboard_handler; + + return true; + +} + +void rdp_freerdp_context_new(freerdp* instance, rdpContext* context) { + context->channels = freerdp_channels_new(); +} + +void rdp_freerdp_context_free(freerdp* instance, rdpContext* context) { + /* EMPTY */ +} + +void __guac_rdp_client_load_keymap(guac_client* client, + const guac_rdp_keymap* keymap) { + + rdp_guac_client_data* guac_client_data = + (rdp_guac_client_data*) client->data; + + /* Get mapping */ + const guac_rdp_keysym_desc* mapping = keymap->mapping; + + /* If parent exists, load parent first */ + if (keymap->parent != NULL) + __guac_rdp_client_load_keymap(client, keymap->parent); + + /* Log load */ + guac_client_log_info(client, "Loading keymap \"%s\"", keymap->name); + + /* Load mapping into keymap */ + while (mapping->keysym != 0) { + + /* Copy mapping */ + GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keymap, mapping->keysym) = + *mapping; + + /* Next keysym */ + mapping++; + + } + +} + +int guac_client_init(guac_client* client, int argc, char** argv) { + + rdp_guac_client_data* guac_client_data; + + freerdp* rdp_inst; + rdpSettings* settings; + + char* hostname; + int port = RDP_DEFAULT_PORT; + boolean bitmap_cache; + + /** + * Selected server-side keymap. Client will be assumed to also use this + * keymap. Keys will be sent to server based on client input on a + * best-effort basis. + */ + const guac_rdp_keymap* chosen_keymap; + + if (argc < RDP_ARGS_COUNT) { + + guac_protocol_send_error(client->socket, + "Wrong argument count received."); + guac_socket_flush(client->socket); + + guac_error = GUAC_STATUS_BAD_ARGUMENT; + guac_error_message = "Wrong argument count received"; + + return 1; + } + + /* If port specified, use it */ + if (argv[IDX_PORT][0] != '\0') + port = atoi(argv[IDX_PORT]); + + hostname = argv[IDX_HOSTNAME]; + + /* Allocate client data */ + guac_client_data = malloc(sizeof(rdp_guac_client_data)); + + /* Init client */ + freerdp_channels_global_init(); + rdp_inst = freerdp_new(); + rdp_inst->PreConnect = rdp_freerdp_pre_connect; + rdp_inst->PostConnect = rdp_freerdp_post_connect; + rdp_inst->ReceiveChannelData = __guac_receive_channel_data; + + /* Allocate FreeRDP context */ + rdp_inst->context_size = sizeof(rdp_freerdp_context); + rdp_inst->ContextNew = (pContextNew) rdp_freerdp_context_new; + rdp_inst->ContextFree = (pContextFree) rdp_freerdp_context_free; + freerdp_context_new(rdp_inst); + + /* Set settings */ + settings = rdp_inst->settings; + + /* Console */ + settings->console_session = (strcmp(argv[IDX_CONSOLE], "true") == 0); + settings->console_audio = (strcmp(argv[IDX_CONSOLE_AUDIO], "true") == 0); + + /* --no-auth */ + settings->authentication = false; + + /* --sec rdp */ + settings->rdp_security = true; + settings->tls_security = false; + settings->nla_security = false; + settings->encryption = true; + settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS; + settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE; + + /* Use optimal width unless overridden */ + settings->width = client->info.optimal_width; + if (argv[IDX_WIDTH][0] != '\0') + settings->width = atoi(argv[IDX_WIDTH]); + + /* Use default width if given width is invalid. */ + if (settings->width <= 0) { + settings->width = RDP_DEFAULT_WIDTH; + guac_client_log_error(client, + "Invalid width: \"%s\". Using default of %i.", + argv[IDX_WIDTH], settings->width); + } + + /* Round width up to nearest multiple of 4 */ + settings->width = (settings->width + 3) & ~0x3; + + /* Use optimal height unless overridden */ + settings->height = client->info.optimal_height; + if (argv[IDX_HEIGHT][0] != '\0') + settings->height = atoi(argv[IDX_HEIGHT]); + + /* Use default height if given height is invalid. */ + if (settings->height <= 0) { + settings->height = RDP_DEFAULT_HEIGHT; + guac_client_log_error(client, + "Invalid height: \"%s\". Using default of %i.", + argv[IDX_WIDTH], settings->height); + } + + /* Set hostname */ + settings->hostname = strdup(hostname); + settings->port = port; + settings->window_title = strdup(hostname); + + /* Domain */ + if (argv[IDX_DOMAIN][0] != '\0') + settings->domain = strdup(argv[IDX_DOMAIN]); + + /* Username */ + if (argv[IDX_USERNAME][0] != '\0') + settings->username = strdup(argv[IDX_USERNAME]); + + /* Password */ + if (argv[IDX_PASSWORD][0] != '\0') { + settings->password = strdup(argv[IDX_PASSWORD]); + settings->autologon = 1; + } + + /* Initial program */ + if (argv[IDX_INITIAL_PROGRAM][0] != '\0') + settings->shell = strdup(argv[IDX_INITIAL_PROGRAM]); + + /* Session color depth */ + settings->color_depth = RDP_DEFAULT_DEPTH; + if (argv[IDX_COLOR_DEPTH][0] != '\0') + settings->color_depth = atoi(argv[IDX_COLOR_DEPTH]); + + /* Use default depth if given depth is invalid. */ + if (settings->color_depth == 0) { + settings->color_depth = RDP_DEFAULT_DEPTH; + guac_client_log_error(client, + "Invalid color-depth: \"%s\". Using default of %i.", + argv[IDX_WIDTH], settings->color_depth); + } + + /* Audio enable/disable */ + guac_client_data->audio_enabled = + (strcmp(argv[IDX_DISABLE_AUDIO], "true") != 0); + + /* Order support */ + bitmap_cache = settings->bitmap_cache; + settings->os_major_type = OSMAJORTYPE_UNSPECIFIED; + settings->os_minor_type = OSMINORTYPE_UNSPECIFIED; + settings->order_support[NEG_DSTBLT_INDEX] = true; + settings->order_support[NEG_PATBLT_INDEX] = false; /* PATBLT not yet supported */ + settings->order_support[NEG_SCRBLT_INDEX] = true; + settings->order_support[NEG_OPAQUE_RECT_INDEX] = true; + settings->order_support[NEG_DRAWNINEGRID_INDEX] = false; + settings->order_support[NEG_MULTIDSTBLT_INDEX] = false; + settings->order_support[NEG_MULTIPATBLT_INDEX] = false; + settings->order_support[NEG_MULTISCRBLT_INDEX] = false; + settings->order_support[NEG_MULTIOPAQUERECT_INDEX] = false; + settings->order_support[NEG_MULTI_DRAWNINEGRID_INDEX] = false; + settings->order_support[NEG_LINETO_INDEX] = false; + settings->order_support[NEG_POLYLINE_INDEX] = false; + settings->order_support[NEG_MEMBLT_INDEX] = bitmap_cache; + settings->order_support[NEG_MEM3BLT_INDEX] = false; + settings->order_support[NEG_MEMBLT_V2_INDEX] = bitmap_cache; + settings->order_support[NEG_MEM3BLT_V2_INDEX] = false; + settings->order_support[NEG_SAVEBITMAP_INDEX] = false; + settings->order_support[NEG_GLYPH_INDEX_INDEX] = true; + settings->order_support[NEG_FAST_INDEX_INDEX] = true; + settings->order_support[NEG_FAST_GLYPH_INDEX] = true; + settings->order_support[NEG_POLYGON_SC_INDEX] = false; + settings->order_support[NEG_POLYGON_CB_INDEX] = false; + settings->order_support[NEG_ELLIPSE_SC_INDEX] = false; + settings->order_support[NEG_ELLIPSE_CB_INDEX] = false; + + /* Store client data */ + guac_client_data->rdp_inst = rdp_inst; + guac_client_data->bounded = false; + guac_client_data->mouse_button_mask = 0; + guac_client_data->current_surface = GUAC_DEFAULT_LAYER; + guac_client_data->clipboard = NULL; + guac_client_data->audio = NULL; + + /* Recursive attribute for locks */ + pthread_mutexattr_init(&(guac_client_data->attributes)); + pthread_mutexattr_settype(&(guac_client_data->attributes), + PTHREAD_MUTEX_RECURSIVE); + + /* Init update lock */ + pthread_mutex_init(&(guac_client_data->update_lock), + &(guac_client_data->attributes)); + + /* Init RDP lock */ + pthread_mutex_init(&(guac_client_data->rdp_lock), + &(guac_client_data->attributes)); + + /* Clear keysym state mapping and keymap */ + memset(guac_client_data->keysym_state, 0, + sizeof(guac_rdp_keysym_state_map)); + + memset(guac_client_data->keymap, 0, + sizeof(guac_rdp_static_keymap)); + + client->data = guac_client_data; + ((rdp_freerdp_context*) rdp_inst->context)->client = client; + + /* Pick keymap based on argument */ + if (argv[IDX_SERVER_LAYOUT][0] != '\0') { + + /* US English Qwerty */ + if (strcmp("en-us-qwerty", argv[IDX_SERVER_LAYOUT]) == 0) + chosen_keymap = &guac_rdp_keymap_en_us; + + /* German Qwertz */ + else if (strcmp("de-de-qwertz", argv[IDX_SERVER_LAYOUT]) == 0) + chosen_keymap = &guac_rdp_keymap_de_de; + + /* French Azerty */ + else if (strcmp("fr-fr-azerty", argv[IDX_SERVER_LAYOUT]) == 0) + chosen_keymap = &guac_rdp_keymap_fr_fr; + + /* Failsafe (Unicode) keymap */ + else if (strcmp("failsafe", argv[IDX_SERVER_LAYOUT]) == 0) + chosen_keymap = &guac_rdp_keymap_failsafe; + + /* If keymap unknown, resort to failsafe */ + else { + + guac_client_log_error(client, + "Unknown layout \"%s\". Using the failsafe layout instead.", + argv[IDX_SERVER_LAYOUT]); + + chosen_keymap = &guac_rdp_keymap_failsafe; + + } + + } + + /* If no keymap requested, assume US */ + else + chosen_keymap = &guac_rdp_keymap_en_us; + + /* Load keymap into client */ + __guac_rdp_client_load_keymap(client, chosen_keymap); + + /* Set server-side keymap */ + settings->kbd_layout = chosen_keymap->freerdp_keyboard_layout; + + /* Connect to RDP server */ + if (!freerdp_connect(rdp_inst)) { + + guac_protocol_send_error(client->socket, + "Error connecting to RDP server"); + guac_socket_flush(client->socket); + + guac_error = GUAC_STATUS_BAD_STATE; + guac_error_message = "Error connecting to RDP server"; + + return 1; + } + + /* Send connection name */ + guac_protocol_send_name(client->socket, settings->window_title); + + /* Send size */ + guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER, + settings->width, settings->height); + + /* Create glyph surfaces */ + guac_client_data->opaque_glyph_surface = cairo_image_surface_create( + CAIRO_FORMAT_RGB24, settings->width, settings->height); + + guac_client_data->trans_glyph_surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, settings->width, settings->height); + + /* Set default pointer */ + guac_rdp_set_default_pointer(client); + + /* Success */ + return 0; + +} + diff --git a/protocols/rdp/src/default_pointer.c b/protocols/rdp/src/default_pointer.c new file mode 100644 index 00000000..e38d7676 --- /dev/null +++ b/protocols/rdp/src/default_pointer.c @@ -0,0 +1,106 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include +#include + +/* Macros for prettying up the embedded image. */ +#define X 0x00,0x00,0x00,0xFF +#define O 0xFF,0xFF,0xFF,0xFF +#define _ 0x00,0x00,0x00,0x00 + +/* Dimensions */ +const int guac_rdp_default_pointer_width = 11; +const int guac_rdp_default_pointer_height = 16; + +/* Format */ +const cairo_format_t guac_rdp_default_pointer_format = CAIRO_FORMAT_ARGB32; +const int guac_rdp_default_pointer_stride = 44; + +/* Embedded pointer graphic */ +unsigned char guac_rdp_default_pointer[] = { + + O,_,_,_,_,_,_,_,_,_,_, + O,O,_,_,_,_,_,_,_,_,_, + O,X,O,_,_,_,_,_,_,_,_, + O,X,X,O,_,_,_,_,_,_,_, + O,X,X,X,O,_,_,_,_,_,_, + O,X,X,X,X,O,_,_,_,_,_, + O,X,X,X,X,X,O,_,_,_,_, + O,X,X,X,X,X,X,O,_,_,_, + O,X,X,X,X,X,X,X,O,_,_, + O,X,X,X,X,X,X,X,X,O,_, + O,X,X,X,X,X,O,O,O,O,O, + O,X,X,O,X,X,O,_,_,_,_, + O,X,O,_,O,X,X,O,_,_,_, + O,O,_,_,O,X,X,O,_,_,_, + O,_,_,_,_,O,X,X,O,_,_, + _,_,_,_,_,O,O,O,O,_,_ + +}; + + +void guac_rdp_set_default_pointer(guac_client* client) { + + guac_socket* socket = client->socket; + + /* Draw to buffer */ + guac_layer* cursor = guac_client_alloc_buffer(client); + + cairo_surface_t* graphic = cairo_image_surface_create_for_data( + guac_rdp_default_pointer, + guac_rdp_default_pointer_format, + guac_rdp_default_pointer_width, + guac_rdp_default_pointer_height, + guac_rdp_default_pointer_stride); + + guac_protocol_send_png(socket, GUAC_COMP_SRC, cursor, 0, 0, graphic); + cairo_surface_destroy(graphic); + + /* Set cursor */ + guac_protocol_send_cursor(socket, 0, 0, cursor, + 0, 0, + guac_rdp_default_pointer_width, + guac_rdp_default_pointer_height); + + /* Free buffer */ + guac_client_free_buffer(client, cursor); + +} + diff --git a/protocols/rdp/src/guac_handlers.c b/protocols/rdp/src/guac_handlers.c new file mode 100644 index 00000000..507c8fe5 --- /dev/null +++ b/protocols/rdp/src/guac_handlers.c @@ -0,0 +1,439 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Hortman + * Jocelyn DELALANDE Ulteo SAS - http://www.ulteo.com + * + * Portions created by Ulteo SAS employees are Copyright (C) 2012 Ulteo SAS + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "client.h" +#include "rdp_keymap.h" +#include "rdp_cliprdr.h" +#include "guac_handlers.h" + +void __guac_rdp_update_keysyms(guac_client* client, const int* keysym_string, int from, int to); +int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed); + + +int rdp_guac_client_free_handler(guac_client* client) { + + rdp_guac_client_data* guac_client_data = + (rdp_guac_client_data*) client->data; + + freerdp* rdp_inst = guac_client_data->rdp_inst; + rdpChannels* channels = rdp_inst->context->channels; + + /* Clean up RDP client */ + freerdp_channels_close(channels, rdp_inst); + freerdp_channels_free(channels); + freerdp_disconnect(rdp_inst); + freerdp_clrconv_free(((rdp_freerdp_context*) rdp_inst->context)->clrconv); + cache_free(rdp_inst->context->cache); + freerdp_free(rdp_inst); + + /* Free client data */ + cairo_surface_destroy(guac_client_data->opaque_glyph_surface); + cairo_surface_destroy(guac_client_data->trans_glyph_surface); + free(guac_client_data->clipboard); + free(guac_client_data); + + return 0; + +} + +int rdp_guac_client_handle_messages(guac_client* client) { + + rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; + freerdp* rdp_inst = guac_client_data->rdp_inst; + rdpChannels* channels = rdp_inst->context->channels; + + int index; + int max_fd, fd; + void* read_fds[32]; + void* write_fds[32]; + int read_count = 0; + int write_count = 0; + fd_set rfds, wfds; + RDP_EVENT* event; + + struct timeval timeout = { + .tv_sec = 0, + .tv_usec = 250000 + }; + + /* get rdp fds */ + if (!freerdp_get_fds(rdp_inst, read_fds, &read_count, write_fds, &write_count)) { + guac_error = GUAC_STATUS_BAD_STATE; + guac_error_message = "Unable to read RDP file descriptors"; + return 1; + } + + /* get channel fds */ + if (!freerdp_channels_get_fds(channels, rdp_inst, read_fds, &read_count, write_fds, &write_count)) { + guac_error = GUAC_STATUS_BAD_STATE; + guac_error_message = "Unable to read RDP channel file descriptors"; + return 1; + } + + /* Construct read fd_set */ + max_fd = 0; + FD_ZERO(&rfds); + for (index = 0; index < read_count; index++) { + fd = (int)(long) (read_fds[index]); + if (fd > max_fd) + max_fd = fd; + FD_SET(fd, &rfds); + } + + /* Construct write fd_set */ + FD_ZERO(&wfds); + for (index = 0; index < write_count; index++) { + fd = (int)(long) (write_fds[index]); + if (fd > max_fd) + max_fd = fd; + FD_SET(fd, &wfds); + } + + /* If no file descriptors, error */ + if (max_fd == 0) { + guac_error = GUAC_STATUS_BAD_STATE; + guac_error_message = "No file descriptors"; + return 1; + } + + /* Otherwise, wait for file descriptors given */ + if (select(max_fd + 1, &rfds, &wfds, NULL, &timeout) == -1) { + /* these are not really errors */ + if (!((errno == EAGAIN) || + (errno == EWOULDBLOCK) || + (errno == EINPROGRESS) || + (errno == EINTR))) /* signal occurred */ + { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Error waiting for file descriptor"; + return 1; + } + } + + pthread_mutex_lock(&(guac_client_data->rdp_lock)); + + /* Check the libfreerdp fds */ + if (!freerdp_check_fds(rdp_inst)) { + guac_error = GUAC_STATUS_BAD_STATE; + guac_error_message = "Error handling RDP file descriptors"; + return 1; + } + + /* Check channel fds */ + if (!freerdp_channels_check_fds(channels, rdp_inst)) { + guac_error = GUAC_STATUS_BAD_STATE; + guac_error_message = "Error handling RDP channel file descriptors"; + return 1; + } + + /* Check for channel events */ + event = freerdp_channels_pop_event(channels); + if (event) { + + /* Handle clipboard events */ + if (event->event_class == RDP_EVENT_CLASS_CLIPRDR) + guac_rdp_process_cliprdr_event(client, event); + + freerdp_event_free(event); + + } + + /* Handle RDP disconnect */ + if (freerdp_shall_disconnect(rdp_inst)) { + guac_error = GUAC_STATUS_NO_INPUT; + guac_error_message = "RDP server closed connection"; + return 1; + } + + pthread_mutex_unlock(&(guac_client_data->rdp_lock)); + + /* Flush any audio */ + if (guac_client_data->audio != NULL) { + pthread_mutex_lock(&(guac_client_data->update_lock)); + guac_socket_flush(guac_client_data->audio->stream->socket); + pthread_mutex_unlock(&(guac_client_data->update_lock)); + } + + /* Success */ + return 0; + +} + +int rdp_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { + + rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; + freerdp* rdp_inst = guac_client_data->rdp_inst; + + pthread_mutex_lock(&(guac_client_data->rdp_lock)); + + /* If button mask unchanged, just send move event */ + if (mask == guac_client_data->mouse_button_mask) + rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_MOVE, x, y); + + /* Otherwise, send events describing button change */ + else { + + /* Mouse buttons which have JUST become released */ + int released_mask = guac_client_data->mouse_button_mask & ~mask; + + /* Mouse buttons which have JUST become pressed */ + int pressed_mask = ~guac_client_data->mouse_button_mask & mask; + + /* Release event */ + if (released_mask & 0x07) { + + /* Calculate flags */ + int flags = 0; + if (released_mask & 0x01) flags |= PTR_FLAGS_BUTTON1; + if (released_mask & 0x02) flags |= PTR_FLAGS_BUTTON3; + if (released_mask & 0x04) flags |= PTR_FLAGS_BUTTON2; + + rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y); + + } + + /* Press event */ + if (pressed_mask & 0x07) { + + /* Calculate flags */ + int flags = PTR_FLAGS_DOWN; + if (pressed_mask & 0x01) flags |= PTR_FLAGS_BUTTON1; + if (pressed_mask & 0x02) flags |= PTR_FLAGS_BUTTON3; + if (pressed_mask & 0x04) flags |= PTR_FLAGS_BUTTON2; + if (pressed_mask & 0x08) flags |= PTR_FLAGS_WHEEL | 0x78; + if (pressed_mask & 0x10) flags |= PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88; + + /* Send event */ + rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y); + + } + + /* Scroll event */ + if (pressed_mask & 0x18) { + + /* Down */ + if (pressed_mask & 0x08) + rdp_inst->input->MouseEvent( + rdp_inst->input, + PTR_FLAGS_WHEEL | 0x78, + x, y); + + /* Up */ + if (pressed_mask & 0x10) + rdp_inst->input->MouseEvent( + rdp_inst->input, + PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88, + x, y); + + } + + + guac_client_data->mouse_button_mask = mask; + } + + pthread_mutex_unlock(&(guac_client_data->rdp_lock)); + + return 0; +} + + +int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) { + + rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; + freerdp* rdp_inst = guac_client_data->rdp_inst; + + /* If keysym can be in lookup table */ + if (GUAC_RDP_KEYSYM_STORABLE(keysym)) { + + /* Look up scancode mapping */ + const guac_rdp_keysym_desc* keysym_desc = + &GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keymap, keysym); + + /* If defined, send event */ + if (keysym_desc->scancode != 0) { + + pthread_mutex_lock(&(guac_client_data->rdp_lock)); + + /* If defined, send any prerequesite keys that must be set */ + if (keysym_desc->set_keysyms != NULL) + __guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 1); + + /* If defined, release any keys that must be cleared */ + if (keysym_desc->clear_keysyms != NULL) + __guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 0); + + /* Send actual key */ + rdp_inst->input->KeyboardEvent( + rdp_inst->input, + keysym_desc->flags + | (pressed ? KBD_FLAGS_DOWN : KBD_FLAGS_RELEASE), + keysym_desc->scancode); + + /* If defined, release any keys that were originally released */ + if (keysym_desc->set_keysyms != NULL) + __guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 0); + + /* If defined, send any keys that were originally set */ + if (keysym_desc->clear_keysyms != NULL) + __guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 1); + + pthread_mutex_unlock(&(guac_client_data->rdp_lock)); + + return 0; + + } + } + + /* Fall back to unicode events if undefined inside current keymap */ + + /* Only send when key pressed - Unicode events do not have + * DOWN/RELEASE flags */ + if (pressed) { + + /* Translate keysym into codepoint */ + int codepoint; + if (keysym <= 0xFF) + codepoint = keysym; + else if (keysym >= 0x1000000) + codepoint = keysym & 0xFFFFFF; + else { + guac_client_log_info(client, + "Unmapped keysym has no equivalent unicode " + "value: 0x%x", keysym); + return 0; + } + + pthread_mutex_lock(&(guac_client_data->rdp_lock)); + + /* Send Unicode event */ + rdp_inst->input->UnicodeKeyboardEvent( + rdp_inst->input, + 0, codepoint); + + pthread_mutex_unlock(&(guac_client_data->rdp_lock)); + + } + + return 0; +} + +void __guac_rdp_update_keysyms(guac_client* client, const int* keysym_string, int from, int to) { + + rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; + int keysym; + + /* Send all keysyms in string, NULL terminated */ + while ((keysym = *keysym_string) != 0) { + + /* Get current keysym state */ + int current_state = GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keysym_state, keysym); + + /* If key is currently in given state, send event for changing it to specified "to" state */ + if (current_state == from) + __guac_rdp_send_keysym(client, *keysym_string, to); + + /* Next keysym */ + keysym_string++; + + } + +} + +int rdp_guac_client_key_handler(guac_client* client, int keysym, int pressed) { + + rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; + + /* Update keysym state */ + if (GUAC_RDP_KEYSYM_STORABLE(keysym)) + GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keysym_state, keysym) = pressed; + + return __guac_rdp_send_keysym(client, keysym, pressed); + +} + +int rdp_guac_client_clipboard_handler(guac_client* client, char* data) { + + rdpChannels* channels = + ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels; + + RDP_CB_FORMAT_LIST_EVENT* format_list = + (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new( + RDP_EVENT_CLASS_CLIPRDR, + RDP_EVENT_TYPE_CB_FORMAT_LIST, + NULL, NULL); + + /* Free existing data */ + free(((rdp_guac_client_data*) client->data)->clipboard); + + /* Store data in client */ + ((rdp_guac_client_data*) client->data)->clipboard = strdup(data); + + /* Notify server that text data is now available */ + format_list->formats = (uint32*) malloc(sizeof(uint32)); + format_list->formats[0] = CB_FORMAT_TEXT; + format_list->num_formats = 1; + + freerdp_channels_send_event(channels, (RDP_EVENT*) format_list); + + return 0; + +} + diff --git a/protocols/rdp/src/ogg_encoder.c b/protocols/rdp/src/ogg_encoder.c new file mode 100644 index 00000000..8c6f97d2 --- /dev/null +++ b/protocols/rdp/src/ogg_encoder.c @@ -0,0 +1,211 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include + +#include +#include + +#include + +#include "audio.h" +#include "ogg_encoder.h" + +void ogg_encoder_begin_handler(audio_stream* audio) { + + /* Allocate stream state */ + ogg_encoder_state* state = (ogg_encoder_state*) + malloc(sizeof(ogg_encoder_state)); + + /* Init state */ + vorbis_info_init(&(state->info)); + vorbis_encode_init_vbr(&(state->info), audio->channels, audio->rate, 0.4); + + vorbis_analysis_init(&(state->vorbis_state), &(state->info)); + vorbis_block_init(&(state->vorbis_state), &(state->vorbis_block)); + + vorbis_comment_init(&(state->comment)); + vorbis_comment_add_tag(&(state->comment), "ENCODER", "libguac-client-rdp"); + + ogg_stream_init(&(state->ogg_state), rand()); + + /* Write headers */ + { + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout( + &(state->vorbis_state), + &(state->comment), + &header, &header_comm, &header_code); + + ogg_stream_packetin(&(state->ogg_state), &header); + ogg_stream_packetin(&(state->ogg_state), &header_comm); + ogg_stream_packetin(&(state->ogg_state), &header_code); + + /* For each packet */ + while (ogg_stream_flush(&(state->ogg_state), &(state->ogg_page)) != 0) { + + /* Write packet header */ + audio_stream_write_encoded(audio, + state->ogg_page.header, + state->ogg_page.header_len); + + /* Write packet body */ + audio_stream_write_encoded(audio, + state->ogg_page.body, + state->ogg_page.body_len); + } + + } + + audio->data = state; + +} + +void ogg_encoder_write_blocks(audio_stream* audio) { + + /* Get state */ + ogg_encoder_state* state = (ogg_encoder_state*) audio->data; + + while (vorbis_analysis_blockout(&(state->vorbis_state), + &(state->vorbis_block)) == 1) { + + /* Analyze */ + vorbis_analysis(&(state->vorbis_block), NULL); + vorbis_bitrate_addblock(&(state->vorbis_block)); + + /* Flush Ogg pages */ + while (vorbis_bitrate_flushpacket(&(state->vorbis_state), + &(state->ogg_packet))) { + + /* Weld packet into bitstream */ + ogg_stream_packetin(&(state->ogg_state), &(state->ogg_packet)); + + /* Write out pages */ + while (ogg_stream_pageout(&(state->ogg_state), + &(state->ogg_page)) != 0) { + + /* Write packet header */ + audio_stream_write_encoded(audio, + state->ogg_page.header, + state->ogg_page.header_len); + + /* Write packet body */ + audio_stream_write_encoded(audio, + state->ogg_page.body, + state->ogg_page.body_len); + + if (ogg_page_eos(&(state->ogg_page))) + break; + + } + + } + + } + +} + +void ogg_encoder_end_handler(audio_stream* audio) { + + /* Get state */ + ogg_encoder_state* state = (ogg_encoder_state*) audio->data; + + /* Write end-of-stream */ + vorbis_analysis_wrote(&(state->vorbis_state), 0); + ogg_encoder_write_blocks(audio); + + /* Clean up encoder */ + ogg_stream_clear(&(state->ogg_state)); + vorbis_block_clear(&(state->vorbis_block)); + vorbis_dsp_clear(&(state->vorbis_state)); + vorbis_comment_clear(&(state->comment)); + vorbis_info_clear(&(state->info)); + + /* Free stream state */ + free(audio->data); + +} + +void ogg_encoder_write_handler(audio_stream* audio, + unsigned char* pcm_data, int length) { + + /* Get state */ + ogg_encoder_state* state = (ogg_encoder_state*) audio->data; + + /* Calculate samples */ + int samples = length / audio->channels * 8 / audio->bps; + int i; + + /* Get buffer */ + float** buffer = vorbis_analysis_buffer(&(state->vorbis_state), samples); + + signed char* readbuffer = (signed char*) pcm_data; + + for (i=0; ivorbis_state), samples); + + /* Write data */ + ogg_encoder_write_blocks(audio); + +} + +/* Encoder handlers */ +audio_encoder _ogg_encoder = { + .mimetype = "audio/ogg", + .begin_handler = ogg_encoder_begin_handler, + .write_handler = ogg_encoder_write_handler, + .end_handler = ogg_encoder_end_handler +}; + +/* Actual encoder */ +audio_encoder* ogg_encoder = &_ogg_encoder; + diff --git a/protocols/rdp/src/rdp_bitmap.c b/protocols/rdp/src/rdp_bitmap.c new file mode 100644 index 00000000..ca11e2e9 --- /dev/null +++ b/protocols/rdp/src/rdp_bitmap.c @@ -0,0 +1,216 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Hortman + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "client.h" +#include "rdp_bitmap.h" + +void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + guac_socket* socket = client->socket; + + /* Allocate buffer */ + guac_layer* buffer = guac_client_alloc_buffer(client); + + /* Cache image data if present */ + if (bitmap->data != NULL) { + + rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; + pthread_mutex_lock(&(data->update_lock)); + + /* Create surface from image data */ + cairo_surface_t* surface = cairo_image_surface_create_for_data( + bitmap->data, CAIRO_FORMAT_RGB24, + bitmap->width, bitmap->height, 4*bitmap->width); + + /* Send surface to buffer */ + guac_protocol_send_png(socket, + GUAC_COMP_SRC, buffer, 0, 0, surface); + + /* Free surface */ + cairo_surface_destroy(surface); + + pthread_mutex_unlock(&(data->update_lock)); + } + + /* Store buffer reference in bitmap */ + ((guac_rdp_bitmap*) bitmap)->layer = buffer; + +} + + +void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) { + + /* Convert image data if present */ + if (bitmap->data != NULL) { + + /* Convert image data to 32-bit RGB */ + unsigned char* image_buffer = freerdp_image_convert(bitmap->data, NULL, + bitmap->width, bitmap->height, + context->instance->settings->color_depth, + 32, ((rdp_freerdp_context*) context)->clrconv); + + /* Free existing image, if any */ + if (image_buffer != bitmap->data) + free(bitmap->data); + + /* Store converted image in bitmap */ + bitmap->data = image_buffer; + + } + + /* No corresponding layer yet - caching is deferred. */ + ((guac_rdp_bitmap*) bitmap)->layer = NULL; + + /* Start at zero usage */ + ((guac_rdp_bitmap*) bitmap)->used = 0; + +} + +void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + guac_socket* socket = client->socket; + + int width = bitmap->right - bitmap->left + 1; + int height = bitmap->bottom - bitmap->top + 1; + + rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; + pthread_mutex_lock(&(data->update_lock)); + + /* If not cached, cache if necessary */ + if (((guac_rdp_bitmap*) bitmap)->layer == NULL + && ((guac_rdp_bitmap*) bitmap)->used >= 1) + guac_rdp_cache_bitmap(context, bitmap); + + /* If cached, retrieve from cache */ + if (((guac_rdp_bitmap*) bitmap)->layer != NULL) + guac_protocol_send_copy(socket, + ((guac_rdp_bitmap*) bitmap)->layer, + 0, 0, width, height, + GUAC_COMP_OVER, + GUAC_DEFAULT_LAYER, bitmap->left, bitmap->top); + + /* Otherwise, draw with stored image data */ + else if (bitmap->data != NULL) { + + /* Create surface from image data */ + cairo_surface_t* surface = cairo_image_surface_create_for_data( + bitmap->data, CAIRO_FORMAT_RGB24, + width, height, 4*bitmap->width); + + /* Send surface to buffer */ + guac_protocol_send_png(socket, + GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, + bitmap->left, bitmap->top, surface); + + /* Free surface */ + cairo_surface_destroy(surface); + + } + + /* Increment usage counter */ + ((guac_rdp_bitmap*) bitmap)->used++; + + pthread_mutex_unlock(&(data->update_lock)); +} + +void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) { + guac_client* client = ((rdp_freerdp_context*) context)->client; + + /* If cached, free buffer */ + if (((guac_rdp_bitmap*) bitmap)->layer != NULL) + guac_client_free_buffer(client, ((guac_rdp_bitmap*) bitmap)->layer); + +} + +void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, boolean primary) { + guac_client* client = ((rdp_freerdp_context*) context)->client; + + if (primary) + ((rdp_guac_client_data*) client->data)->current_surface + = GUAC_DEFAULT_LAYER; + + else { + + /* If not available as a surface, make available. */ + if (((guac_rdp_bitmap*) bitmap)->layer == NULL) + guac_rdp_cache_bitmap(context, bitmap); + + ((rdp_guac_client_data*) client->data)->current_surface + = ((guac_rdp_bitmap*) bitmap)->layer; + + } + +} + +void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap, uint8* data, int width, int height, int bpp, int length, boolean compressed) { + + int size = width * height * (bpp + 7) / 8; + + if (bitmap->data == NULL) + bitmap->data = (uint8*) xmalloc(size); + else + bitmap->data = (uint8*) xrealloc(bitmap->data, size); + + if (compressed) + bitmap_decompress(data, bitmap->data, width, height, length, bpp, bpp); + else + freerdp_image_flip(data, bitmap->data, width, height, bpp); + + bitmap->compressed = false; + bitmap->length = size; + bitmap->bpp = bpp; + +} + diff --git a/protocols/rdp/src/rdp_cliprdr.c b/protocols/rdp/src/rdp_cliprdr.c new file mode 100644 index 00000000..168e3dd0 --- /dev/null +++ b/protocols/rdp/src/rdp_cliprdr.c @@ -0,0 +1,204 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#include +#include +#include +#include + +#include +#include + +#include "client.h" +#include "rdp_cliprdr.h" + +void guac_rdp_process_cliprdr_event(guac_client* client, RDP_EVENT* event) { + + switch (event->event_type) { + + case RDP_EVENT_TYPE_CB_MONITOR_READY: + guac_rdp_process_cb_monitor_ready(client, event); + break; + + case RDP_EVENT_TYPE_CB_FORMAT_LIST: + guac_rdp_process_cb_format_list(client, + (RDP_CB_FORMAT_LIST_EVENT*) event); + break; + + case RDP_EVENT_TYPE_CB_DATA_REQUEST: + guac_rdp_process_cb_data_request(client, + (RDP_CB_DATA_REQUEST_EVENT*) event); + break; + + case RDP_EVENT_TYPE_CB_DATA_RESPONSE: + guac_rdp_process_cb_data_response(client, + (RDP_CB_DATA_RESPONSE_EVENT*) event); + break; + + default: + guac_client_log_info(client, + "Unknown cliprdr event type: 0x%x", + event->event_type); + } + +} + +void guac_rdp_process_cb_monitor_ready(guac_client* client, RDP_EVENT* event) { + + rdpChannels* channels = + ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels; + + RDP_CB_FORMAT_LIST_EVENT* format_list = + (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new( + RDP_EVENT_CLASS_CLIPRDR, + RDP_EVENT_TYPE_CB_FORMAT_LIST, + NULL, NULL); + + /* Received notification of clipboard support. */ + + /* Respond with supported format list */ + format_list->formats = (uint32*) malloc(sizeof(uint32)); + format_list->formats[0] = CB_FORMAT_TEXT; + format_list->num_formats = 1; + + freerdp_channels_send_event(channels, (RDP_EVENT*) format_list); + +} + +void guac_rdp_process_cb_format_list(guac_client* client, + RDP_CB_FORMAT_LIST_EVENT* event) { + + rdpChannels* channels = + ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels; + + /* Received notification of available data */ + + int i; + for (i=0; inum_formats; i++) { + + /* If plain text available, request it */ + if (event->formats[i] == CB_FORMAT_TEXT) { + + /* Create new data request */ + RDP_CB_DATA_REQUEST_EVENT* data_request = + (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new( + RDP_EVENT_CLASS_CLIPRDR, + RDP_EVENT_TYPE_CB_DATA_REQUEST, + NULL, NULL); + + /* We want plain text */ + data_request->format = CB_FORMAT_TEXT; + + /* Send request */ + freerdp_channels_send_event(channels, (RDP_EVENT*) data_request); + return; + + } + + } + + /* Otherwise, no supported data available */ + guac_client_log_info(client, "Ignoring unsupported clipboard data"); + +} + +void guac_rdp_process_cb_data_request(guac_client* client, + RDP_CB_DATA_REQUEST_EVENT* event) { + + rdpChannels* channels = + ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels; + + /* If text requested, send clipboard text contents */ + if (event->format == CB_FORMAT_TEXT) { + + /* Get clipboard data */ + const char* clipboard = + ((rdp_guac_client_data*) client->data)->clipboard; + + /* Create new data response */ + RDP_CB_DATA_RESPONSE_EVENT* data_response = + (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new( + RDP_EVENT_CLASS_CLIPRDR, + RDP_EVENT_TYPE_CB_DATA_RESPONSE, + NULL, NULL); + + /* Set data and length */ + if (clipboard != NULL) { + data_response->data = (uint8*) strdup(clipboard); + data_response->size = strlen(clipboard) + 1; + } + else { + data_response->data = (uint8*) strdup(""); + data_response->size = 1; + } + + /* Send response */ + freerdp_channels_send_event(channels, (RDP_EVENT*) data_response); + + } + + /* Otherwise ... failure */ + else + guac_client_log_error(client, + "Server requested unsupported clipboard data type"); + +} + +void guac_rdp_process_cb_data_response(guac_client* client, + RDP_CB_DATA_RESPONSE_EVENT* event) { + + /* Received clipboard data */ + if (event->data[event->size - 1] == '\0') { + + /* Free existing data */ + free(((rdp_guac_client_data*) client->data)->clipboard); + + /* Store clipboard data */ + ((rdp_guac_client_data*) client->data)->clipboard = + strdup((char*) event->data); + + /* Send clipboard data */ + guac_protocol_send_clipboard(client->socket, (char*) event->data); + + } + else + guac_client_log_error(client, + "Clipboard data missing null terminator"); + +} + diff --git a/protocols/rdp/src/rdp_gdi.c b/protocols/rdp/src/rdp_gdi.c new file mode 100644 index 00000000..8fc587e1 --- /dev/null +++ b/protocols/rdp/src/rdp_gdi.c @@ -0,0 +1,522 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Hortman + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include + +#include + +#include "client.h" +#include "rdp_bitmap.h" + +static void __guac_rdp_clip_rect(rdp_guac_client_data* data, int* x, int* y, int* w, int* h) { + + if (data->bounded) { + + /* Get rect coordinates */ + int clipped_left = *x; + int clipped_top = *y; + int clipped_right = clipped_left + *w - 1; + int clipped_bottom = clipped_top + *h - 1; + + /* Clip left */ + if (clipped_left < data->bounds_left) clipped_left = data->bounds_left; + else if (clipped_left > data->bounds_right) clipped_left = data->bounds_right; + + /* Clip right */ + if (clipped_right < data->bounds_left) clipped_right = data->bounds_left; + else if (clipped_right > data->bounds_right) clipped_right = data->bounds_right; + + /* Clip top */ + if (clipped_top < data->bounds_top) clipped_top = data->bounds_top; + else if (clipped_top > data->bounds_bottom) clipped_top = data->bounds_bottom; + + /* Clip bottom */ + if (clipped_bottom < data->bounds_top) clipped_bottom = data->bounds_top; + else if (clipped_bottom > data->bounds_bottom) clipped_bottom = data->bounds_bottom; + + /* Store new rect dimensions */ + *x = clipped_left; + *y = clipped_top; + *w = clipped_right - clipped_left + 1; + *h = clipped_bottom - clipped_top + 1; + + } + +} + +guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client, + int rop3) { + + /* Translate supported ROP3 opcodes into composite modes */ + switch (rop3) { + + /* "DSon" !(src | dest) */ + case 0x11: return GUAC_TRANSFER_BINARY_NOR; + + /* "DSna" !src & dest */ + case 0x22: return GUAC_TRANSFER_BINARY_NSRC_AND; + + /* "Sn" !src */ + case 0x33: return GUAC_TRANSFER_BINARY_NSRC; + + /* "SDna" (src & !dest) */ + case 0x44: return GUAC_TRANSFER_BINARY_NDEST_AND; + + /* "Dn" !dest */ + case 0x55: return GUAC_TRANSFER_BINARY_NDEST; + + /* "SRCINVERT" (src ^ dest) */ + case 0x66: return GUAC_TRANSFER_BINARY_XOR; + + /* "DSan" !(src & dest) */ + case 0x77: return GUAC_TRANSFER_BINARY_NAND; + + /* "SRCAND" (src & dest) */ + case 0x88: return GUAC_TRANSFER_BINARY_AND; + + /* "DSxn" !(src ^ dest) */ + case 0x99: return GUAC_TRANSFER_BINARY_XNOR; + + /* "MERGEPAINT" (!src | dest)*/ + case 0xBB: return GUAC_TRANSFER_BINARY_NSRC_OR; + + /* "SDno" (src | !dest) */ + case 0xDD: return GUAC_TRANSFER_BINARY_NDEST_OR; + + /* "SRCPAINT" (src | dest) */ + case 0xEE: return GUAC_TRANSFER_BINARY_OR; + + /* 0x00 = "BLACKNESS" (0) */ + /* 0xAA = "NOP" (dest) */ + /* 0xCC = "SRCCOPY" (src) */ + /* 0xFF = "WHITENESS" (1) */ + + } + + /* Log warning if ROP3 opcode not supported */ + guac_client_log_info (client, "guac_rdp_rop3_transfer_function: " + "UNSUPPORTED opcode = 0x%02X", rop3); + + /* Default to BINARY_SRC */ + return GUAC_TRANSFER_BINARY_SRC; + +} + +void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface; + + int x = dstblt->nLeftRect; + int y = dstblt->nTopRect; + int w = dstblt->nWidth; + int h = dstblt->nHeight; + + rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; + pthread_mutex_lock(&(data->update_lock)); + + /* Clip operation to bounds */ + __guac_rdp_clip_rect(data, &x, &y, &w, &h); + + switch (dstblt->bRop) { + + /* Blackness */ + case 0: + + /* Send black rectangle */ + guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); + + guac_protocol_send_cfill(client->socket, + GUAC_COMP_OVER, current_layer, + 0, 0, 0, 255); + + break; + + /* DSTINVERT */ + case 0x55: + + /* Invert */ + guac_protocol_send_transfer(client->socket, + current_layer, x, y, w, h, + GUAC_TRANSFER_BINARY_NDEST, + current_layer, x, y); + + break; + + /* NOP */ + case 0xAA: + break; + + /* Whiteness */ + case 0xFF: + guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); + + guac_protocol_send_cfill(client->socket, + GUAC_COMP_OVER, current_layer, + 0xFF, 0xFF, 0xFF, 0xFF); + break; + + /* Unsupported ROP3 */ + default: + guac_client_log_info(client, + "guac_rdp_gdi_dstblt(rop3=0x%x)", dstblt->bRop); + + } + + pthread_mutex_unlock(&(data->update_lock)); + +} + + +void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) { + + /* + * Note that this is not a full implementation of PATBLT. This is a + * fallback implementation which only renders a solid block of background + * color using the specified ROP3 operation, ignoring whatever brush + * was actually specified. + * + * As libguac-client-rdp explicitly tells the server not to send PATBLT, + * well-behaved RDP servers will not use this operation at all, while + * others will at least have a fallback. + */ + + /* Get client and current layer */ + guac_client* client = ((rdp_freerdp_context*) context)->client; + const guac_layer* current_layer = + ((rdp_guac_client_data*) client->data)->current_surface; + + int x = patblt->nLeftRect; + int y = patblt->nTopRect; + int w = patblt->nWidth; + int h = patblt->nHeight; + + rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; + + /* Layer for actual transfer */ + guac_layer* buffer; + + /* + * Warn that rendering is a fallback, as the server should not be sending + * this order. + */ + guac_client_log_info(client, "Using fallback PATBLT (server is ignoring " + "negotiated client capabilities)"); + + /* Clip operation to bounds */ + __guac_rdp_clip_rect(data, &x, &y, &w, &h); + + /* Render rectangle based on ROP */ + switch (patblt->bRop) { + + /* If blackness, send black rectangle */ + case 0x00: + guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); + + guac_protocol_send_cfill(client->socket, + GUAC_COMP_OVER, current_layer, + 0x00, 0x00, 0x00, 0xFF); + break; + + /* If NOP, do nothing */ + case 0xAA: + break; + + /* If operation is just a copy, send foreground only */ + case 0xCC: + case 0xF0: + guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); + + guac_protocol_send_cfill(client->socket, + GUAC_COMP_OVER, current_layer, + (patblt->foreColor >> 16) & 0xFF, + (patblt->foreColor >> 8 ) & 0xFF, + (patblt->foreColor ) & 0xFF, + 0xFF); + break; + + /* If whiteness, send white rectangle */ + case 0xFF: + guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); + + guac_protocol_send_cfill(client->socket, + GUAC_COMP_OVER, current_layer, + 0xFF, 0xFF, 0xFF, 0xFF); + break; + + /* Otherwise, invert entire rect */ + default: + + /* Allocate buffer for transfer */ + buffer = guac_client_alloc_buffer(client); + + /* Send rectangle stroke */ + guac_protocol_send_rect(client->socket, buffer, + 0, 0, w, h); + + /* Fill rectangle with fore color only */ + guac_protocol_send_cfill(client->socket, GUAC_COMP_OVER, buffer, + 0xFF, 0xFF, 0xFF, 0xFF); + + /* Transfer */ + guac_protocol_send_transfer(client->socket, + + /* ... from buffer */ + buffer, 0, 0, w, h, + + /* ... inverting */ + GUAC_TRANSFER_BINARY_XOR, + + /* ... to current layer */ + current_layer, x, y); + + /* Done with buffer */ + guac_client_free_buffer(client, buffer); + + } + +} + +void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface; + + int x = scrblt->nLeftRect; + int y = scrblt->nTopRect; + int w = scrblt->nWidth; + int h = scrblt->nHeight; + + int x_src = scrblt->nXSrc; + int y_src = scrblt->nYSrc; + + rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; + pthread_mutex_lock(&(data->update_lock)); + + /* Clip operation to bounds */ + __guac_rdp_clip_rect(data, &x, &y, &w, &h); + + /* Update source coordinates */ + x_src += x - scrblt->nLeftRect; + y_src += y - scrblt->nTopRect; + + /* Copy screen rect to current surface */ + guac_protocol_send_copy(client->socket, + GUAC_DEFAULT_LAYER, x_src, y_src, w, h, + GUAC_COMP_OVER, current_layer, x, y); + + pthread_mutex_unlock(&(data->update_lock)); + +} + +void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface; + guac_socket* socket = client->socket; + guac_rdp_bitmap* bitmap = (guac_rdp_bitmap*) memblt->bitmap; + + int x = memblt->nLeftRect; + int y = memblt->nTopRect; + int w = memblt->nWidth; + int h = memblt->nHeight; + + int x_src = memblt->nXSrc; + int y_src = memblt->nYSrc; + + rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; + pthread_mutex_lock(&(data->update_lock)); + + /* Clip operation to bounds */ + __guac_rdp_clip_rect(data, &x, &y, &w, &h); + + /* Update source coordinates */ + x_src += x - memblt->nLeftRect; + y_src += y - memblt->nTopRect; + + switch (memblt->bRop) { + + /* If blackness, send black rectangle */ + case 0x00: + guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); + + guac_protocol_send_cfill(client->socket, + GUAC_COMP_OVER, current_layer, + 0x00, 0x00, 0x00, 0xFF); + break; + + /* If NOP, do nothing */ + case 0xAA: + break; + + /* If operation is just SRC, simply copy */ + case 0xCC: + + /* If not cached, cache if necessary */ + if (((guac_rdp_bitmap*) bitmap)->layer == NULL + && ((guac_rdp_bitmap*) bitmap)->used >= 1) + guac_rdp_cache_bitmap(context, memblt->bitmap); + + /* If not cached, send as PNG */ + if (bitmap->layer == NULL) { + if (memblt->bitmap->data != NULL) { + + /* Create surface from image data */ + cairo_surface_t* surface = cairo_image_surface_create_for_data( + memblt->bitmap->data + 4*(x_src + y_src*memblt->bitmap->width), + CAIRO_FORMAT_RGB24, w, h, 4*memblt->bitmap->width); + + /* Send surface to buffer */ + guac_protocol_send_png(socket, + GUAC_COMP_OVER, current_layer, + x, y, surface); + + /* Free surface */ + cairo_surface_destroy(surface); + + } + } + + /* Otherwise, copy */ + else + guac_protocol_send_copy(socket, + bitmap->layer, x_src, y_src, w, h, + GUAC_COMP_OVER, current_layer, x, y); + + /* Increment usage counter */ + ((guac_rdp_bitmap*) bitmap)->used++; + + break; + + /* If whiteness, send white rectangle */ + case 0xFF: + guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); + + guac_protocol_send_cfill(client->socket, + GUAC_COMP_OVER, current_layer, + 0xFF, 0xFF, 0xFF, 0xFF); + break; + + /* Otherwise, use transfer */ + default: + + /* If not available as a surface, make available. */ + if (bitmap->layer == NULL) + guac_rdp_cache_bitmap(context, memblt->bitmap); + + guac_protocol_send_transfer(socket, + bitmap->layer, x_src, y_src, w, h, + guac_rdp_rop3_transfer_function(client, memblt->bRop), + current_layer, x, y); + + /* Increment usage counter */ + ((guac_rdp_bitmap*) bitmap)->used++; + + } + + pthread_mutex_unlock(&(data->update_lock)); + +} + +void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + uint32 color = freerdp_color_convert_var(opaque_rect->color, + context->instance->settings->color_depth, 32, + ((rdp_freerdp_context*) context)->clrconv); + + const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface; + + rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; + pthread_mutex_lock(&(data->update_lock)); + + int x = opaque_rect->nLeftRect; + int y = opaque_rect->nTopRect; + int w = opaque_rect->nWidth; + int h = opaque_rect->nHeight; + + /* Clip operation to bounds */ + __guac_rdp_clip_rect(data, &x, &y, &w, &h); + + guac_protocol_send_rect(client->socket, current_layer, x, y, w, h); + + guac_protocol_send_cfill(client->socket, + GUAC_COMP_OVER, current_layer, + (color >> 16) & 0xFF, + (color >> 8 ) & 0xFF, + (color ) & 0xFF, + 255); + + pthread_mutex_unlock(&(data->update_lock)); + +} + +void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette) { + + CLRCONV* clrconv = ((rdp_freerdp_context*) context)->clrconv; + clrconv->palette->count = palette->number; + clrconv->palette->entries = palette->entries; + +} + +void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; + + /* If no bounds given, clear bounding rect */ + if (bounds == NULL) + data->bounded = false; + + /* Otherwise, set bounding rectangle */ + else { + data->bounded = true; + data->bounds_left = bounds->left; + data->bounds_top = bounds->top; + data->bounds_right = bounds->right; + data->bounds_bottom = bounds->bottom; + } + +} + +void guac_rdp_gdi_end_paint(rdpContext* context) { + guac_client* client = ((rdp_freerdp_context*) context)->client; + guac_socket_flush(client->socket); +} + diff --git a/protocols/rdp/src/rdp_glyph.c b/protocols/rdp/src/rdp_glyph.c new file mode 100644 index 00000000..50b4f178 --- /dev/null +++ b/protocols/rdp/src/rdp_glyph.c @@ -0,0 +1,243 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Hortman + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include + +#include +#include + +#include "client.h" +#include "rdp_glyph.h" + +void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph) { + + int x, y, i; + int stride; + unsigned char* image_buffer; + unsigned char* image_buffer_row; + + unsigned char* data = glyph->aj; + int width = glyph->cx; + int height = glyph->cy; + + /* Init Cairo buffer */ + stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); + image_buffer = malloc(height*stride); + image_buffer_row = image_buffer; + + /* Copy image data from image data to buffer */ + for (y = 0; ysurface = cairo_image_surface_create_for_data( + image_buffer, CAIRO_FORMAT_ARGB32, width, height, stride); + +} + +void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; + + /* Do not attempt to draw glyphs if glyph drawing is not begun */ + if (guac_client_data->glyph_cairo == NULL) + return; + + /* Use glyph as mask */ + cairo_mask_surface( + guac_client_data->glyph_cairo, + ((guac_rdp_glyph*) glyph)->surface, x, y); + +} + +void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph) { + + unsigned char* image_buffer = cairo_image_surface_get_data( + ((guac_rdp_glyph*) glyph)->surface); + + /* Free surface */ + cairo_surface_destroy(((guac_rdp_glyph*) glyph)->surface); + free(image_buffer); + +} + +void guac_rdp_glyph_begindraw(rdpContext* context, + int x, int y, int width, int height, uint32 fgcolor, uint32 bgcolor) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + rdp_guac_client_data* guac_client_data = + (rdp_guac_client_data*) client->data; + + /* Convert foreground color */ + fgcolor = freerdp_color_convert_var(fgcolor, + context->instance->settings->color_depth, 32, + ((rdp_freerdp_context*) context)->clrconv); + + /* Fill background with color if specified */ + if (width != 0 && height != 0) { + + /* Prepare for opaque glyphs */ + guac_client_data->glyph_surface = + guac_client_data->opaque_glyph_surface; + + /* Create cairo instance */ + guac_client_data->glyph_cairo = cairo_create( + guac_client_data->glyph_surface); + + /* Convert background color */ + bgcolor = freerdp_color_convert_var(bgcolor, + context->instance->settings->color_depth, 32, + ((rdp_freerdp_context*) context)->clrconv); + + /* Fill background */ + cairo_rectangle(guac_client_data->glyph_cairo, + x, y, width, height); + + cairo_set_source_rgb(guac_client_data->glyph_cairo, + ((bgcolor & 0xFF0000) >> 16) / 255.0, + ((bgcolor & 0x00FF00) >> 8 ) / 255.0, + ( bgcolor & 0x0000FF ) / 255.0); + + cairo_fill(guac_client_data->glyph_cairo); + + } + + /* Otherwise, prepare for transparent glyphs */ + else { + + /* Select transparent glyph surface */ + guac_client_data->glyph_surface = + guac_client_data->trans_glyph_surface; + + guac_client_data->glyph_cairo = cairo_create( + guac_client_data->glyph_surface); + + /* Clear surface */ + cairo_set_operator(guac_client_data->glyph_cairo, + CAIRO_OPERATOR_SOURCE); + + cairo_set_source_rgba(guac_client_data->glyph_cairo, 0, 0, 0, 0); + cairo_paint(guac_client_data->glyph_cairo); + + /* Restore operator */ + cairo_set_operator(guac_client_data->glyph_cairo, + CAIRO_OPERATOR_OVER); + + } + + /* Prepare for glyph drawing */ + cairo_set_source_rgb(guac_client_data->glyph_cairo, + ((fgcolor & 0xFF0000) >> 16) / 255.0, + ((fgcolor & 0x00FF00) >> 8 ) / 255.0, + ( fgcolor & 0x0000FF ) / 255.0); + +} + +void guac_rdp_glyph_enddraw(rdpContext* context, + int x, int y, int width, int height, uint32 fgcolor, uint32 bgcolor) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data; + const guac_layer* current_layer = ((rdp_guac_client_data*) client->data)->current_surface; + + pthread_mutex_lock(&(guac_client_data->update_lock)); + + /* Use glyph surface to provide image data for glyph rectangle */ + cairo_surface_t* glyph_surface = guac_client_data->glyph_surface; + int stride = cairo_image_surface_get_stride(glyph_surface); + + /* Calculate bounds */ + int max_width = cairo_image_surface_get_width(glyph_surface) - x; + int max_height = cairo_image_surface_get_height(glyph_surface) - y; + + /* Ensure dimensions of glyph do not exceed bounds */ + if (width > max_width) width = max_width; + if (height > max_height) height = max_height; + + /* Ensure data is ready */ + cairo_surface_flush(glyph_surface); + + /* Create surface for subsection with text */ + cairo_surface_t* surface = cairo_image_surface_create_for_data( + cairo_image_surface_get_data(glyph_surface) + 4*x + y*stride, + cairo_image_surface_get_format(glyph_surface), + width, height, stride); + + /* Send surface with all glyphs to layer */ + guac_protocol_send_png(client->socket, + GUAC_COMP_OVER, current_layer, x, y, + surface); + + /* Destroy surface */ + cairo_surface_destroy(surface); + + /* Destroy cairo instance */ + cairo_destroy(guac_client_data->glyph_cairo); + + pthread_mutex_unlock(&(guac_client_data->update_lock)); +} + diff --git a/protocols/rdp/src/rdp_keymap.c b/protocols/rdp/src/rdp_keymap.c new file mode 100644 index 00000000..82d49414 --- /dev/null +++ b/protocols/rdp/src/rdp_keymap.c @@ -0,0 +1,61 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "rdp_keymap.h" + +const int GUAC_KEYSYMS_SHIFT[] = {0xFFE1, 0}; +const int GUAC_KEYSYMS_ALL_SHIFT[] = {0xFFE1, 0xFFE2, 0}; + +const int GUAC_KEYSYMS_ALTGR[] = {0xFFEA, 0}; +const int GUAC_KEYSYMS_SHIFT_ALTGR[] = {0xFFE1, 0xFFEA, 0}; +const int GUAC_KEYSYMS_ALL_SHIFT_ALTGR[] = {0xFFE1, 0xFFE2, 0xFFEA, 0}; + +const int GUAC_KEYSYMS_CTRL[] = {0xFFE3, 0}; +const int GUAC_KEYSYMS_ALL_CTRL[] = {0xFFE3, 0xFFE4, 0}; + +const int GUAC_KEYSYMS_ALT[] = {0xFFE9, 0}; +const int GUAC_KEYSYMS_ALL_ALT[] = {0xFFE9, 0xFFEA, 0}; + +const int GUAC_KEYSYMS_CTRL_ALT[] = {0xFFE3, 0xFFE9, 0}; + +const int GUAC_KEYSYMS_ALL_MODIFIERS[] = { + 0xFFE1, 0xFFE2, /* Left and right shift */ + 0xFFE3, 0xFFE4, /* Left and right control */ + 0xFFE9, 0xFFEA, /* Left and right alt (AltGr) */ + 0 +}; + diff --git a/protocols/rdp/src/rdp_keymap_base.c b/protocols/rdp/src/rdp_keymap_base.c new file mode 100644 index 00000000..dd99e5ad --- /dev/null +++ b/protocols/rdp/src/rdp_keymap_base.c @@ -0,0 +1,220 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Hortman + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include + +#include "rdp_keymap.h" + +static guac_rdp_keysym_desc __guac_rdp_keymap_mapping[] = { + + /* BackSpace */ + { .keysym = 0xff08, .scancode = 0x0E }, + + /* Tab */ + { .keysym = 0xff09, .scancode = 0x0F }, + + /* Return */ + { .keysym = 0xff0d, .scancode = 0x1C }, + + /* Scroll_Lock */ + { .keysym = 0xff14, .scancode = 0x46 }, + + /* Escape */ + { .keysym = 0xff1b, .scancode = 0x01 }, + + /* Home */ + { .keysym = 0xff50, .scancode = 0x47, + .flags = KBD_FLAGS_EXTENDED }, + + /* Left */ + { .keysym = 0xff51, .scancode = 0x4B, + .flags = KBD_FLAGS_EXTENDED }, + + /* Up */ + { .keysym = 0xff52, .scancode = 0x48, + .flags = KBD_FLAGS_EXTENDED }, + + /* Right */ + { .keysym = 0xff53, .scancode = 0x4D, + .flags = KBD_FLAGS_EXTENDED }, + + /* Down */ + { .keysym = 0xff54, .scancode = 0x50, + .flags = KBD_FLAGS_EXTENDED }, + + /* Page_Up */ + { .keysym = 0xff55, .scancode = 0x49, + .flags = KBD_FLAGS_EXTENDED }, + + /* Menu */ + { .keysym = 0xff67, .scancode = 0x5D, + .flags = KBD_FLAGS_EXTENDED }, + + /* Page_Down */ + { .keysym = 0xff56, .scancode = 0x51, + .flags = KBD_FLAGS_EXTENDED }, + + /* End */ + { .keysym = 0xff57, .scancode = 0x4F, + .flags = KBD_FLAGS_EXTENDED }, + + /* Insert */ + { .keysym = 0xff63, .scancode = 0x52, + .flags = KBD_FLAGS_EXTENDED }, + + /* Num_Lock */ + { .keysym = 0xff7f, .scancode = 0x45 }, + + /* KP_0 */ + { .keysym = 0xffb0, .scancode = 0x52 }, + + /* KP_1 */ + { .keysym = 0xffb1, .scancode = 0x4F }, + + /* KP_2 */ + { .keysym = 0xffb2, .scancode = 0x50 }, + + /* KP_3 */ + { .keysym = 0xffb3, .scancode = 0x51 }, + + /* KP_4 */ + { .keysym = 0xffb4, .scancode = 0x4B }, + + /* KP_5 */ + { .keysym = 0xffb5, .scancode = 0x4C }, + + /* KP_6 */ + { .keysym = 0xffb6, .scancode = 0x4D }, + + /* KP_7 */ + { .keysym = 0xffb7, .scancode = 0x47 }, + + /* KP_8 */ + { .keysym = 0xffb8, .scancode = 0x48 }, + + /* KP_9 */ + { .keysym = 0xffb9, .scancode = 0x49 }, + + /* F1 */ + { .keysym = 0xffbe, .scancode = 0x3B }, + + /* F2 */ + { .keysym = 0xffbf, .scancode = 0x3C }, + + /* F3 */ + { .keysym = 0xffc0, .scancode = 0x3D }, + + /* F4 */ + { .keysym = 0xffc1, .scancode = 0x3E }, + + /* F5 */ + { .keysym = 0xffc2, .scancode = 0x3F }, + + /* F6 */ + { .keysym = 0xffc3, .scancode = 0x40 }, + + /* F7 */ + { .keysym = 0xffc4, .scancode = 0x41 }, + + /* F8 */ + { .keysym = 0xffc5, .scancode = 0x42 }, + + /* F9 */ + { .keysym = 0xffc6, .scancode = 0x43 }, + + /* F10 */ + { .keysym = 0xffc7, .scancode = 0x44 }, + + /* F11 */ + { .keysym = 0xffc8, .scancode = 0x57 }, + + /* F12 */ + { .keysym = 0xffc9, .scancode = 0x58 }, + + /* Shift_L */ + { .keysym = 0xffe1, .scancode = 0x2A }, + + /* Shift_R */ + { .keysym = 0xffe2, .scancode = 0x36 }, + + /* Control_L */ + { .keysym = 0xffe3, .scancode = 0x1D }, + + /* Control_R */ + { .keysym = 0xffe4, .scancode = 0x9D }, + + /* Caps_Lock */ + { .keysym = 0xffe5, .scancode = 0x3A, + .flags = KBD_FLAGS_EXTENDED }, + + /* Alt_L */ + { .keysym = 0xffe9, .scancode = 0x38 }, + + /* Alt_R */ + { .keysym = 0xffea, .scancode = 0x38, + .flags = KBD_FLAGS_EXTENDED }, + + /* ISO_Level3_Shift */ + { .keysym = 0xfe03, .scancode = 0x38, + .flags = KBD_FLAGS_EXTENDED }, + + /* Super_L */ + { .keysym = 0xffeb, .scancode = 0x5B, + .flags = KBD_FLAGS_EXTENDED }, + + /* Super_R */ + { .keysym = 0xffec, .scancode = 0x5C, + .flags = KBD_FLAGS_EXTENDED }, + + /* Delete */ + { .keysym = 0xffff, .scancode = 0x53, + .flags = KBD_FLAGS_EXTENDED }, + + {0} + +}; + +const guac_rdp_keymap guac_rdp_keymap_base = { + + .name = "base", + + .parent = NULL, + .mapping = __guac_rdp_keymap_mapping + +}; + diff --git a/protocols/rdp/src/rdp_keymap_de_de.c b/protocols/rdp/src/rdp_keymap_de_de.c new file mode 100644 index 00000000..8b2143f6 --- /dev/null +++ b/protocols/rdp/src/rdp_keymap_de_de.c @@ -0,0 +1,552 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include + +#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H +#include +#else +#include +#endif + +#include "rdp_keymap.h" + +static guac_rdp_keysym_desc __guac_rdp_keymap_mapping[] = { + + /* space */ + { .keysym = 0x0020, .scancode = 0x39 }, + + /* exclam */ + { .keysym = 0x0021, .scancode = 0x02, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* quotedbl */ + { .keysym = 0x0022, .scancode = 0x03, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* numbersign */ + { .keysym = 0x0023, .scancode = 0x2b, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* dollar */ + { .keysym = 0x0024, .scancode = 0x05, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* percent */ + { .keysym = 0x0025, .scancode = 0x06, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* ampersand */ + { .keysym = 0x0026, .scancode = 0x07, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* quoteright */ + { .keysym = 0x0027, .scancode = 0x2b, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* parenleft */ + { .keysym = 0x0028, .scancode = 0x09, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* parenright */ + { .keysym = 0x0029, .scancode = 0x0A, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* asterisk */ + { .keysym = 0x002a, .scancode = 0x1b, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* plus */ + { .keysym = 0x002b, .scancode = 0x1b, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* comma */ + { .keysym = 0x002c, .scancode = 0x33, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* minus */ + { .keysym = 0x002d, .scancode = 0x35, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* period */ + { .keysym = 0x002e, .scancode = 0x34, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* slash */ + { .keysym = 0x002f, .scancode = 0x08, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 0 */ + { .keysym = 0x0030, .scancode = 0x0B, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* 1 */ + { .keysym = 0x0031, .scancode = 0x02, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* 2 */ + { .keysym = 0x0032, .scancode = 0x03, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* 3 */ + { .keysym = 0x0033, .scancode = 0x04, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* 4 */ + { .keysym = 0x0034, .scancode = 0x05, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* 5 */ + { .keysym = 0x0035, .scancode = 0x06, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* 6 */ + { .keysym = 0x0036, .scancode = 0x07, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* 7 */ + { .keysym = 0x0037, .scancode = 0x08, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* 8 */ + { .keysym = 0x0038, .scancode = 0x09, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* 9 */ + { .keysym = 0x0039, .scancode = 0x0A, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* colon */ + { .keysym = 0x003a, .scancode = 0x34, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* semicolon */ + { .keysym = 0x003b, .scancode = 0x33, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* less */ + { .keysym = 0x003c, .scancode = 0x56, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* equal */ + { .keysym = 0x003d, .scancode = 0x0B, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* greater */ + { .keysym = 0x003e, .scancode = 0x56, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* question */ + { .keysym = 0x003f, .scancode = 0x0c, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* at */ + { .keysym = 0x0040, .scancode = 0x10, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* A */ + { .keysym = 0x0041, .scancode = 0x1E, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* B */ + { .keysym = 0x0042, .scancode = 0x30, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* C */ + { .keysym = 0x0043, .scancode = 0x2E, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* D */ + { .keysym = 0x0044, .scancode = 0x20, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* E */ + { .keysym = 0x0045, .scancode = 0x12, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* F */ + { .keysym = 0x0046, .scancode = 0x21, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* G */ + { .keysym = 0x0047, .scancode = 0x22, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* H */ + { .keysym = 0x0048, .scancode = 0x23, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* I */ + { .keysym = 0x0049, .scancode = 0x17, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* J */ + { .keysym = 0x004a, .scancode = 0x24, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* K */ + { .keysym = 0x004b, .scancode = 0x25, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* L */ + { .keysym = 0x004c, .scancode = 0x26, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* M */ + { .keysym = 0x004d, .scancode = 0x32, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* N */ + { .keysym = 0x004e, .scancode = 0x31, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* O */ + { .keysym = 0x004f, .scancode = 0x18, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* P */ + { .keysym = 0x0050, .scancode = 0x19, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* Q */ + { .keysym = 0x0051, .scancode = 0x10, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* R */ + { .keysym = 0x0052, .scancode = 0x13, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* S */ + { .keysym = 0x0053, .scancode = 0x1F, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* T */ + { .keysym = 0x0054, .scancode = 0x14, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* U */ + { .keysym = 0x0055, .scancode = 0x16, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* V */ + { .keysym = 0x0056, .scancode = 0x2F, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* W */ + { .keysym = 0x0057, .scancode = 0x11, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* X */ + { .keysym = 0x0058, .scancode = 0x2D, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* Y */ + { .keysym = 0x0059, .scancode = 0x2c, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* Z */ + { .keysym = 0x005a, .scancode = 0x15, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* bracketleft */ + { .keysym = 0x005b, .scancode = 0x09, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* backslash */ + { .keysym = 0x005c, .scancode = 0x0c, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* bracketright */ + { .keysym = 0x005d, .scancode = 0x0a, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* underscore */ + { .keysym = 0x005f, .scancode = 0x35, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* dead grave */ + { .keysym = 0xfe50, .scancode = 0x0d, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* dead acute */ + { .keysym = 0xfe51, .scancode = 0x0d, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* dead circum */ + { .keysym = 0xfe52, .scancode = 0x29, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* degree */ + { .keysym = 0x00b0, .scancode = 0x29, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* a */ + { .keysym = 0x0061, .scancode = 0x1E, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* b */ + { .keysym = 0x0062, .scancode = 0x30, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* c */ + { .keysym = 0x0063, .scancode = 0x2E, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* d */ + { .keysym = 0x0064, .scancode = 0x20, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* e */ + { .keysym = 0x0065, .scancode = 0x12, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* f */ + { .keysym = 0x0066, .scancode = 0x21, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* g */ + { .keysym = 0x0067, .scancode = 0x22, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* h */ + { .keysym = 0x0068, .scancode = 0x23, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* i */ + { .keysym = 0x0069, .scancode = 0x17, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* j */ + { .keysym = 0x006a, .scancode = 0x24, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* k */ + { .keysym = 0x006b, .scancode = 0x25, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* l */ + { .keysym = 0x006c, .scancode = 0x26, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* m */ + { .keysym = 0x006d, .scancode = 0x32, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* n */ + { .keysym = 0x006e, .scancode = 0x31, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* o */ + { .keysym = 0x006f, .scancode = 0x18, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* p */ + { .keysym = 0x0070, .scancode = 0x19, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* q */ + { .keysym = 0x0071, .scancode = 0x10, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* r */ + { .keysym = 0x0072, .scancode = 0x13, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* s */ + { .keysym = 0x0073, .scancode = 0x1F, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* t */ + { .keysym = 0x0074, .scancode = 0x14, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* u */ + { .keysym = 0x0075, .scancode = 0x16, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* v */ + { .keysym = 0x0076, .scancode = 0x2F, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* w */ + { .keysym = 0x0077, .scancode = 0x11, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* x */ + { .keysym = 0x0078, .scancode = 0x2D, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* y */ + { .keysym = 0x0079, .scancode = 0x2c, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* z */ + { .keysym = 0x007a, .scancode = 0x15, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* ä */ + { .keysym = 0x00e4, .scancode = 0x28, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* Ä */ + { .keysym = 0x00c4, .scancode = 0x28, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* ö */ + { .keysym = 0x00f6, .scancode = 0x27, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* Ö */ + { .keysym = 0x00d6, .scancode = 0x27, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* ü */ + { .keysym = 0x00fc, .scancode = 0x1a, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* Ü */ + { .keysym = 0x00dc, .scancode = 0x1a, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* ß */ + { .keysym = 0x00df, .scancode = 0x0c, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* braceleft */ + { .keysym = 0x007b, .scancode = 0x08, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* bar */ + { .keysym = 0x007c, .scancode = 0x56, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* braceright */ + { .keysym = 0x007d, .scancode = 0x0b, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* dead tilde */ + { .keysym = 0xfe53, .scancode = 0x1b, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* euro */ + { .keysym = 0x10020ac, .scancode = 0x12, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* mu */ + { .keysym = 0x00b5, .scancode = 0x32, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* two superior */ + { .keysym = 0x00b2, .scancode = 0x03, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* three superior */ + { .keysym = 0x00b3, .scancode = 0x04, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + {0} + +}; + +const guac_rdp_keymap guac_rdp_keymap_de_de = { + + .name = "de-de-qwertz", + + .parent = &guac_rdp_keymap_base, + .mapping = __guac_rdp_keymap_mapping, + .freerdp_keyboard_layout = KBD_GERMAN + +}; + diff --git a/protocols/rdp/src/rdp_keymap_en_us.c b/protocols/rdp/src/rdp_keymap_en_us.c new file mode 100644 index 00000000..2e6dc6f6 --- /dev/null +++ b/protocols/rdp/src/rdp_keymap_en_us.c @@ -0,0 +1,442 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Matt Hortman + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include + +#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H +#include +#else +#include +#endif + +#include "rdp_keymap.h" + +static guac_rdp_keysym_desc __guac_rdp_keymap_mapping[] = { + + /* space */ + { .keysym = 0x0020, .scancode = 0x39 }, + + /* exclam */ + { .keysym = 0x0021, .scancode = 0x02, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* quotedbl */ + { .keysym = 0x0022, .scancode = 0x28, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* numbersign */ + { .keysym = 0x0023, .scancode = 0x04, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* dollar */ + { .keysym = 0x0024, .scancode = 0x05, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* percent */ + { .keysym = 0x0025, .scancode = 0x06, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* ampersand */ + { .keysym = 0x0026, .scancode = 0x08, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* quoteright */ + { .keysym = 0x0027, .scancode = 0x28 }, + + /* parenleft */ + { .keysym = 0x0028, .scancode = 0x0A, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* parenright */ + { .keysym = 0x0029, .scancode = 0x0B, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* asterisk */ + { .keysym = 0x002a, .scancode = 0x09, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* plus */ + { .keysym = 0x002b, .scancode = 0x0D, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* comma */ + { .keysym = 0x002c, .scancode = 0x33, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* minus */ + { .keysym = 0x002d, .scancode = 0x0C, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* period */ + { .keysym = 0x002e, .scancode = 0x34, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* slash */ + { .keysym = 0x002f, .scancode = 0x35, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* 0 */ + { .keysym = 0x0030, .scancode = 0x0B, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* 1 */ + { .keysym = 0x0031, .scancode = 0x02, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* 2 */ + { .keysym = 0x0032, .scancode = 0x03, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* 3 */ + { .keysym = 0x0033, .scancode = 0x04, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* 4 */ + { .keysym = 0x0034, .scancode = 0x05, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* 5 */ + { .keysym = 0x0035, .scancode = 0x06, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* 6 */ + { .keysym = 0x0036, .scancode = 0x07, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* 7 */ + { .keysym = 0x0037, .scancode = 0x08, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* 8 */ + { .keysym = 0x0038, .scancode = 0x09, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* 9 */ + { .keysym = 0x0039, .scancode = 0x0A, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* colon */ + { .keysym = 0x003a, .scancode = 0x27, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* semicolon */ + { .keysym = 0x003b, .scancode = 0x27, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* less */ + { .keysym = 0x003c, .scancode = 0x33, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* equal */ + { .keysym = 0x003d, .scancode = 0x0D, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* greater */ + { .keysym = 0x003e, .scancode = 0x34, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* question */ + { .keysym = 0x003f, .scancode = 0x35, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* at */ + { .keysym = 0x0040, .scancode = 0x03, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* A */ + { .keysym = 0x0041, .scancode = 0x1E, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* B */ + { .keysym = 0x0042, .scancode = 0x30, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* C */ + { .keysym = 0x0043, .scancode = 0x2E, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* D */ + { .keysym = 0x0044, .scancode = 0x20, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* E */ + { .keysym = 0x0045, .scancode = 0x12, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* F */ + { .keysym = 0x0046, .scancode = 0x21, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* G */ + { .keysym = 0x0047, .scancode = 0x22, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* H */ + { .keysym = 0x0048, .scancode = 0x23, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* I */ + { .keysym = 0x0049, .scancode = 0x17, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* J */ + { .keysym = 0x004a, .scancode = 0x24, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* K */ + { .keysym = 0x004b, .scancode = 0x25, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* L */ + { .keysym = 0x004c, .scancode = 0x26, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* M */ + { .keysym = 0x004d, .scancode = 0x32, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* N */ + { .keysym = 0x004e, .scancode = 0x31, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* O */ + { .keysym = 0x004f, .scancode = 0x18, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* P */ + { .keysym = 0x0050, .scancode = 0x19, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* Q */ + { .keysym = 0x0051, .scancode = 0x10, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* R */ + { .keysym = 0x0052, .scancode = 0x13, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* S */ + { .keysym = 0x0053, .scancode = 0x1F, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* T */ + { .keysym = 0x0054, .scancode = 0x14, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* U */ + { .keysym = 0x0055, .scancode = 0x16, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* V */ + { .keysym = 0x0056, .scancode = 0x2F, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* W */ + { .keysym = 0x0057, .scancode = 0x11, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* X */ + { .keysym = 0x0058, .scancode = 0x2D, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* Y */ + { .keysym = 0x0059, .scancode = 0x15, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* Z */ + { .keysym = 0x005a, .scancode = 0x2C, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* bracketleft */ + { .keysym = 0x005b, .scancode = 0x1A, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* backslash */ + { .keysym = 0x005c, .scancode = 0x2B, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* bracketright */ + { .keysym = 0x005d, .scancode = 0x1B, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* asciicircum */ + { .keysym = 0x005e, .scancode = 0x07, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* underscore */ + { .keysym = 0x005f, .scancode = 0x0C, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* quoteleft */ + { .keysym = 0x0060, .scancode = 0x29, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* a */ + { .keysym = 0x0061, .scancode = 0x1E, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* b */ + { .keysym = 0x0062, .scancode = 0x30, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* c */ + { .keysym = 0x0063, .scancode = 0x2E, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* d */ + { .keysym = 0x0064, .scancode = 0x20, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* e */ + { .keysym = 0x0065, .scancode = 0x12, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* f */ + { .keysym = 0x0066, .scancode = 0x21, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* g */ + { .keysym = 0x0067, .scancode = 0x22, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* h */ + { .keysym = 0x0068, .scancode = 0x23, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* i */ + { .keysym = 0x0069, .scancode = 0x17, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* j */ + { .keysym = 0x006a, .scancode = 0x24, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* k */ + { .keysym = 0x006b, .scancode = 0x25, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* l */ + { .keysym = 0x006c, .scancode = 0x26, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* m */ + { .keysym = 0x006d, .scancode = 0x32, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* n */ + { .keysym = 0x006e, .scancode = 0x31, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* o */ + { .keysym = 0x006f, .scancode = 0x18, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* p */ + { .keysym = 0x0070, .scancode = 0x19, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* q */ + { .keysym = 0x0071, .scancode = 0x10, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* r */ + { .keysym = 0x0072, .scancode = 0x13, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* s */ + { .keysym = 0x0073, .scancode = 0x1F, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* t */ + { .keysym = 0x0074, .scancode = 0x14, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* u */ + { .keysym = 0x0075, .scancode = 0x16, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* v */ + { .keysym = 0x0076, .scancode = 0x2F, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* w */ + { .keysym = 0x0077, .scancode = 0x11, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* x */ + { .keysym = 0x0078, .scancode = 0x2D, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* y */ + { .keysym = 0x0079, .scancode = 0x15, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* z */ + { .keysym = 0x007a, .scancode = 0x2C, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT }, + + /* braceleft */ + { .keysym = 0x007b, .scancode = 0x1A, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* bar */ + { .keysym = 0x007c, .scancode = 0x2B, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* braceright */ + { .keysym = 0x007d, .scancode = 0x1B, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* asciitilde */ + { .keysym = 0x007e, .scancode = 0x29, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + {0} + +}; + +const guac_rdp_keymap guac_rdp_keymap_en_us = { + + .name = "en-us-qwerty", + + .parent = &guac_rdp_keymap_base, + .mapping = __guac_rdp_keymap_mapping, + .freerdp_keyboard_layout = KBD_US + +}; + diff --git a/protocols/rdp/src/rdp_keymap_failsafe.c b/protocols/rdp/src/rdp_keymap_failsafe.c new file mode 100644 index 00000000..e4ecc67c --- /dev/null +++ b/protocols/rdp/src/rdp_keymap_failsafe.c @@ -0,0 +1,63 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include + +#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H +#include +#else +#include +#endif + +#include "rdp_keymap.h" + +static guac_rdp_keysym_desc __guac_rdp_keymap_mapping[] = { + + {0} + +}; + +const guac_rdp_keymap guac_rdp_keymap_failsafe = { + + .name = "failsafe", + + .parent = &guac_rdp_keymap_base, + .mapping = __guac_rdp_keymap_mapping, + .freerdp_keyboard_layout = KBD_US + +}; + diff --git a/protocols/rdp/src/rdp_keymap_fr_fr.c b/protocols/rdp/src/rdp_keymap_fr_fr.c new file mode 100644 index 00000000..64b63922 --- /dev/null +++ b/protocols/rdp/src/rdp_keymap_fr_fr.c @@ -0,0 +1,560 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alexandre Devely + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include + +#ifdef HAVE_FREERDP_LOCALE_KEYBOARD_H +#include +#else +#include +#endif + +#include "rdp_keymap.h" + + +static guac_rdp_keysym_desc __guac_rdp_keymap_mapping[] = { + + /* space */ + { .keysym = 0x0020, .scancode = 0x39 }, + + /* exclam */ + { .keysym = 0x0021, .scancode = 0x35, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* quotedbl */ + { .keysym = 0x0022, .scancode = 0x04, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* numbersign */ + { .keysym = 0x0023, .scancode = 0x04, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* dollar */ + { .keysym = 0x0024, .scancode = 0x1b, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* percent */ + { .keysym = 0x0025, .scancode = 0x28, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* ampersand */ + { .keysym = 0x0026, .scancode = 0x02, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* quoteright */ + { .keysym = 0x0027, .scancode = 0x05, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* parenleft */ + { .keysym = 0x0028, .scancode = 0x06, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* parenright */ + { .keysym = 0x0029, .scancode = 0x0c, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* asterisk */ + { .keysym = 0x002a, .scancode = 0x2b, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* plus */ + { .keysym = 0x002b, .scancode = 0x0D, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* comma */ + { .keysym = 0x002c, .scancode = 0x32, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* minus */ + { .keysym = 0x002d, .scancode = 0x07, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* period */ + { .keysym = 0x002e, .scancode = 0x33, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* slash */ + { .keysym = 0x002f, .scancode = 0x34, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 0 */ + { .keysym = 0x0030, .scancode = 0x0B, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 1 */ + { .keysym = 0x0031, .scancode = 0x02, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 2 */ + { .keysym = 0x0032, .scancode = 0x03, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 3 */ + { .keysym = 0x0033, .scancode = 0x04, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 4 */ + { .keysym = 0x0034, .scancode = 0x05, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 5 */ + { .keysym = 0x0035, .scancode = 0x06, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 6 */ + { .keysym = 0x0036, .scancode = 0x07, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 7 */ + { .keysym = 0x0037, .scancode = 0x08, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 8 */ + { .keysym = 0x0038, .scancode = 0x09, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* 9 */ + { .keysym = 0x0039, .scancode = 0x0A, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* colon */ + { .keysym = 0x003a, .scancode = 0x34, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* semicolon */ + { .keysym = 0x003b, .scancode = 0x33, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* less */ + { .keysym = 0x003c, .scancode = 0x56, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* equal */ + { .keysym = 0x003d, .scancode = 0x0D, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* greater */ + { .keysym = 0x003e, .scancode = 0x56, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* question */ + { .keysym = 0x003f, .scancode = 0x32, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* at */ + { .keysym = 0x0040, .scancode = 0x0B, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* A */ + { .keysym = 0x0041, .scancode = 0x10, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* B */ + { .keysym = 0x0042, .scancode = 0x30, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* C */ + { .keysym = 0x0043, .scancode = 0x2E, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* D */ + { .keysym = 0x0044, .scancode = 0x20, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* E */ + { .keysym = 0x0045, .scancode = 0x12, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* F */ + { .keysym = 0x0046, .scancode = 0x21, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* G */ + { .keysym = 0x0047, .scancode = 0x22, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* H */ + { .keysym = 0x0048, .scancode = 0x23, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* I */ + { .keysym = 0x0049, .scancode = 0x17, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* J */ + { .keysym = 0x004a, .scancode = 0x24, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* K */ + { .keysym = 0x004b, .scancode = 0x25, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* L */ + { .keysym = 0x004c, .scancode = 0x26, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* M */ + { .keysym = 0x004d, .scancode = 0x27, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* N */ + { .keysym = 0x004e, .scancode = 0x31, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* O */ + { .keysym = 0x004f, .scancode = 0x18, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* P */ + { .keysym = 0x0050, .scancode = 0x19, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* Q */ + { .keysym = 0x0051, .scancode = 0x1E, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* R */ + { .keysym = 0x0052, .scancode = 0x13, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* S */ + { .keysym = 0x0053, .scancode = 0x1F, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* T */ + { .keysym = 0x0054, .scancode = 0x14, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* U */ + { .keysym = 0x0055, .scancode = 0x16, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* V */ + { .keysym = 0x0056, .scancode = 0x2F, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* W */ + { .keysym = 0x0057, .scancode = 0x2C, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* X */ + { .keysym = 0x0058, .scancode = 0x2D, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* Y */ + { .keysym = 0x0059, .scancode = 0x15, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* Z */ + { .keysym = 0x005a, .scancode = 0x11, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* bracketleft */ + { .keysym = 0x005b, .scancode = 0x06, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* backslash */ + { .keysym = 0x005c, .scancode = 0x09, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* bracketright */ + { .keysym = 0x005d, .scancode = 0x0c, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* asciicircum */ + { .keysym = 0x005e, .scancode = 0x0a, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* underscore */ + { .keysym = 0x005f, .scancode = 0x09, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* quoteleft */ + { .keysym = 0x0060, .scancode = 0x08, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* a */ + { .keysym = 0x0061, .scancode = 0x10, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* b */ + { .keysym = 0x0062, .scancode = 0x30, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* c */ + { .keysym = 0x0063, .scancode = 0x2E, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* d */ + { .keysym = 0x0064, .scancode = 0x20, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* e */ + { .keysym = 0x0065, .scancode = 0x12, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* f */ + { .keysym = 0x0066, .scancode = 0x21, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* g */ + { .keysym = 0x0067, .scancode = 0x22, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* h */ + { .keysym = 0x0068, .scancode = 0x23, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* i */ + { .keysym = 0x0069, .scancode = 0x17, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* j */ + { .keysym = 0x006a, .scancode = 0x24, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* k */ + { .keysym = 0x006b, .scancode = 0x25, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* l */ + { .keysym = 0x006c, .scancode = 0x26, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* m */ + { .keysym = 0x006d, .scancode = 0x27, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* n */ + { .keysym = 0x006e, .scancode = 0x31, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* o */ + { .keysym = 0x006f, .scancode = 0x18, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* p */ + { .keysym = 0x0070, .scancode = 0x19, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* q */ + { .keysym = 0x0071, .scancode = 0x1E, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* r */ + { .keysym = 0x0072, .scancode = 0x13, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* s */ + { .keysym = 0x0073, .scancode = 0x1F, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* t */ + { .keysym = 0x0074, .scancode = 0x14, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* u */ + { .keysym = 0x0075, .scancode = 0x16, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* v */ + { .keysym = 0x0076, .scancode = 0x2F, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* w */ + { .keysym = 0x0077, .scancode = 0x2C, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* x */ + { .keysym = 0x0078, .scancode = 0x2D, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* y */ + { .keysym = 0x0079, .scancode = 0x15, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* z */ + { .keysym = 0x007a, .scancode = 0x11, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* braceleft */ + { .keysym = 0x007b, .scancode = 0x05, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* bar */ + { .keysym = 0x007c, .scancode = 0x07, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* braceright */ + { .keysym = 0x007d, .scancode = 0x0D, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* asciitilde */ + { .keysym = 0x007e, .scancode = 0x03, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* pound */ + { .keysym = 0x00a3, .scancode = 0x1b, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* currency */ + { .keysym = 0x00a4, .scancode = 0x1b, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* section */ + { .keysym = 0x00a7, .scancode = 0x35, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* degree */ + { .keysym = 0x00b0, .scancode = 0x0c, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* micro */ + { .keysym = 0x00b5, .scancode = 0x2b, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* dead umlaut */ + { .keysym = 0xfe57, .scancode = 0x1a, + .clear_keysyms = GUAC_KEYSYMS_ALTGR, + .set_keysyms = GUAC_KEYSYMS_SHIFT }, + + /* dead circum */ + { .keysym = 0xfe52, .scancode = 0x1a, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* à */ + { .keysym = 0x00e0, .scancode = 0x0b, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* ç */ + { .keysym = 0x00e7, .scancode = 0x0a, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* è */ + { .keysym = 0x00e8, .scancode = 0x08, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* é */ + { .keysym = 0x00e9, .scancode = 0x03, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* ù */ + { .keysym = 0x00f9, .scancode = 0x28, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + /* euro */ + { .keysym = 0x10020ac, .scancode = 0x12, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT, + .set_keysyms = GUAC_KEYSYMS_ALTGR }, + + /* two superior */ + { .keysym = 0x00b2, .scancode = 0x29, + .clear_keysyms = GUAC_KEYSYMS_ALL_SHIFT_ALTGR }, + + {0} + +}; + +const guac_rdp_keymap guac_rdp_keymap_fr_fr = { + + .name = "fr-fr-azerty", + + .parent = &guac_rdp_keymap_base, + .mapping = __guac_rdp_keymap_mapping, + .freerdp_keyboard_layout = KBD_FRENCH + +}; + diff --git a/protocols/rdp/src/rdp_pointer.c b/protocols/rdp/src/rdp_pointer.c new file mode 100644 index 00000000..a52e5863 --- /dev/null +++ b/protocols/rdp/src/rdp_pointer.c @@ -0,0 +1,120 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * David PHAM-VAN Ulteo SAS - http://www.ulteo.com + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include + +#include + +#include "client.h" +#include "rdp_pointer.h" +#include "default_pointer.h" + +void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + guac_socket* socket = client->socket; + + /* Allocate data for image */ + unsigned char* data = + (unsigned char*) malloc(pointer->width * pointer->height * 4); + + /* Allocate layer */ + guac_layer* buffer = guac_client_alloc_buffer(client); + + cairo_surface_t* surface; + + rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data; + pthread_mutex_lock(&(client_data->update_lock)); + + /* Convert to alpha cursor if mask data present */ + if (pointer->andMaskData && pointer->xorMaskData) + freerdp_alpha_cursor_convert(data, + pointer->xorMaskData, pointer->andMaskData, + pointer->width, pointer->height, pointer->xorBpp, + ((rdp_freerdp_context*) context)->clrconv); + + /* Create surface from image data */ + surface = cairo_image_surface_create_for_data( + data, CAIRO_FORMAT_ARGB32, + pointer->width, pointer->height, 4*pointer->width); + + /* Send surface to buffer */ + guac_protocol_send_png(socket, GUAC_COMP_SRC, buffer, 0, 0, surface); + + /* Free surface */ + cairo_surface_destroy(surface); + free(data); + + /* Remember buffer */ + ((guac_rdp_pointer*) pointer)->layer = buffer; + + pthread_mutex_unlock(&(client_data->update_lock)); +} + +void guac_rdp_pointer_set(rdpContext* context, rdpPointer* pointer) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + guac_socket* socket = client->socket; + + rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; + pthread_mutex_lock(&(data->update_lock)); + + /* Set cursor */ + guac_protocol_send_cursor(socket, pointer->xPos, pointer->yPos, + ((guac_rdp_pointer*) pointer)->layer, + 0, 0, pointer->width, pointer->height); + + pthread_mutex_unlock(&(data->update_lock)); +} + +void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + guac_client_free_buffer(client, ((guac_rdp_pointer*) pointer)->layer); + +} + +void guac_rdp_pointer_set_null(rdpContext* context) { + /* STUB */ +} + +void guac_rdp_pointer_set_default(rdpContext* context) { + /* STUB */ +} + diff --git a/protocols/rdp/src/wav_encoder.c b/protocols/rdp/src/wav_encoder.c new file mode 100644 index 00000000..0a306675 --- /dev/null +++ b/protocols/rdp/src/wav_encoder.c @@ -0,0 +1,201 @@ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libguac-client-rdp. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#define WAV_BUFFER_SIZE 0x4000 + +#include +#include + +#include +#include + +#include "audio.h" +#include "wav_encoder.h" + +void wav_encoder_begin_handler(audio_stream* audio) { + + /* Allocate stream state */ + wav_encoder_state* state = (wav_encoder_state*) + malloc(sizeof(wav_encoder_state)); + + /* Initialize buffer */ + state->length = WAV_BUFFER_SIZE; + state->used = 0; + state->data_buffer = (unsigned char*) malloc(state->length); + + audio->data = state; + +} + +void _wav_encoder_write_le(unsigned char* buffer, int value, int length) { + + int offset; + + /* Write all bytes in the given value in little-endian byte order */ + for (offset=0; offset>= 8; + buffer++; + + } + +} + +void wav_encoder_end_handler(audio_stream* audio) { + + /* + * Static header init + */ + + wav_encoder_riff_header riff_header = { + .chunk_id = "RIFF", + .chunk_format = "WAVE" + }; + + wav_encoder_fmt_header fmt_header = { + .subchunk_id = "fmt ", + .subchunk_size = {0x10, 0x00, 0x00, 0x00}, /* 16 */ + .subchunk_format = {0x01, 0x00} /* 1 = PCM */ + }; + + wav_encoder_data_header data_header = { + .subchunk_id = "data" + }; + + /* Get state */ + wav_encoder_state* state = (wav_encoder_state*) audio->data; + + /* + * RIFF HEADER + */ + + /* Chunk size */ + _wav_encoder_write_le(riff_header.chunk_size, + 4 + sizeof(fmt_header) + sizeof(data_header) + state->used, + sizeof(riff_header.chunk_size)); + + audio_stream_write_encoded(audio, + (unsigned char*) &riff_header, + sizeof(riff_header)); + + /* + * FMT HEADER + */ + + /* Channels */ + _wav_encoder_write_le(fmt_header.subchunk_channels, + audio->channels, sizeof(fmt_header.subchunk_channels)); + + /* Sample rate */ + _wav_encoder_write_le(fmt_header.subchunk_sample_rate, + audio->rate, sizeof(fmt_header.subchunk_sample_rate)); + + /* Byte rate */ + _wav_encoder_write_le(fmt_header.subchunk_byte_rate, + audio->rate * audio->channels * audio->bps / 8, + sizeof(fmt_header.subchunk_byte_rate)); + + /* Block align */ + _wav_encoder_write_le(fmt_header.subchunk_block_align, + audio->channels * audio->bps / 8, + sizeof(fmt_header.subchunk_block_align)); + + /* Bits per second */ + _wav_encoder_write_le(fmt_header.subchunk_bps, + audio->bps, sizeof(fmt_header.subchunk_bps)); + + audio_stream_write_encoded(audio, + (unsigned char*) &fmt_header, + sizeof(fmt_header)); + + /* + * DATA HEADER + */ + + /* PCM data size */ + _wav_encoder_write_le(data_header.subchunk_size, + state->used, sizeof(data_header.subchunk_size)); + + audio_stream_write_encoded(audio, + (unsigned char*) &data_header, + sizeof(data_header)); + + /* Write .wav data */ + audio_stream_write_encoded(audio, state->data_buffer, state->used); + + /* Free stream state */ + free(state); + +} + +void wav_encoder_write_handler(audio_stream* audio, + unsigned char* pcm_data, int length) { + + /* Get state */ + wav_encoder_state* state = (wav_encoder_state*) audio->data; + + /* Increase size of buffer if necessary */ + if (state->used + length > state->length) { + + /* Increase to double concatenated size to accomodate */ + state->length = (state->length + length)*2; + state->data_buffer = realloc(state->data_buffer, + state->length); + + } + + /* Append to buffer */ + memcpy(&(state->data_buffer[state->used]), pcm_data, length); + state->used += length; + +} + +/* Encoder handlers */ +audio_encoder _wav_encoder = { + .mimetype = "audio/wav", + .begin_handler = wav_encoder_begin_handler, + .write_handler = wav_encoder_write_handler, + .end_handler = wav_encoder_end_handler +}; + +/* Actual encoder */ +audio_encoder* wav_encoder = &_wav_encoder; +