From 0195a6ad1c6d7b1da22b8fdaa09acfa3f9fa19f7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 30 Jul 2011 15:12:28 -0700 Subject: [PATCH 001/169] Initial commit (stubs) --- protocols/ssh/.gitignore | 35 +++ protocols/ssh/AUTHORS | 1 + protocols/ssh/ChangeLog | 1 + protocols/ssh/LICENSE | 470 +++++++++++++++++++++++++++++++++ protocols/ssh/Makefile.am | 47 ++++ protocols/ssh/README | 74 ++++++ protocols/ssh/configure.in | 57 ++++ protocols/ssh/src/ssh_client.c | 52 ++++ 8 files changed, 737 insertions(+) create mode 100644 protocols/ssh/.gitignore create mode 100644 protocols/ssh/AUTHORS create mode 100644 protocols/ssh/ChangeLog create mode 100644 protocols/ssh/LICENSE create mode 100644 protocols/ssh/Makefile.am create mode 100644 protocols/ssh/README create mode 100644 protocols/ssh/configure.in create mode 100644 protocols/ssh/src/ssh_client.c diff --git a/protocols/ssh/.gitignore b/protocols/ssh/.gitignore new file mode 100644 index 00000000..e37f9166 --- /dev/null +++ b/protocols/ssh/.gitignore @@ -0,0 +1,35 @@ + +# 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/ +config.guess +config.log +config.status +config.sub +configure +depcomp +install-sh +libtool +ltmain.sh +missing + diff --git a/protocols/ssh/AUTHORS b/protocols/ssh/AUTHORS new file mode 100644 index 00000000..517d7a79 --- /dev/null +++ b/protocols/ssh/AUTHORS @@ -0,0 +1 @@ +Michael Jumper diff --git a/protocols/ssh/ChangeLog b/protocols/ssh/ChangeLog new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/protocols/ssh/ChangeLog @@ -0,0 +1 @@ + diff --git a/protocols/ssh/LICENSE b/protocols/ssh/LICENSE new file mode 100644 index 00000000..7714141d --- /dev/null +++ b/protocols/ssh/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/ssh/Makefile.am b/protocols/ssh/Makefile.am new file mode 100644 index 00000000..c14a8d0d --- /dev/null +++ b/protocols/ssh/Makefile.am @@ -0,0 +1,47 @@ +# ***** 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-ssh. +# +# 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 -pedantic -Iinclude + +lib_LTLIBRARIES = libguac-client-ssh.la + +libguac_client_ssh_la_SOURCES = src/ssh.c + +libguac_client_ssh_la_LDFLAGS = -version-info 0:0:0 + diff --git a/protocols/ssh/README b/protocols/ssh/README new file mode 100644 index 00000000..fe1f04a3 --- /dev/null +++ b/protocols/ssh/README @@ -0,0 +1,74 @@ + +------------------------------------------------------------ + 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-ssh? +------------------------------------------------------------ + +libguac-client-ssh is a protocol support plugin for the Guacamole proxy (guacd) +which provides support for SSH, the secure shell. + + +------------------------------------------------------------ + Compiling and installing libguac-client-ssh +------------------------------------------------------------ + +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-ssh 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-ssh will now compile. + +3) Install (as root) + + # make install + + libguac-client-ssh 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://sourceforge.net/apps/trac/guacamole/ + diff --git a/protocols/ssh/configure.in b/protocols/ssh/configure.in new file mode 100644 index 00000000..91f922ae --- /dev/null +++ b/protocols/ssh/configure.in @@ -0,0 +1,57 @@ +# ***** 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-ssh. +# +# 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/ssh_client.c) +AM_INIT_AUTOMAKE([libguac-client-ssh], 0.4.0) +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_get_client],, AC_MSG_ERROR("libguac is required for communication via the guacamole protocol")) +AC_CHECK_LIB([cairo], [cairo_create],, AC_MSG_ERROR("cairo is required for drawing instructions")) + +# Checks for header files. +AC_CHECK_HEADERS([guacamole/client.h guacamole/guacio.h guacamole/protocol.h]) + +# Checks for library functions. +AC_FUNC_MALLOC + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c new file mode 100644 index 00000000..713b3c6e --- /dev/null +++ b/protocols/ssh/src/ssh_client.c @@ -0,0 +1,52 @@ + +/* ***** 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-ssh. + * + * 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 + +int guac_client_init(guac_client* client, int argc, char** argv) { + + /* Success */ + return 0; + +} + From 69dbead349fa1292ce791dba90ca6e0eb507f715 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 31 Jul 2011 20:51:19 -0700 Subject: [PATCH 002/169] Pango + working print() function --- protocols/ssh/Makefile.am | 5 +- protocols/ssh/configure.in | 2 + protocols/ssh/src/ssh_client.c | 234 +++++++++++++++++++++++++++++++++ 3 files changed, 239 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index c14a8d0d..c728c364 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -37,11 +37,12 @@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 -AM_CFLAGS = -Werror -Wall -pedantic -Iinclude lib_LTLIBRARIES = libguac-client-ssh.la -libguac_client_ssh_la_SOURCES = src/ssh.c +libguac_client_ssh_la_SOURCES = src/ssh_client.c +libguac_client_ssh_la_CFLAGS = -Werror -Wall -pedantic -Iinclude @PANGO_CFLAGS@ @PANGOCAIRO_CFLAGS@ +libguac_client_ssh_la_LIBADD = @PANGO_LIBS@ @PANGOCAIRO_LIBS@ libguac_client_ssh_la_LDFLAGS = -version-info 0:0:0 diff --git a/protocols/ssh/configure.in b/protocols/ssh/configure.in index 91f922ae..e4ea8ae4 100644 --- a/protocols/ssh/configure.in +++ b/protocols/ssh/configure.in @@ -46,6 +46,8 @@ AC_PROG_LIBTOOL # Checks for libraries. AC_CHECK_LIB([guac], [guac_get_client],, AC_MSG_ERROR("libguac is required for communication via the guacamole protocol")) AC_CHECK_LIB([cairo], [cairo_create],, AC_MSG_ERROR("cairo is required for drawing instructions")) +PKG_CHECK_MODULES([PANGO], pango); +PKG_CHECK_MODULES([PANGOCAIRO], pangocairo); # Checks for header files. AC_CHECK_HEADERS([guacamole/client.h guacamole/guacio.h guacamole/protocol.h]) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 713b3c6e..3f108072 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -38,15 +38,249 @@ #include #include +#include +#include + #include #include #include #include +/* Client plugin arguments */ +const char* GUAC_CLIENT_ARGS[] = { + "hostname", + "port", + NULL +}; + +typedef struct ssh_guac_client_data { + + PangoFontDescription* font_desc; + + guac_layer* glyphs[256]; + + int char_width; + int char_height; + + int term_width; + int term_height; + + int cursor_row; + int cursor_col; + +} ssh_guac_client_data; + +int ssh_guac_client_send_glyph(guac_client* client, int row, int col, char c); +int ssh_guac_client_print(guac_client* client, const char* c); + int guac_client_init(guac_client* client, int argc, char** argv) { + GUACIO* io = client->io; + + PangoFontMap* font_map; + PangoFont* font; + PangoFontMetrics* metrics; + PangoContext* context; + + ssh_guac_client_data* client_data = malloc(sizeof(ssh_guac_client_data)); + + client_data->cursor_row = 0; + client_data->cursor_col = 0; + + client_data->term_width = 80; + client_data->term_height = 25; + + /* Get font */ + client_data->font_desc = pango_font_description_new(); + pango_font_description_set_family(client_data->font_desc, "monospace"); + pango_font_description_set_weight(client_data->font_desc, PANGO_WEIGHT_NORMAL); + pango_font_description_set_size(client_data->font_desc, 16*PANGO_SCALE); + + font_map = pango_cairo_font_map_get_default(); + context = pango_font_map_create_context(font_map); + + font = pango_font_map_load_font(font_map, context, client_data->font_desc); + if (font == NULL) { + guac_log_error("Unable to get font."); + return 1; + } + + metrics = pango_font_get_metrics(font, NULL); + if (metrics == NULL) { + guac_log_error("Unable to get font metrics."); + return 1; + } + + /* Calculate character dimensions */ + client_data->char_width = + pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE; + client_data->char_height = + (pango_font_metrics_get_descent(metrics) + + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; + + client->data = client_data; + + /* Send name and dimensions */ + guac_send_name(io, "SSH TEST"); + guac_send_size(io, + client_data->char_width * client_data->term_width, + client_data->char_height * client_data->term_height); + + guac_flush(io); + + ssh_guac_client_print(client, "Hello World!\r\nThis is a test of the new Guacamole SSH client plugin!!!\r\n"); + guac_flush(io); + + ssh_guac_client_print(client, "ROW 1\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 2\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 3\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 4\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 5\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 6\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 8\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 9\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 10\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 11\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 12\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 13\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 14\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 15\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 16\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 17\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 18\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 19\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 20\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 21\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 22\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 23\r\n"); guac_flush(io); + ssh_guac_client_print(client, "ROW 24\r\n"); guac_flush(io); + /* Success */ return 0; } +guac_layer* ssh_guac_client_get_glyph(guac_client* client, char c) { + + GUACIO* io = client->io; + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + guac_layer* glyph; + + cairo_surface_t* surface; + cairo_t* cairo; + + PangoLayout* layout; + + /* Return glyph if exists */ + if (client_data->glyphs[(int) c]) + return client_data->glyphs[(int) c]; + + /* Otherwise, draw glyph */ + surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, + client_data->char_width, client_data->char_height); + cairo = cairo_create(surface); + + /* Get layout */ + layout = pango_cairo_create_layout(cairo); + pango_layout_set_font_description(layout, client_data->font_desc); + pango_layout_set_text(layout, &c, 1); + + /* Draw */ + cairo_set_source_rgba(cairo, 1.0, 1.0, 1.0, 1.0); + cairo_move_to(cairo, 0.0, 0.0); + pango_cairo_show_layout(cairo, layout); + + /* Free all */ + g_object_unref(layout); + cairo_destroy(cairo); + + /* Send glyph and save */ + glyph = guac_client_alloc_buffer(client); + guac_send_png(io, GUAC_COMP_OVER, glyph, 0, 0, surface); + client_data->glyphs[(int) c] = glyph; + + guac_flush(io); + cairo_surface_destroy(surface); + + /* Return glyph */ + return glyph; + +} + +int ssh_guac_client_send_glyph(guac_client* client, int row, int col, char c) { + + GUACIO* io = client->io; + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + guac_layer* glyph = ssh_guac_client_get_glyph(client, c); + + return guac_send_copy(io, + glyph, 0, 0, client_data->char_width, client_data->char_height, + GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, + client_data->char_width * col, + client_data->char_height * row); + +} + +int ssh_guac_client_print(guac_client* client, const char* c) { + + GUACIO* io = client->io; + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + + while (*c != 0) { + + switch (*c) { + + /* Carriage return */ + case '\r': + client_data->cursor_col = 0; + break; + + /* Line feed */ + case '\n': + client_data->cursor_row++; + break; + + /* Displayable chars */ + default: + ssh_guac_client_send_glyph(client, + client_data->cursor_row, + client_data->cursor_col, + *c); + + /* Advance cursor, wrap if necessary */ + client_data->cursor_col++; + if (client_data->cursor_col >= client_data->term_width) { + client_data->cursor_col = 0; + client_data->cursor_row++; + } + } + + /* Scroll up if necessary */ + if (client_data->cursor_row >= client_data->term_height) { + client_data->cursor_row = client_data->term_height - 1; + + /* Copy screen up by one row */ + guac_send_copy(io, + GUAC_DEFAULT_LAYER, 0, client_data->char_height, + client_data->char_width * client_data->term_width, + client_data->char_height * (client_data->term_height - 1), + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, 0); + + /* Fill bottom row with background */ + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, client_data->char_height * (client_data->term_height - 1), + client_data->char_width * client_data->term_width, + client_data->char_height * client_data->term_height, + 0, 0, 0, 255); + + } + + c++; + } + + return 0; + +} + From 4f7c8c98e7d443c31fcd11356e382885bd449c68 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 1 Aug 2011 13:31:48 -0700 Subject: [PATCH 003/169] Actual SSH connection and terminal emulation (testing) --- protocols/ssh/configure.in | 1 + protocols/ssh/src/ssh_client.c | 491 ++++++++++++++++++++++++++++----- 2 files changed, 424 insertions(+), 68 deletions(-) diff --git a/protocols/ssh/configure.in b/protocols/ssh/configure.in index e4ea8ae4..2decbe9d 100644 --- a/protocols/ssh/configure.in +++ b/protocols/ssh/configure.in @@ -46,6 +46,7 @@ AC_PROG_LIBTOOL # Checks for libraries. AC_CHECK_LIB([guac], [guac_get_client],, AC_MSG_ERROR("libguac is required for communication via the guacamole protocol")) AC_CHECK_LIB([cairo], [cairo_create],, AC_MSG_ERROR("cairo is required for drawing instructions")) +AC_CHECK_LIB([ssh], [ssh_new],, AC_MSG_ERROR("libssh is required")) PKG_CHECK_MODULES([PANGO], pango); PKG_CHECK_MODULES([PANGOCAIRO], pangocairo); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 3f108072..91749e21 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -38,6 +38,8 @@ #include #include +#include + #include #include @@ -46,15 +48,26 @@ #include #include +#define SSH_TERM_STATE_NULL 0 +#define SSH_TERM_STATE_ECHO 1 +#define SSH_TERM_STATE_ESC 2 +#define SSH_TERM_STATE_CSI 3 +#define SSH_TERM_STATE_OSC 4 +#define SSH_TERM_STATE_CHARSET 5 + /* Client plugin arguments */ const char* GUAC_CLIENT_ARGS[] = { "hostname", - "port", + "user", + "password", NULL }; typedef struct ssh_guac_client_data { + ssh_session session; + ssh_channel term_channel; + PangoFontDescription* font_desc; guac_layer* glyphs[256]; @@ -64,14 +77,22 @@ typedef struct ssh_guac_client_data { int term_width; int term_height; + int term_state; + + int term_seq_argc; + int term_seq_argv[16]; + char term_seq_argv_buffer[16]; + int term_seq_argv_buffer_current; int cursor_row; int cursor_col; } ssh_guac_client_data; +int ssh_guac_client_handle_messages(guac_client* client); +int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed); int ssh_guac_client_send_glyph(guac_client* client, int row, int col, char c); -int ssh_guac_client_print(guac_client* client, const char* c); +int ssh_guac_client_write(guac_client* client, const char* c, int size); int guac_client_init(guac_client* client, int argc, char** argv) { @@ -87,14 +108,15 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->cursor_row = 0; client_data->cursor_col = 0; - client_data->term_width = 80; - client_data->term_height = 25; + client_data->term_width = 160; + client_data->term_height = 50; + client_data->term_state = SSH_TERM_STATE_ECHO; /* Get font */ client_data->font_desc = pango_font_description_new(); pango_font_description_set_family(client_data->font_desc, "monospace"); pango_font_description_set_weight(client_data->font_desc, PANGO_WEIGHT_NORMAL); - pango_font_description_set_size(client_data->font_desc, 16*PANGO_SCALE); + pango_font_description_set_size(client_data->font_desc, 8*PANGO_SCALE); font_map = pango_cairo_font_map_get_default(); context = pango_font_map_create_context(font_map); @@ -128,32 +150,73 @@ int guac_client_init(guac_client* client, int argc, char** argv) { guac_flush(io); - ssh_guac_client_print(client, "Hello World!\r\nThis is a test of the new Guacamole SSH client plugin!!!\r\n"); - guac_flush(io); + /* Open SSH session */ + client_data->session = ssh_new(); + if (client_data->session == NULL) { + guac_send_error(io, "Unable to create SSH session."); + guac_flush(io); + return 1; + } - ssh_guac_client_print(client, "ROW 1\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 2\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 3\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 4\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 5\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 6\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 8\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 9\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 10\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 11\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 12\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 13\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 14\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 15\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 16\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 17\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 18\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 19\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 20\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 21\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 22\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 23\r\n"); guac_flush(io); - ssh_guac_client_print(client, "ROW 24\r\n"); guac_flush(io); + /* Set session options */ + ssh_options_set(client_data->session, SSH_OPTIONS_HOST, argv[0]); + ssh_options_set(client_data->session, SSH_OPTIONS_USER, argv[1]); + + /* Connect */ + if (ssh_connect(client_data->session) != SSH_OK) { + guac_send_error(io, "Unable to connect via SSH."); + guac_flush(io); + return 1; + } + + /* Authenticate */ + if (ssh_userauth_password(client_data->session, NULL, argv[2]) != SSH_AUTH_SUCCESS) { + guac_send_error(io, "SSH auth failed."); + guac_flush(io); + return 1; + } + + /* Open channel for terminal */ + client_data->term_channel = channel_new(client_data->session); + if (client_data->term_channel == NULL) { + guac_send_error(io, "Unable to open channel."); + guac_flush(io); + return 1; + } + + /* Open session for channel */ + if (channel_open_session(client_data->term_channel) != SSH_OK) { + guac_send_error(io, "Unable to open channel session."); + guac_flush(io); + return 1; + } + + /* Request PTY */ + if (channel_request_pty(client_data->term_channel) != SSH_OK) { + guac_send_error(io, "Unable to allocate PTY for channel."); + guac_flush(io); + return 1; + } + + /* Request PTY size */ + if (channel_change_pty_size(client_data->term_channel, client_data->term_width, client_data->term_height) != SSH_OK) { + guac_send_error(io, "Unable to change PTY size."); + guac_flush(io); + return 1; + } + + /* Request shell */ + if (channel_request_shell(client_data->term_channel) != SSH_OK) { + guac_send_error(io, "Unable to associate shell with PTY."); + guac_flush(io); + return 1; + } + + guac_log_info("SSH connection successful."); + + /* Set handlers */ + client->handle_messages = ssh_guac_client_handle_messages; + client->key_handler = ssh_guac_client_key_handler; /* Success */ return 0; @@ -208,6 +271,34 @@ guac_layer* ssh_guac_client_get_glyph(guac_client* client, char c) { } +int ssh_guac_client_handle_messages(guac_client* client) { + + GUACIO* io = client->io; + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + char buffer[8192]; + + /* While data available, write to terminal */ + int bytes_read = 0; + while (channel_is_open(client_data->term_channel) + && !channel_is_eof(client_data->term_channel) + && (bytes_read = channel_read_nonblocking(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) { + + ssh_guac_client_write(client, buffer, bytes_read); + guac_flush(io); + + } + + /* Notify on error */ + if (bytes_read < 0) { + guac_send_error(io, "Error reading data."); + guac_flush(io); + return 1; + } + + return 0; + +} + int ssh_guac_client_send_glyph(guac_client* client, int row, int col, char c) { GUACIO* io = client->io; @@ -216,71 +307,335 @@ int ssh_guac_client_send_glyph(guac_client* client, int row, int col, char c) { return guac_send_copy(io, glyph, 0, 0, client_data->char_width, client_data->char_height, - GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, client_data->char_width * col, client_data->char_height * row); } -int ssh_guac_client_print(guac_client* client, const char* c) { +int ssh_guac_client_write(guac_client* client, const char* c, int size) { GUACIO* io = client->io; ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - while (*c != 0) { + while (size > 0) { - switch (*c) { + switch (client_data->term_state) { - /* Carriage return */ - case '\r': - client_data->cursor_col = 0; + case SSH_TERM_STATE_NULL: break; - /* Line feed */ - case '\n': - client_data->cursor_row++; - break; + case SSH_TERM_STATE_ECHO: - /* Displayable chars */ - default: - ssh_guac_client_send_glyph(client, - client_data->cursor_row, - client_data->cursor_col, - *c); - - /* Advance cursor, wrap if necessary */ - client_data->cursor_col++; + /* Wrap if necessary */ if (client_data->cursor_col >= client_data->term_width) { client_data->cursor_col = 0; client_data->cursor_row++; } - } - /* Scroll up if necessary */ - if (client_data->cursor_row >= client_data->term_height) { - client_data->cursor_row = client_data->term_height - 1; - - /* Copy screen up by one row */ - guac_send_copy(io, - GUAC_DEFAULT_LAYER, 0, client_data->char_height, - client_data->char_width * client_data->term_width, - client_data->char_height * (client_data->term_height - 1), - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, 0); + /* Scroll up if necessary */ + if (client_data->cursor_row >= client_data->term_height) { + client_data->cursor_row = client_data->term_height - 1; + + /* Copy screen up by one row */ + guac_send_copy(io, + GUAC_DEFAULT_LAYER, 0, client_data->char_height, + client_data->char_width * client_data->term_width, + client_data->char_height * (client_data->term_height - 1), + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, 0); - /* Fill bottom row with background */ - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, client_data->char_height * (client_data->term_height - 1), - client_data->char_width * client_data->term_width, - client_data->char_height * client_data->term_height, - 0, 0, 0, 255); + /* Fill bottom row with background */ + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, client_data->char_height * (client_data->term_height - 1), + client_data->char_width * client_data->term_width, + client_data->char_height * client_data->term_height, + 0, 0, 0, 255); + + } + + + + switch (*c) { + + /* Bell */ + case 0x07: + break; + + /* Backspace */ + case 0x08: + if (client_data->cursor_col >= 1) + client_data->cursor_col--; + break; + + /* Carriage return */ + case '\r': + client_data->cursor_col = 0; + break; + + /* Line feed */ + case '\n': + client_data->cursor_row++; + break; + + /* ESC */ + case 0x1B: + client_data->term_state = SSH_TERM_STATE_ESC; + break; + + /* Displayable chars */ + default: + ssh_guac_client_send_glyph(client, + client_data->cursor_row, + client_data->cursor_col, + *c); + + /* Advance cursor */ + client_data->cursor_col++; + } + + /* End of SSH_TERM_STATE_ECHO */ + break; + + case SSH_TERM_STATE_CHARSET: + client_data->term_state = SSH_TERM_STATE_ECHO; + break; + + case SSH_TERM_STATE_ESC: + + switch (*c) { + + case '(': + client_data->term_state = SSH_TERM_STATE_CHARSET; + break; + + case ']': + client_data->term_state = SSH_TERM_STATE_OSC; + client_data->term_seq_argc = 0; + client_data->term_seq_argv_buffer_current = 0; + break; + + case '[': + client_data->term_state = SSH_TERM_STATE_CSI; + client_data->term_seq_argc = 0; + client_data->term_seq_argv_buffer_current = 0; + break; + + default: + guac_log_info("Unhandled ESC sequence: %c", *c); + client_data->term_state = SSH_TERM_STATE_ECHO; + + } + + /* End of SSH_TERM_STATE_ESC */ + break; + + case SSH_TERM_STATE_OSC: + + /* TODO: Implement OSC */ + if (*c == 0x9C || *c == 0x5C || *c == 0x07) /* ECMA-48 ST (String Terminator */ + client_data->term_state = SSH_TERM_STATE_ECHO; + + /* End of SSH_TERM_STATE_OSC */ + break; + + case SSH_TERM_STATE_CSI: + + /* FIXME: "The sequence of parameters may be preceded by a single question mark. */ + if (*c == '?') + break; /* Ignore question marks for now... */ + + /* Digits get concatenated into argv */ + if (*c >= '0' && *c <= '9') { + + /* Concatenate digit if there is space in buffer */ + if (client_data->term_seq_argv_buffer_current < + sizeof(client_data->term_seq_argv_buffer)) { + + client_data->term_seq_argv_buffer[ + client_data->term_seq_argv_buffer_current++ + ] = *c; + } + + } + + /* Any non-digit stops the parameter, and possibly the sequence */ + else { + + /* At most 16 parameters */ + if (client_data->term_seq_argc < 16) { + /* Finish parameter */ + client_data->term_seq_argv_buffer[client_data->term_seq_argv_buffer_current] = 0; + client_data->term_seq_argv[client_data->term_seq_argc++] = + atoi(client_data->term_seq_argv_buffer); + + /* Prepare for next parameter */ + client_data->term_seq_argv_buffer_current = 0; + } + + /* Handle CSI functions */ + switch (*c) { + + /* H: Move cursor */ + case 'H': + client_data->cursor_row = client_data->term_seq_argv[0] - 1; + client_data->cursor_col = client_data->term_seq_argv[1] - 1; + break; + + /* J: Erase display */ + case 'J': + + /* Erase from cursor to end of display */ + if (client_data->term_seq_argv[0] == 0) { + + /* Until end of line */ + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + client_data->cursor_col * client_data->char_width, + client_data->cursor_row * client_data->char_height, + (client_data->term_width - client_data->cursor_col) * client_data->char_width, + client_data->char_height, + 0, 0, 0, 255); /* Background color */ + + /* Until end of display */ + if (client_data->cursor_row < client_data->term_height - 1) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + (client_data->cursor_row+1) * client_data->char_height, + client_data->term_width * client_data->char_width, + client_data->term_height * client_data->char_height, + 0, 0, 0, 255); /* Background color */ + } + + } + + /* Erase from start to cursor */ + else if (client_data->term_seq_argv[0] == 1) { + + /* Until start of line */ + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + client_data->cursor_row * client_data->char_height, + client_data->cursor_col * client_data->char_width, + client_data->char_height, + 0, 0, 0, 255); /* Background color */ + + /* From start */ + if (client_data->cursor_row >= 1) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + 0, + client_data->term_width * client_data->char_width, + (client_data->cursor_row-1) * client_data->char_height, + 0, 0, 0, 255); /* Background color */ + } + + } + + /* Entire screen */ + else if (client_data->term_seq_argv[0] == 2) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + 0, + client_data->term_width * client_data->char_width, + client_data->term_height * client_data->char_height, + 0, 0, 0, 255); /* Background color */ + } + + break; + + /* K: Erase line */ + case 'K': + + /* Erase from cursor to end of line */ + if (client_data->term_seq_argv[0] == 0) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + client_data->cursor_col * client_data->char_width, + client_data->cursor_row * client_data->char_height, + (client_data->term_width - client_data->cursor_col) * client_data->char_width, + client_data->char_height, + 0, 0, 0, 255); /* Background color */ + } + + /* Erase from start to cursor */ + else if (client_data->term_seq_argv[0] == 1) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + client_data->cursor_row * client_data->char_height, + client_data->cursor_col * client_data->char_width, + client_data->char_height, + 0, 0, 0, 255); /* Background color */ + } + + /* Erase line */ + else if (client_data->term_seq_argv[0] == 2) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + client_data->cursor_row * client_data->char_height, + client_data->term_width * client_data->char_width, + client_data->char_height, + 0, 0, 0, 255); /* Background color */ + } + + break; + + /* Warn of unhandled codes */ + default: + if (*c != ';') + guac_log_info("Unhandled CSI sequence: %c", *c); + + } + + /* If not a semicolon, end of CSI sequence */ + if (*c != ';') + client_data->term_state = SSH_TERM_STATE_ECHO; + + } + + /* End of SSH_TERM_STATE_CSI */ + break; } c++; + size--; } return 0; } +int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { + + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + + /* If key pressed */ + if (pressed) { + + char data; + + /* If simple ASCII key */ + if (keysym >= 0x00 && keysym <= 0xFF) + data = (char) keysym; + + else if (keysym == 0xFF08) data = 0x08; + else if (keysym == 0xFF09) data = 0x09; + else if (keysym == 0xFF0D) data = 0x0D; + + else + return 0; + + return channel_write(client_data->term_channel, &data, 1); + + } + + return 0; + +} From 974aa93e90ac45daabd996e91e3979b105f329cd Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 4 Aug 2011 11:46:21 -0700 Subject: [PATCH 004/169] Moved terminal code into ssh-independent implementation, separated handlers from client auth and init. --- protocols/ssh/Makefile.am | 2 +- protocols/ssh/include/ssh_client.h | 70 ++++ protocols/ssh/include/ssh_handlers.h | 47 +++ protocols/ssh/include/ssh_terminal.h | 89 +++++ protocols/ssh/src/ssh_client.c | 518 ++------------------------- protocols/ssh/src/ssh_handlers.c | 125 +++++++ protocols/ssh/src/ssh_terminal.c | 461 ++++++++++++++++++++++++ 7 files changed, 823 insertions(+), 489 deletions(-) create mode 100644 protocols/ssh/include/ssh_client.h create mode 100644 protocols/ssh/include/ssh_handlers.h create mode 100644 protocols/ssh/include/ssh_terminal.h create mode 100644 protocols/ssh/src/ssh_handlers.c create mode 100644 protocols/ssh/src/ssh_terminal.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index c728c364..015802a6 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -40,7 +40,7 @@ ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libguac-client-ssh.la -libguac_client_ssh_la_SOURCES = src/ssh_client.c +libguac_client_ssh_la_SOURCES = src/ssh_client.c src/ssh_handlers.c src/ssh_terminal.c libguac_client_ssh_la_CFLAGS = -Werror -Wall -pedantic -Iinclude @PANGO_CFLAGS@ @PANGOCAIRO_CFLAGS@ libguac_client_ssh_la_LIBADD = @PANGO_LIBS@ @PANGOCAIRO_LIBS@ diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h new file mode 100644 index 00000000..326140e5 --- /dev/null +++ b/protocols/ssh/include/ssh_client.h @@ -0,0 +1,70 @@ + +/* ***** 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-ssh. + * + * 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 _SSH_GUAC_CLIENT_H +#define _SSH_GUAC_CLIENT_H + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include "ssh_client.h" +#include "ssh_handlers.h" +#include "ssh_terminal.h" + +typedef struct ssh_guac_client_data { + + ssh_session session; + ssh_channel term_channel; + + ssh_guac_terminal* term; + +} ssh_guac_client_data; + +int ssh_guac_client_auth(guac_client* client, const char* password); + +#endif + diff --git a/protocols/ssh/include/ssh_handlers.h b/protocols/ssh/include/ssh_handlers.h new file mode 100644 index 00000000..379a4043 --- /dev/null +++ b/protocols/ssh/include/ssh_handlers.h @@ -0,0 +1,47 @@ + +/* ***** 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-ssh. + * + * 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 _SSH_GUAC_HANDLERS_H +#define _SSH_GUAC_HANDLERS_H + +#include + +int ssh_guac_client_handle_messages(guac_client* client); +int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed); + +#endif + diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h new file mode 100644 index 00000000..ab96aee4 --- /dev/null +++ b/protocols/ssh/include/ssh_terminal.h @@ -0,0 +1,89 @@ + +/* ***** 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-ssh. + * + * 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 _SSH_GUAC_TERMINAL_H +#define _SSH_GUAC_TERMINAL_H + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define SSH_TERM_STATE_NULL 0 +#define SSH_TERM_STATE_ECHO 1 +#define SSH_TERM_STATE_ESC 2 +#define SSH_TERM_STATE_CSI 3 +#define SSH_TERM_STATE_OSC 4 +#define SSH_TERM_STATE_CHARSET 5 + +typedef struct ssh_guac_terminal { + + PangoFontDescription* font_desc; + + guac_client* client; + guac_layer* glyphs[256]; + + int char_width; + int char_height; + + int term_width; + int term_height; + int term_state; + + int term_seq_argc; + int term_seq_argv[16]; + char term_seq_argv_buffer[16]; + int term_seq_argv_buffer_current; + + int cursor_row; + int cursor_col; + +} ssh_guac_terminal; + +ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client); +void ssh_guac_terminal_free(ssh_guac_terminal* term); + +int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size); + +#endif + diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 91749e21..4c992a44 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -40,14 +40,15 @@ #include -#include -#include - #include #include #include #include +#include "ssh_client.h" +#include "ssh_handlers.h" +#include "ssh_terminal.h" + #define SSH_TERM_STATE_NULL 0 #define SSH_TERM_STATE_ECHO 1 #define SSH_TERM_STATE_ESC 2 @@ -63,32 +64,6 @@ const char* GUAC_CLIENT_ARGS[] = { NULL }; -typedef struct ssh_guac_client_data { - - ssh_session session; - ssh_channel term_channel; - - PangoFontDescription* font_desc; - - guac_layer* glyphs[256]; - - int char_width; - int char_height; - - int term_width; - int term_height; - int term_state; - - int term_seq_argc; - int term_seq_argv[16]; - char term_seq_argv_buffer[16]; - int term_seq_argv_buffer_current; - - int cursor_row; - int cursor_col; - -} ssh_guac_client_data; - int ssh_guac_client_handle_messages(guac_client* client); int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed); int ssh_guac_client_send_glyph(guac_client* client, int row, int col, char c); @@ -98,55 +73,18 @@ int guac_client_init(guac_client* client, int argc, char** argv) { GUACIO* io = client->io; - PangoFontMap* font_map; - PangoFont* font; - PangoFontMetrics* metrics; - PangoContext* context; - ssh_guac_client_data* client_data = malloc(sizeof(ssh_guac_client_data)); + ssh_guac_terminal* term = ssh_guac_terminal_create(client); - client_data->cursor_row = 0; - client_data->cursor_col = 0; - - client_data->term_width = 160; - client_data->term_height = 50; - client_data->term_state = SSH_TERM_STATE_ECHO; - - /* Get font */ - client_data->font_desc = pango_font_description_new(); - pango_font_description_set_family(client_data->font_desc, "monospace"); - pango_font_description_set_weight(client_data->font_desc, PANGO_WEIGHT_NORMAL); - pango_font_description_set_size(client_data->font_desc, 8*PANGO_SCALE); - - font_map = pango_cairo_font_map_get_default(); - context = pango_font_map_create_context(font_map); - - font = pango_font_map_load_font(font_map, context, client_data->font_desc); - if (font == NULL) { - guac_log_error("Unable to get font."); - return 1; - } - - metrics = pango_font_get_metrics(font, NULL); - if (metrics == NULL) { - guac_log_error("Unable to get font metrics."); - return 1; - } - - /* Calculate character dimensions */ - client_data->char_width = - pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE; - client_data->char_height = - (pango_font_metrics_get_descent(metrics) - + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; - + /* Init client data */ client->data = client_data; + client_data->term = term; /* Send name and dimensions */ guac_send_name(io, "SSH TEST"); guac_send_size(io, - client_data->char_width * client_data->term_width, - client_data->char_height * client_data->term_height); + term->char_width * term->term_width, + term->char_height * term->term_height); guac_flush(io); @@ -169,8 +107,27 @@ int guac_client_init(guac_client* client, int argc, char** argv) { return 1; } + /* If password provided, authenticate now */ + if (argv[2][0] != '\0') + return ssh_guac_client_auth(client, argv[2]); + + /* Otherwise, prompt for password */ + else { + } + + /* Success */ + return 0; + +} + +int ssh_guac_client_auth(guac_client* client, const char* password) { + + GUACIO* io = client->io; + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + ssh_guac_terminal* term = client_data->term; + /* Authenticate */ - if (ssh_userauth_password(client_data->session, NULL, argv[2]) != SSH_AUTH_SUCCESS) { + if (ssh_userauth_password(client_data->session, NULL, password) != SSH_AUTH_SUCCESS) { guac_send_error(io, "SSH auth failed."); guac_flush(io); return 1; @@ -199,7 +156,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { } /* Request PTY size */ - if (channel_change_pty_size(client_data->term_channel, client_data->term_width, client_data->term_height) != SSH_OK) { + if (channel_change_pty_size(client_data->term_channel, term->term_width, term->term_height) != SSH_OK) { guac_send_error(io, "Unable to change PTY size."); guac_flush(io); return 1; @@ -223,419 +180,4 @@ int guac_client_init(guac_client* client, int argc, char** argv) { } -guac_layer* ssh_guac_client_get_glyph(guac_client* client, char c) { - GUACIO* io = client->io; - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - guac_layer* glyph; - - cairo_surface_t* surface; - cairo_t* cairo; - - PangoLayout* layout; - - /* Return glyph if exists */ - if (client_data->glyphs[(int) c]) - return client_data->glyphs[(int) c]; - - /* Otherwise, draw glyph */ - surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, - client_data->char_width, client_data->char_height); - cairo = cairo_create(surface); - - /* Get layout */ - layout = pango_cairo_create_layout(cairo); - pango_layout_set_font_description(layout, client_data->font_desc); - pango_layout_set_text(layout, &c, 1); - - /* Draw */ - cairo_set_source_rgba(cairo, 1.0, 1.0, 1.0, 1.0); - cairo_move_to(cairo, 0.0, 0.0); - pango_cairo_show_layout(cairo, layout); - - /* Free all */ - g_object_unref(layout); - cairo_destroy(cairo); - - /* Send glyph and save */ - glyph = guac_client_alloc_buffer(client); - guac_send_png(io, GUAC_COMP_OVER, glyph, 0, 0, surface); - client_data->glyphs[(int) c] = glyph; - - guac_flush(io); - cairo_surface_destroy(surface); - - /* Return glyph */ - return glyph; - -} - -int ssh_guac_client_handle_messages(guac_client* client) { - - GUACIO* io = client->io; - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - char buffer[8192]; - - /* While data available, write to terminal */ - int bytes_read = 0; - while (channel_is_open(client_data->term_channel) - && !channel_is_eof(client_data->term_channel) - && (bytes_read = channel_read_nonblocking(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) { - - ssh_guac_client_write(client, buffer, bytes_read); - guac_flush(io); - - } - - /* Notify on error */ - if (bytes_read < 0) { - guac_send_error(io, "Error reading data."); - guac_flush(io); - return 1; - } - - return 0; - -} - -int ssh_guac_client_send_glyph(guac_client* client, int row, int col, char c) { - - GUACIO* io = client->io; - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - guac_layer* glyph = ssh_guac_client_get_glyph(client, c); - - return guac_send_copy(io, - glyph, 0, 0, client_data->char_width, client_data->char_height, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - client_data->char_width * col, - client_data->char_height * row); - -} - -int ssh_guac_client_write(guac_client* client, const char* c, int size) { - - GUACIO* io = client->io; - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - - while (size > 0) { - - switch (client_data->term_state) { - - case SSH_TERM_STATE_NULL: - break; - - case SSH_TERM_STATE_ECHO: - - /* Wrap if necessary */ - if (client_data->cursor_col >= client_data->term_width) { - client_data->cursor_col = 0; - client_data->cursor_row++; - } - - /* Scroll up if necessary */ - if (client_data->cursor_row >= client_data->term_height) { - client_data->cursor_row = client_data->term_height - 1; - - /* Copy screen up by one row */ - guac_send_copy(io, - GUAC_DEFAULT_LAYER, 0, client_data->char_height, - client_data->char_width * client_data->term_width, - client_data->char_height * (client_data->term_height - 1), - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, 0); - - /* Fill bottom row with background */ - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, client_data->char_height * (client_data->term_height - 1), - client_data->char_width * client_data->term_width, - client_data->char_height * client_data->term_height, - 0, 0, 0, 255); - - } - - - - switch (*c) { - - /* Bell */ - case 0x07: - break; - - /* Backspace */ - case 0x08: - if (client_data->cursor_col >= 1) - client_data->cursor_col--; - break; - - /* Carriage return */ - case '\r': - client_data->cursor_col = 0; - break; - - /* Line feed */ - case '\n': - client_data->cursor_row++; - break; - - /* ESC */ - case 0x1B: - client_data->term_state = SSH_TERM_STATE_ESC; - break; - - /* Displayable chars */ - default: - ssh_guac_client_send_glyph(client, - client_data->cursor_row, - client_data->cursor_col, - *c); - - /* Advance cursor */ - client_data->cursor_col++; - } - - /* End of SSH_TERM_STATE_ECHO */ - break; - - case SSH_TERM_STATE_CHARSET: - client_data->term_state = SSH_TERM_STATE_ECHO; - break; - - case SSH_TERM_STATE_ESC: - - switch (*c) { - - case '(': - client_data->term_state = SSH_TERM_STATE_CHARSET; - break; - - case ']': - client_data->term_state = SSH_TERM_STATE_OSC; - client_data->term_seq_argc = 0; - client_data->term_seq_argv_buffer_current = 0; - break; - - case '[': - client_data->term_state = SSH_TERM_STATE_CSI; - client_data->term_seq_argc = 0; - client_data->term_seq_argv_buffer_current = 0; - break; - - default: - guac_log_info("Unhandled ESC sequence: %c", *c); - client_data->term_state = SSH_TERM_STATE_ECHO; - - } - - /* End of SSH_TERM_STATE_ESC */ - break; - - case SSH_TERM_STATE_OSC: - - /* TODO: Implement OSC */ - if (*c == 0x9C || *c == 0x5C || *c == 0x07) /* ECMA-48 ST (String Terminator */ - client_data->term_state = SSH_TERM_STATE_ECHO; - - /* End of SSH_TERM_STATE_OSC */ - break; - - case SSH_TERM_STATE_CSI: - - /* FIXME: "The sequence of parameters may be preceded by a single question mark. */ - if (*c == '?') - break; /* Ignore question marks for now... */ - - /* Digits get concatenated into argv */ - if (*c >= '0' && *c <= '9') { - - /* Concatenate digit if there is space in buffer */ - if (client_data->term_seq_argv_buffer_current < - sizeof(client_data->term_seq_argv_buffer)) { - - client_data->term_seq_argv_buffer[ - client_data->term_seq_argv_buffer_current++ - ] = *c; - } - - } - - /* Any non-digit stops the parameter, and possibly the sequence */ - else { - - /* At most 16 parameters */ - if (client_data->term_seq_argc < 16) { - /* Finish parameter */ - client_data->term_seq_argv_buffer[client_data->term_seq_argv_buffer_current] = 0; - client_data->term_seq_argv[client_data->term_seq_argc++] = - atoi(client_data->term_seq_argv_buffer); - - /* Prepare for next parameter */ - client_data->term_seq_argv_buffer_current = 0; - } - - /* Handle CSI functions */ - switch (*c) { - - /* H: Move cursor */ - case 'H': - client_data->cursor_row = client_data->term_seq_argv[0] - 1; - client_data->cursor_col = client_data->term_seq_argv[1] - 1; - break; - - /* J: Erase display */ - case 'J': - - /* Erase from cursor to end of display */ - if (client_data->term_seq_argv[0] == 0) { - - /* Until end of line */ - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - client_data->cursor_col * client_data->char_width, - client_data->cursor_row * client_data->char_height, - (client_data->term_width - client_data->cursor_col) * client_data->char_width, - client_data->char_height, - 0, 0, 0, 255); /* Background color */ - - /* Until end of display */ - if (client_data->cursor_row < client_data->term_height - 1) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - (client_data->cursor_row+1) * client_data->char_height, - client_data->term_width * client_data->char_width, - client_data->term_height * client_data->char_height, - 0, 0, 0, 255); /* Background color */ - } - - } - - /* Erase from start to cursor */ - else if (client_data->term_seq_argv[0] == 1) { - - /* Until start of line */ - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - client_data->cursor_row * client_data->char_height, - client_data->cursor_col * client_data->char_width, - client_data->char_height, - 0, 0, 0, 255); /* Background color */ - - /* From start */ - if (client_data->cursor_row >= 1) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - 0, - client_data->term_width * client_data->char_width, - (client_data->cursor_row-1) * client_data->char_height, - 0, 0, 0, 255); /* Background color */ - } - - } - - /* Entire screen */ - else if (client_data->term_seq_argv[0] == 2) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - 0, - client_data->term_width * client_data->char_width, - client_data->term_height * client_data->char_height, - 0, 0, 0, 255); /* Background color */ - } - - break; - - /* K: Erase line */ - case 'K': - - /* Erase from cursor to end of line */ - if (client_data->term_seq_argv[0] == 0) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - client_data->cursor_col * client_data->char_width, - client_data->cursor_row * client_data->char_height, - (client_data->term_width - client_data->cursor_col) * client_data->char_width, - client_data->char_height, - 0, 0, 0, 255); /* Background color */ - } - - /* Erase from start to cursor */ - else if (client_data->term_seq_argv[0] == 1) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - client_data->cursor_row * client_data->char_height, - client_data->cursor_col * client_data->char_width, - client_data->char_height, - 0, 0, 0, 255); /* Background color */ - } - - /* Erase line */ - else if (client_data->term_seq_argv[0] == 2) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - client_data->cursor_row * client_data->char_height, - client_data->term_width * client_data->char_width, - client_data->char_height, - 0, 0, 0, 255); /* Background color */ - } - - break; - - /* Warn of unhandled codes */ - default: - if (*c != ';') - guac_log_info("Unhandled CSI sequence: %c", *c); - - } - - /* If not a semicolon, end of CSI sequence */ - if (*c != ';') - client_data->term_state = SSH_TERM_STATE_ECHO; - - } - - /* End of SSH_TERM_STATE_CSI */ - break; - - } - - c++; - size--; - } - - return 0; - -} - -int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { - - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - - /* If key pressed */ - if (pressed) { - - char data; - - /* If simple ASCII key */ - if (keysym >= 0x00 && keysym <= 0xFF) - data = (char) keysym; - - else if (keysym == 0xFF08) data = 0x08; - else if (keysym == 0xFF09) data = 0x09; - else if (keysym == 0xFF0D) data = 0x0D; - - else - return 0; - - return channel_write(client_data->term_channel, &data, 1); - - } - - return 0; - -} diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c new file mode 100644 index 00000000..ddb1b939 --- /dev/null +++ b/protocols/ssh/src/ssh_handlers.c @@ -0,0 +1,125 @@ + +/* ***** 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-ssh. + * + * 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 "ssh_handlers.h" +#include "ssh_client.h" + +int ssh_guac_client_handle_messages(guac_client* client) { + + GUACIO* io = client->io; + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + char buffer[8192]; + + ssh_channel read_channels[2]; + struct timeval timeout; + + /* Channels to read */ + read_channels[0] = client_data->term_channel; + read_channels[1] = NULL; + + /* Time to wait */ + timeout.tv_sec = GUAC_SYNC_FREQUENCY / 1000; + timeout.tv_usec = (GUAC_SYNC_FREQUENCY % 1000) * 1000; + + /* Wait for data to be available */ + if (channel_select(read_channels, NULL, NULL, &timeout) == SSH_OK) { + + int bytes_read = 0; + + /* While data available, write to terminal */ + while (channel_is_open(client_data->term_channel) + && !channel_is_eof(client_data->term_channel) + && (bytes_read = channel_read_nonblocking(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) { + + ssh_guac_terminal_write(client_data->term, buffer, bytes_read); + guac_flush(io); + + } + + /* Notify on error */ + if (bytes_read < 0) { + guac_send_error(io, "Error reading data."); + guac_flush(io); + return 1; + } + } + + return 0; + +} + +int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { + + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + + /* If key pressed */ + if (pressed) { + + char data; + + /* If simple ASCII key */ + if (keysym >= 0x00 && keysym <= 0xFF) + data = (char) keysym; + + else if (keysym == 0xFF08) data = 0x08; + else if (keysym == 0xFF09) data = 0x09; + else if (keysym == 0xFF0D) data = 0x0D; + + else + return 0; + + return channel_write(client_data->term_channel, &data, 1); + + } + + return 0; + +} + diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c new file mode 100644 index 00000000..d55a0383 --- /dev/null +++ b/protocols/ssh/src/ssh_terminal.c @@ -0,0 +1,461 @@ + +/* ***** 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-ssh. + * + * 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 "ssh_terminal.h" + +ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { + + PangoFontMap* font_map; + PangoFont* font; + PangoFontMetrics* metrics; + PangoContext* context; + + ssh_guac_terminal* term = malloc(sizeof(ssh_guac_terminal)); + term->client = client; + + term->cursor_row = 0; + term->cursor_col = 0; + + term->term_width = 160; + term->term_height = 50; + term->term_state = SSH_TERM_STATE_ECHO; + + /* Get font */ + term->font_desc = pango_font_description_new(); + pango_font_description_set_family(term->font_desc, "monospace"); + pango_font_description_set_weight(term->font_desc, PANGO_WEIGHT_NORMAL); + pango_font_description_set_size(term->font_desc, 8*PANGO_SCALE); + + font_map = pango_cairo_font_map_get_default(); + context = pango_font_map_create_context(font_map); + + font = pango_font_map_load_font(font_map, context, term->font_desc); + if (font == NULL) { + guac_log_error("Unable to get font."); + return NULL; + } + + metrics = pango_font_get_metrics(font, NULL); + if (metrics == NULL) { + guac_log_error("Unable to get font metrics."); + return NULL; + } + + /* Calculate character dimensions */ + term->char_width = + pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE; + term->char_height = + (pango_font_metrics_get_descent(metrics) + + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; + + return term; + +} + +void ssh_guac_terminal_free(ssh_guac_terminal* term) { + /* STUB */ +} + +guac_layer* __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { + + GUACIO* io = term->client->io; + guac_layer* glyph; + + cairo_surface_t* surface; + cairo_t* cairo; + + PangoLayout* layout; + + /* Return glyph if exists */ + if (term->glyphs[(int) c]) + return term->glyphs[(int) c]; + + /* Otherwise, draw glyph */ + surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, + term->char_width, term->char_height); + cairo = cairo_create(surface); + + /* Get layout */ + layout = pango_cairo_create_layout(cairo); + pango_layout_set_font_description(layout, term->font_desc); + pango_layout_set_text(layout, &c, 1); + + /* Draw */ + cairo_set_source_rgba(cairo, 1.0, 1.0, 1.0, 1.0); + cairo_move_to(cairo, 0.0, 0.0); + pango_cairo_show_layout(cairo, layout); + + /* Free all */ + g_object_unref(layout); + cairo_destroy(cairo); + + /* Send glyph and save */ + glyph = guac_client_alloc_buffer(term->client); + guac_send_png(io, GUAC_COMP_OVER, glyph, 0, 0, surface); + term->glyphs[(int) c] = glyph; + + guac_flush(io); + cairo_surface_destroy(surface); + + /* Return glyph */ + return glyph; + +} + +int __ssh_guac_terminal_send_glyph(ssh_guac_terminal* term, int row, int col, char c) { + + GUACIO* io = term->client->io; + guac_layer* glyph = __ssh_guac_terminal_get_glyph(term, c); + + return guac_send_copy(io, + glyph, 0, 0, term->char_width, term->char_height, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + term->char_width * col, + term->char_height * row); + +} + +int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size) { + + GUACIO* io = term->client->io; + + while (size > 0) { + + switch (term->term_state) { + + case SSH_TERM_STATE_NULL: + break; + + case SSH_TERM_STATE_ECHO: + + /* Wrap if necessary */ + if (term->cursor_col >= term->term_width) { + term->cursor_col = 0; + term->cursor_row++; + } + + /* Scroll up if necessary */ + if (term->cursor_row >= term->term_height) { + term->cursor_row = term->term_height - 1; + + /* Copy screen up by one row */ + guac_send_copy(io, + GUAC_DEFAULT_LAYER, 0, term->char_height, + term->char_width * term->term_width, + term->char_height * (term->term_height - 1), + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, 0); + + /* Fill bottom row with background */ + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, term->char_height * (term->term_height - 1), + term->char_width * term->term_width, + term->char_height * term->term_height, + 0, 0, 0, 255); + + } + + + + switch (*c) { + + /* Bell */ + case 0x07: + break; + + /* Backspace */ + case 0x08: + if (term->cursor_col >= 1) + term->cursor_col--; + break; + + /* Carriage return */ + case '\r': + term->cursor_col = 0; + break; + + /* Line feed */ + case '\n': + term->cursor_row++; + break; + + /* ESC */ + case 0x1B: + term->term_state = SSH_TERM_STATE_ESC; + break; + + /* Displayable chars */ + default: + __ssh_guac_terminal_send_glyph(term, + term->cursor_row, + term->cursor_col, + *c); + + /* Advance cursor */ + term->cursor_col++; + } + + /* End of SSH_TERM_STATE_ECHO */ + break; + + case SSH_TERM_STATE_CHARSET: + term->term_state = SSH_TERM_STATE_ECHO; + break; + + case SSH_TERM_STATE_ESC: + + switch (*c) { + + case '(': + term->term_state = SSH_TERM_STATE_CHARSET; + break; + + case ']': + term->term_state = SSH_TERM_STATE_OSC; + term->term_seq_argc = 0; + term->term_seq_argv_buffer_current = 0; + break; + + case '[': + term->term_state = SSH_TERM_STATE_CSI; + term->term_seq_argc = 0; + term->term_seq_argv_buffer_current = 0; + break; + + default: + guac_log_info("Unhandled ESC sequence: %c", *c); + term->term_state = SSH_TERM_STATE_ECHO; + + } + + /* End of SSH_TERM_STATE_ESC */ + break; + + case SSH_TERM_STATE_OSC: + + /* TODO: Implement OSC */ + if (*c == 0x9C || *c == 0x5C || *c == 0x07) /* ECMA-48 ST (String Terminator */ + term->term_state = SSH_TERM_STATE_ECHO; + + /* End of SSH_TERM_STATE_OSC */ + break; + + case SSH_TERM_STATE_CSI: + + /* FIXME: "The sequence of parameters may be preceded by a single question mark. */ + if (*c == '?') + break; /* Ignore question marks for now... */ + + /* Digits get concatenated into argv */ + if (*c >= '0' && *c <= '9') { + + /* Concatenate digit if there is space in buffer */ + if (term->term_seq_argv_buffer_current < + sizeof(term->term_seq_argv_buffer)) { + + term->term_seq_argv_buffer[ + term->term_seq_argv_buffer_current++ + ] = *c; + } + + } + + /* Any non-digit stops the parameter, and possibly the sequence */ + else { + + /* At most 16 parameters */ + if (term->term_seq_argc < 16) { + /* Finish parameter */ + term->term_seq_argv_buffer[term->term_seq_argv_buffer_current] = 0; + term->term_seq_argv[term->term_seq_argc++] = + atoi(term->term_seq_argv_buffer); + + /* Prepare for next parameter */ + term->term_seq_argv_buffer_current = 0; + } + + /* Handle CSI functions */ + switch (*c) { + + /* H: Move cursor */ + case 'H': + term->cursor_row = term->term_seq_argv[0] - 1; + term->cursor_col = term->term_seq_argv[1] - 1; + break; + + /* J: Erase display */ + case 'J': + + /* Erase from cursor to end of display */ + if (term->term_seq_argv[0] == 0) { + + /* Until end of line */ + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + term->cursor_col * term->char_width, + term->cursor_row * term->char_height, + (term->term_width - term->cursor_col) * term->char_width, + term->char_height, + 0, 0, 0, 255); /* Background color */ + + /* Until end of display */ + if (term->cursor_row < term->term_height - 1) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + (term->cursor_row+1) * term->char_height, + term->term_width * term->char_width, + term->term_height * term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + } + + /* Erase from start to cursor */ + else if (term->term_seq_argv[0] == 1) { + + /* Until start of line */ + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + term->cursor_row * term->char_height, + term->cursor_col * term->char_width, + term->char_height, + 0, 0, 0, 255); /* Background color */ + + /* From start */ + if (term->cursor_row >= 1) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + 0, + term->term_width * term->char_width, + (term->cursor_row-1) * term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + } + + /* Entire screen */ + else if (term->term_seq_argv[0] == 2) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + 0, + term->term_width * term->char_width, + term->term_height * term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + break; + + /* K: Erase line */ + case 'K': + + /* Erase from cursor to end of line */ + if (term->term_seq_argv[0] == 0) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + term->cursor_col * term->char_width, + term->cursor_row * term->char_height, + (term->term_width - term->cursor_col) * term->char_width, + term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + /* Erase from start to cursor */ + else if (term->term_seq_argv[0] == 1) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + term->cursor_row * term->char_height, + term->cursor_col * term->char_width, + term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + /* Erase line */ + else if (term->term_seq_argv[0] == 2) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + term->cursor_row * term->char_height, + term->term_width * term->char_width, + term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + break; + + /* Warn of unhandled codes */ + default: + if (*c != ';') + guac_log_info("Unhandled CSI sequence: %c", *c); + + } + + /* If not a semicolon, end of CSI sequence */ + if (*c != ';') + term->term_state = SSH_TERM_STATE_ECHO; + + } + + /* End of SSH_TERM_STATE_CSI */ + break; + + } + + c++; + size--; + } + + return 0; + +} + From e3d1a3f02367ec65bbba5e36f2be540e54df2c73 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 4 Aug 2011 19:17:44 -0700 Subject: [PATCH 005/169] Switching to handler functions rather than state values. --- protocols/ssh/Makefile.am | 2 +- protocols/ssh/include/ssh_client.h | 3 + protocols/ssh/include/ssh_terminal.h | 16 +- protocols/ssh/include/ssh_terminal_handlers.h | 61 ++++ protocols/ssh/src/ssh_client.c | 55 ++- protocols/ssh/src/ssh_terminal.c | 294 +--------------- protocols/ssh/src/ssh_terminal_handlers.c | 332 ++++++++++++++++++ 7 files changed, 459 insertions(+), 304 deletions(-) create mode 100644 protocols/ssh/include/ssh_terminal_handlers.h create mode 100644 protocols/ssh/src/ssh_terminal_handlers.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 015802a6..915f9ba9 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -40,7 +40,7 @@ ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libguac-client-ssh.la -libguac_client_ssh_la_SOURCES = src/ssh_client.c src/ssh_handlers.c src/ssh_terminal.c +libguac_client_ssh_la_SOURCES = src/ssh_client.c src/ssh_handlers.c src/ssh_terminal.c src/ssh_terminal_handlers.c libguac_client_ssh_la_CFLAGS = -Werror -Wall -pedantic -Iinclude @PANGO_CFLAGS@ @PANGOCAIRO_CFLAGS@ libguac_client_ssh_la_LIBADD = @PANGO_LIBS@ @PANGOCAIRO_LIBS@ diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index 326140e5..205cc930 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -62,6 +62,9 @@ typedef struct ssh_guac_client_data { ssh_guac_terminal* term; + char password[1024]; + int password_length; + } ssh_guac_client_data; int ssh_guac_client_auth(guac_client* client, const char* password); diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index ab96aee4..b5cb33ed 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -49,14 +49,11 @@ #include #include -#define SSH_TERM_STATE_NULL 0 -#define SSH_TERM_STATE_ECHO 1 -#define SSH_TERM_STATE_ESC 2 -#define SSH_TERM_STATE_CSI 3 -#define SSH_TERM_STATE_OSC 4 -#define SSH_TERM_STATE_CHARSET 5 +typedef struct ssh_guac_terminal ssh_guac_terminal; -typedef struct ssh_guac_terminal { +typedef int ssh_guac_terminal_char_handler(ssh_guac_terminal* term, char c); + +struct ssh_guac_terminal { PangoFontDescription* font_desc; @@ -68,7 +65,7 @@ typedef struct ssh_guac_terminal { int term_width; int term_height; - int term_state; + ssh_guac_terminal_char_handler* char_handler; int term_seq_argc; int term_seq_argv[16]; @@ -78,12 +75,13 @@ typedef struct ssh_guac_terminal { int cursor_row; int cursor_col; -} ssh_guac_terminal; +}; ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client); void ssh_guac_terminal_free(ssh_guac_terminal* term); int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size); +int ssh_guac_terminal_send_glyph(ssh_guac_terminal* term, int row, int col, char c); #endif diff --git a/protocols/ssh/include/ssh_terminal_handlers.h b/protocols/ssh/include/ssh_terminal_handlers.h new file mode 100644 index 00000000..ed6b7430 --- /dev/null +++ b/protocols/ssh/include/ssh_terminal_handlers.h @@ -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-ssh. + * + * 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 _SSH_GUAC_TERMINAL_HANDLERS +#define _SSH_GUAC_TERMINAL_HANDLERS + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "ssh_terminal.h" + +int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c); +int ssh_guac_terminal_escape(ssh_guac_terminal* term, char c); +int ssh_guac_terminal_charset(ssh_guac_terminal* term, char c); +int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c); +int ssh_guac_terminal_osc(ssh_guac_terminal* term, char c); + +#endif + diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 4c992a44..49147fb2 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -64,10 +64,49 @@ const char* GUAC_CLIENT_ARGS[] = { NULL }; -int ssh_guac_client_handle_messages(guac_client* client); -int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed); -int ssh_guac_client_send_glyph(guac_client* client, int row, int col, char c); -int ssh_guac_client_write(guac_client* client, const char* c, int size); +int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pressed) { + + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + + /* If key pressed */ + if (pressed) { + + /* If simple ASCII key */ + if (keysym >= 0x00 && keysym <= 0xFF) { + /* Add to password */ + client_data->password[client_data->password_length++] = keysym; + ssh_guac_terminal_write(client_data->term, "*", 1); + guac_flush(client->io); + } + else if (keysym == 0xFF08) { + + if (client_data->password_length > 0) { + client_data->password_length--; + + /* Backspace */ + ssh_guac_terminal_write(client_data->term, "\x08\x1B[K", 4); + guac_flush(client->io); + } + + } + else if (keysym == 0xFF0D) { + + /* Finish password */ + client_data->password[client_data->password_length] = '\0'; + + /* Clear screen */ + ssh_guac_terminal_write(client_data->term, "\x1B[2J\x1B[1;1H", 10); + guac_flush(client->io); + + return ssh_guac_client_auth(client, client_data->password); + + } + + } + + return 0; + +} int guac_client_init(guac_client* client, int argc, char** argv) { @@ -113,6 +152,13 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Otherwise, prompt for password */ else { + + client_data->password_length = 0; + ssh_guac_terminal_write(client_data->term, "Password: ", 10); + guac_flush(client->io); + + client->key_handler = ssh_guac_client_password_key_handler; + } /* Success */ @@ -181,3 +227,4 @@ int ssh_guac_client_auth(guac_client* client, const char* password) { } + diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index d55a0383..99aa273c 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -47,6 +47,7 @@ #include #include "ssh_terminal.h" +#include "ssh_terminal_handlers.h" ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { @@ -63,7 +64,7 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { term->term_width = 160; term->term_height = 50; - term->term_state = SSH_TERM_STATE_ECHO; + term->char_handler = ssh_guac_terminal_echo; /* Get font */ term->font_desc = pango_font_description_new(); @@ -148,7 +149,7 @@ guac_layer* __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { } -int __ssh_guac_terminal_send_glyph(ssh_guac_terminal* term, int row, int col, char c) { +int ssh_guac_terminal_send_glyph(ssh_guac_terminal* term, int row, int col, char c) { GUACIO* io = term->client->io; guac_layer* glyph = __ssh_guac_terminal_get_glyph(term, c); @@ -163,295 +164,8 @@ int __ssh_guac_terminal_send_glyph(ssh_guac_terminal* term, int row, int col, ch int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size) { - GUACIO* io = term->client->io; - while (size > 0) { - - switch (term->term_state) { - - case SSH_TERM_STATE_NULL: - break; - - case SSH_TERM_STATE_ECHO: - - /* Wrap if necessary */ - if (term->cursor_col >= term->term_width) { - term->cursor_col = 0; - term->cursor_row++; - } - - /* Scroll up if necessary */ - if (term->cursor_row >= term->term_height) { - term->cursor_row = term->term_height - 1; - - /* Copy screen up by one row */ - guac_send_copy(io, - GUAC_DEFAULT_LAYER, 0, term->char_height, - term->char_width * term->term_width, - term->char_height * (term->term_height - 1), - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, 0); - - /* Fill bottom row with background */ - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, term->char_height * (term->term_height - 1), - term->char_width * term->term_width, - term->char_height * term->term_height, - 0, 0, 0, 255); - - } - - - - switch (*c) { - - /* Bell */ - case 0x07: - break; - - /* Backspace */ - case 0x08: - if (term->cursor_col >= 1) - term->cursor_col--; - break; - - /* Carriage return */ - case '\r': - term->cursor_col = 0; - break; - - /* Line feed */ - case '\n': - term->cursor_row++; - break; - - /* ESC */ - case 0x1B: - term->term_state = SSH_TERM_STATE_ESC; - break; - - /* Displayable chars */ - default: - __ssh_guac_terminal_send_glyph(term, - term->cursor_row, - term->cursor_col, - *c); - - /* Advance cursor */ - term->cursor_col++; - } - - /* End of SSH_TERM_STATE_ECHO */ - break; - - case SSH_TERM_STATE_CHARSET: - term->term_state = SSH_TERM_STATE_ECHO; - break; - - case SSH_TERM_STATE_ESC: - - switch (*c) { - - case '(': - term->term_state = SSH_TERM_STATE_CHARSET; - break; - - case ']': - term->term_state = SSH_TERM_STATE_OSC; - term->term_seq_argc = 0; - term->term_seq_argv_buffer_current = 0; - break; - - case '[': - term->term_state = SSH_TERM_STATE_CSI; - term->term_seq_argc = 0; - term->term_seq_argv_buffer_current = 0; - break; - - default: - guac_log_info("Unhandled ESC sequence: %c", *c); - term->term_state = SSH_TERM_STATE_ECHO; - - } - - /* End of SSH_TERM_STATE_ESC */ - break; - - case SSH_TERM_STATE_OSC: - - /* TODO: Implement OSC */ - if (*c == 0x9C || *c == 0x5C || *c == 0x07) /* ECMA-48 ST (String Terminator */ - term->term_state = SSH_TERM_STATE_ECHO; - - /* End of SSH_TERM_STATE_OSC */ - break; - - case SSH_TERM_STATE_CSI: - - /* FIXME: "The sequence of parameters may be preceded by a single question mark. */ - if (*c == '?') - break; /* Ignore question marks for now... */ - - /* Digits get concatenated into argv */ - if (*c >= '0' && *c <= '9') { - - /* Concatenate digit if there is space in buffer */ - if (term->term_seq_argv_buffer_current < - sizeof(term->term_seq_argv_buffer)) { - - term->term_seq_argv_buffer[ - term->term_seq_argv_buffer_current++ - ] = *c; - } - - } - - /* Any non-digit stops the parameter, and possibly the sequence */ - else { - - /* At most 16 parameters */ - if (term->term_seq_argc < 16) { - /* Finish parameter */ - term->term_seq_argv_buffer[term->term_seq_argv_buffer_current] = 0; - term->term_seq_argv[term->term_seq_argc++] = - atoi(term->term_seq_argv_buffer); - - /* Prepare for next parameter */ - term->term_seq_argv_buffer_current = 0; - } - - /* Handle CSI functions */ - switch (*c) { - - /* H: Move cursor */ - case 'H': - term->cursor_row = term->term_seq_argv[0] - 1; - term->cursor_col = term->term_seq_argv[1] - 1; - break; - - /* J: Erase display */ - case 'J': - - /* Erase from cursor to end of display */ - if (term->term_seq_argv[0] == 0) { - - /* Until end of line */ - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - term->cursor_col * term->char_width, - term->cursor_row * term->char_height, - (term->term_width - term->cursor_col) * term->char_width, - term->char_height, - 0, 0, 0, 255); /* Background color */ - - /* Until end of display */ - if (term->cursor_row < term->term_height - 1) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - (term->cursor_row+1) * term->char_height, - term->term_width * term->char_width, - term->term_height * term->char_height, - 0, 0, 0, 255); /* Background color */ - } - - } - - /* Erase from start to cursor */ - else if (term->term_seq_argv[0] == 1) { - - /* Until start of line */ - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - term->cursor_row * term->char_height, - term->cursor_col * term->char_width, - term->char_height, - 0, 0, 0, 255); /* Background color */ - - /* From start */ - if (term->cursor_row >= 1) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - 0, - term->term_width * term->char_width, - (term->cursor_row-1) * term->char_height, - 0, 0, 0, 255); /* Background color */ - } - - } - - /* Entire screen */ - else if (term->term_seq_argv[0] == 2) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - 0, - term->term_width * term->char_width, - term->term_height * term->char_height, - 0, 0, 0, 255); /* Background color */ - } - - break; - - /* K: Erase line */ - case 'K': - - /* Erase from cursor to end of line */ - if (term->term_seq_argv[0] == 0) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - term->cursor_col * term->char_width, - term->cursor_row * term->char_height, - (term->term_width - term->cursor_col) * term->char_width, - term->char_height, - 0, 0, 0, 255); /* Background color */ - } - - /* Erase from start to cursor */ - else if (term->term_seq_argv[0] == 1) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - term->cursor_row * term->char_height, - term->cursor_col * term->char_width, - term->char_height, - 0, 0, 0, 255); /* Background color */ - } - - /* Erase line */ - else if (term->term_seq_argv[0] == 2) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - term->cursor_row * term->char_height, - term->term_width * term->char_width, - term->char_height, - 0, 0, 0, 255); /* Background color */ - } - - break; - - /* Warn of unhandled codes */ - default: - if (*c != ';') - guac_log_info("Unhandled CSI sequence: %c", *c); - - } - - /* If not a semicolon, end of CSI sequence */ - if (*c != ';') - term->term_state = SSH_TERM_STATE_ECHO; - - } - - /* End of SSH_TERM_STATE_CSI */ - break; - - } - - c++; + term->char_handler(term, *(c++)); size--; } diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c new file mode 100644 index 00000000..710a6eeb --- /dev/null +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -0,0 +1,332 @@ + +/* ***** 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-ssh. + * + * 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 "ssh_terminal.h" +#include "ssh_terminal_handlers.h" + +int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { + + GUACIO* io = term->client->io; + + /* Wrap if necessary */ + if (term->cursor_col >= term->term_width) { + term->cursor_col = 0; + term->cursor_row++; + } + + /* Scroll up if necessary */ + if (term->cursor_row >= term->term_height) { + term->cursor_row = term->term_height - 1; + + /* Copy screen up by one row */ + guac_send_copy(io, + GUAC_DEFAULT_LAYER, 0, term->char_height, + term->char_width * term->term_width, + term->char_height * (term->term_height - 1), + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, 0); + + /* Fill bottom row with background */ + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, term->char_height * (term->term_height - 1), + term->char_width * term->term_width, + term->char_height * term->term_height, + 0, 0, 0, 255); + + } + + switch (c) { + + /* Bell */ + case 0x07: + break; + + /* Backspace */ + case 0x08: + if (term->cursor_col >= 1) + term->cursor_col--; + break; + + /* Carriage return */ + case '\r': + term->cursor_col = 0; + break; + + /* Line feed */ + case '\n': + term->cursor_row++; + break; + + /* ESC */ + case 0x1B: + term->char_handler = ssh_guac_terminal_escape; + break; + + /* Displayable chars */ + default: + ssh_guac_terminal_send_glyph(term, + term->cursor_row, + term->cursor_col, + c); + + /* Advance cursor */ + term->cursor_col++; + } + + return 0; + +} + +int ssh_guac_terminal_escape(ssh_guac_terminal* term, char c) { + + switch (c) { + + case '(': + term->char_handler = ssh_guac_terminal_charset; + break; + + case ']': + term->char_handler = ssh_guac_terminal_osc; + term->term_seq_argc = 0; + term->term_seq_argv_buffer_current = 0; + break; + + case '[': + term->char_handler = ssh_guac_terminal_csi; + term->term_seq_argc = 0; + term->term_seq_argv_buffer_current = 0; + break; + + default: + guac_log_info("Unhandled ESC sequence: %c", c); + term->char_handler = ssh_guac_terminal_echo; + + } + + return 0; + +} + +int ssh_guac_terminal_charset(ssh_guac_terminal* term, char c) { + term->char_handler = ssh_guac_terminal_echo; + return 0; +} + +int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { + + GUACIO* io = term->client->io; + + /* FIXME: "The sequence of parameters may be preceded by a single question mark. */ + if (c == '?') + return 0; + + /* Digits get concatenated into argv */ + if (c >= '0' && c <= '9') { + + /* Concatenate digit if there is space in buffer */ + if (term->term_seq_argv_buffer_current < + sizeof(term->term_seq_argv_buffer)) { + + term->term_seq_argv_buffer[ + term->term_seq_argv_buffer_current++ + ] = c; + } + + } + + /* Any non-digit stops the parameter, and possibly the sequence */ + else { + + /* At most 16 parameters */ + if (term->term_seq_argc < 16) { + /* Finish parameter */ + term->term_seq_argv_buffer[term->term_seq_argv_buffer_current] = 0; + term->term_seq_argv[term->term_seq_argc++] = + atoi(term->term_seq_argv_buffer); + + /* Prepare for next parameter */ + term->term_seq_argv_buffer_current = 0; + } + + /* Handle CSI functions */ + switch (c) { + + /* H: Move cursor */ + case 'H': + term->cursor_row = term->term_seq_argv[0] - 1; + term->cursor_col = term->term_seq_argv[1] - 1; + break; + + /* J: Erase display */ + case 'J': + + /* Erase from cursor to end of display */ + if (term->term_seq_argv[0] == 0) { + + /* Until end of line */ + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + term->cursor_col * term->char_width, + term->cursor_row * term->char_height, + (term->term_width - term->cursor_col) * term->char_width, + term->char_height, + 0, 0, 0, 255); /* Background color */ + + /* Until end of display */ + if (term->cursor_row < term->term_height - 1) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + (term->cursor_row+1) * term->char_height, + term->term_width * term->char_width, + term->term_height * term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + } + + /* Erase from start to cursor */ + else if (term->term_seq_argv[0] == 1) { + + /* Until start of line */ + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + term->cursor_row * term->char_height, + term->cursor_col * term->char_width, + term->char_height, + 0, 0, 0, 255); /* Background color */ + + /* From start */ + if (term->cursor_row >= 1) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + 0, + term->term_width * term->char_width, + (term->cursor_row-1) * term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + } + + /* Entire screen */ + else if (term->term_seq_argv[0] == 2) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + 0, + term->term_width * term->char_width, + term->term_height * term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + break; + + /* K: Erase line */ + case 'K': + + /* Erase from cursor to end of line */ + if (term->term_seq_argv[0] == 0) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + term->cursor_col * term->char_width, + term->cursor_row * term->char_height, + (term->term_width - term->cursor_col) * term->char_width, + term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + /* Erase from start to cursor */ + else if (term->term_seq_argv[0] == 1) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + term->cursor_row * term->char_height, + term->cursor_col * term->char_width, + term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + /* Erase line */ + else if (term->term_seq_argv[0] == 2) { + guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + 0, + term->cursor_row * term->char_height, + term->term_width * term->char_width, + term->char_height, + 0, 0, 0, 255); /* Background color */ + } + + break; + + /* Warn of unhandled codes */ + default: + if (c != ';') + guac_log_info("Unhandled CSI sequence: %c", c); + + } + + /* If not a semicolon, end of CSI sequence */ + if (c != ';') + term->char_handler = ssh_guac_terminal_echo; + + } + + return 0; + +} + +int ssh_guac_terminal_osc(ssh_guac_terminal* term, char c) { + /* TODO: Implement OSC */ + if (c == 0x9C || c == 0x5C || c == 0x07) /* ECMA-48 ST (String Terminator */ + term->char_handler = ssh_guac_terminal_echo; + return 0; +} + From f2732acc5a7a3daf3e2a641a6e156f5e68b22e1c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 4 Aug 2011 19:36:04 -0700 Subject: [PATCH 006/169] Switching to per-state static variables rather than per-terminal instance variables (states need not be thread safe) --- protocols/ssh/include/ssh_terminal.h | 12 ++--- protocols/ssh/src/ssh_terminal_handlers.c | 57 ++++++++++++----------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index b5cb33ed..bf0d020f 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -55,9 +55,9 @@ typedef int ssh_guac_terminal_char_handler(ssh_guac_terminal* term, char c); struct ssh_guac_terminal { - PangoFontDescription* font_desc; - guac_client* client; + + PangoFontDescription* font_desc; guac_layer* glyphs[256]; int char_width; @@ -65,16 +65,12 @@ struct ssh_guac_terminal { int term_width; int term_height; - ssh_guac_terminal_char_handler* char_handler; - - int term_seq_argc; - int term_seq_argv[16]; - char term_seq_argv_buffer[16]; - int term_seq_argv_buffer_current; int cursor_row; int cursor_col; + ssh_guac_terminal_char_handler* char_handler; + }; ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client); diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 710a6eeb..b86f85d1 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -132,14 +132,10 @@ int ssh_guac_terminal_escape(ssh_guac_terminal* term, char c) { case ']': term->char_handler = ssh_guac_terminal_osc; - term->term_seq_argc = 0; - term->term_seq_argv_buffer_current = 0; break; case '[': term->char_handler = ssh_guac_terminal_csi; - term->term_seq_argc = 0; - term->term_seq_argv_buffer_current = 0; break; default: @@ -161,6 +157,14 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { GUACIO* io = term->client->io; + /* CSI function arguments */ + static int argc = 0; + static int argv[16]; + + /* Argument building counter and buffer */ + static int argv_length = 0; + static char argv_buffer[256]; + /* FIXME: "The sequence of parameters may be preceded by a single question mark. */ if (c == '?') return 0; @@ -169,13 +173,8 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { if (c >= '0' && c <= '9') { /* Concatenate digit if there is space in buffer */ - if (term->term_seq_argv_buffer_current < - sizeof(term->term_seq_argv_buffer)) { - - term->term_seq_argv_buffer[ - term->term_seq_argv_buffer_current++ - ] = c; - } + if (argv_length < sizeof(argv_buffer)-1) + argv_buffer[argv_length++] = c; } @@ -183,14 +182,15 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { else { /* At most 16 parameters */ - if (term->term_seq_argc < 16) { + if (argc < 16) { + /* Finish parameter */ - term->term_seq_argv_buffer[term->term_seq_argv_buffer_current] = 0; - term->term_seq_argv[term->term_seq_argc++] = - atoi(term->term_seq_argv_buffer); + argv_buffer[argv_length] = 0; + argv[argc++] = atoi(argv_buffer); /* Prepare for next parameter */ - term->term_seq_argv_buffer_current = 0; + argv_length = 0; + } /* Handle CSI functions */ @@ -198,15 +198,15 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* H: Move cursor */ case 'H': - term->cursor_row = term->term_seq_argv[0] - 1; - term->cursor_col = term->term_seq_argv[1] - 1; + term->cursor_row = argv[0] - 1; + term->cursor_col = argv[1] - 1; break; /* J: Erase display */ case 'J': /* Erase from cursor to end of display */ - if (term->term_seq_argv[0] == 0) { + if (argv[0] == 0) { /* Until end of line */ guac_send_rect(io, @@ -231,7 +231,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { } /* Erase from start to cursor */ - else if (term->term_seq_argv[0] == 1) { + else if (argv[0] == 1) { /* Until start of line */ guac_send_rect(io, @@ -256,7 +256,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { } /* Entire screen */ - else if (term->term_seq_argv[0] == 2) { + else if (argv[0] == 2) { guac_send_rect(io, GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, @@ -272,7 +272,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { case 'K': /* Erase from cursor to end of line */ - if (term->term_seq_argv[0] == 0) { + if (argv[0] == 0) { guac_send_rect(io, GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, term->cursor_col * term->char_width, @@ -283,7 +283,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { } /* Erase from start to cursor */ - else if (term->term_seq_argv[0] == 1) { + else if (argv[0] == 1) { guac_send_rect(io, GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, @@ -294,7 +294,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { } /* Erase line */ - else if (term->term_seq_argv[0] == 2) { + else if (argv[0] == 2) { guac_send_rect(io, GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, @@ -314,8 +314,13 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { } /* If not a semicolon, end of CSI sequence */ - if (c != ';') - term->char_handler = ssh_guac_terminal_echo; + if (c != ';') { + term->char_handler = ssh_guac_terminal_echo; + + /* Reset argument counters */ + argc = 0; + argv_length = 0; + } } From 638776e70094aa2570af0a7ed38ec220dad3d753 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Aug 2011 00:20:09 -0700 Subject: [PATCH 007/169] Added more abstract terminal functions, refactored away use of guac_send_* in terminial handlers, simplified code. --- protocols/ssh/include/ssh_terminal.h | 13 +++ protocols/ssh/src/ssh_terminal.c | 99 ++++++++++++++++ protocols/ssh/src/ssh_terminal_handlers.c | 130 +++++----------------- 3 files changed, 138 insertions(+), 104 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index bf0d020f..7c9b7f4a 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -79,5 +79,18 @@ void ssh_guac_terminal_free(ssh_guac_terminal* term); int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size); int ssh_guac_terminal_send_glyph(ssh_guac_terminal* term, int row, int col, char c); +int ssh_guac_terminal_copy(ssh_guac_terminal* term, + int src_row, int src_col, int rows, int cols, + int dst_row, int dst_col); +int ssh_guac_terminal_clear(ssh_guac_terminal* term, + int row, int col, int rows, int cols); + +int ssh_guac_terminal_scroll_up(ssh_guac_terminal* term, + int start_row, int end_row, int amount); + +int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, + int start_row, int start_col, + int end_row, int end_col); + #endif diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 99aa273c..6ed440ec 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -173,3 +173,102 @@ int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size) { } +int ssh_guac_terminal_copy(ssh_guac_terminal* term, + int src_row, int src_col, int rows, int cols, + int dst_row, int dst_col) { + + GUACIO* io = term->client->io; + + /* Send copy instruction */ + return guac_send_copy(io, + + GUAC_DEFAULT_LAYER, + src_col * term->char_width, src_row * term->char_height, + cols * term->char_width, rows * term->char_height, + + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + dst_col * term->char_width, dst_row * term->char_height); + +} + + +int ssh_guac_terminal_clear(ssh_guac_terminal* term, + int row, int col, int rows, int cols) { + + GUACIO* io = term->client->io; + + /* Fill with background */ + return guac_send_rect(io, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + + col * term->char_width, row * term->char_height, + cols * term->char_width, rows * term->char_height, + + /* Background */ + 0, 0, 0, 255); + +} + +int ssh_guac_terminal_scroll_up(ssh_guac_terminal* term, + int start_row, int end_row, int amount) { + + /* Calculate height of scroll region */ + int height = end_row - start_row + 1; + + return + + /* Move rows within scroll region up by the given amount */ + ssh_guac_terminal_copy(term, + start_row + amount, 0, + height - amount, term->term_width, + start_row, 0) + + /* Fill new rows with background */ + || ssh_guac_terminal_clear(term, + end_row - amount + 1, 0, amount, term->term_width); + +} + + +int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, + int start_row, int start_col, + int end_row, int end_col) { + + /* If not at far left, must clear sub-region to far right */ + if (start_col > 0) { + + /* Clear from start_col to far right */ + if (ssh_guac_terminal_clear(term, + start_row, start_col, 1, term->term_width - start_col)) + return 1; + + /* One less row to clear */ + start_row++; + } + + /* If not at far right, must clear sub-region to far left */ + if (end_col < term->term_width - 1) { + + /* Clear from far left to end_col */ + if (ssh_guac_terminal_clear(term, + end_row, 0, 1, end_col + 1)) + return 1; + + /* One less row to clear */ + end_row--; + + } + + /* Remaining region now guaranteed rectangular. Clear, if possible */ + if (start_row <= end_row) { + + if (ssh_guac_terminal_clear(term, + start_row, 0, end_row - start_row + 1, term->term_width)) + return 1; + + } + + return 0; + +} + diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index b86f85d1..532d51d7 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -42,17 +42,12 @@ #include #include -#include -#include -#include #include "ssh_terminal.h" #include "ssh_terminal_handlers.h" int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { - GUACIO* io = term->client->io; - /* Wrap if necessary */ if (term->cursor_col >= term->term_width) { term->cursor_col = 0; @@ -62,21 +57,9 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { /* Scroll up if necessary */ if (term->cursor_row >= term->term_height) { term->cursor_row = term->term_height - 1; - - /* Copy screen up by one row */ - guac_send_copy(io, - GUAC_DEFAULT_LAYER, 0, term->char_height, - term->char_width * term->term_width, - term->char_height * (term->term_height - 1), - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, 0, 0); - /* Fill bottom row with background */ - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, term->char_height * (term->term_height - 1), - term->char_width * term->term_width, - term->char_height * term->term_height, - 0, 0, 0, 255); + /* Scroll up by one row */ + ssh_guac_terminal_scroll_up(term, 0, term->term_height - 1, 1); } @@ -155,8 +138,6 @@ int ssh_guac_terminal_charset(ssh_guac_terminal* term, char c) { int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { - GUACIO* io = term->client->io; - /* CSI function arguments */ static int argc = 0; static int argv[16]; @@ -204,67 +185,23 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* J: Erase display */ case 'J': - + /* Erase from cursor to end of display */ - if (argv[0] == 0) { - - /* Until end of line */ - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - term->cursor_col * term->char_width, - term->cursor_row * term->char_height, - (term->term_width - term->cursor_col) * term->char_width, - term->char_height, - 0, 0, 0, 255); /* Background color */ - - /* Until end of display */ - if (term->cursor_row < term->term_height - 1) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - (term->cursor_row+1) * term->char_height, - term->term_width * term->char_width, - term->term_height * term->char_height, - 0, 0, 0, 255); /* Background color */ - } - - } + if (argv[0] == 0) + ssh_guac_terminal_clear_range(term, + term->cursor_row, term->cursor_col, + term->term_height-1, term->term_width-1); /* Erase from start to cursor */ - else if (argv[0] == 1) { - - /* Until start of line */ - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - term->cursor_row * term->char_height, - term->cursor_col * term->char_width, - term->char_height, - 0, 0, 0, 255); /* Background color */ - - /* From start */ - if (term->cursor_row >= 1) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - 0, - term->term_width * term->char_width, - (term->cursor_row-1) * term->char_height, - 0, 0, 0, 255); /* Background color */ - } - - } + else if (argv[0] == 1) + ssh_guac_terminal_clear_range(term, + 0, 0, + term->cursor_row, term->cursor_col); /* Entire screen */ - else if (argv[0] == 2) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - 0, - term->term_width * term->char_width, - term->term_height * term->char_height, - 0, 0, 0, 255); /* Background color */ - } + else if (argv[0] == 2) + ssh_guac_terminal_clear(term, + 0, 0, term->term_height, term->term_width); break; @@ -272,37 +209,22 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { case 'K': /* Erase from cursor to end of line */ - if (argv[0] == 0) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - term->cursor_col * term->char_width, - term->cursor_row * term->char_height, - (term->term_width - term->cursor_col) * term->char_width, - term->char_height, - 0, 0, 0, 255); /* Background color */ - } + if (argv[0] == 0) + ssh_guac_terminal_clear(term, + term->cursor_row, term->cursor_col, + 1, term->term_width - term->cursor_col); /* Erase from start to cursor */ - else if (argv[0] == 1) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - term->cursor_row * term->char_height, - term->cursor_col * term->char_width, - term->char_height, - 0, 0, 0, 255); /* Background color */ - } + else if (argv[0] == 1) + ssh_guac_terminal_clear(term, + term->cursor_row, 0, + 1, term->cursor_col + 1); /* Erase line */ - else if (argv[0] == 2) { - guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - 0, - term->cursor_row * term->char_height, - term->term_width * term->char_width, - term->char_height, - 0, 0, 0, 255); /* Background color */ - } + else if (argv[0] == 2) + ssh_guac_terminal_clear(term, + term->cursor_row, 0, + 1, term->term_width); break; From fc1683da30bd0b546a8334b0d889f42f7bbd5a40 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Aug 2011 00:22:26 -0700 Subject: [PATCH 008/169] Removed unnecessary includes from term handlers. --- protocols/ssh/src/ssh_terminal_handlers.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 532d51d7..3a2735c8 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -36,10 +36,6 @@ * ***** END LICENSE BLOCK ***** */ #include -#include - -#include -#include #include From 5f59ccf5c1ebf463b665b82bc672686c2a6c2354 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Aug 2011 12:14:15 -0700 Subject: [PATCH 009/169] Color palette --- protocols/ssh/include/ssh_terminal.h | 19 ++++++- protocols/ssh/src/ssh_terminal.c | 68 +++++++++++++++++++---- protocols/ssh/src/ssh_terminal_handlers.c | 19 +++++-- 3 files changed, 87 insertions(+), 19 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index 7c9b7f4a..0c4f83cc 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -69,10 +69,24 @@ struct ssh_guac_terminal { int cursor_row; int cursor_col; + int foreground; + int background; + + int default_foreground; + int default_background; + ssh_guac_terminal_char_handler* char_handler; }; +typedef struct ssh_guac_terminal_color { + int red; + int green; + int blue; +} ssh_guac_terminal_color; + +extern const ssh_guac_terminal_color ssh_guac_terminal_palette[16]; + ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client); void ssh_guac_terminal_free(ssh_guac_terminal* term); @@ -82,15 +96,16 @@ int ssh_guac_terminal_send_glyph(ssh_guac_terminal* term, int row, int col, char int ssh_guac_terminal_copy(ssh_guac_terminal* term, int src_row, int src_col, int rows, int cols, int dst_row, int dst_col); + int ssh_guac_terminal_clear(ssh_guac_terminal* term, - int row, int col, int rows, int cols); + int row, int col, int rows, int cols, int background_color); int ssh_guac_terminal_scroll_up(ssh_guac_terminal* term, int start_row, int end_row, int amount); int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, int start_row, int start_col, - int end_row, int end_col); + int end_row, int end_col, int background_color); #endif diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 6ed440ec..0cb506fd 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -49,6 +49,30 @@ #include "ssh_terminal.h" #include "ssh_terminal_handlers.h" +const ssh_guac_terminal_color ssh_guac_terminal_palette[16] = { + + /* Normal colors */ + {0x00, 0x00, 0x00}, /* Black */ + {0x80, 0x00, 0x00}, /* Red */ + {0x00, 0x80, 0x00}, /* Green */ + {0x80, 0x80, 0x00}, /* Brown */ + {0x00, 0x00, 0x80}, /* Blue */ + {0x80, 0x00, 0x80}, /* Magenta */ + {0x00, 0x80, 0x80}, /* Cyan */ + {0x80, 0x80, 0x80}, /* White */ + + /* Intense colors */ + {0x40, 0x40, 0x40}, /* Black */ + {0xFF, 0x00, 0x00}, /* Red */ + {0x00, 0xFF, 0x00}, /* Green */ + {0xFF, 0xFF, 0x00}, /* Brown */ + {0x00, 0x00, 0xFF}, /* Blue */ + {0xFF, 0x00, 0xFF}, /* Magenta */ + {0x00, 0xFF, 0xFF}, /* Cyan */ + {0xFF, 0xFF, 0xFF}, /* White */ + +}; + ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { PangoFontMap* font_map; @@ -59,6 +83,9 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { ssh_guac_terminal* term = malloc(sizeof(ssh_guac_terminal)); term->client = client; + term->foreground = term->default_foreground = 7; /* White */ + term->background = term->default_background = 0; /* Black */ + term->cursor_row = 0; term->cursor_col = 0; @@ -94,6 +121,11 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { (pango_font_metrics_get_descent(metrics) + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; + /* Clear with background color */ + ssh_guac_terminal_clear(term, + 0, 0, term->term_width, term->term_height, + term->background); + return term; } @@ -106,6 +138,10 @@ guac_layer* __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { GUACIO* io = term->client->io; guac_layer* glyph; + + /* Use default foreground color */ + const ssh_guac_terminal_color* color = + &ssh_guac_terminal_palette[term->default_foreground]; cairo_surface_t* surface; cairo_t* cairo; @@ -128,7 +164,12 @@ guac_layer* __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { pango_layout_set_text(layout, &c, 1); /* Draw */ - cairo_set_source_rgba(cairo, 1.0, 1.0, 1.0, 1.0); + cairo_set_source_rgba(cairo, + color->red / 255.0, + color->green / 255.0, + color->blue / 255.0, + 1.0 /* alpha */ ); + cairo_move_to(cairo, 0.0, 0.0); pango_cairo_show_layout(cairo, layout); @@ -193,19 +234,20 @@ int ssh_guac_terminal_copy(ssh_guac_terminal* term, int ssh_guac_terminal_clear(ssh_guac_terminal* term, - int row, int col, int rows, int cols) { + int row, int col, int rows, int cols, int background_color) { GUACIO* io = term->client->io; + const ssh_guac_terminal_color* color = + &ssh_guac_terminal_palette[background_color]; - /* Fill with background */ + /* Fill with color */ return guac_send_rect(io, GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, col * term->char_width, row * term->char_height, cols * term->char_width, rows * term->char_height, - /* Background */ - 0, 0, 0, 255); + color->red, color->green, color->blue, 255); } @@ -214,7 +256,7 @@ int ssh_guac_terminal_scroll_up(ssh_guac_terminal* term, /* Calculate height of scroll region */ int height = end_row - start_row + 1; - + return /* Move rows within scroll region up by the given amount */ @@ -225,21 +267,23 @@ int ssh_guac_terminal_scroll_up(ssh_guac_terminal* term, /* Fill new rows with background */ || ssh_guac_terminal_clear(term, - end_row - amount + 1, 0, amount, term->term_width); + end_row - amount + 1, 0, amount, term->term_width, + term->background); } int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, int start_row, int start_col, - int end_row, int end_col) { + int end_row, int end_col, int background_color) { /* If not at far left, must clear sub-region to far right */ if (start_col > 0) { /* Clear from start_col to far right */ if (ssh_guac_terminal_clear(term, - start_row, start_col, 1, term->term_width - start_col)) + start_row, start_col, 1, term->term_width - start_col, + background_color)) return 1; /* One less row to clear */ @@ -251,7 +295,8 @@ int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, /* Clear from far left to end_col */ if (ssh_guac_terminal_clear(term, - end_row, 0, 1, end_col + 1)) + end_row, 0, 1, end_col + 1, + background_color)) return 1; /* One less row to clear */ @@ -263,7 +308,8 @@ int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, if (start_row <= end_row) { if (ssh_guac_terminal_clear(term, - start_row, 0, end_row - start_row + 1, term->term_width)) + start_row, 0, end_row - start_row + 1, term->term_width, + background_color)) return 1; } diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 3a2735c8..7dcc0e43 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -186,18 +186,21 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { if (argv[0] == 0) ssh_guac_terminal_clear_range(term, term->cursor_row, term->cursor_col, - term->term_height-1, term->term_width-1); + term->term_height-1, term->term_width-1, + term->background); /* Erase from start to cursor */ else if (argv[0] == 1) ssh_guac_terminal_clear_range(term, 0, 0, - term->cursor_row, term->cursor_col); + term->cursor_row, term->cursor_col, + term->background); /* Entire screen */ else if (argv[0] == 2) ssh_guac_terminal_clear(term, - 0, 0, term->term_height, term->term_width); + 0, 0, term->term_height, term->term_width, + term->background); break; @@ -208,19 +211,23 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { if (argv[0] == 0) ssh_guac_terminal_clear(term, term->cursor_row, term->cursor_col, - 1, term->term_width - term->cursor_col); + 1, term->term_width - term->cursor_col, + term->background); + /* Erase from start to cursor */ else if (argv[0] == 1) ssh_guac_terminal_clear(term, term->cursor_row, 0, - 1, term->cursor_col + 1); + 1, term->cursor_col + 1, + term->background); /* Erase line */ else if (argv[0] == 2) ssh_guac_terminal_clear(term, term->cursor_row, 0, - 1, term->term_width); + 1, term->term_width, + term->background); break; From 18cdf2808d4083a1584a2f1bee1b8c2bb2b29261 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Aug 2011 12:41:21 -0700 Subject: [PATCH 010/169] Working colors in CSI --- protocols/ssh/include/ssh_terminal.h | 3 +- protocols/ssh/src/ssh_terminal.c | 54 ++++++++++++++++++++--- protocols/ssh/src/ssh_terminal_handlers.c | 34 +++++++++++++- 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index 0c4f83cc..025d38c0 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -91,7 +91,8 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client); void ssh_guac_terminal_free(ssh_guac_terminal* term); int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size); -int ssh_guac_terminal_send_glyph(ssh_guac_terminal* term, int row, int col, char c); +int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, + char c, int foreground, int background); int ssh_guac_terminal_copy(ssh_guac_terminal* term, int src_row, int src_col, int rows, int cols, diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 0cb506fd..f32027c6 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -123,7 +123,7 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { /* Clear with background color */ ssh_guac_terminal_clear(term, - 0, 0, term->term_width, term->term_height, + 0, 0, term->term_height, term->term_width, term->background); return term; @@ -190,16 +190,56 @@ guac_layer* __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { } -int ssh_guac_terminal_send_glyph(ssh_guac_terminal* term, int row, int col, char c) { +int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, + char c, int foreground, int background) { GUACIO* io = term->client->io; guac_layer* glyph = __ssh_guac_terminal_get_glyph(term, c); - return guac_send_copy(io, - glyph, 0, 0, term->char_width, term->char_height, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - term->char_width * col, - term->char_height * row); + /* Get background color */ + const ssh_guac_terminal_color* background_color = + &ssh_guac_terminal_palette[background]; + + guac_send_copy(io, + glyph, 0, 0, term->char_width, term->char_height, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + term->char_width * col, + term->char_height * row); + + /* If foreground different from default, colorize */ + if (foreground != term->default_foreground) { + + /* Get color */ + const ssh_guac_terminal_color* color = + &ssh_guac_terminal_palette[foreground]; + + /* Colorize letter */ + guac_send_rect(io, + GUAC_COMP_ATOP, GUAC_DEFAULT_LAYER, + + term->char_width * col, term->char_height * row, + term->char_width, term->char_height, + + color->red, + color->green, + color->blue, + 255); + + } + + /* Set background */ + guac_send_rect(io, + GUAC_COMP_ROVER, GUAC_DEFAULT_LAYER, + + term->char_width * col, term->char_height * row, + term->char_width, term->char_height, + + background_color->red, + background_color->green, + background_color->blue, + 255); + + return 0; } diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 7dcc0e43..0ecee494 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -88,10 +88,10 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { /* Displayable chars */ default: - ssh_guac_terminal_send_glyph(term, + ssh_guac_terminal_set(term, term->cursor_row, term->cursor_col, - c); + c, term->foreground, term->background); /* Advance cursor */ term->cursor_col++; @@ -158,6 +158,8 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Any non-digit stops the parameter, and possibly the sequence */ else { + int i; + /* At most 16 parameters */ if (argc < 16) { @@ -173,6 +175,34 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Handle CSI functions */ switch (c) { + /* m: Set graphics rendition */ + case 'm': + + for (i=0; iforeground = term->default_foreground; + term->background = term->default_background; + } + + /* Foreground */ + else if (value >= 30 && value <= 37) + term->foreground = value - 30; + + /* Background */ + else if (value >= 40 && value <= 47) + term->background = value - 40; + + else + guac_log_info("Unhandled graphics rendition: %i", value); + + } + + break; + /* H: Move cursor */ case 'H': term->cursor_row = argv[0] - 1; From e0f38ded99b2fbffa63610ca140bec0014d742e4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Aug 2011 13:49:47 -0700 Subject: [PATCH 011/169] Reverse video --- protocols/ssh/include/ssh_terminal.h | 1 + protocols/ssh/src/ssh_terminal.c | 12 ++++++++++-- protocols/ssh/src/ssh_terminal_handlers.c | 9 +++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index 025d38c0..6a0adea3 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -71,6 +71,7 @@ struct ssh_guac_terminal { int foreground; int background; + int reverse; int default_foreground; int default_background; diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index f32027c6..9ca19e4d 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -85,6 +85,7 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { term->foreground = term->default_foreground = 7; /* White */ term->background = term->default_background = 0; /* Black */ + term->reverse = 0; /* Normal video */ term->cursor_row = 0; term->cursor_col = 0; @@ -195,10 +196,17 @@ int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, GUACIO* io = term->client->io; guac_layer* glyph = __ssh_guac_terminal_get_glyph(term, c); + const ssh_guac_terminal_color* background_color; + + /* Handle reverse video */ + if (term->reverse) { + int swap = background; + background = foreground; + foreground = swap; + } /* Get background color */ - const ssh_guac_terminal_color* background_color = - &ssh_guac_terminal_palette[background]; + background_color = &ssh_guac_terminal_palette[background]; guac_send_copy(io, glyph, 0, 0, term->char_width, term->char_height, diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 0ecee494..fb098541 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -186,6 +186,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { if (value == 0) { term->foreground = term->default_foreground; term->background = term->default_background; + term->reverse = 0; } /* Foreground */ @@ -196,6 +197,14 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { else if (value >= 40 && value <= 47) term->background = value - 40; + /* Reverse video */ + else if (value == 7) + term->reverse = 1; + + /* Reset reverse video */ + else if (value == 27) + term->reverse = 0; + else guac_log_info("Unhandled graphics rendition: %i", value); From 8892c018e0a25ec8b6a41f5ee20525a1c4ea20b0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Aug 2011 14:30:10 -0700 Subject: [PATCH 012/169] Handle bold, underscore. Proper handling of reverse video (should not be in *_set()) --- protocols/ssh/include/ssh_terminal.h | 2 + protocols/ssh/src/ssh_terminal.c | 11 ++---- protocols/ssh/src/ssh_terminal_handlers.c | 47 ++++++++++++++++++++++- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index 6a0adea3..6d8178c9 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -72,6 +72,8 @@ struct ssh_guac_terminal { int foreground; int background; int reverse; + int bold; + int underscore; int default_foreground; int default_background; diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 9ca19e4d..34cb09f3 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -85,7 +85,9 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { term->foreground = term->default_foreground = 7; /* White */ term->background = term->default_background = 0; /* Black */ - term->reverse = 0; /* Normal video */ + term->reverse = 0; /* Normal video */ + term->bold = 0; /* Normal intensity */ + term->underscore = 0; /* No underline */ term->cursor_row = 0; term->cursor_col = 0; @@ -198,13 +200,6 @@ int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, guac_layer* glyph = __ssh_guac_terminal_get_glyph(term, c); const ssh_guac_terminal_color* background_color; - /* Handle reverse video */ - if (term->reverse) { - int swap = background; - background = foreground; - foreground = swap; - } - /* Get background color */ background_color = &ssh_guac_terminal_palette[background]; diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index fb098541..875f3185 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -44,6 +44,9 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { + int foreground = term->foreground; + int background = term->background; + /* Wrap if necessary */ if (term->cursor_col >= term->term_width) { term->cursor_col = 0; @@ -88,10 +91,22 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { /* Displayable chars */ default: + + /* Handle reverse video */ + if (term->reverse) { + int swap = background; + background = foreground; + foreground = swap; + } + + /* Handle bold */ + if (term->bold && foreground <= 7) + foreground += 8; + ssh_guac_terminal_set(term, term->cursor_row, term->cursor_col, - c, term->foreground, term->background); + c, foreground, background); /* Advance cursor */ term->cursor_col++; @@ -187,8 +202,18 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { term->foreground = term->default_foreground; term->background = term->default_background; term->reverse = 0; + term->underscore = 0; + term->bold = 0; } + /* Bold */ + else if (value == 1) + term->bold = 1; + + /* Underscore on */ + else if (value == 4) + term->underscore = 1; + /* Foreground */ else if (value >= 30 && value <= 37) term->foreground = value - 30; @@ -197,6 +222,22 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { else if (value >= 40 && value <= 47) term->background = value - 40; + /* Underscore on, default foreground */ + else if (value == 38) { + term->underscore = 1; + term->foreground = term->default_foreground; + } + + /* Underscore off, default foreground */ + else if (value == 39) { + term->underscore = 0; + term->foreground = term->default_foreground; + } + + /* Reset background */ + else if (value == 49) + term->background = term->default_background; + /* Reverse video */ else if (value == 7) term->reverse = 1; @@ -205,6 +246,10 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { else if (value == 27) term->reverse = 0; + /* Reset intensity */ + else if (value == 27) + term->bold = 0; + else guac_log_info("Unhandled graphics rendition: %i", value); From adb59fe3411e34cf43d345dec39aef27f7564f75 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Aug 2011 14:39:11 -0700 Subject: [PATCH 013/169] Proper handling of cursor reset. Clear argv[] when done. --- protocols/ssh/src/ssh_terminal_handlers.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 875f3185..b696e241 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -151,7 +151,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* CSI function arguments */ static int argc = 0; - static int argv[16]; + static int argv[16] = {0}; /* Argument building counter and buffer */ static int argv_length = 0; @@ -173,7 +173,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Any non-digit stops the parameter, and possibly the sequence */ else { - int i; + int i, row, col; /* At most 16 parameters */ if (argc < 16) { @@ -259,8 +259,12 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* H: Move cursor */ case 'H': - term->cursor_row = argv[0] - 1; - term->cursor_col = argv[1] - 1; + + row = argv[0]; if (row != 0) row--; + col = argv[1]; if (col != 0) col--; + + term->cursor_row = row; + term->cursor_col = col; break; /* J: Erase display */ @@ -326,6 +330,10 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { if (c != ';') { term->char_handler = ssh_guac_terminal_echo; + /* Reset parameters */ + for (i=0; i Date: Fri, 5 Aug 2011 14:46:35 -0700 Subject: [PATCH 014/169] Only scroll when DISPLAYING a character. --- protocols/ssh/src/ssh_terminal_handlers.c | 40 ++++++++++++++--------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index b696e241..3af9b19f 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -47,21 +47,6 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { int foreground = term->foreground; int background = term->background; - /* Wrap if necessary */ - if (term->cursor_col >= term->term_width) { - term->cursor_col = 0; - term->cursor_row++; - } - - /* Scroll up if necessary */ - if (term->cursor_row >= term->term_height) { - term->cursor_row = term->term_height - 1; - - /* Scroll up by one row */ - ssh_guac_terminal_scroll_up(term, 0, term->term_height - 1, 1); - - } - switch (c) { /* Bell */ @@ -82,6 +67,15 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { /* Line feed */ case '\n': term->cursor_row++; + + /* Scroll up if necessary */ + if (term->cursor_row >= term->term_height) { + term->cursor_row = term->term_height - 1; + + /* Scroll up by one row */ + ssh_guac_terminal_scroll_up(term, 0, term->term_height - 1, 1); + + } break; /* ESC */ @@ -92,6 +86,21 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { /* Displayable chars */ default: + /* Wrap if necessary */ + if (term->cursor_col >= term->term_width) { + term->cursor_col = 0; + term->cursor_row++; + } + + /* Scroll up if necessary */ + if (term->cursor_row >= term->term_height) { + term->cursor_row = term->term_height - 1; + + /* Scroll up by one row */ + ssh_guac_terminal_scroll_up(term, 0, term->term_height - 1, 1); + + } + /* Handle reverse video */ if (term->reverse) { int swap = background; @@ -110,6 +119,7 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { /* Advance cursor */ term->cursor_col++; + } return 0; From 9de0e18d11670307de42ebf42e9e22b7fd1a63e5 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Aug 2011 17:09:44 -0700 Subject: [PATCH 015/169] More CSI handlers. --- protocols/ssh/src/ssh_client.c | 7 --- protocols/ssh/src/ssh_terminal_handlers.c | 74 ++++++++++++++++++++++- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 49147fb2..81e0c174 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -49,13 +49,6 @@ #include "ssh_handlers.h" #include "ssh_terminal.h" -#define SSH_TERM_STATE_NULL 0 -#define SSH_TERM_STATE_ECHO 1 -#define SSH_TERM_STATE_ESC 2 -#define SSH_TERM_STATE_CSI 3 -#define SSH_TERM_STATE_OSC 4 -#define SSH_TERM_STATE_CHARSET 5 - /* Client plugin arguments */ const char* GUAC_CLIENT_ARGS[] = { "hostname", diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 3af9b19f..8fc76131 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -183,7 +183,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Any non-digit stops the parameter, and possibly the sequence */ else { - int i, row, col; + int i, row, col, amount; /* At most 16 parameters */ if (argc < 16) { @@ -200,6 +200,65 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Handle CSI functions */ switch (c) { + /* A: Move up */ + case 'A': + + /* Get move amount */ + amount = argv[0]; + if (amount == 0) amount = 1; + + /* Move cursor */ + term->cursor_row -= amount; + if (term->cursor_row < 0) + term->cursor_row = 0; + + break; + + /* B: Move down */ + case 'B': + + /* Get move amount */ + amount = argv[0]; + if (amount == 0) amount = 1; + + /* Move cursor */ + term->cursor_row += amount; + if (term->cursor_row >= term->term_height) + term->cursor_row = term->term_height - 1; + + break; + + /* D: Move left */ + case 'D': + + /* Get move amount */ + amount = argv[0]; + if (amount == 0) amount = 1; + + /* Move cursor */ + term->cursor_col -= amount; + if (term->cursor_col < 0) + term->cursor_col = 0; + + break; + + /* C: Move right */ + case 'C': + + /* Get move amount */ + amount = argv[0]; + if (amount == 0) amount = 1; + + /* Move cursor */ + term->cursor_col += amount; + if (term->cursor_col >= term->term_width) + term->cursor_col = term->term_width - 1; + + break; + + + + /* m: Set graphics rendition */ case 'm': @@ -277,6 +336,19 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { term->cursor_col = col; break; + /* G: Move cursor, current row */ + case 'G': + col = argv[0]; if (col != 0) col--; + term->cursor_col = col; + break; + + /* d: Move cursor, current col */ + case 'd': + row = argv[0]; if (row != 0) row--; + term->cursor_row = row; + break; + + /* J: Erase display */ case 'J': From 93d93a64eb7e22874c6524803decaa2534c2a1ef Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Aug 2011 22:59:42 -0700 Subject: [PATCH 016/169] More CSI, scroll region --- protocols/ssh/include/ssh_terminal.h | 3 +++ protocols/ssh/src/ssh_handlers.c | 1 + protocols/ssh/src/ssh_terminal.c | 9 ++++++--- protocols/ssh/src/ssh_terminal_handlers.c | 18 ++++++++++++------ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index 6d8178c9..3220d9e6 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -66,6 +66,9 @@ struct ssh_guac_terminal { int term_width; int term_height; + int scroll_start; + int scroll_end; + int cursor_row; int cursor_col; diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index ddb1b939..dce3f44f 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -111,6 +111,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { else if (keysym == 0xFF08) data = 0x08; else if (keysym == 0xFF09) data = 0x09; else if (keysym == 0xFF0D) data = 0x0D; + else if (keysym == 0xFF1B) data = 0x1B; else return 0; diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 34cb09f3..bcd6320b 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -92,15 +92,18 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { term->cursor_row = 0; term->cursor_col = 0; - term->term_width = 160; - term->term_height = 50; + term->term_width = 80; + term->term_height = 24; term->char_handler = ssh_guac_terminal_echo; + term->scroll_start = 0; + term->scroll_end = term->term_height - 1; + /* Get font */ term->font_desc = pango_font_description_new(); pango_font_description_set_family(term->font_desc, "monospace"); pango_font_description_set_weight(term->font_desc, PANGO_WEIGHT_NORMAL); - pango_font_description_set_size(term->font_desc, 8*PANGO_SCALE); + pango_font_description_set_size(term->font_desc, 14*PANGO_SCALE); font_map = pango_cairo_font_map_get_default(); context = pango_font_map_create_context(font_map); diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 8fc76131..4ee4f44d 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -69,11 +69,11 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { term->cursor_row++; /* Scroll up if necessary */ - if (term->cursor_row >= term->term_height) { - term->cursor_row = term->term_height - 1; + if (term->cursor_row > term->scroll_end) { + term->cursor_row = term->scroll_end; /* Scroll up by one row */ - ssh_guac_terminal_scroll_up(term, 0, term->term_height - 1, 1); + ssh_guac_terminal_scroll_up(term, term->scroll_start, term->scroll_end, 1); } break; @@ -93,11 +93,11 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { } /* Scroll up if necessary */ - if (term->cursor_row >= term->term_height) { - term->cursor_row = term->term_height - 1; + if (term->cursor_row > term->scroll_end) { + term->cursor_row = term->scroll_end; /* Scroll up by one row */ - ssh_guac_terminal_scroll_up(term, 0, term->term_height - 1, 1); + ssh_guac_terminal_scroll_up(term, term->scroll_start, term->scroll_end, 1); } @@ -326,6 +326,12 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { break; + /* r: Set scrolling region */ + case 'r': + term->scroll_start = argv[0]-1; + term->scroll_end = argv[1]-1; + break; + /* H: Move cursor */ case 'H': From d57bdb06e45b134b6c5249a03c312a59aeacf62b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 9 Aug 2011 12:31:03 -0700 Subject: [PATCH 017/169] Added scrollback buffer storage. --- protocols/ssh/include/ssh_terminal.h | 83 ++++++++++++++++++++++++++-- protocols/ssh/src/ssh_terminal.c | 24 ++++++++ 2 files changed, 103 insertions(+), 4 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index 3220d9e6..e7e3bf80 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -38,10 +38,6 @@ #ifndef _SSH_GUAC_TERMINAL_H #define _SSH_GUAC_TERMINAL_H -#include -#include - -#include #include #include @@ -51,25 +47,103 @@ typedef struct ssh_guac_terminal ssh_guac_terminal; +/** + * Handler for characters printed to the terminal. When a character is printed, + * the current char handler for the terminal is called and given that + * character. + */ typedef int ssh_guac_terminal_char_handler(ssh_guac_terminal* term, char c); +/** + * Represents a single character for display in a terminal, including actual + * character value, foreground color, and background color. + */ +typedef struct ssh_guac_terminal_char { + + /** + * The character value of the character to display. + */ + char value; + + /** + * The foreground color of the character to display. + */ + int foreground; + + /** + * The background color of the character to display. + */ + int background; + +} ssh_guac_terminal_char; + +/** + * Represents a terminal emulator which uses a given Guacamole client to + * render itself. + */ struct ssh_guac_terminal { + /** + * The Guacamole client this terminal emulator will use for rendering. + */ guac_client* client; + /** + * The description of the font to use for rendering. + */ PangoFontDescription* font_desc; + + /** + * A simple mapping of glyphs to their corresponding buffers. When a new + * glyph is drawn, the data for that glyph is saved into an off-screen + * buffer for later reuse. + */ guac_layer* glyphs[256]; + /** + * Array of scrollback buffer rows, where each row is an array of + * characters. + */ + ssh_guac_terminal_char** scrollback; + + /** + * The width of each character, in pixels. + */ int char_width; + + /** + * The height of each character, in pixels. + */ int char_height; + /** + * The width of the terminal, in characters. + */ int term_width; + + /** + * The height of the terminal, in characters. + */ int term_height; + /** + * The index of the first row in the scrolling region. + */ int scroll_start; + + /** + * The index of the last row in the scrolling region. + */ int scroll_end; + /** + * The current row location of the cursor. + */ int cursor_row; + + /** + * The current column location of the cursor. + */ int cursor_col; int foreground; @@ -97,6 +171,7 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client); void ssh_guac_terminal_free(ssh_guac_terminal* term); int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size); + int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, char c, int foreground, int background); diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index bcd6320b..c005148f 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -75,6 +75,8 @@ const ssh_guac_terminal_color ssh_guac_terminal_palette[16] = { ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { + int row, col; + PangoFontMap* font_map; PangoFont* font; PangoFontMetrics* metrics; @@ -99,6 +101,28 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { term->scroll_start = 0; term->scroll_end = term->term_height - 1; + /* Create scrollback buffer */ + term->scrollback = malloc(term->term_height * sizeof(ssh_guac_terminal_char*)); + + /* Init buffer */ + for (row = 0; row < term->term_height; row++) { + + /* Create row */ + ssh_guac_terminal_char* current_row = + term->scrollback[row] = malloc(term->term_width * sizeof(ssh_guac_terminal_char)); + + /* Init row */ + for (col = 0; col < term->term_width; col++) { + + /* Empty character, default colors */ + current_row[col].value = '\0'; + current_row[col].foreground = term->default_foreground; + current_row[col].background = term->default_background; + + } + + } + /* Get font */ term->font_desc = pango_font_description_new(); pango_font_description_set_family(term->font_desc, "monospace"); From f695f5c629cb865466cd7ebb3c6673aa0fcd3929 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 9 Aug 2011 18:32:54 -0700 Subject: [PATCH 018/169] Support for downward scrolling and CSI operation L --- protocols/ssh/include/ssh_terminal.h | 10 +++++ protocols/ssh/src/ssh_client.c | 4 ++ protocols/ssh/src/ssh_handlers.c | 11 +++++- protocols/ssh/src/ssh_terminal.c | 48 +++++++++++++++++++++++ protocols/ssh/src/ssh_terminal_handlers.c | 11 ++++++ 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index e7e3bf80..e3f7c54c 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -146,6 +146,11 @@ struct ssh_guac_terminal { */ int cursor_col; + /** + * Simple cursor layer until scrollback, etc. is implemented. + */ + guac_layer* cursor_layer; + int foreground; int background; int reverse; @@ -172,6 +177,8 @@ void ssh_guac_terminal_free(ssh_guac_terminal* term); int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size); +int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term); + int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, char c, int foreground, int background); @@ -185,6 +192,9 @@ int ssh_guac_terminal_clear(ssh_guac_terminal* term, int ssh_guac_terminal_scroll_up(ssh_guac_terminal* term, int start_row, int end_row, int amount); +int ssh_guac_terminal_scroll_down(ssh_guac_terminal* term, + int start_row, int end_row, int amount); + int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, int start_row, int start_col, int end_row, int end_col, int background_color); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 81e0c174..8715da2d 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -69,6 +69,7 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Add to password */ client_data->password[client_data->password_length++] = keysym; ssh_guac_terminal_write(client_data->term, "*", 1); + ssh_guac_terminal_redraw_cursor(client_data->term); guac_flush(client->io); } else if (keysym == 0xFF08) { @@ -78,6 +79,7 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Backspace */ ssh_guac_terminal_write(client_data->term, "\x08\x1B[K", 4); + ssh_guac_terminal_redraw_cursor(client_data->term); guac_flush(client->io); } @@ -89,6 +91,7 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Clear screen */ ssh_guac_terminal_write(client_data->term, "\x1B[2J\x1B[1;1H", 10); + ssh_guac_terminal_redraw_cursor(client_data->term); guac_flush(client->io); return ssh_guac_client_auth(client, client_data->password); @@ -148,6 +151,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->password_length = 0; ssh_guac_terminal_write(client_data->term, "Password: ", 10); + ssh_guac_terminal_redraw_cursor(client_data->term); guac_flush(client->io); client->key_handler = ssh_guac_client_password_key_handler; diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index dce3f44f..a5ec6d58 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -60,6 +60,8 @@ int ssh_guac_client_handle_messages(guac_client* client) { ssh_channel read_channels[2]; struct timeval timeout; + guac_log_info("ENTER HANDLE MESSAGES..."); + /* Channels to read */ read_channels[0] = client_data->term_channel; read_channels[1] = NULL; @@ -73,13 +75,17 @@ int ssh_guac_client_handle_messages(guac_client* client) { int bytes_read = 0; + guac_log_info("DONE WAITING (%i)", GUAC_SYNC_FREQUENCY); + /* While data available, write to terminal */ while (channel_is_open(client_data->term_channel) && !channel_is_eof(client_data->term_channel) && (bytes_read = channel_read_nonblocking(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) { - ssh_guac_terminal_write(client_data->term, buffer, bytes_read); - guac_flush(io); + if (ssh_guac_terminal_write(client_data->term, buffer, bytes_read) + || ssh_guac_terminal_redraw_cursor(client_data->term) + || guac_flush(io)) + return 1; } @@ -91,6 +97,7 @@ int ssh_guac_client_handle_messages(guac_client* client) { } } + guac_log_info("LEAVE HANDLE MESSAGES"); return 0; } diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index c005148f..5431569e 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -93,6 +93,7 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { term->cursor_row = 0; term->cursor_col = 0; + term->cursor_layer = guac_client_alloc_layer(client, 1); term->term_width = 80; term->term_height = 24; @@ -220,6 +221,33 @@ guac_layer* __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { } +int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term) { + + GUACIO* io = term->client->io; + + /* Erase old cursor */ + return + guac_send_rect(io, + GUAC_COMP_SRC, term->cursor_layer, + + 0, 0, + term->char_width * term->term_width, + term->char_height * term->term_height, + + 0, 0, 0, 0) + + || guac_send_rect(io, + GUAC_COMP_SRC, term->cursor_layer, + + term->char_width * term->cursor_col, + term->char_height * term->cursor_row, + term->char_width, term->char_height, + + 0x40, 0xFF, 0x80, + 0x80); + +} + int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, char c, int foreground, int background) { @@ -342,6 +370,26 @@ int ssh_guac_terminal_scroll_up(ssh_guac_terminal* term, } +int ssh_guac_terminal_scroll_down(ssh_guac_terminal* term, + int start_row, int end_row, int amount) { + + /* Calculate height of scroll region */ + int height = end_row - start_row + 1; + + return + + /* Move rows within scroll region down by the given amount */ + ssh_guac_terminal_copy(term, + start_row, 0, + height - amount, term->term_width, + start_row + amount, 0) + + /* Fill new rows with background */ + || ssh_guac_terminal_clear(term, + start_row, 0, amount, term->term_width, + term->background); + +} int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, int start_row, int start_col, diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 4ee4f44d..f3dc497d 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -407,6 +407,17 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { break; + /* L: Insert blank lines (scroll down) */ + case 'L': + + amount = argv[0]; + if (amount == 0) amount = 1; + + ssh_guac_terminal_scroll_down(term, + term->cursor_row, term->scroll_end, amount); + + break; + /* Warn of unhandled codes */ default: if (c != ';') From 15ae8d79a25f790cb9bd944e8be8fad77d0b1a16 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 10 Aug 2011 00:02:06 -0700 Subject: [PATCH 019/169] Using ssh_select (works) instead of channel_select (doesn't work). --- protocols/ssh/src/ssh_handlers.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index a5ec6d58..a00b8589 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -35,6 +35,8 @@ * * ***** END LICENSE BLOCK ***** */ +#include + #include #include @@ -57,26 +59,32 @@ int ssh_guac_client_handle_messages(guac_client* client) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; char buffer[8192]; - ssh_channel read_channels[2]; + ssh_channel channels[2], out_channels[2]; struct timeval timeout; - - guac_log_info("ENTER HANDLE MESSAGES..."); + fd_set fds; + int ssh_fd; /* Channels to read */ - read_channels[0] = client_data->term_channel; - read_channels[1] = NULL; + channels[0] = client_data->term_channel; + channels[1] = NULL; + + /* Get SSH file descriptor */ + ssh_fd = ssh_get_fd(client_data->session); + + /* Build fd_set */ + FD_ZERO(&fds); + FD_SET(ssh_fd, &fds); /* Time to wait */ timeout.tv_sec = GUAC_SYNC_FREQUENCY / 1000; timeout.tv_usec = (GUAC_SYNC_FREQUENCY % 1000) * 1000; /* Wait for data to be available */ - if (channel_select(read_channels, NULL, NULL, &timeout) == SSH_OK) { + if (ssh_select(channels, out_channels, ssh_fd+1, &fds, &timeout) + == SSH_OK) { int bytes_read = 0; - guac_log_info("DONE WAITING (%i)", GUAC_SYNC_FREQUENCY); - /* While data available, write to terminal */ while (channel_is_open(client_data->term_channel) && !channel_is_eof(client_data->term_channel) @@ -97,7 +105,6 @@ int ssh_guac_client_handle_messages(guac_client* client) { } } - guac_log_info("LEAVE HANDLE MESSAGES"); return 0; } From f9d42c7182a46bfd0547eb8ef3d1ec10f3072d54 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 10 Aug 2011 00:16:50 -0700 Subject: [PATCH 020/169] Handle CSI M (delete lines) --- protocols/ssh/src/ssh_terminal_handlers.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index f3dc497d..d31faae6 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -418,6 +418,17 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { break; + /* L: Delete lines (scroll up) */ + case 'M': + + amount = argv[0]; + if (amount == 0) amount = 1; + + ssh_guac_terminal_scroll_up(term, + term->cursor_row, term->scroll_end, amount); + + break; + /* Warn of unhandled codes */ default: if (c != ';') From 52e14322a32eb755f233c6fcef8d38bd76861865 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 10 Aug 2011 09:31:12 -0700 Subject: [PATCH 021/169] Handler for CSI P (delete characters at cursor) --- protocols/ssh/src/ssh_terminal_handlers.c | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index d31faae6..71a00cc5 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -418,7 +418,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { break; - /* L: Delete lines (scroll up) */ + /* M: Delete lines (scroll up) */ case 'M': amount = argv[0]; @@ -429,6 +429,28 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { break; + /* P: Delete characters (scroll left) */ + case 'P': + + amount = argv[0]; + if (amount == 0) amount = 1; + + /* Scroll left by amount */ + if (term->cursor_col + amount < term->term_width) + ssh_guac_terminal_copy(term, + term->cursor_row, term->cursor_col + amount, + 1, + term->term_width - term->cursor_col - amount, + term->cursor_row, term->cursor_col); + + /* Clear right */ + ssh_guac_terminal_clear(term, + term->cursor_row, term->term_width - amount, + 1, amount, + term->background); + + break; + /* Warn of unhandled codes */ default: if (c != ';') From 31e8e8432abd3120eb48504a5ce15c47a4385592 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 10 Aug 2011 11:03:38 -0700 Subject: [PATCH 022/169] More CSI, moving to faster drawing technique. --- protocols/ssh/src/ssh_handlers.c | 31 +++++++++++++++-------- protocols/ssh/src/ssh_terminal.c | 4 +-- protocols/ssh/src/ssh_terminal_handlers.c | 21 +++++++++++++++ 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index a00b8589..7580b574 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -116,21 +116,30 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { /* If key pressed */ if (pressed) { - char data; - /* If simple ASCII key */ - if (keysym >= 0x00 && keysym <= 0xFF) - data = (char) keysym; + if (keysym >= 0x00 && keysym <= 0xFF) { + char data = (char) keysym; + return channel_write(client_data->term_channel, &data, 1); + } - else if (keysym == 0xFF08) data = 0x08; - else if (keysym == 0xFF09) data = 0x09; - else if (keysym == 0xFF0D) data = 0x0D; - else if (keysym == 0xFF1B) data = 0x1B; + else { - else - return 0; + int length = 0; + const char* data = NULL; - return channel_write(client_data->term_channel, &data, 1); + if (keysym == 0xFF08) { data = "\x08"; length = 1; } + else if (keysym == 0xFF09) { data = "\x09"; length = 1; } + else if (keysym == 0xFF0D) { data = "\x0D"; length = 1; } + else if (keysym == 0xFF1B) { data = "\x1B"; length = 1; } + + /* Arrow keys */ + else if (keysym == 0xFF52) { data = "\x1B\x5B""A"; length = 3; } + else if (keysym == 0xFF54) { data = "\x1B\x5B""B"; length = 3; } + else if (keysym == 0xFF53) { data = "\x1B\x5B""C"; length = 3; } + else if (keysym == 0xFF51) { data = "\x1B\x5B""D"; length = 3; } + + return channel_write(client_data->term_channel, data, length); + } } diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 5431569e..5850a323 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -286,7 +286,7 @@ int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, } /* Set background */ - guac_send_rect(io, + /*guac_send_rect(io, GUAC_COMP_ROVER, GUAC_DEFAULT_LAYER, term->char_width * col, term->char_height * row, @@ -295,7 +295,7 @@ int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, background_color->red, background_color->green, background_color->blue, - 255); + 255);*/ return 0; diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 71a00cc5..14044766 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -451,6 +451,27 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { break; + /* @: Insert characters (scroll right) */ + case '@': + + amount = argv[0]; + if (amount == 0) amount = 1; + + /* Scroll right by amount */ + if (term->cursor_col + amount < term->term_width) + ssh_guac_terminal_copy(term, + term->cursor_row, term->cursor_col, + 1, term->term_width - term->cursor_col - amount, + term->cursor_row, term->cursor_col + amount); + + /* Clear left */ + ssh_guac_terminal_clear(term, + term->cursor_row, term->cursor_col, + 1, amount, + term->background); + + break; + /* Warn of unhandled codes */ default: if (c != ';') From e5619531a6c1f2a7424925b9a32a60cecc3c14c7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 17 Aug 2011 12:30:53 -0700 Subject: [PATCH 023/169] Improved palette (more contrast). Started move to more efficient drawing. --- protocols/ssh/include/ssh_terminal.h | 13 +++++++---- protocols/ssh/src/ssh_terminal.c | 34 ++++++++++++++-------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index e3f7c54c..de2e0396 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -94,11 +94,16 @@ struct ssh_guac_terminal { PangoFontDescription* font_desc; /** - * A simple mapping of glyphs to their corresponding buffers. When a new - * glyph is drawn, the data for that glyph is saved into an off-screen - * buffer for later reuse. + * A single wide layer holding each glyph, with each glyph only + * colored with foreground color (background remains transparent). */ - guac_layer* glyphs[256]; + guac_layer* glyph_stroke; + + /** + * A single wide layer holding each glyph, with each glyph properly + * colored with foreground and background color (no transparency at all). + */ + guac_layer* filled_glyphs; /** * Array of scrollback buffer rows, where each row is an array of diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 5850a323..da075b54 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -53,22 +53,22 @@ const ssh_guac_terminal_color ssh_guac_terminal_palette[16] = { /* Normal colors */ {0x00, 0x00, 0x00}, /* Black */ - {0x80, 0x00, 0x00}, /* Red */ - {0x00, 0x80, 0x00}, /* Green */ - {0x80, 0x80, 0x00}, /* Brown */ - {0x00, 0x00, 0x80}, /* Blue */ - {0x80, 0x00, 0x80}, /* Magenta */ - {0x00, 0x80, 0x80}, /* Cyan */ - {0x80, 0x80, 0x80}, /* White */ + {0x99, 0x3E, 0x3E}, /* Red */ + {0x3E, 0x99, 0x3E}, /* Green */ + {0x99, 0x99, 0x3E}, /* Brown */ + {0x3E, 0x3E, 0x99}, /* Blue */ + {0x99, 0x3E, 0x99}, /* Magenta */ + {0x3E, 0x99, 0x99}, /* Cyan */ + {0x99, 0x99, 0x99}, /* White */ /* Intense colors */ - {0x40, 0x40, 0x40}, /* Black */ - {0xFF, 0x00, 0x00}, /* Red */ - {0x00, 0xFF, 0x00}, /* Green */ - {0xFF, 0xFF, 0x00}, /* Brown */ - {0x00, 0x00, 0xFF}, /* Blue */ - {0xFF, 0x00, 0xFF}, /* Magenta */ - {0x00, 0xFF, 0xFF}, /* Cyan */ + {0x3E, 0x3E, 0x3E}, /* Black */ + {0xFF, 0x67, 0x67}, /* Red */ + {0x67, 0xFF, 0x67}, /* Green */ + {0xFF, 0xFF, 0x67}, /* Brown */ + {0x67, 0x67, 0xFF}, /* Blue */ + {0xFF, 0x67, 0xFF}, /* Magenta */ + {0x67, 0xFF, 0xFF}, /* Cyan */ {0xFF, 0xFF, 0xFF}, /* White */ }; @@ -95,8 +95,8 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { term->cursor_col = 0; term->cursor_layer = guac_client_alloc_layer(client, 1); - term->term_width = 80; - term->term_height = 24; + term->term_width = 100; + term->term_height = 37; term->char_handler = ssh_guac_terminal_echo; term->scroll_start = 0; @@ -128,7 +128,7 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { term->font_desc = pango_font_description_new(); pango_font_description_set_family(term->font_desc, "monospace"); pango_font_description_set_weight(term->font_desc, PANGO_WEIGHT_NORMAL); - pango_font_description_set_size(term->font_desc, 14*PANGO_SCALE); + pango_font_description_set_size(term->font_desc, 10*PANGO_SCALE); font_map = pango_cairo_font_map_get_default(); context = pango_font_map_create_context(font_map); From 7b0994884228b380c24639dcea92351f7e90f2c9 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 18 Aug 2011 18:09:20 -0700 Subject: [PATCH 024/169] Much faster drawing method (complete). --- protocols/ssh/include/ssh_terminal.h | 26 ++++- protocols/ssh/src/ssh_terminal.c | 117 +++++++++++++++------- protocols/ssh/src/ssh_terminal_handlers.c | 5 +- 3 files changed, 109 insertions(+), 39 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index de2e0396..813236a7 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -93,6 +93,26 @@ struct ssh_guac_terminal { */ PangoFontDescription* font_desc; + /** + * Index of next glyph to create + */ + int next_glyph; + + /** + * Index of locations for each glyph in the stroke and fill layers. + */ + int glyphs[256]; + + /** + * Color of glyphs in copy buffer + */ + int glyph_foreground; + + /** + * Color of glyphs in copy buffer + */ + int glyph_background; + /** * A single wide layer holding each glyph, with each glyph only * colored with foreground color (background remains transparent). @@ -184,8 +204,10 @@ int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size); int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term); -int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, - char c, int foreground, int background); +int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, + int foreground, int background); + +int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, char c); int ssh_guac_terminal_copy(ssh_guac_terminal* term, int src_row, int src_col, int rows, int cols, diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index da075b54..b1bdb6db 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -85,12 +85,16 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { ssh_guac_terminal* term = malloc(sizeof(ssh_guac_terminal)); term->client = client; - term->foreground = term->default_foreground = 7; /* White */ - term->background = term->default_background = 0; /* Black */ + term->glyph_foreground = term->foreground = term->default_foreground = 7; /* White */ + term->glyph_background = term->background = term->default_background = 0; /* Black */ term->reverse = 0; /* Normal video */ term->bold = 0; /* Normal intensity */ term->underscore = 0; /* No underline */ + memset(term->glyphs, 0, sizeof(term->glyphs)); + term->glyph_stroke = guac_client_alloc_buffer(client); + term->filled_glyphs = guac_client_alloc_buffer(client); + term->cursor_row = 0; term->cursor_col = 0; term->cursor_layer = guac_client_alloc_layer(client, 1); @@ -165,14 +169,18 @@ void ssh_guac_terminal_free(ssh_guac_terminal* term) { /* STUB */ } -guac_layer* __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { +int __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { GUACIO* io = term->client->io; - guac_layer* glyph; + int location; - /* Use default foreground color */ + /* Use foreground color */ const ssh_guac_terminal_color* color = - &ssh_guac_terminal_palette[term->default_foreground]; + &ssh_guac_terminal_palette[term->glyph_foreground]; + + /* Use background color */ + const ssh_guac_terminal_color* background = + &ssh_guac_terminal_palette[term->glyph_background]; cairo_surface_t* surface; cairo_t* cairo; @@ -181,7 +189,9 @@ guac_layer* __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { /* Return glyph if exists */ if (term->glyphs[(int) c]) - return term->glyphs[(int) c]; + return term->glyphs[(int) c] - 1; + + location = term->next_glyph++; /* Otherwise, draw glyph */ surface = cairo_image_surface_create( @@ -208,16 +218,27 @@ guac_layer* __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { g_object_unref(layout); cairo_destroy(cairo); - /* Send glyph and save */ - glyph = guac_client_alloc_buffer(term->client); - guac_send_png(io, GUAC_COMP_OVER, glyph, 0, 0, surface); - term->glyphs[(int) c] = glyph; + /* Send glyph and update filled flyphs */ + guac_send_png(io, GUAC_COMP_OVER, term->glyph_stroke, location * term->char_width, 0, surface); + + guac_send_rect(io, GUAC_COMP_SRC, term->filled_glyphs, + location * term->char_width, 0, + term->char_width, term->char_height, + background->red, + background->green, + background->blue, + 0xFF); + + guac_send_copy(io, term->glyph_stroke, + location * term->char_width, 0, term->char_width, term->char_height, + GUAC_COMP_OVER, term->filled_glyphs, location * term->char_width, 0); + + term->glyphs[(int) c] = location+1; - guac_flush(io); cairo_surface_destroy(surface); /* Return glyph */ - return glyph; + return location; } @@ -248,24 +269,17 @@ int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term) { } -int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, - char c, int foreground, int background) { +int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, + int foreground, int background) { GUACIO* io = term->client->io; - guac_layer* glyph = __ssh_guac_terminal_get_glyph(term, c); const ssh_guac_terminal_color* background_color; /* Get background color */ background_color = &ssh_guac_terminal_palette[background]; - guac_send_copy(io, - glyph, 0, 0, term->char_width, term->char_height, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, - term->char_width * col, - term->char_height * row); - - /* If foreground different from default, colorize */ - if (foreground != term->default_foreground) { + /* If foreground different from current, colorize */ + if (foreground != term->glyph_foreground) { /* Get color */ const ssh_guac_terminal_color* color = @@ -273,10 +287,10 @@ int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, /* Colorize letter */ guac_send_rect(io, - GUAC_COMP_ATOP, GUAC_DEFAULT_LAYER, + GUAC_COMP_ATOP, term->glyph_stroke, - term->char_width * col, term->char_height * row, - term->char_width, term->char_height, + 0, 0, + term->char_width * term->next_glyph, term->char_height, color->red, color->green, @@ -285,22 +299,53 @@ int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, } - /* Set background */ - /*guac_send_rect(io, - GUAC_COMP_ROVER, GUAC_DEFAULT_LAYER, + /* If any color change at all, update filled */ + if (foreground != term->glyph_foreground || background != term->glyph_background) { - term->char_width * col, term->char_height * row, - term->char_width, term->char_height, + /* Set background */ + guac_send_rect(io, + GUAC_COMP_SRC, term->filled_glyphs, - background_color->red, - background_color->green, - background_color->blue, - 255);*/ + 0, 0, + term->char_width * term->next_glyph, term->char_height, + + background_color->red, + background_color->green, + background_color->blue, + 255); + + /* Copy stroke */ + guac_send_copy(io, term->glyph_stroke, + + 0, 0, + term->char_width * term->next_glyph, term->char_height, + + GUAC_COMP_OVER, term->filled_glyphs, + 0, 0); + + } + + term->glyph_foreground = foreground; + term->glyph_background = background; return 0; } +int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, char c) { + + GUACIO* io = term->client->io; + int location = __ssh_guac_terminal_get_glyph(term, c); + + return guac_send_copy(io, + term->filled_glyphs, + location * term->char_width, 0, term->char_width, term->char_height, + GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + term->char_width * col, + term->char_height * row); + +} + int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size) { while (size > 0) { diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 14044766..21e6da47 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -112,10 +112,13 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { if (term->bold && foreground <= 7) foreground += 8; + ssh_guac_terminal_set_colors(term, + foreground, background); + ssh_guac_terminal_set(term, term->cursor_row, term->cursor_col, - c, foreground, background); + c); /* Advance cursor */ term->cursor_col++; From e7607b1e9ba0a5cb4e044c1dad1bba98b3fc03e4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 21 Aug 2011 23:24:40 -0700 Subject: [PATCH 025/169] Ctrl button --- protocols/ssh/include/ssh_client.h | 2 ++ protocols/ssh/src/ssh_client.c | 1 + protocols/ssh/src/ssh_handlers.c | 17 ++++++++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index 205cc930..4fcd357a 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -65,6 +65,8 @@ typedef struct ssh_guac_client_data { char password[1024]; int password_length; + int mod_ctrl; + } ssh_guac_client_data; int ssh_guac_client_auth(guac_client* client, const char* password); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 8715da2d..c8d3fbf8 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -114,6 +114,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Init client data */ client->data = client_data; client_data->term = term; + client_data->mod_ctrl = 0; /* Send name and dimensions */ guac_send_name(io, "SSH TEST"); diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 7580b574..0a072a62 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -113,13 +113,28 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + /* Track modifiers */ + if (keysym == 0xFFE3) { + client_data->mod_ctrl = pressed; + } + /* If key pressed */ - if (pressed) { + else if (pressed) { /* If simple ASCII key */ if (keysym >= 0x00 && keysym <= 0xFF) { char data = (char) keysym; + + /* Handle Ctrl modifier */ + if (client_data->mod_ctrl) { + if (keysym >= 'A' && keysym <= 'Z') + data = (char) (keysym - 'A' + 1); + else if (keysym >= 'a' && keysym <= 'z') + data = (char) (keysym - 'a' + 1); + } + return channel_write(client_data->term_channel, &data, 1); + } else { From ff8e2450282eb6acfebbe113229f7862ea9c1f46 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 6 Sep 2011 00:01:37 -0700 Subject: [PATCH 026/169] Using safe composite ops. --- protocols/ssh/src/ssh_terminal.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index b1bdb6db..9157a098 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -221,7 +221,7 @@ int __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { /* Send glyph and update filled flyphs */ guac_send_png(io, GUAC_COMP_OVER, term->glyph_stroke, location * term->char_width, 0, surface); - guac_send_rect(io, GUAC_COMP_SRC, term->filled_glyphs, + guac_send_rect(io, GUAC_COMP_OVER, term->filled_glyphs, location * term->char_width, 0, term->char_width, term->char_height, background->red, @@ -249,16 +249,16 @@ int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term) { /* Erase old cursor */ return guac_send_rect(io, - GUAC_COMP_SRC, term->cursor_layer, + GUAC_COMP_ROUT, term->cursor_layer, 0, 0, term->char_width * term->term_width, term->char_height * term->term_height, - 0, 0, 0, 0) + 0, 0, 0, 0xFF) || guac_send_rect(io, - GUAC_COMP_SRC, term->cursor_layer, + GUAC_COMP_OVER, term->cursor_layer, term->char_width * term->cursor_col, term->char_height * term->cursor_row, @@ -304,7 +304,7 @@ int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, /* Set background */ guac_send_rect(io, - GUAC_COMP_SRC, term->filled_glyphs, + GUAC_COMP_OVER, term->filled_glyphs, 0, 0, term->char_width * term->next_glyph, term->char_height, @@ -340,7 +340,7 @@ int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, char c) { return guac_send_copy(io, term->filled_glyphs, location * term->char_width, 0, term->char_width, term->char_height, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, term->char_width * col, term->char_height * row); @@ -370,7 +370,7 @@ int ssh_guac_terminal_copy(ssh_guac_terminal* term, src_col * term->char_width, src_row * term->char_height, cols * term->char_width, rows * term->char_height, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, dst_col * term->char_width, dst_row * term->char_height); } @@ -385,7 +385,7 @@ int ssh_guac_terminal_clear(ssh_guac_terminal* term, /* Fill with color */ return guac_send_rect(io, - GUAC_COMP_SRC, GUAC_DEFAULT_LAYER, + GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, col * term->char_width, row * term->char_height, cols * term->char_width, rows * term->char_height, From 3515ec9021d4b7093d58793aefcfa0d35cb200c4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 26 Nov 2011 15:35:45 -0800 Subject: [PATCH 027/169] Conversion to new libguac API. --- protocols/ssh/configure.in | 4 +- protocols/ssh/include/ssh_client.h | 9 --- protocols/ssh/include/ssh_terminal.h | 3 - protocols/ssh/include/ssh_terminal_handlers.h | 11 ---- protocols/ssh/src/ssh_client.c | 55 +++++++++---------- protocols/ssh/src/ssh_handlers.c | 15 +++-- protocols/ssh/src/ssh_terminal.c | 41 +++++++------- protocols/ssh/src/ssh_terminal_handlers.c | 8 +-- 8 files changed, 59 insertions(+), 87 deletions(-) diff --git a/protocols/ssh/configure.in b/protocols/ssh/configure.in index 2decbe9d..8a473689 100644 --- a/protocols/ssh/configure.in +++ b/protocols/ssh/configure.in @@ -35,7 +35,7 @@ # ***** END LICENSE BLOCK ***** AC_INIT(src/ssh_client.c) -AM_INIT_AUTOMAKE([libguac-client-ssh], 0.4.0) +AM_INIT_AUTOMAKE([libguac-client-ssh], 0.5.0) AC_CONFIG_MACRO_DIR([m4]) # Checks for programs. @@ -44,7 +44,7 @@ AC_PROG_CC_C99 AC_PROG_LIBTOOL # Checks for libraries. -AC_CHECK_LIB([guac], [guac_get_client],, AC_MSG_ERROR("libguac is required for communication via the guacamole protocol")) +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([ssh], [ssh_new],, AC_MSG_ERROR("libssh is required")) PKG_CHECK_MODULES([PANGO], pango); diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index 4fcd357a..7446b5dc 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -38,17 +38,8 @@ #ifndef _SSH_GUAC_CLIENT_H #define _SSH_GUAC_CLIENT_H -#include -#include - #include -#include -#include - -#include -#include -#include #include #include "ssh_client.h" diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index 813236a7..e29e6736 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -40,9 +40,6 @@ #include -#include -#include -#include #include typedef struct ssh_guac_terminal ssh_guac_terminal; diff --git a/protocols/ssh/include/ssh_terminal_handlers.h b/protocols/ssh/include/ssh_terminal_handlers.h index ed6b7430..6cd8de94 100644 --- a/protocols/ssh/include/ssh_terminal_handlers.h +++ b/protocols/ssh/include/ssh_terminal_handlers.h @@ -38,17 +38,6 @@ #ifndef _SSH_GUAC_TERMINAL_HANDLERS #define _SSH_GUAC_TERMINAL_HANDLERS -#include -#include - -#include -#include - -#include -#include -#include -#include - #include "ssh_terminal.h" int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index c8d3fbf8..5a4e40eb 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -40,8 +40,7 @@ #include -#include -#include +#include #include #include @@ -70,7 +69,7 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr client_data->password[client_data->password_length++] = keysym; ssh_guac_terminal_write(client_data->term, "*", 1); ssh_guac_terminal_redraw_cursor(client_data->term); - guac_flush(client->io); + guac_socket_flush(client->socket); } else if (keysym == 0xFF08) { @@ -80,7 +79,7 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Backspace */ ssh_guac_terminal_write(client_data->term, "\x08\x1B[K", 4); ssh_guac_terminal_redraw_cursor(client_data->term); - guac_flush(client->io); + guac_socket_flush(client->socket); } } @@ -92,7 +91,7 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Clear screen */ ssh_guac_terminal_write(client_data->term, "\x1B[2J\x1B[1;1H", 10); ssh_guac_terminal_redraw_cursor(client_data->term); - guac_flush(client->io); + guac_socket_flush(client->socket); return ssh_guac_client_auth(client, client_data->password); @@ -106,7 +105,7 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr int guac_client_init(guac_client* client, int argc, char** argv) { - GUACIO* io = client->io; + guac_socket* socket = client->socket; ssh_guac_client_data* client_data = malloc(sizeof(ssh_guac_client_data)); ssh_guac_terminal* term = ssh_guac_terminal_create(client); @@ -117,18 +116,18 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->mod_ctrl = 0; /* Send name and dimensions */ - guac_send_name(io, "SSH TEST"); - guac_send_size(io, + guac_protocol_send_name(socket, "SSH TEST"); + guac_protocol_send_size(socket, term->char_width * term->term_width, term->char_height * term->term_height); - guac_flush(io); + guac_socket_flush(socket); /* Open SSH session */ client_data->session = ssh_new(); if (client_data->session == NULL) { - guac_send_error(io, "Unable to create SSH session."); - guac_flush(io); + guac_protocol_send_error(socket, "Unable to create SSH session."); + guac_socket_flush(socket); return 1; } @@ -138,8 +137,8 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Connect */ if (ssh_connect(client_data->session) != SSH_OK) { - guac_send_error(io, "Unable to connect via SSH."); - guac_flush(io); + guac_protocol_send_error(socket, "Unable to connect via SSH."); + guac_socket_flush(socket); return 1; } @@ -153,7 +152,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->password_length = 0; ssh_guac_terminal_write(client_data->term, "Password: ", 10); ssh_guac_terminal_redraw_cursor(client_data->term); - guac_flush(client->io); + guac_socket_flush(client->socket); client->key_handler = ssh_guac_client_password_key_handler; @@ -166,54 +165,54 @@ int guac_client_init(guac_client* client, int argc, char** argv) { int ssh_guac_client_auth(guac_client* client, const char* password) { - GUACIO* io = client->io; + guac_socket* socket = client->socket; ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; ssh_guac_terminal* term = client_data->term; /* Authenticate */ if (ssh_userauth_password(client_data->session, NULL, password) != SSH_AUTH_SUCCESS) { - guac_send_error(io, "SSH auth failed."); - guac_flush(io); + guac_protocol_send_error(socket, "SSH auth failed."); + guac_socket_flush(socket); return 1; } /* Open channel for terminal */ client_data->term_channel = channel_new(client_data->session); if (client_data->term_channel == NULL) { - guac_send_error(io, "Unable to open channel."); - guac_flush(io); + guac_protocol_send_error(socket, "Unable to open channel."); + guac_socket_flush(socket); return 1; } /* Open session for channel */ if (channel_open_session(client_data->term_channel) != SSH_OK) { - guac_send_error(io, "Unable to open channel session."); - guac_flush(io); + guac_protocol_send_error(socket, "Unable to open channel session."); + guac_socket_flush(socket); return 1; } /* Request PTY */ if (channel_request_pty(client_data->term_channel) != SSH_OK) { - guac_send_error(io, "Unable to allocate PTY for channel."); - guac_flush(io); + guac_protocol_send_error(socket, "Unable to allocate PTY for channel."); + guac_socket_flush(socket); return 1; } /* Request PTY size */ if (channel_change_pty_size(client_data->term_channel, term->term_width, term->term_height) != SSH_OK) { - guac_send_error(io, "Unable to change PTY size."); - guac_flush(io); + guac_protocol_send_error(socket, "Unable to change PTY size."); + guac_socket_flush(socket); return 1; } /* Request shell */ if (channel_request_shell(client_data->term_channel) != SSH_OK) { - guac_send_error(io, "Unable to associate shell with PTY."); - guac_flush(io); + guac_protocol_send_error(socket, "Unable to associate shell with PTY."); + guac_socket_flush(socket); return 1; } - guac_log_info("SSH connection successful."); + guac_client_log_info(client, "SSH connection successful."); /* Set handlers */ client->handle_messages = ssh_guac_client_handle_messages; diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 0a072a62..3d8ed9d2 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -45,8 +45,7 @@ #include #include -#include -#include +#include #include #include @@ -55,7 +54,7 @@ int ssh_guac_client_handle_messages(guac_client* client) { - GUACIO* io = client->io; + guac_socket* socket = client->socket; ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; char buffer[8192]; @@ -76,8 +75,8 @@ int ssh_guac_client_handle_messages(guac_client* client) { FD_SET(ssh_fd, &fds); /* Time to wait */ - timeout.tv_sec = GUAC_SYNC_FREQUENCY / 1000; - timeout.tv_usec = (GUAC_SYNC_FREQUENCY % 1000) * 1000; + timeout.tv_sec = 1; + timeout.tv_usec = 0; /* Wait for data to be available */ if (ssh_select(channels, out_channels, ssh_fd+1, &fds, &timeout) @@ -92,15 +91,15 @@ int ssh_guac_client_handle_messages(guac_client* client) { if (ssh_guac_terminal_write(client_data->term, buffer, bytes_read) || ssh_guac_terminal_redraw_cursor(client_data->term) - || guac_flush(io)) + || guac_socket_flush(socket)) return 1; } /* Notify on error */ if (bytes_read < 0) { - guac_send_error(io, "Error reading data."); - guac_flush(io); + guac_protocol_send_error(socket, "Error reading data."); + guac_socket_flush(socket); return 1; } } diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 9157a098..4d3affc8 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -41,8 +41,7 @@ #include #include -#include -#include +#include #include #include @@ -139,13 +138,13 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { font = pango_font_map_load_font(font_map, context, term->font_desc); if (font == NULL) { - guac_log_error("Unable to get font."); + guac_client_log_error(term->client, "Unable to get font."); return NULL; } metrics = pango_font_get_metrics(font, NULL); if (metrics == NULL) { - guac_log_error("Unable to get font metrics."); + guac_client_log_error(term->client, "Unable to get font metrics."); return NULL; } @@ -171,7 +170,7 @@ void ssh_guac_terminal_free(ssh_guac_terminal* term) { int __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { - GUACIO* io = term->client->io; + guac_socket* socket = term->client->socket; int location; /* Use foreground color */ @@ -219,9 +218,9 @@ int __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { cairo_destroy(cairo); /* Send glyph and update filled flyphs */ - guac_send_png(io, GUAC_COMP_OVER, term->glyph_stroke, location * term->char_width, 0, surface); + guac_protocol_send_png(socket, GUAC_COMP_OVER, term->glyph_stroke, location * term->char_width, 0, surface); - guac_send_rect(io, GUAC_COMP_OVER, term->filled_glyphs, + guac_protocol_send_rect(socket, GUAC_COMP_OVER, term->filled_glyphs, location * term->char_width, 0, term->char_width, term->char_height, background->red, @@ -229,7 +228,7 @@ int __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { background->blue, 0xFF); - guac_send_copy(io, term->glyph_stroke, + guac_protocol_send_copy(socket, term->glyph_stroke, location * term->char_width, 0, term->char_width, term->char_height, GUAC_COMP_OVER, term->filled_glyphs, location * term->char_width, 0); @@ -244,11 +243,11 @@ int __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term) { - GUACIO* io = term->client->io; + guac_socket* socket = term->client->socket; /* Erase old cursor */ return - guac_send_rect(io, + guac_protocol_send_rect(socket, GUAC_COMP_ROUT, term->cursor_layer, 0, 0, @@ -257,7 +256,7 @@ int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term) { 0, 0, 0, 0xFF) - || guac_send_rect(io, + || guac_protocol_send_rect(socket, GUAC_COMP_OVER, term->cursor_layer, term->char_width * term->cursor_col, @@ -272,7 +271,7 @@ int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term) { int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, int foreground, int background) { - GUACIO* io = term->client->io; + guac_socket* socket = term->client->socket; const ssh_guac_terminal_color* background_color; /* Get background color */ @@ -286,7 +285,7 @@ int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, &ssh_guac_terminal_palette[foreground]; /* Colorize letter */ - guac_send_rect(io, + guac_protocol_send_rect(socket, GUAC_COMP_ATOP, term->glyph_stroke, 0, 0, @@ -303,7 +302,7 @@ int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, if (foreground != term->glyph_foreground || background != term->glyph_background) { /* Set background */ - guac_send_rect(io, + guac_protocol_send_rect(socket, GUAC_COMP_OVER, term->filled_glyphs, 0, 0, @@ -315,7 +314,7 @@ int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, 255); /* Copy stroke */ - guac_send_copy(io, term->glyph_stroke, + guac_protocol_send_copy(socket, term->glyph_stroke, 0, 0, term->char_width * term->next_glyph, term->char_height, @@ -334,10 +333,10 @@ int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, char c) { - GUACIO* io = term->client->io; + guac_socket* socket = term->client->socket; int location = __ssh_guac_terminal_get_glyph(term, c); - return guac_send_copy(io, + return guac_protocol_send_copy(socket, term->filled_glyphs, location * term->char_width, 0, term->char_width, term->char_height, GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, @@ -361,10 +360,10 @@ int ssh_guac_terminal_copy(ssh_guac_terminal* term, int src_row, int src_col, int rows, int cols, int dst_row, int dst_col) { - GUACIO* io = term->client->io; + guac_socket* socket = term->client->socket; /* Send copy instruction */ - return guac_send_copy(io, + return guac_protocol_send_copy(socket, GUAC_DEFAULT_LAYER, src_col * term->char_width, src_row * term->char_height, @@ -379,12 +378,12 @@ int ssh_guac_terminal_copy(ssh_guac_terminal* term, int ssh_guac_terminal_clear(ssh_guac_terminal* term, int row, int col, int rows, int cols, int background_color) { - GUACIO* io = term->client->io; + guac_socket* socket = term->client->socket; const ssh_guac_terminal_color* color = &ssh_guac_terminal_palette[background_color]; /* Fill with color */ - return guac_send_rect(io, + return guac_protocol_send_rect(socket, GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, col * term->char_width, row * term->char_height, diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/ssh_terminal_handlers.c index 21e6da47..9503ec01 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/ssh_terminal_handlers.c @@ -37,8 +37,6 @@ #include -#include - #include "ssh_terminal.h" #include "ssh_terminal_handlers.h" @@ -146,7 +144,7 @@ int ssh_guac_terminal_escape(ssh_guac_terminal* term, char c) { break; default: - guac_log_info("Unhandled ESC sequence: %c", c); + guac_client_log_info(term->client, "Unhandled ESC sequence: %c", c); term->char_handler = ssh_guac_terminal_echo; } @@ -323,7 +321,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { term->bold = 0; else - guac_log_info("Unhandled graphics rendition: %i", value); + guac_client_log_info(term->client, "Unhandled graphics rendition: %i", value); } @@ -478,7 +476,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Warn of unhandled codes */ default: if (c != ';') - guac_log_info("Unhandled CSI sequence: %c", c); + guac_client_log_info(term->client, "Unhandled CSI sequence: %c", c); } From 4b0bbd8f4dbcb1fd6898870d4e0e4d0e52ae2eba Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 11 Dec 2011 14:51:05 -0800 Subject: [PATCH 028/169] Updated README to point to new Trac. --- protocols/ssh/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/ssh/README b/protocols/ssh/README index fe1f04a3..f2514ec9 100644 --- a/protocols/ssh/README +++ b/protocols/ssh/README @@ -70,5 +70,5 @@ the standard configure script. Please report any bugs encountered by opening a new ticket at the Trac system hosted at: - http://sourceforge.net/apps/trac/guacamole/ + http://guac-dev.org/trac/ From e6a6780591ff3c9747ad327210089c3904edd714 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Fri, 30 Dec 2011 14:34:04 -0800 Subject: [PATCH 029/169] implemented clipboard paste and free handlers --- protocols/ssh/include/ssh_client.h | 4 ++ protocols/ssh/include/ssh_handlers.h | 4 ++ protocols/ssh/src/ssh_client.c | 6 +++ protocols/ssh/src/ssh_handlers.c | 57 ++++++++++++++++++++++++++++ protocols/ssh/src/ssh_terminal.c | 8 +++- 5 files changed, 78 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index 7446b5dc..247da330 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * James Muehlner * * 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 @@ -52,11 +53,14 @@ typedef struct ssh_guac_client_data { ssh_channel term_channel; ssh_guac_terminal* term; + + char * clipboard_data; char password[1024]; int password_length; int mod_ctrl; + int mouse_mask; } ssh_guac_client_data; diff --git a/protocols/ssh/include/ssh_handlers.h b/protocols/ssh/include/ssh_handlers.h index 379a4043..300e93b0 100644 --- a/protocols/ssh/include/ssh_handlers.h +++ b/protocols/ssh/include/ssh_handlers.h @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * James Muehlner * * 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 @@ -42,6 +43,9 @@ int ssh_guac_client_handle_messages(guac_client* client); int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed); +int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask); +int ssh_guac_client_clipboard_handler(guac_client* client, char* data); +int ssh_guac_client_free_handler(guac_client* client); #endif diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 5a4e40eb..71f2d6a4 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * James Muehlner * * 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 @@ -114,6 +115,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client->data = client_data; client_data->term = term; client_data->mod_ctrl = 0; + client_data->clipboard_data = NULL; /* Send name and dimensions */ guac_protocol_send_name(socket, "SSH TEST"); @@ -216,7 +218,11 @@ int ssh_guac_client_auth(guac_client* client, const char* password) { /* Set handlers */ client->handle_messages = ssh_guac_client_handle_messages; + client->free_handler = ssh_guac_client_free_handler; + client->mouse_handler = ssh_guac_client_mouse_handler; client->key_handler = ssh_guac_client_key_handler; + client->clipboard_handler = ssh_guac_client_clipboard_handler; + /* Success */ return 0; diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 3d8ed9d2..2a0c0c3d 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * James Muehlner * * 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 @@ -108,6 +109,46 @@ int ssh_guac_client_handle_messages(guac_client* client) { } +int ssh_guac_client_clipboard_handler(guac_client* client, char* data) { + + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + + free(client_data->clipboard_data); + + client_data->clipboard_data = strdup(data); + + guac_client_log_info(client, "Clipboard data recieved: %s", data); + + return 0; +} + + + +int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { + + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + + /* Mouse just changed from down to up */ + int mouse_up = + (client_data->mouse_mask & GUAC_CLIENT_MOUSE_RIGHT) + && !(mask & GUAC_CLIENT_MOUSE_RIGHT); + + guac_client_log_info(client, "CLICK? x=%i, y=%i, mask=%i, mouse_up=%i", x, y, mask, mouse_up); + + client_data->mouse_mask = mask; + + /* Paste contents of clipboard on right mouse button up */ + if(mouse_up && client_data->clipboard_data != NULL) { + + int length = strlen(client_data->clipboard_data); + + if(length) + return channel_write(client_data->term_channel, client_data->clipboard_data, length); + } + + return 0; +} + int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; @@ -161,3 +202,19 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { } +int ssh_guac_client_free_handler(guac_client* client) { + + ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data; + + /* Free terminal */ + ssh_guac_terminal_free(guac_client_data->term); + + /* Free clipboard data */ + free(guac_client_data->clipboard_data); + + /* Free generic data struct */ + free(client->data); + + return 0; +} + diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 4d3affc8..c73441a9 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * James Muehlner * * 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 @@ -165,7 +166,12 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { } void ssh_guac_terminal_free(ssh_guac_terminal* term) { - /* STUB */ + + /* Free scrollback buffer */ + for (int row = 0; row < term->term_height; row++) + free(term->scrollback[row]); + + free(term->scrollback); } int __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { From 2ea97181675ab51a6c81b98935cd8325d9d32504 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 3 Jan 2012 09:09:50 -0800 Subject: [PATCH 030/169] removed debug lines --- protocols/ssh/src/ssh_handlers.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 2a0c0c3d..16e329cb 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -117,8 +117,6 @@ int ssh_guac_client_clipboard_handler(guac_client* client, char* data) { client_data->clipboard_data = strdup(data); - guac_client_log_info(client, "Clipboard data recieved: %s", data); - return 0; } @@ -133,8 +131,6 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { (client_data->mouse_mask & GUAC_CLIENT_MOUSE_RIGHT) && !(mask & GUAC_CLIENT_MOUSE_RIGHT); - guac_client_log_info(client, "CLICK? x=%i, y=%i, mask=%i, mouse_up=%i", x, y, mask, mouse_up); - client_data->mouse_mask = mask; /* Paste contents of clipboard on right mouse button up */ From cdae593fc59e0de3bb5e251521c939112b3f6c83 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 11 Feb 2012 19:12:40 -0800 Subject: [PATCH 031/169] Migrate to 0.6.0 libguac API. --- protocols/ssh/src/ssh_client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 71f2d6a4..462950dc 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -120,6 +120,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Send name and dimensions */ guac_protocol_send_name(socket, "SSH TEST"); guac_protocol_send_size(socket, + GUAC_DEFAULT_LAYER, term->char_width * term->term_width, term->char_height * term->term_height); From 19564cd133112242d6d2ace2f54584a3313f1e30 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 Feb 2012 11:04:08 -0800 Subject: [PATCH 032/169] Move cursor layer rather than redraw. --- protocols/ssh/src/ssh_client.c | 10 ++++++++++ protocols/ssh/src/ssh_terminal.c | 19 ++++--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 462950dc..185b175f 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -124,6 +124,16 @@ int guac_client_init(guac_client* client, int argc, char** argv) { term->char_width * term->term_width, term->char_height * term->term_height); + /* Cursor layer need only be one char */ + guac_protocol_send_size(socket, term->cursor_layer, term->char_width, term->char_height); + + /* Draw cursor */ + guac_protocol_send_rect(socket, + GUAC_COMP_OVER, term->cursor_layer, + 0, 0, term->char_width, term->char_height, + 0x40, 0xFF, 0x80, + 0x80); + guac_socket_flush(socket); /* Open SSH session */ diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index c73441a9..34d1b87f 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -253,24 +253,13 @@ int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term) { /* Erase old cursor */ return - guac_protocol_send_rect(socket, - GUAC_COMP_ROUT, term->cursor_layer, - - 0, 0, - term->char_width * term->term_width, - term->char_height * term->term_height, - - 0, 0, 0, 0xFF) - - || guac_protocol_send_rect(socket, - GUAC_COMP_OVER, term->cursor_layer, + guac_protocol_send_move(socket, + term->cursor_layer, + GUAC_DEFAULT_LAYER, term->char_width * term->cursor_col, term->char_height * term->cursor_row, - term->char_width, term->char_height, - - 0x40, 0xFF, 0x80, - 0x80); + 1); } From 5122386c8105f0fe06da9767efbf4b719319e00c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 23 Feb 2012 14:49:45 -0800 Subject: [PATCH 033/169] Bumped version number to 0.6.0. Added LICENSE to dist tarball. --- protocols/ssh/Makefile.am | 2 ++ protocols/ssh/configure.in | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 915f9ba9..953c3b31 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -46,3 +46,5 @@ libguac_client_ssh_la_CFLAGS = -Werror -Wall -pedantic -Iinclude @PANGO_CFLAGS@ libguac_client_ssh_la_LIBADD = @PANGO_LIBS@ @PANGOCAIRO_LIBS@ libguac_client_ssh_la_LDFLAGS = -version-info 0:0:0 +EXTRA_DIST = LICENSE + diff --git a/protocols/ssh/configure.in b/protocols/ssh/configure.in index 8a473689..89e055e0 100644 --- a/protocols/ssh/configure.in +++ b/protocols/ssh/configure.in @@ -35,7 +35,7 @@ # ***** END LICENSE BLOCK ***** AC_INIT(src/ssh_client.c) -AM_INIT_AUTOMAKE([libguac-client-ssh], 0.5.0) +AM_INIT_AUTOMAKE([libguac-client-ssh], 0.6.0) AC_CONFIG_MACRO_DIR([m4]) # Checks for programs. From 32519767999f9becd326adf19077e0717bd7e288 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 11 Mar 2012 21:46:43 -0700 Subject: [PATCH 034/169] Convert to new instruction set. --- protocols/ssh/src/ssh_client.c | 9 +++++---- protocols/ssh/src/ssh_terminal.c | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 185b175f..d2062780 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -128,11 +128,12 @@ int guac_client_init(guac_client* client, int argc, char** argv) { guac_protocol_send_size(socket, term->cursor_layer, term->char_width, term->char_height); /* Draw cursor */ - guac_protocol_send_rect(socket, + guac_protocol_send_rect(socket, term->cursor_layer, + 0, 0, term->char_width, term->char_height); + + guac_protocol_send_cfill(socket, GUAC_COMP_OVER, term->cursor_layer, - 0, 0, term->char_width, term->char_height, - 0x40, 0xFF, 0x80, - 0x80); + 0x40, 0xFF, 0x80, 0x80); guac_socket_flush(socket); diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 34d1b87f..0cb453ee 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -97,7 +97,7 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { term->cursor_row = 0; term->cursor_col = 0; - term->cursor_layer = guac_client_alloc_layer(client, 1); + term->cursor_layer = guac_client_alloc_layer(client); term->term_width = 100; term->term_height = 37; @@ -226,9 +226,11 @@ int __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { /* Send glyph and update filled flyphs */ guac_protocol_send_png(socket, GUAC_COMP_OVER, term->glyph_stroke, location * term->char_width, 0, surface); - guac_protocol_send_rect(socket, GUAC_COMP_OVER, term->filled_glyphs, + guac_protocol_send_rect(socket, term->filled_glyphs, location * term->char_width, 0, - term->char_width, term->char_height, + term->char_width, term->char_height); + + guac_protocol_send_cfill(socket, GUAC_COMP_OVER, term->filled_glyphs, background->red, background->green, background->blue, @@ -280,12 +282,11 @@ int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, &ssh_guac_terminal_palette[foreground]; /* Colorize letter */ - guac_protocol_send_rect(socket, - GUAC_COMP_ATOP, term->glyph_stroke, - + guac_protocol_send_rect(socket, term->glyph_stroke, 0, 0, - term->char_width * term->next_glyph, term->char_height, + term->char_width * term->next_glyph, term->char_height); + guac_protocol_send_cfill(socket, GUAC_COMP_ATOP, term->glyph_stroke, color->red, color->green, color->blue, @@ -297,12 +298,11 @@ int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, if (foreground != term->glyph_foreground || background != term->glyph_background) { /* Set background */ - guac_protocol_send_rect(socket, - GUAC_COMP_OVER, term->filled_glyphs, - + guac_protocol_send_rect(socket, term->filled_glyphs, 0, 0, - term->char_width * term->next_glyph, term->char_height, + term->char_width * term->next_glyph, term->char_height); + guac_protocol_send_cfill(socket, GUAC_COMP_OVER, term->filled_glyphs, background_color->red, background_color->green, background_color->blue, @@ -378,12 +378,12 @@ int ssh_guac_terminal_clear(ssh_guac_terminal* term, &ssh_guac_terminal_palette[background_color]; /* Fill with color */ - return guac_protocol_send_rect(socket, - GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, - + return + guac_protocol_send_rect(socket, GUAC_DEFAULT_LAYER, col * term->char_width, row * term->char_height, - cols * term->char_width, rows * term->char_height, + cols * term->char_width, rows * term->char_height) + || guac_protocol_send_cfill(socket, GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, color->red, color->green, color->blue, 255); } From 4d007e7e788aae0f936003e123f96ff2e7b08472 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 23 Oct 2012 01:38:10 -0700 Subject: [PATCH 035/169] Automatically fit to screen. --- protocols/ssh/include/ssh_terminal.h | 4 +- protocols/ssh/src/ssh_client.c | 3 +- protocols/ssh/src/ssh_terminal.c | 70 ++++++++++++++-------------- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/ssh_terminal.h index e29e6736..7f56d59a 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/ssh_terminal.h @@ -194,7 +194,9 @@ typedef struct ssh_guac_terminal_color { extern const ssh_guac_terminal_color ssh_guac_terminal_palette[16]; -ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client); +ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client, + int width, int height); + void ssh_guac_terminal_free(ssh_guac_terminal* term); int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index d2062780..69cb528c 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -109,7 +109,8 @@ int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket* socket = client->socket; ssh_guac_client_data* client_data = malloc(sizeof(ssh_guac_client_data)); - ssh_guac_terminal* term = ssh_guac_terminal_create(client); + ssh_guac_terminal* term = ssh_guac_terminal_create(client, + client->info.optimal_width, client->info.optimal_height); /* Init client data */ client->data = client_data; diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/ssh_terminal.c index 0cb453ee..58f9d7dc 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/ssh_terminal.c @@ -73,7 +73,8 @@ const ssh_guac_terminal_color ssh_guac_terminal_palette[16] = { }; -ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { +ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client, + int width, int height) { int row, col; @@ -95,39 +96,6 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { term->glyph_stroke = guac_client_alloc_buffer(client); term->filled_glyphs = guac_client_alloc_buffer(client); - term->cursor_row = 0; - term->cursor_col = 0; - term->cursor_layer = guac_client_alloc_layer(client); - - term->term_width = 100; - term->term_height = 37; - term->char_handler = ssh_guac_terminal_echo; - - term->scroll_start = 0; - term->scroll_end = term->term_height - 1; - - /* Create scrollback buffer */ - term->scrollback = malloc(term->term_height * sizeof(ssh_guac_terminal_char*)); - - /* Init buffer */ - for (row = 0; row < term->term_height; row++) { - - /* Create row */ - ssh_guac_terminal_char* current_row = - term->scrollback[row] = malloc(term->term_width * sizeof(ssh_guac_terminal_char)); - - /* Init row */ - for (col = 0; col < term->term_width; col++) { - - /* Empty character, default colors */ - current_row[col].value = '\0'; - current_row[col].foreground = term->default_foreground; - current_row[col].background = term->default_background; - - } - - } - /* Get font */ term->font_desc = pango_font_description_new(); pango_font_description_set_family(term->font_desc, "monospace"); @@ -156,6 +124,40 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client) { (pango_font_metrics_get_descent(metrics) + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; + + term->cursor_row = 0; + term->cursor_col = 0; + term->cursor_layer = guac_client_alloc_layer(client); + + term->term_width = width / term->char_width; + term->term_height = height / term->char_height; + term->char_handler = ssh_guac_terminal_echo; + + term->scroll_start = 0; + term->scroll_end = term->term_height - 1; + + /* Create scrollback buffer */ + term->scrollback = malloc(term->term_height * sizeof(ssh_guac_terminal_char*)); + + /* Init buffer */ + for (row = 0; row < term->term_height; row++) { + + /* Create row */ + ssh_guac_terminal_char* current_row = + term->scrollback[row] = malloc(term->term_width * sizeof(ssh_guac_terminal_char)); + + /* Init row */ + for (col = 0; col < term->term_width; col++) { + + /* Empty character, default colors */ + current_row[col].value = '\0'; + current_row[col].foreground = term->default_foreground; + current_row[col].background = term->default_background; + + } + + } + /* Clear with background color */ ssh_guac_terminal_clear(term, 0, 0, term->term_height, term->term_width, From baa6d98724badc54bf20b9167ac4bdabba17ee89 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 16 Dec 2012 17:51:49 -0800 Subject: [PATCH 036/169] Ignore unknown keys (fixes #162). --- protocols/ssh/src/ssh_handlers.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 16e329cb..32a71867 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -189,6 +189,9 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { else if (keysym == 0xFF53) { data = "\x1B\x5B""C"; length = 3; } else if (keysym == 0xFF51) { data = "\x1B\x5B""D"; length = 3; } + /* Ignore other keys */ + else return 0; + return channel_write(client_data->term_channel, data, length); } From 80a56bc9fd204112f89747f39af871b3ecd10a60 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 9 Dec 2012 00:35:37 -0800 Subject: [PATCH 037/169] Clean up code ... rename ssh_guac_terminal to guac_terminal (preparing for generic). --- protocols/ssh/Makefile.am | 12 ++- protocols/ssh/include/ssh_client.h | 4 +- .../include/{ssh_terminal.h => terminal.h} | 42 +++++----- ...erminal_handlers.h => terminal_handlers.h} | 12 +-- protocols/ssh/src/ssh_client.c | 22 +++--- protocols/ssh/src/ssh_handlers.c | 6 +- .../ssh/src/{ssh_terminal.c => terminal.c} | 78 +++++++++---------- ...erminal_handlers.c => terminal_handlers.c} | 62 +++++++-------- 8 files changed, 124 insertions(+), 114 deletions(-) rename protocols/ssh/include/{ssh_terminal.h => terminal.h} (81%) rename protocols/ssh/include/{ssh_terminal_handlers.h => terminal_handlers.h} (84%) rename protocols/ssh/src/{ssh_terminal.c => terminal.c} (85%) rename protocols/ssh/src/{ssh_terminal_handlers.c => terminal_handlers.c} (88%) diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 953c3b31..310d7b02 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -40,7 +40,17 @@ ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libguac-client-ssh.la -libguac_client_ssh_la_SOURCES = src/ssh_client.c src/ssh_handlers.c src/ssh_terminal.c src/ssh_terminal_handlers.c +libguac_client_ssh_la_SOURCES = \ + src/ssh_client.c \ + src/ssh_handlers.c \ + src/terminal.c \ + src/terminal_handlers.c + +noinst_HEADERS = \ + include/ssh_client.h \ + include/ssh_handlers.h \ + include/terminal.h \ + include/terminal_handlers.h libguac_client_ssh_la_CFLAGS = -Werror -Wall -pedantic -Iinclude @PANGO_CFLAGS@ @PANGOCAIRO_CFLAGS@ libguac_client_ssh_la_LIBADD = @PANGO_LIBS@ @PANGOCAIRO_LIBS@ diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index 247da330..2dd3cebd 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -45,14 +45,14 @@ #include "ssh_client.h" #include "ssh_handlers.h" -#include "ssh_terminal.h" +#include "terminal.h" typedef struct ssh_guac_client_data { ssh_session session; ssh_channel term_channel; - ssh_guac_terminal* term; + guac_terminal* term; char * clipboard_data; diff --git a/protocols/ssh/include/ssh_terminal.h b/protocols/ssh/include/terminal.h similarity index 81% rename from protocols/ssh/include/ssh_terminal.h rename to protocols/ssh/include/terminal.h index 7f56d59a..ab31b9fc 100644 --- a/protocols/ssh/include/ssh_terminal.h +++ b/protocols/ssh/include/terminal.h @@ -42,20 +42,20 @@ #include -typedef struct ssh_guac_terminal ssh_guac_terminal; +typedef struct guac_terminal guac_terminal; /** * Handler for characters printed to the terminal. When a character is printed, * the current char handler for the terminal is called and given that * character. */ -typedef int ssh_guac_terminal_char_handler(ssh_guac_terminal* term, char c); +typedef int guac_terminal_char_handler(guac_terminal* term, char c); /** * Represents a single character for display in a terminal, including actual * character value, foreground color, and background color. */ -typedef struct ssh_guac_terminal_char { +typedef struct guac_terminal_char { /** * The character value of the character to display. @@ -72,13 +72,13 @@ typedef struct ssh_guac_terminal_char { */ int background; -} ssh_guac_terminal_char; +} guac_terminal_char; /** * Represents a terminal emulator which uses a given Guacamole client to * render itself. */ -struct ssh_guac_terminal { +struct guac_terminal { /** * The Guacamole client this terminal emulator will use for rendering. @@ -126,7 +126,7 @@ struct ssh_guac_terminal { * Array of scrollback buffer rows, where each row is an array of * characters. */ - ssh_guac_terminal_char** scrollback; + guac_terminal_char** scrollback; /** * The width of each character, in pixels. @@ -182,46 +182,46 @@ struct ssh_guac_terminal { int default_foreground; int default_background; - ssh_guac_terminal_char_handler* char_handler; + guac_terminal_char_handler* char_handler; }; -typedef struct ssh_guac_terminal_color { +typedef struct guac_terminal_color { int red; int green; int blue; -} ssh_guac_terminal_color; +} guac_terminal_color; -extern const ssh_guac_terminal_color ssh_guac_terminal_palette[16]; +extern const guac_terminal_color guac_terminal_palette[16]; -ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client, +guac_terminal* guac_terminal_create(guac_client* client, int width, int height); -void ssh_guac_terminal_free(ssh_guac_terminal* term); +void guac_terminal_free(guac_terminal* term); -int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size); +int guac_terminal_write(guac_terminal* term, const char* c, int size); -int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term); +int guac_terminal_redraw_cursor(guac_terminal* term); -int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, +int guac_terminal_set_colors(guac_terminal* term, int foreground, int background); -int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, char c); +int guac_terminal_set(guac_terminal* term, int row, int col, char c); -int ssh_guac_terminal_copy(ssh_guac_terminal* term, +int guac_terminal_copy(guac_terminal* term, int src_row, int src_col, int rows, int cols, int dst_row, int dst_col); -int ssh_guac_terminal_clear(ssh_guac_terminal* term, +int guac_terminal_clear(guac_terminal* term, int row, int col, int rows, int cols, int background_color); -int ssh_guac_terminal_scroll_up(ssh_guac_terminal* term, +int guac_terminal_scroll_up(guac_terminal* term, int start_row, int end_row, int amount); -int ssh_guac_terminal_scroll_down(ssh_guac_terminal* term, +int guac_terminal_scroll_down(guac_terminal* term, int start_row, int end_row, int amount); -int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, +int guac_terminal_clear_range(guac_terminal* term, int start_row, int start_col, int end_row, int end_col, int background_color); diff --git a/protocols/ssh/include/ssh_terminal_handlers.h b/protocols/ssh/include/terminal_handlers.h similarity index 84% rename from protocols/ssh/include/ssh_terminal_handlers.h rename to protocols/ssh/include/terminal_handlers.h index 6cd8de94..0a5a1e87 100644 --- a/protocols/ssh/include/ssh_terminal_handlers.h +++ b/protocols/ssh/include/terminal_handlers.h @@ -38,13 +38,13 @@ #ifndef _SSH_GUAC_TERMINAL_HANDLERS #define _SSH_GUAC_TERMINAL_HANDLERS -#include "ssh_terminal.h" +#include "terminal.h" -int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c); -int ssh_guac_terminal_escape(ssh_guac_terminal* term, char c); -int ssh_guac_terminal_charset(ssh_guac_terminal* term, char c); -int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c); -int ssh_guac_terminal_osc(ssh_guac_terminal* term, char c); +int guac_terminal_echo(guac_terminal* term, char c); +int guac_terminal_escape(guac_terminal* term, char c); +int guac_terminal_charset(guac_terminal* term, char c); +int guac_terminal_csi(guac_terminal* term, char c); +int guac_terminal_osc(guac_terminal* term, char c); #endif diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 69cb528c..4c9e7bff 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -47,7 +47,7 @@ #include "ssh_client.h" #include "ssh_handlers.h" -#include "ssh_terminal.h" +#include "terminal.h" /* Client plugin arguments */ const char* GUAC_CLIENT_ARGS[] = { @@ -68,8 +68,8 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr if (keysym >= 0x00 && keysym <= 0xFF) { /* Add to password */ client_data->password[client_data->password_length++] = keysym; - ssh_guac_terminal_write(client_data->term, "*", 1); - ssh_guac_terminal_redraw_cursor(client_data->term); + guac_terminal_write(client_data->term, "*", 1); + guac_terminal_redraw_cursor(client_data->term); guac_socket_flush(client->socket); } else if (keysym == 0xFF08) { @@ -78,8 +78,8 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr client_data->password_length--; /* Backspace */ - ssh_guac_terminal_write(client_data->term, "\x08\x1B[K", 4); - ssh_guac_terminal_redraw_cursor(client_data->term); + guac_terminal_write(client_data->term, "\x08\x1B[K", 4); + guac_terminal_redraw_cursor(client_data->term); guac_socket_flush(client->socket); } @@ -90,8 +90,8 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr client_data->password[client_data->password_length] = '\0'; /* Clear screen */ - ssh_guac_terminal_write(client_data->term, "\x1B[2J\x1B[1;1H", 10); - ssh_guac_terminal_redraw_cursor(client_data->term); + guac_terminal_write(client_data->term, "\x1B[2J\x1B[1;1H", 10); + guac_terminal_redraw_cursor(client_data->term); guac_socket_flush(client->socket); return ssh_guac_client_auth(client, client_data->password); @@ -109,7 +109,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket* socket = client->socket; ssh_guac_client_data* client_data = malloc(sizeof(ssh_guac_client_data)); - ssh_guac_terminal* term = ssh_guac_terminal_create(client, + guac_terminal* term = guac_terminal_create(client, client->info.optimal_width, client->info.optimal_height); /* Init client data */ @@ -165,8 +165,8 @@ int guac_client_init(guac_client* client, int argc, char** argv) { else { client_data->password_length = 0; - ssh_guac_terminal_write(client_data->term, "Password: ", 10); - ssh_guac_terminal_redraw_cursor(client_data->term); + guac_terminal_write(client_data->term, "Password: ", 10); + guac_terminal_redraw_cursor(client_data->term); guac_socket_flush(client->socket); client->key_handler = ssh_guac_client_password_key_handler; @@ -182,7 +182,7 @@ int ssh_guac_client_auth(guac_client* client, const char* password) { guac_socket* socket = client->socket; ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - ssh_guac_terminal* term = client_data->term; + guac_terminal* term = client_data->term; /* Authenticate */ if (ssh_userauth_password(client_data->session, NULL, password) != SSH_AUTH_SUCCESS) { diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 32a71867..e7bd0dac 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -90,8 +90,8 @@ int ssh_guac_client_handle_messages(guac_client* client) { && !channel_is_eof(client_data->term_channel) && (bytes_read = channel_read_nonblocking(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) { - if (ssh_guac_terminal_write(client_data->term, buffer, bytes_read) - || ssh_guac_terminal_redraw_cursor(client_data->term) + if (guac_terminal_write(client_data->term, buffer, bytes_read) + || guac_terminal_redraw_cursor(client_data->term) || guac_socket_flush(socket)) return 1; @@ -206,7 +206,7 @@ int ssh_guac_client_free_handler(guac_client* client) { ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data; /* Free terminal */ - ssh_guac_terminal_free(guac_client_data->term); + guac_terminal_free(guac_client_data->term); /* Free clipboard data */ free(guac_client_data->clipboard_data); diff --git a/protocols/ssh/src/ssh_terminal.c b/protocols/ssh/src/terminal.c similarity index 85% rename from protocols/ssh/src/ssh_terminal.c rename to protocols/ssh/src/terminal.c index 58f9d7dc..e47cd669 100644 --- a/protocols/ssh/src/ssh_terminal.c +++ b/protocols/ssh/src/terminal.c @@ -46,10 +46,10 @@ #include #include -#include "ssh_terminal.h" -#include "ssh_terminal_handlers.h" +#include "terminal.h" +#include "terminal_handlers.h" -const ssh_guac_terminal_color ssh_guac_terminal_palette[16] = { +const guac_terminal_color guac_terminal_palette[16] = { /* Normal colors */ {0x00, 0x00, 0x00}, /* Black */ @@ -73,7 +73,7 @@ const ssh_guac_terminal_color ssh_guac_terminal_palette[16] = { }; -ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client, +guac_terminal* guac_terminal_create(guac_client* client, int width, int height) { int row, col; @@ -83,7 +83,7 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client, PangoFontMetrics* metrics; PangoContext* context; - ssh_guac_terminal* term = malloc(sizeof(ssh_guac_terminal)); + guac_terminal* term = malloc(sizeof(guac_terminal)); term->client = client; term->glyph_foreground = term->foreground = term->default_foreground = 7; /* White */ @@ -131,20 +131,20 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client, term->term_width = width / term->char_width; term->term_height = height / term->char_height; - term->char_handler = ssh_guac_terminal_echo; + term->char_handler = guac_terminal_echo; term->scroll_start = 0; term->scroll_end = term->term_height - 1; /* Create scrollback buffer */ - term->scrollback = malloc(term->term_height * sizeof(ssh_guac_terminal_char*)); + term->scrollback = malloc(term->term_height * sizeof(guac_terminal_char*)); /* Init buffer */ for (row = 0; row < term->term_height; row++) { /* Create row */ - ssh_guac_terminal_char* current_row = - term->scrollback[row] = malloc(term->term_width * sizeof(ssh_guac_terminal_char)); + guac_terminal_char* current_row = + term->scrollback[row] = malloc(term->term_width * sizeof(guac_terminal_char)); /* Init row */ for (col = 0; col < term->term_width; col++) { @@ -159,7 +159,7 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client, } /* Clear with background color */ - ssh_guac_terminal_clear(term, + guac_terminal_clear(term, 0, 0, term->term_height, term->term_width, term->background); @@ -167,7 +167,7 @@ ssh_guac_terminal* ssh_guac_terminal_create(guac_client* client, } -void ssh_guac_terminal_free(ssh_guac_terminal* term) { +void guac_terminal_free(guac_terminal* term) { /* Free scrollback buffer */ for (int row = 0; row < term->term_height; row++) @@ -176,18 +176,18 @@ void ssh_guac_terminal_free(ssh_guac_terminal* term) { free(term->scrollback); } -int __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { +int __guac_terminal_get_glyph(guac_terminal* term, char c) { guac_socket* socket = term->client->socket; int location; /* Use foreground color */ - const ssh_guac_terminal_color* color = - &ssh_guac_terminal_palette[term->glyph_foreground]; + const guac_terminal_color* color = + &guac_terminal_palette[term->glyph_foreground]; /* Use background color */ - const ssh_guac_terminal_color* background = - &ssh_guac_terminal_palette[term->glyph_background]; + const guac_terminal_color* background = + &guac_terminal_palette[term->glyph_background]; cairo_surface_t* surface; cairo_t* cairo; @@ -251,7 +251,7 @@ int __ssh_guac_terminal_get_glyph(ssh_guac_terminal* term, char c) { } -int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term) { +int guac_terminal_redraw_cursor(guac_terminal* term) { guac_socket* socket = term->client->socket; @@ -267,21 +267,21 @@ int ssh_guac_terminal_redraw_cursor(ssh_guac_terminal* term) { } -int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, +int guac_terminal_set_colors(guac_terminal* term, int foreground, int background) { guac_socket* socket = term->client->socket; - const ssh_guac_terminal_color* background_color; + const guac_terminal_color* background_color; /* Get background color */ - background_color = &ssh_guac_terminal_palette[background]; + background_color = &guac_terminal_palette[background]; /* If foreground different from current, colorize */ if (foreground != term->glyph_foreground) { /* Get color */ - const ssh_guac_terminal_color* color = - &ssh_guac_terminal_palette[foreground]; + const guac_terminal_color* color = + &guac_terminal_palette[foreground]; /* Colorize letter */ guac_protocol_send_rect(socket, term->glyph_stroke, @@ -328,10 +328,10 @@ int ssh_guac_terminal_set_colors(ssh_guac_terminal* term, } -int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, char c) { +int guac_terminal_set(guac_terminal* term, int row, int col, char c) { guac_socket* socket = term->client->socket; - int location = __ssh_guac_terminal_get_glyph(term, c); + int location = __guac_terminal_get_glyph(term, c); return guac_protocol_send_copy(socket, term->filled_glyphs, @@ -342,7 +342,7 @@ int ssh_guac_terminal_set(ssh_guac_terminal* term, int row, int col, char c) { } -int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size) { +int guac_terminal_write(guac_terminal* term, const char* c, int size) { while (size > 0) { term->char_handler(term, *(c++)); @@ -353,7 +353,7 @@ int ssh_guac_terminal_write(ssh_guac_terminal* term, const char* c, int size) { } -int ssh_guac_terminal_copy(ssh_guac_terminal* term, +int guac_terminal_copy(guac_terminal* term, int src_row, int src_col, int rows, int cols, int dst_row, int dst_col) { @@ -372,12 +372,12 @@ int ssh_guac_terminal_copy(ssh_guac_terminal* term, } -int ssh_guac_terminal_clear(ssh_guac_terminal* term, +int guac_terminal_clear(guac_terminal* term, int row, int col, int rows, int cols, int background_color) { guac_socket* socket = term->client->socket; - const ssh_guac_terminal_color* color = - &ssh_guac_terminal_palette[background_color]; + const guac_terminal_color* color = + &guac_terminal_palette[background_color]; /* Fill with color */ return @@ -390,7 +390,7 @@ int ssh_guac_terminal_clear(ssh_guac_terminal* term, } -int ssh_guac_terminal_scroll_up(ssh_guac_terminal* term, +int guac_terminal_scroll_up(guac_terminal* term, int start_row, int end_row, int amount) { /* Calculate height of scroll region */ @@ -399,19 +399,19 @@ int ssh_guac_terminal_scroll_up(ssh_guac_terminal* term, return /* Move rows within scroll region up by the given amount */ - ssh_guac_terminal_copy(term, + guac_terminal_copy(term, start_row + amount, 0, height - amount, term->term_width, start_row, 0) /* Fill new rows with background */ - || ssh_guac_terminal_clear(term, + || guac_terminal_clear(term, end_row - amount + 1, 0, amount, term->term_width, term->background); } -int ssh_guac_terminal_scroll_down(ssh_guac_terminal* term, +int guac_terminal_scroll_down(guac_terminal* term, int start_row, int end_row, int amount) { /* Calculate height of scroll region */ @@ -420,19 +420,19 @@ int ssh_guac_terminal_scroll_down(ssh_guac_terminal* term, return /* Move rows within scroll region down by the given amount */ - ssh_guac_terminal_copy(term, + guac_terminal_copy(term, start_row, 0, height - amount, term->term_width, start_row + amount, 0) /* Fill new rows with background */ - || ssh_guac_terminal_clear(term, + || guac_terminal_clear(term, start_row, 0, amount, term->term_width, term->background); } -int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, +int guac_terminal_clear_range(guac_terminal* term, int start_row, int start_col, int end_row, int end_col, int background_color) { @@ -440,7 +440,7 @@ int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, if (start_col > 0) { /* Clear from start_col to far right */ - if (ssh_guac_terminal_clear(term, + if (guac_terminal_clear(term, start_row, start_col, 1, term->term_width - start_col, background_color)) return 1; @@ -453,7 +453,7 @@ int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, if (end_col < term->term_width - 1) { /* Clear from far left to end_col */ - if (ssh_guac_terminal_clear(term, + if (guac_terminal_clear(term, end_row, 0, 1, end_col + 1, background_color)) return 1; @@ -466,7 +466,7 @@ int ssh_guac_terminal_clear_range(ssh_guac_terminal* term, /* Remaining region now guaranteed rectangular. Clear, if possible */ if (start_row <= end_row) { - if (ssh_guac_terminal_clear(term, + if (guac_terminal_clear(term, start_row, 0, end_row - start_row + 1, term->term_width, background_color)) return 1; diff --git a/protocols/ssh/src/ssh_terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c similarity index 88% rename from protocols/ssh/src/ssh_terminal_handlers.c rename to protocols/ssh/src/terminal_handlers.c index 9503ec01..2c5b6011 100644 --- a/protocols/ssh/src/ssh_terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -37,10 +37,10 @@ #include -#include "ssh_terminal.h" -#include "ssh_terminal_handlers.h" +#include "terminal.h" +#include "terminal_handlers.h" -int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { +int guac_terminal_echo(guac_terminal* term, char c) { int foreground = term->foreground; int background = term->background; @@ -71,14 +71,14 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { term->cursor_row = term->scroll_end; /* Scroll up by one row */ - ssh_guac_terminal_scroll_up(term, term->scroll_start, term->scroll_end, 1); + guac_terminal_scroll_up(term, term->scroll_start, term->scroll_end, 1); } break; /* ESC */ case 0x1B: - term->char_handler = ssh_guac_terminal_escape; + term->char_handler = guac_terminal_escape; break; /* Displayable chars */ @@ -95,7 +95,7 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { term->cursor_row = term->scroll_end; /* Scroll up by one row */ - ssh_guac_terminal_scroll_up(term, term->scroll_start, term->scroll_end, 1); + guac_terminal_scroll_up(term, term->scroll_start, term->scroll_end, 1); } @@ -110,10 +110,10 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { if (term->bold && foreground <= 7) foreground += 8; - ssh_guac_terminal_set_colors(term, + guac_terminal_set_colors(term, foreground, background); - ssh_guac_terminal_set(term, + guac_terminal_set(term, term->cursor_row, term->cursor_col, c); @@ -127,25 +127,25 @@ int ssh_guac_terminal_echo(ssh_guac_terminal* term, char c) { } -int ssh_guac_terminal_escape(ssh_guac_terminal* term, char c) { +int guac_terminal_escape(guac_terminal* term, char c) { switch (c) { case '(': - term->char_handler = ssh_guac_terminal_charset; + term->char_handler = guac_terminal_charset; break; case ']': - term->char_handler = ssh_guac_terminal_osc; + term->char_handler = guac_terminal_osc; break; case '[': - term->char_handler = ssh_guac_terminal_csi; + term->char_handler = guac_terminal_csi; break; default: guac_client_log_info(term->client, "Unhandled ESC sequence: %c", c); - term->char_handler = ssh_guac_terminal_echo; + term->char_handler = guac_terminal_echo; } @@ -153,12 +153,12 @@ int ssh_guac_terminal_escape(ssh_guac_terminal* term, char c) { } -int ssh_guac_terminal_charset(ssh_guac_terminal* term, char c) { - term->char_handler = ssh_guac_terminal_echo; +int guac_terminal_charset(guac_terminal* term, char c) { + term->char_handler = guac_terminal_echo; return 0; } -int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { +int guac_terminal_csi(guac_terminal* term, char c) { /* CSI function arguments */ static int argc = 0; @@ -361,21 +361,21 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Erase from cursor to end of display */ if (argv[0] == 0) - ssh_guac_terminal_clear_range(term, + guac_terminal_clear_range(term, term->cursor_row, term->cursor_col, term->term_height-1, term->term_width-1, term->background); /* Erase from start to cursor */ else if (argv[0] == 1) - ssh_guac_terminal_clear_range(term, + guac_terminal_clear_range(term, 0, 0, term->cursor_row, term->cursor_col, term->background); /* Entire screen */ else if (argv[0] == 2) - ssh_guac_terminal_clear(term, + guac_terminal_clear(term, 0, 0, term->term_height, term->term_width, term->background); @@ -386,7 +386,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Erase from cursor to end of line */ if (argv[0] == 0) - ssh_guac_terminal_clear(term, + guac_terminal_clear(term, term->cursor_row, term->cursor_col, 1, term->term_width - term->cursor_col, term->background); @@ -394,14 +394,14 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Erase from start to cursor */ else if (argv[0] == 1) - ssh_guac_terminal_clear(term, + guac_terminal_clear(term, term->cursor_row, 0, 1, term->cursor_col + 1, term->background); /* Erase line */ else if (argv[0] == 2) - ssh_guac_terminal_clear(term, + guac_terminal_clear(term, term->cursor_row, 0, 1, term->term_width, term->background); @@ -414,7 +414,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { amount = argv[0]; if (amount == 0) amount = 1; - ssh_guac_terminal_scroll_down(term, + guac_terminal_scroll_down(term, term->cursor_row, term->scroll_end, amount); break; @@ -425,7 +425,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { amount = argv[0]; if (amount == 0) amount = 1; - ssh_guac_terminal_scroll_up(term, + guac_terminal_scroll_up(term, term->cursor_row, term->scroll_end, amount); break; @@ -438,14 +438,14 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Scroll left by amount */ if (term->cursor_col + amount < term->term_width) - ssh_guac_terminal_copy(term, + guac_terminal_copy(term, term->cursor_row, term->cursor_col + amount, 1, term->term_width - term->cursor_col - amount, term->cursor_row, term->cursor_col); /* Clear right */ - ssh_guac_terminal_clear(term, + guac_terminal_clear(term, term->cursor_row, term->term_width - amount, 1, amount, term->background); @@ -460,13 +460,13 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* Scroll right by amount */ if (term->cursor_col + amount < term->term_width) - ssh_guac_terminal_copy(term, + guac_terminal_copy(term, term->cursor_row, term->cursor_col, 1, term->term_width - term->cursor_col - amount, term->cursor_row, term->cursor_col + amount); /* Clear left */ - ssh_guac_terminal_clear(term, + guac_terminal_clear(term, term->cursor_row, term->cursor_col, 1, amount, term->background); @@ -482,7 +482,7 @@ int ssh_guac_terminal_csi(ssh_guac_terminal* term, char c) { /* If not a semicolon, end of CSI sequence */ if (c != ';') { - term->char_handler = ssh_guac_terminal_echo; + term->char_handler = guac_terminal_echo; /* Reset parameters */ for (i=0; ichar_handler = ssh_guac_terminal_echo; + term->char_handler = guac_terminal_echo; return 0; } From 15a0e44474fcafb2c574aa5f4c053ee2cf256cf0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 15 Mar 2013 13:19:27 -0700 Subject: [PATCH 038/169] Ensure m4/ directory is present. --- protocols/ssh/.gitignore | 3 ++- protocols/ssh/m4/README | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 protocols/ssh/m4/README diff --git a/protocols/ssh/.gitignore b/protocols/ssh/.gitignore index e37f9166..3f725332 100644 --- a/protocols/ssh/.gitignore +++ b/protocols/ssh/.gitignore @@ -21,7 +21,8 @@ Makefile Makefile.in aclocal.m4 autom4te.cache/ -m4/ +m4/* +!README config.guess config.log config.status diff --git a/protocols/ssh/m4/README b/protocols/ssh/m4/README new file mode 100644 index 00000000..c9c9cbc4 --- /dev/null +++ b/protocols/ssh/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. From b7af1d45f556e47babcf1b04c49f8df3b25356c1 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 19 Mar 2013 22:48:43 -0700 Subject: [PATCH 039/169] Initial conversion to new architecture, stub out deltas. --- protocols/ssh/Makefile.am | 2 + protocols/ssh/include/delta.h | 158 ++++++++++++++++++++++++++ protocols/ssh/include/terminal.h | 112 ++++++++++++++---- protocols/ssh/src/delta.c | 72 ++++++++++++ protocols/ssh/src/ssh_client.c | 4 - protocols/ssh/src/ssh_handlers.c | 1 - protocols/ssh/src/terminal.c | 26 +++-- protocols/ssh/src/terminal_handlers.c | 63 +++++----- 8 files changed, 367 insertions(+), 71 deletions(-) create mode 100644 protocols/ssh/include/delta.h create mode 100644 protocols/ssh/src/delta.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 310d7b02..16a5ad47 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -41,12 +41,14 @@ ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libguac-client-ssh.la libguac_client_ssh_la_SOURCES = \ + src/delta.c \ src/ssh_client.c \ src/ssh_handlers.c \ src/terminal.c \ src/terminal_handlers.c noinst_HEADERS = \ + include/delta.h \ include/ssh_client.h \ include/ssh_handlers.h \ include/terminal.h \ diff --git a/protocols/ssh/include/delta.h b/protocols/ssh/include/delta.h new file mode 100644 index 00000000..bae25ca4 --- /dev/null +++ b/protocols/ssh/include/delta.h @@ -0,0 +1,158 @@ + +/* ***** 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-ssh. + * + * 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 _SSH_GUAC_DELTA_H +#define _SSH_GUAC_DELTA_H + +#include +#include "terminal.h" + +/** + * All available terminal operations which affect character cells. + */ +typedef enum guac_terminal_operation_type { + + /** + * Operation which does nothing. + */ + GUAC_CHAR_NOP, + + /** + * Operation which copies a character from a given row/column coordinate. + */ + GUAC_CHAR_COPY, + + /** + * Operation which sets the character and attributes. + */ + GUAC_CHAR_SET + +} guac_terminal_operation_type; + +/** + * A pairing of a guac_terminal_operation_type and all parameters required by + * that operation type. + */ +typedef struct guac_terminal_operation { + + /** + * The type of operation to perform. + */ + guac_terminal_operation_type type; + + /** + * The character (and attributes) to set the current location to. This is + * only applicable to GUAC_CHAR_SET. + */ + guac_terminal_char character; + + /** + * The row to copy a character from. This is only applicable to + * GUAC_CHAR_COPY. + */ + int row; + + /** + * The column to copy a character from. This is only applicable to + * GUAC_CHAR_COPY. + */ + int column; + +} guac_terminal_operation; + +/** + * Set of all pending operations for the currently-visible screen area. + */ +typedef struct guac_terminal_delta { + + /** + * Array of all operations pending for the visible screen area. + */ + guac_terminal_operation* operations; + + /** + * The width of the screen, in characters. + */ + int width; + + /** + * The height of the screen, in characters. + */ + int height; + +} guac_terminal_delta; + +/** + * Allocates a new guac_terminal_delta. + */ +guac_terminal_delta* guac_terminal_delta_alloc(int width, int height); + +/** + * Frees the given guac_terminal_delta. + */ +void guac_terminal_delta_free(guac_terminal_delta* delta); + +/** + * Resizes the given guac_terminal_delta to the given dimensions. + */ +void guac_terminal_delta_resize(guac_terminal_delta* delta, + int width, int height); + +/** + * Stores a set operation at the given location. + */ +void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, + guac_terminal_char* character); + +/** + * Stores a rectangle of copy operations, copying existing operations as + * necessary. + */ +void guac_terminal_delta_copy(guac_terminal_delta* delta, + int dst_row, int dst_column, + int src_row, int src_column, + int w, int h); + +/** + * Flushes all pending operations within the given guac_client_delta to the + * given guac_terminal. + */ +void guac_terminal_delta_flush(guac_terminal_delta* delta, + guac_terminal* terminal); + +#endif + diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index ab31b9fc..6b60809b 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -38,6 +38,8 @@ #ifndef _SSH_GUAC_TERMINAL_H #define _SSH_GUAC_TERMINAL_H +#include + #include #include @@ -51,6 +53,67 @@ typedef struct guac_terminal guac_terminal; */ typedef int guac_terminal_char_handler(guac_terminal* term, char c); +/** + * An RGB color, where each component ranges from 0 to 255. + */ +typedef struct guac_terminal_color { + + /** + * The red component of this color. + */ + int red; + + /** + * The green component of this color. + */ + int green; + + /** + * The blue component of this color. + */ + int blue; + +} guac_terminal_color; + +/** + * Terminal attributes, as can be applied to a single character. + */ +typedef struct guac_terminal_attributes { + + /** + * Whether the character should be rendered bold. + */ + bool bold; + + /** + * Whether the character should be rendered with reversed colors + * (background becomes foreground and vice-versa). + */ + bool reverse; + + /** + * Whether to render the character with underscore. + */ + bool underscore; + + /** + * The foreground color of this character, as a palette index. + */ + int foreground; + + /** + * The background color of this character, as a palette index. + */ + int background; + +} guac_terminal_attributes; + +/** + * The available color palette. All integer colors within structures + * here are indices into this palette. + */ +extern const guac_terminal_color guac_terminal_palette[16]; + /** * Represents a single character for display in a terminal, including actual * character value, foreground color, and background color. @@ -63,14 +126,9 @@ typedef struct guac_terminal_char { char value; /** - * The foreground color of the character to display. + * The attributes of the character to display. */ - int foreground; - - /** - * The background color of the character to display. - */ - int background; + guac_terminal_attributes attributes; } guac_terminal_char; @@ -173,36 +231,42 @@ struct guac_terminal { */ guac_layer* cursor_layer; - int foreground; - int background; - int reverse; - int bold; - int underscore; + /** + * The attributes which will be applied to future characters. + */ + guac_terminal_attributes current_attributes; - int default_foreground; - int default_background; + /** + * The attributes which will be applied to characters by default, unless + * other attributes are explicitly specified. + */ + guac_terminal_attributes default_attributes; + /** + * Handler which will receive all printed characters, updating the terminal + * accordingly. + */ guac_terminal_char_handler* char_handler; }; -typedef struct guac_terminal_color { - int red; - int green; - int blue; -} guac_terminal_color; - -extern const guac_terminal_color guac_terminal_palette[16]; - +/** + * Creates a new guac_terminal, having the given width and height, and + * rendering to the given client. + */ guac_terminal* guac_terminal_create(guac_client* client, int width, int height); +/** + * Frees all resources associated with the given terminal. + */ void guac_terminal_free(guac_terminal* term); +/** + * Writes the given string of characters to the terminal. + */ int guac_terminal_write(guac_terminal* term, const char* c, int size); -int guac_terminal_redraw_cursor(guac_terminal* term); - int guac_terminal_set_colors(guac_terminal* term, int foreground, int background); diff --git a/protocols/ssh/src/delta.c b/protocols/ssh/src/delta.c new file mode 100644 index 00000000..359cc749 --- /dev/null +++ b/protocols/ssh/src/delta.c @@ -0,0 +1,72 @@ + +/* ***** 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-ssh. + * + * 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 "terminal.h" +#include "delta.h" + +guac_terminal_delta* guac_terminal_delta_alloc(int width, int height) { + /* STUB */ + return NULL; +} + +void guac_terminal_delta_free(guac_terminal_delta* delta) { + /* STUB */ +} + +void guac_terminal_delta_resize(guac_terminal_delta* delta, + int width, int height) { + /* STUB */ +} + +void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, + guac_terminal_char* character) { + /* STUB */ +} + +void guac_terminal_delta_copy(guac_terminal_delta* delta, + int dst_row, int dst_column, + int src_row, int src_column, + int w, int h) { + /* STUB */ +} + +void guac_terminal_delta_flush(guac_terminal_delta* delta, + guac_terminal* terminal) { + /* STUB */ +} + diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 4c9e7bff..2da7d481 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -69,7 +69,6 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Add to password */ client_data->password[client_data->password_length++] = keysym; guac_terminal_write(client_data->term, "*", 1); - guac_terminal_redraw_cursor(client_data->term); guac_socket_flush(client->socket); } else if (keysym == 0xFF08) { @@ -79,7 +78,6 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Backspace */ guac_terminal_write(client_data->term, "\x08\x1B[K", 4); - guac_terminal_redraw_cursor(client_data->term); guac_socket_flush(client->socket); } @@ -91,7 +89,6 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Clear screen */ guac_terminal_write(client_data->term, "\x1B[2J\x1B[1;1H", 10); - guac_terminal_redraw_cursor(client_data->term); guac_socket_flush(client->socket); return ssh_guac_client_auth(client, client_data->password); @@ -166,7 +163,6 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->password_length = 0; guac_terminal_write(client_data->term, "Password: ", 10); - guac_terminal_redraw_cursor(client_data->term); guac_socket_flush(client->socket); client->key_handler = ssh_guac_client_password_key_handler; diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index e7bd0dac..fd4c584c 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -91,7 +91,6 @@ int ssh_guac_client_handle_messages(guac_client* client) { && (bytes_read = channel_read_nonblocking(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) { if (guac_terminal_write(client_data->term, buffer, bytes_read) - || guac_terminal_redraw_cursor(client_data->term) || guac_socket_flush(socket)) return 1; diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index e47cd669..4d7fdbe1 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -76,6 +76,14 @@ const guac_terminal_color guac_terminal_palette[16] = { guac_terminal* guac_terminal_create(guac_client* client, int width, int height) { + guac_terminal_attributes default_attributes = { + .foreground = 7, + .background = 0, + .bold = false, + .reverse = false, + .underscore = false + }; + int row, col; PangoFontMap* font_map; @@ -86,11 +94,10 @@ guac_terminal* guac_terminal_create(guac_client* client, guac_terminal* term = malloc(sizeof(guac_terminal)); term->client = client; - term->glyph_foreground = term->foreground = term->default_foreground = 7; /* White */ - term->glyph_background = term->background = term->default_background = 0; /* Black */ - term->reverse = 0; /* Normal video */ - term->bold = 0; /* Normal intensity */ - term->underscore = 0; /* No underline */ + term->current_attributes = + term->default_attributes = default_attributes; + term->glyph_foreground = default_attributes.foreground; + term->glyph_background = default_attributes.background; memset(term->glyphs, 0, sizeof(term->glyphs)); term->glyph_stroke = guac_client_alloc_buffer(client); @@ -151,8 +158,7 @@ guac_terminal* guac_terminal_create(guac_client* client, /* Empty character, default colors */ current_row[col].value = '\0'; - current_row[col].foreground = term->default_foreground; - current_row[col].background = term->default_background; + current_row[col].attributes = term->default_attributes; } @@ -161,7 +167,7 @@ guac_terminal* guac_terminal_create(guac_client* client, /* Clear with background color */ guac_terminal_clear(term, 0, 0, term->term_height, term->term_width, - term->background); + term->current_attributes.background); return term; @@ -407,7 +413,7 @@ int guac_terminal_scroll_up(guac_terminal* term, /* Fill new rows with background */ || guac_terminal_clear(term, end_row - amount + 1, 0, amount, term->term_width, - term->background); + term->current_attributes.background); } @@ -428,7 +434,7 @@ int guac_terminal_scroll_down(guac_terminal* term, /* Fill new rows with background */ || guac_terminal_clear(term, start_row, 0, amount, term->term_width, - term->background); + term->current_attributes.background); } diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 2c5b6011..54e93eae 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -42,8 +42,8 @@ int guac_terminal_echo(guac_terminal* term, char c) { - int foreground = term->foreground; - int background = term->background; + int foreground = term->current_attributes.foreground; + int background = term->current_attributes.background; switch (c) { @@ -100,14 +100,14 @@ int guac_terminal_echo(guac_terminal* term, char c) { } /* Handle reverse video */ - if (term->reverse) { + if (term->current_attributes.reverse) { int swap = background; background = foreground; foreground = swap; } /* Handle bold */ - if (term->bold && foreground <= 7) + if (term->current_attributes.bold && foreground <= 7) foreground += 8; guac_terminal_set_colors(term, @@ -268,60 +268,59 @@ int guac_terminal_csi(guac_terminal* term, char c) { int value = argv[i]; /* Reset attributes */ - if (value == 0) { - term->foreground = term->default_foreground; - term->background = term->default_background; - term->reverse = 0; - term->underscore = 0; - term->bold = 0; - } + if (value == 0) + term->current_attributes = term->default_attributes; /* Bold */ else if (value == 1) - term->bold = 1; + term->current_attributes.bold = true; /* Underscore on */ else if (value == 4) - term->underscore = 1; + term->current_attributes.underscore = true; /* Foreground */ else if (value >= 30 && value <= 37) - term->foreground = value - 30; + term->current_attributes.foreground = value - 30; /* Background */ else if (value >= 40 && value <= 47) - term->background = value - 40; + term->current_attributes.background = value - 40; /* Underscore on, default foreground */ else if (value == 38) { - term->underscore = 1; - term->foreground = term->default_foreground; + term->current_attributes.underscore = true; + term->current_attributes.foreground = + term->default_attributes.foreground; } /* Underscore off, default foreground */ else if (value == 39) { - term->underscore = 0; - term->foreground = term->default_foreground; + term->current_attributes.underscore = false; + term->current_attributes.foreground = + term->default_attributes.foreground; } /* Reset background */ else if (value == 49) - term->background = term->default_background; + term->current_attributes.background = + term->default_attributes.background; /* Reverse video */ else if (value == 7) - term->reverse = 1; + term->current_attributes.reverse = true; /* Reset reverse video */ else if (value == 27) - term->reverse = 0; + term->current_attributes.reverse = false; /* Reset intensity */ else if (value == 27) - term->bold = 0; + term->current_attributes.bold = false; else - guac_client_log_info(term->client, "Unhandled graphics rendition: %i", value); + guac_client_log_info(term->client, + "Unhandled graphics rendition: %i", value); } @@ -364,20 +363,20 @@ int guac_terminal_csi(guac_terminal* term, char c) { guac_terminal_clear_range(term, term->cursor_row, term->cursor_col, term->term_height-1, term->term_width-1, - term->background); + term->current_attributes.background); /* Erase from start to cursor */ else if (argv[0] == 1) guac_terminal_clear_range(term, 0, 0, term->cursor_row, term->cursor_col, - term->background); + term->current_attributes.background); /* Entire screen */ else if (argv[0] == 2) guac_terminal_clear(term, 0, 0, term->term_height, term->term_width, - term->background); + term->current_attributes.background); break; @@ -389,7 +388,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { guac_terminal_clear(term, term->cursor_row, term->cursor_col, 1, term->term_width - term->cursor_col, - term->background); + term->current_attributes.background); /* Erase from start to cursor */ @@ -397,14 +396,14 @@ int guac_terminal_csi(guac_terminal* term, char c) { guac_terminal_clear(term, term->cursor_row, 0, 1, term->cursor_col + 1, - term->background); + term->current_attributes.background); /* Erase line */ else if (argv[0] == 2) guac_terminal_clear(term, term->cursor_row, 0, 1, term->term_width, - term->background); + term->current_attributes.background); break; @@ -448,7 +447,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { guac_terminal_clear(term, term->cursor_row, term->term_width - amount, 1, amount, - term->background); + term->current_attributes.background); break; @@ -469,7 +468,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { guac_terminal_clear(term, term->cursor_row, term->cursor_col, 1, amount, - term->background); + term->current_attributes.background); break; From 38907abc1e2109c0ed052c9e6e9d2aee3a9746a8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 23 Mar 2013 17:06:02 -0700 Subject: [PATCH 040/169] Move delta.* into terminal.* (interdependent). --- protocols/ssh/Makefile.am | 2 - protocols/ssh/include/delta.h | 158 ------------------------------- protocols/ssh/include/terminal.h | 113 ++++++++++++++++++++++ protocols/ssh/src/delta.c | 72 -------------- protocols/ssh/src/terminal.c | 31 ++++++ 5 files changed, 144 insertions(+), 232 deletions(-) delete mode 100644 protocols/ssh/include/delta.h delete mode 100644 protocols/ssh/src/delta.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 16a5ad47..310d7b02 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -41,14 +41,12 @@ ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libguac-client-ssh.la libguac_client_ssh_la_SOURCES = \ - src/delta.c \ src/ssh_client.c \ src/ssh_handlers.c \ src/terminal.c \ src/terminal_handlers.c noinst_HEADERS = \ - include/delta.h \ include/ssh_client.h \ include/ssh_handlers.h \ include/terminal.h \ diff --git a/protocols/ssh/include/delta.h b/protocols/ssh/include/delta.h deleted file mode 100644 index bae25ca4..00000000 --- a/protocols/ssh/include/delta.h +++ /dev/null @@ -1,158 +0,0 @@ - -/* ***** 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-ssh. - * - * 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 _SSH_GUAC_DELTA_H -#define _SSH_GUAC_DELTA_H - -#include -#include "terminal.h" - -/** - * All available terminal operations which affect character cells. - */ -typedef enum guac_terminal_operation_type { - - /** - * Operation which does nothing. - */ - GUAC_CHAR_NOP, - - /** - * Operation which copies a character from a given row/column coordinate. - */ - GUAC_CHAR_COPY, - - /** - * Operation which sets the character and attributes. - */ - GUAC_CHAR_SET - -} guac_terminal_operation_type; - -/** - * A pairing of a guac_terminal_operation_type and all parameters required by - * that operation type. - */ -typedef struct guac_terminal_operation { - - /** - * The type of operation to perform. - */ - guac_terminal_operation_type type; - - /** - * The character (and attributes) to set the current location to. This is - * only applicable to GUAC_CHAR_SET. - */ - guac_terminal_char character; - - /** - * The row to copy a character from. This is only applicable to - * GUAC_CHAR_COPY. - */ - int row; - - /** - * The column to copy a character from. This is only applicable to - * GUAC_CHAR_COPY. - */ - int column; - -} guac_terminal_operation; - -/** - * Set of all pending operations for the currently-visible screen area. - */ -typedef struct guac_terminal_delta { - - /** - * Array of all operations pending for the visible screen area. - */ - guac_terminal_operation* operations; - - /** - * The width of the screen, in characters. - */ - int width; - - /** - * The height of the screen, in characters. - */ - int height; - -} guac_terminal_delta; - -/** - * Allocates a new guac_terminal_delta. - */ -guac_terminal_delta* guac_terminal_delta_alloc(int width, int height); - -/** - * Frees the given guac_terminal_delta. - */ -void guac_terminal_delta_free(guac_terminal_delta* delta); - -/** - * Resizes the given guac_terminal_delta to the given dimensions. - */ -void guac_terminal_delta_resize(guac_terminal_delta* delta, - int width, int height); - -/** - * Stores a set operation at the given location. - */ -void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, - guac_terminal_char* character); - -/** - * Stores a rectangle of copy operations, copying existing operations as - * necessary. - */ -void guac_terminal_delta_copy(guac_terminal_delta* delta, - int dst_row, int dst_column, - int src_row, int src_column, - int w, int h); - -/** - * Flushes all pending operations within the given guac_client_delta to the - * given guac_terminal. - */ -void guac_terminal_delta_flush(guac_terminal_delta* delta, - guac_terminal* terminal); - -#endif - diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 6b60809b..53764933 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -132,6 +132,81 @@ typedef struct guac_terminal_char { } guac_terminal_char; +/** + * All available terminal operations which affect character cells. + */ +typedef enum guac_terminal_operation_type { + + /** + * Operation which does nothing. + */ + GUAC_CHAR_NOP, + + /** + * Operation which copies a character from a given row/column coordinate. + */ + GUAC_CHAR_COPY, + + /** + * Operation which sets the character and attributes. + */ + GUAC_CHAR_SET + +} guac_terminal_operation_type; + +/** + * A pairing of a guac_terminal_operation_type and all parameters required by + * that operation type. + */ +typedef struct guac_terminal_operation { + + /** + * The type of operation to perform. + */ + guac_terminal_operation_type type; + + /** + * The character (and attributes) to set the current location to. This is + * only applicable to GUAC_CHAR_SET. + */ + guac_terminal_char character; + + /** + * The row to copy a character from. This is only applicable to + * GUAC_CHAR_COPY. + */ + int row; + + /** + * The column to copy a character from. This is only applicable to + * GUAC_CHAR_COPY. + */ + int column; + +} guac_terminal_operation; + +/** + * Set of all pending operations for the currently-visible screen area. + */ +typedef struct guac_terminal_delta { + + /** + * Array of all operations pending for the visible screen area. + */ + guac_terminal_operation* operations; + + /** + * The width of the screen, in characters. + */ + int width; + + /** + * The height of the screen, in characters. + */ + int height; + +} guac_terminal_delta; + /** * Represents a terminal emulator which uses a given Guacamole client to * render itself. @@ -289,5 +364,43 @@ int guac_terminal_clear_range(guac_terminal* term, int start_row, int start_col, int end_row, int end_col, int background_color); +/** + * Allocates a new guac_terminal_delta. + */ +guac_terminal_delta* guac_terminal_delta_alloc(int width, int height); + +/** + * Frees the given guac_terminal_delta. + */ +void guac_terminal_delta_free(guac_terminal_delta* delta); + +/** + * Resizes the given guac_terminal_delta to the given dimensions. + */ +void guac_terminal_delta_resize(guac_terminal_delta* delta, + int width, int height); + +/** + * Stores a set operation at the given location. + */ +void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, + guac_terminal_char* character); + +/** + * Stores a rectangle of copy operations, copying existing operations as + * necessary. + */ +void guac_terminal_delta_copy(guac_terminal_delta* delta, + int dst_row, int dst_column, + int src_row, int src_column, + int w, int h); + +/** + * Flushes all pending operations within the given guac_client_delta to the + * given guac_terminal. + */ +void guac_terminal_delta_flush(guac_terminal_delta* delta, + guac_terminal* terminal); + #endif diff --git a/protocols/ssh/src/delta.c b/protocols/ssh/src/delta.c deleted file mode 100644 index 359cc749..00000000 --- a/protocols/ssh/src/delta.c +++ /dev/null @@ -1,72 +0,0 @@ - -/* ***** 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-ssh. - * - * 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 "terminal.h" -#include "delta.h" - -guac_terminal_delta* guac_terminal_delta_alloc(int width, int height) { - /* STUB */ - return NULL; -} - -void guac_terminal_delta_free(guac_terminal_delta* delta) { - /* STUB */ -} - -void guac_terminal_delta_resize(guac_terminal_delta* delta, - int width, int height) { - /* STUB */ -} - -void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, - guac_terminal_char* character) { - /* STUB */ -} - -void guac_terminal_delta_copy(guac_terminal_delta* delta, - int dst_row, int dst_column, - int src_row, int src_column, - int w, int h) { - /* STUB */ -} - -void guac_terminal_delta_flush(guac_terminal_delta* delta, - guac_terminal* terminal) { - /* STUB */ -} - diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 4d7fdbe1..df8bd681 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -483,3 +483,34 @@ int guac_terminal_clear_range(guac_terminal* term, } +guac_terminal_delta* guac_terminal_delta_alloc(int width, int height) { + /* STUB */ + return NULL; +} + +void guac_terminal_delta_free(guac_terminal_delta* delta) { + /* STUB */ +} + +void guac_terminal_delta_resize(guac_terminal_delta* delta, + int width, int height) { + /* STUB */ +} + +void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, + guac_terminal_char* character) { + /* STUB */ +} + +void guac_terminal_delta_copy(guac_terminal_delta* delta, + int dst_row, int dst_column, + int src_row, int src_column, + int w, int h) { + /* STUB */ +} + +void guac_terminal_delta_flush(guac_terminal_delta* delta, + guac_terminal* terminal) { + /* STUB */ +} + From 10cac18b418baaa60d5492f61e5427312383ef69 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 23 Mar 2013 17:43:35 -0700 Subject: [PATCH 041/169] Implement delta alloc and free. --- protocols/ssh/include/terminal.h | 6 +++++ protocols/ssh/src/terminal.c | 44 +++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 53764933..650b7226 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -323,6 +323,12 @@ struct guac_terminal { */ guac_terminal_char_handler* char_handler; + /** + * The difference between the currently-rendered screen and the current + * state of the terminal. + */ + guac_terminal_delta* delta; + }; /** diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index df8bd681..461cd88a 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -164,6 +164,9 @@ guac_terminal* guac_terminal_create(guac_client* client, } + /* Init delta */ + term->delta = guac_terminal_delta_alloc(width, height); + /* Clear with background color */ guac_terminal_clear(term, 0, 0, term->term_height, term->term_width, @@ -180,6 +183,10 @@ void guac_terminal_free(guac_terminal* term) { free(term->scrollback[row]); free(term->scrollback); + + /* Free delta */ + guac_terminal_delta_free(term->delta); + } int __guac_terminal_get_glyph(guac_terminal* term, char c) { @@ -484,12 +491,43 @@ int guac_terminal_clear_range(guac_terminal* term, } guac_terminal_delta* guac_terminal_delta_alloc(int width, int height) { - /* STUB */ - return NULL; + + guac_terminal_operation* current; + int x, y; + + /* Allocate delta */ + guac_terminal_delta* delta = malloc(sizeof(guac_terminal_delta)); + + /* Set width and height */ + delta->width = width; + delta->height = height; + + /* Alloc operations */ + delta->operations = malloc(width * height * + sizeof(guac_terminal_operation)); + + /* Init each operation buffer row */ + current = delta->operations; + for (y=0; ytype = GUAC_CHAR_NOP; + + } + + return delta; + } void guac_terminal_delta_free(guac_terminal_delta* delta) { - /* STUB */ + + /* Free operations buffer */ + free(delta->operations); + + /* Free delta */ + free(delta); + } void guac_terminal_delta_resize(guac_terminal_delta* delta, From 73d1cb42851b622bfe3ba578c4868659f787d722 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 23 Mar 2013 18:39:23 -0700 Subject: [PATCH 042/169] Remove cursor redraw function. --- protocols/ssh/src/terminal.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 461cd88a..231bcb54 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -264,22 +264,6 @@ int __guac_terminal_get_glyph(guac_terminal* term, char c) { } -int guac_terminal_redraw_cursor(guac_terminal* term) { - - guac_socket* socket = term->client->socket; - - /* Erase old cursor */ - return - guac_protocol_send_move(socket, - term->cursor_layer, - - GUAC_DEFAULT_LAYER, - term->char_width * term->cursor_col, - term->char_height * term->cursor_row, - 1); - -} - int guac_terminal_set_colors(guac_terminal* term, int foreground, int background) { From 96b12c7722557c3f6f8575a3f4afc844f73203ae Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 24 Mar 2013 16:56:17 -0700 Subject: [PATCH 043/169] Replace drawing with calls to delta functions, document terminal functions. --- protocols/ssh/include/terminal.h | 36 ++++++++++---- protocols/ssh/src/terminal.c | 68 +++++++++++++++++++++------ protocols/ssh/src/terminal_handlers.c | 24 ++-------- 3 files changed, 86 insertions(+), 42 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 650b7226..dc80d156 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -348,28 +348,46 @@ void guac_terminal_free(guac_terminal* term); */ int guac_terminal_write(guac_terminal* term, const char* c, int size); -int guac_terminal_set_colors(guac_terminal* term, - int foreground, int background); - +/** + * Sets the character at the given row and column to the specified value. + */ int guac_terminal_set(guac_terminal* term, int row, int col, char c); +/** + * Copies a rectangular region of characters which may overlap with the + * destination. + */ int guac_terminal_copy(guac_terminal* term, int src_row, int src_col, int rows, int cols, int dst_row, int dst_col); +/** + * Clears a rectangular region of characters, replacing them with the + * given background color. + */ int guac_terminal_clear(guac_terminal* term, int row, int col, int rows, int cols, int background_color); -int guac_terminal_scroll_up(guac_terminal* term, - int start_row, int end_row, int amount); - -int guac_terminal_scroll_down(guac_terminal* term, - int start_row, int end_row, int amount); - +/** + * Clears the given region from right-to-left, top-to-bottom, replacing + * all characters with the given background color. + */ int guac_terminal_clear_range(guac_terminal* term, int start_row, int start_col, int end_row, int end_col, int background_color); +/** + * Scrolls the terminal's current scroll region up by one row. + */ +int guac_terminal_scroll_up(guac_terminal* term, + int start_row, int end_row, int amount); + +/** + * Scrolls the terminal's current scroll region down by one row. + */ +int guac_terminal_scroll_down(guac_terminal* term, + int start_row, int end_row, int amount); + /** * Allocates a new guac_terminal_delta. */ diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 231bcb54..46e4658b 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -189,6 +189,12 @@ void guac_terminal_free(guac_terminal* term) { } +/** + * Returns the location of the given character in the glyph cache layer, + * sending it first if necessary. The location returned is in characters, + * and thus must be multiplied by the glyph width to obtain the actual + * location within the glyph cache layer. + */ int __guac_terminal_get_glyph(guac_terminal* term, char c) { guac_socket* socket = term->client->socket; @@ -264,11 +270,30 @@ int __guac_terminal_get_glyph(guac_terminal* term, char c) { } -int guac_terminal_set_colors(guac_terminal* term, - int foreground, int background) { +/** + * Sets the attributes of the glyph cache layer such that future copies from + * this layer will display as expected. + */ +int __guac_terminal_set_colors(guac_terminal* term, + guac_terminal_attributes* attributes) { guac_socket* socket = term->client->socket; const guac_terminal_color* background_color; + int background, foreground; + + /* Handle reverse video */ + if (attributes->reverse) { + background = attributes->foreground; + foreground = attributes->background; + } + else { + foreground = attributes->foreground; + background = attributes->background; + } + + /* Handle bold */ + if (attributes->bold && foreground <= 7) + foreground += 8; /* Get background color */ background_color = &guac_terminal_palette[background]; @@ -294,7 +319,8 @@ int guac_terminal_set_colors(guac_terminal* term, } /* If any color change at all, update filled */ - if (foreground != term->glyph_foreground || background != term->glyph_background) { + if (foreground != term->glyph_foreground + || background != term->glyph_background) { /* Set background */ guac_protocol_send_rect(socket, term->filled_glyphs, @@ -325,7 +351,12 @@ int guac_terminal_set_colors(guac_terminal* term, } -int guac_terminal_set(guac_terminal* term, int row, int col, char c) { +/** + * Sends the given character to the terminal at the given row and column, + * rendering the charater immediately. This bypasses the guac_terminal_delta + * mechanism and is intended for flushing of updates only. + */ +int __guac_terminal_set(guac_terminal* term, int row, int col, char c) { guac_socket* socket = term->client->socket; int location = __guac_terminal_get_glyph(term, c); @@ -339,6 +370,19 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { } +int guac_terminal_set(guac_terminal* term, int row, int col, char c) { + + /* Build character with current attributes */ + guac_terminal_char guac_char; + guac_char.value = c; + guac_char.attributes = term->current_attributes; + + /* Set delta */ + guac_terminal_delta_set(term->delta, row, col, &guac_char); + return 0; + +} + int guac_terminal_write(guac_terminal* term, const char* c, int size) { while (size > 0) { @@ -354,17 +398,13 @@ int guac_terminal_copy(guac_terminal* term, int src_row, int src_col, int rows, int cols, int dst_row, int dst_col) { - guac_socket* socket = term->client->socket; + /* Update delta */ + guac_terminal_delta_copy(term->delta, + dst_row, dst_col, + src_row, src_col, + cols, rows); - /* Send copy instruction */ - return guac_protocol_send_copy(socket, - - GUAC_DEFAULT_LAYER, - src_col * term->char_width, src_row * term->char_height, - cols * term->char_width, rows * term->char_height, - - GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, - dst_col * term->char_width, dst_row * term->char_height); + return 0; } diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 54e93eae..02082068 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -42,9 +42,6 @@ int guac_terminal_echo(guac_terminal* term, char c) { - int foreground = term->current_attributes.foreground; - int background = term->current_attributes.background; - switch (c) { /* Bell */ @@ -71,7 +68,8 @@ int guac_terminal_echo(guac_terminal* term, char c) { term->cursor_row = term->scroll_end; /* Scroll up by one row */ - guac_terminal_scroll_up(term, term->scroll_start, term->scroll_end, 1); + guac_terminal_scroll_up(term, term->scroll_start, + term->scroll_end, 1); } break; @@ -95,24 +93,12 @@ int guac_terminal_echo(guac_terminal* term, char c) { term->cursor_row = term->scroll_end; /* Scroll up by one row */ - guac_terminal_scroll_up(term, term->scroll_start, term->scroll_end, 1); + guac_terminal_scroll_up(term, term->scroll_start, + term->scroll_end, 1); } - /* Handle reverse video */ - if (term->current_attributes.reverse) { - int swap = background; - background = foreground; - foreground = swap; - } - - /* Handle bold */ - if (term->current_attributes.bold && foreground <= 7) - foreground += 8; - - guac_terminal_set_colors(term, - foreground, background); - + /* Write character */ guac_terminal_set(term, term->cursor_row, term->cursor_col, From a102ba4a86fae61838baa4c04ee0ffb53d700e06 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 24 Mar 2013 18:05:15 -0700 Subject: [PATCH 044/169] Add set_rect, use it in clear. --- protocols/ssh/include/terminal.h | 7 +++++++ protocols/ssh/src/terminal.c | 23 ++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index dc80d156..087615b2 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -419,6 +419,13 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, int src_row, int src_column, int w, int h); +/** + * Sets a rectangle of character data to the given character value. + */ +void guac_terminal_delta_set_rect(guac_terminal_delta* delta, + int row, int column, int w, int h, + guac_terminal_char* character); + /** * Flushes all pending operations within the given guac_client_delta to the * given guac_terminal. diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 46e4658b..b975e1e1 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -412,18 +412,17 @@ int guac_terminal_copy(guac_terminal* term, int guac_terminal_clear(guac_terminal* term, int row, int col, int rows, int cols, int background_color) { - guac_socket* socket = term->client->socket; - const guac_terminal_color* color = - &guac_terminal_palette[background_color]; + /* Build space */ + guac_terminal_char character; + character.value = ' '; + character.attributes.reverse = false; + character.attributes.background = background_color; /* Fill with color */ - return - guac_protocol_send_rect(socket, GUAC_DEFAULT_LAYER, - col * term->char_width, row * term->char_height, - cols * term->char_width, rows * term->char_height) + guac_terminal_delta_set_rect(term->delta, + row, col, cols, rows, &character); - || guac_protocol_send_cfill(socket, GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, - color->red, color->green, color->blue, 255); + return 0; } @@ -571,6 +570,12 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, /* STUB */ } +void guac_terminal_delta_set_rect(guac_terminal_delta* delta, + int row, int column, int w, int h, + guac_terminal_char* character) { + /* STUB */ +} + void guac_terminal_delta_flush(guac_terminal_delta* delta, guac_terminal* terminal) { /* STUB */ From 3e21d1c3c780bf9d5d4ce1a28a7f981737eefbe3 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 24 Mar 2013 18:16:04 -0700 Subject: [PATCH 045/169] Implement delta set. --- protocols/ssh/src/terminal.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index b975e1e1..c12b4991 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -560,7 +560,14 @@ void guac_terminal_delta_resize(guac_terminal_delta* delta, void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, guac_terminal_char* character) { - /* STUB */ + + /* Get operation at coordinate */ + guac_terminal_operation* op = &(delta->operations[r*delta->width + c]); + + /* Store operation */ + op->type = GUAC_CHAR_SET; + op->character = *character; + } void guac_terminal_delta_copy(guac_terminal_delta* delta, From 1408248282a150e56ddbde25fc1d5e0db034ab7e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 24 Mar 2013 18:46:47 -0700 Subject: [PATCH 046/169] Partial implementation of flush (set only). --- protocols/ssh/src/ssh_handlers.c | 2 ++ protocols/ssh/src/terminal.c | 26 +++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index fd4c584c..05e70d13 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -104,6 +104,8 @@ int ssh_guac_client_handle_messages(guac_client* client) { } } + /* Flush terminal delta */ + guac_terminal_delta_flush(client_data->term->delta, client_data->term); return 0; } diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index c12b4991..369d7631 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -585,6 +585,30 @@ void guac_terminal_delta_set_rect(guac_terminal_delta* delta, void guac_terminal_delta_flush(guac_terminal_delta* delta, guac_terminal* terminal) { - /* STUB */ + + guac_terminal_operation* current = delta->operations; + int row, col; + + /* For each operation */ + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { + + /* Perform given operation */ + if (current->type == GUAC_CHAR_SET) { + + __guac_terminal_set(terminal, row, col, + current->character.value); + + /* Mark operation as handled */ + current->type = GUAC_CHAR_NOP; + + } + + /* Next operation */ + current++; + + } + } + } From 91bf5e7e3b42ac33e347decd31c8eda56ef10b3f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 25 Mar 2013 01:54:44 -0700 Subject: [PATCH 047/169] Implement clear. --- protocols/ssh/src/terminal.c | 150 ++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 369d7631..533f3c91 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -165,7 +165,8 @@ guac_terminal* guac_terminal_create(guac_client* client, } /* Init delta */ - term->delta = guac_terminal_delta_alloc(width, height); + term->delta = guac_terminal_delta_alloc(term->term_width, + term->term_height); /* Clear with background color */ guac_terminal_clear(term, @@ -583,7 +584,133 @@ void guac_terminal_delta_set_rect(guac_terminal_delta* delta, /* STUB */ } -void guac_terminal_delta_flush(guac_terminal_delta* delta, +void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, + guac_terminal* terminal) { + /* COPY */ +} + +void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, + guac_terminal* terminal) { + + guac_terminal_operation* current = delta->operations; + int row, col; + + /* For each operation */ + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { + + /* If operation is a cler operation (set to space) */ + if (current->type == GUAC_CHAR_SET && + current->character.value == ' ') { + + /* The determined bounds of the rectangle of contiguous + * operations */ + int detected_right = -1; + int detected_bottom = row; + + /* The current row or column within a rectangle */ + int rect_row, rect_col; + + /* The dimensions of the rectangle as determined */ + int rect_width, rect_height; + + /* Color of the rectangle to draw */ + int color = current->character.attributes.background; + const guac_terminal_color* guac_color = + &guac_terminal_palette[color]; + + /* Current row within a subrect */ + guac_terminal_operation* rect_current_row; + + /* Determine bounds of rectangle */ + rect_current_row = current; + for (rect_row=row; rect_rowheight; rect_row++) { + + guac_terminal_operation* rect_current = rect_current_row; + + /* Find width */ + for (rect_col=col; rect_colwidth; rect_col++) { + + /* If not identical operation, stop */ + if (rect_current->type != GUAC_CHAR_SET + || rect_current->character.value != ' ' + || rect_current->character.attributes.background != color) + break; + + /* Next column */ + rect_current++; + + } + + /* If too small, cannot append row */ + if (rect_col-1 < detected_right) + break; + + /* As row has been accepted, update rect_row of rect */ + detected_bottom = rect_row; + + /* For now, only set rect_col bound if uninitialized */ + if (detected_right == -1) + detected_right = rect_col - 1; + + /* Next row */ + rect_current_row += delta->width; + + } + + /* Calculate dimensions */ + rect_width = detected_right - col + 1; + rect_height = detected_bottom - row + 1; + + /* Mark rect as NOP (as it has been handled) */ + rect_current_row = current; + for (rect_row=0; rect_rowtype == GUAC_CHAR_SET + && rect_current->character.value == ' ' + && rect_current->character.attributes.background == color) + rect_current->type = GUAC_CHAR_NOP; + + /* Next column */ + rect_current++; + + } + + /* Next row */ + rect_current_row += delta->width; + + } + + /* Send rect */ + guac_protocol_send_rect(terminal->client->socket, + GUAC_DEFAULT_LAYER, + col * terminal->char_width, + row * terminal->char_height, + rect_width * terminal->char_width, + rect_height * terminal->char_height); + + guac_protocol_send_cfill(terminal->client->socket, + GUAC_COMP_OVER, + GUAC_DEFAULT_LAYER, + guac_color->red, guac_color->green, guac_color->blue, + 0xFF); + + } + + /* Next operation */ + current++; + + } + } + +} + +void __guac_terminal_delta_flush_set(guac_terminal_delta* delta, guac_terminal* terminal) { guac_terminal_operation* current = delta->operations; @@ -596,6 +723,11 @@ void guac_terminal_delta_flush(guac_terminal_delta* delta, /* Perform given operation */ if (current->type == GUAC_CHAR_SET) { + /* Set attributes */ + __guac_terminal_set_colors(terminal, + &(current->character.attributes)); + + /* Send character */ __guac_terminal_set(terminal, row, col, current->character.value); @@ -612,3 +744,17 @@ void guac_terminal_delta_flush(guac_terminal_delta* delta, } +void guac_terminal_delta_flush(guac_terminal_delta* delta, + guac_terminal* terminal) { + + /* Flush copy operations first */ + __guac_terminal_delta_flush_copy(delta, terminal); + + /* Flush clear operations (as they're just rects) */ + __guac_terminal_delta_flush_clear(delta, terminal); + + /* Flush set operations (the only operations remaining) */ + __guac_terminal_delta_flush_set(delta, terminal); + +} + From 06fee87073cb0891041a3b1df39e98ff67f6c638 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 25 Mar 2013 02:32:23 -0700 Subject: [PATCH 048/169] Unstub set_rect, properly handle reverse color in clear. --- protocols/ssh/src/terminal.c | 51 ++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 533f3c91..0b0414cb 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -581,7 +581,31 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, void guac_terminal_delta_set_rect(guac_terminal_delta* delta, int row, int column, int w, int h, guac_terminal_char* character) { - /* STUB */ + + guac_terminal_operation* current_row = + &(delta->operations[row*delta->width + column]); + + /* Set rectangle contents to given character */ + for (row=0; rowtype = GUAC_CHAR_SET; + current->character = *character; + + /* Next column */ + current++; + + } + + /* Next row */ + current_row += delta->width; + + } + } void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, @@ -615,7 +639,12 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, int rect_width, rect_height; /* Color of the rectangle to draw */ - int color = current->character.attributes.background; + int color; + if (current->character.attributes.reverse) + color = current->character.attributes.foreground; + else + color = current->character.attributes.background; + const guac_terminal_color* guac_color = &guac_terminal_palette[color]; @@ -631,10 +660,16 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, /* Find width */ for (rect_col=col; rect_colwidth; rect_col++) { + int joining_color; + if (rect_current->character.attributes.reverse) + joining_color = current->character.attributes.foreground; + else + joining_color = current->character.attributes.background; + /* If not identical operation, stop */ if (rect_current->type != GUAC_CHAR_SET || rect_current->character.value != ' ' - || rect_current->character.attributes.background != color) + || joining_color != color) break; /* Next column */ @@ -670,10 +705,16 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, for (rect_col=0; rect_colcharacter.attributes.reverse) + joining_color = current->character.attributes.foreground; + else + joining_color = current->character.attributes.background; + /* Mark clear operations as NOP */ if (rect_current->type == GUAC_CHAR_SET && rect_current->character.value == ' ' - && rect_current->character.attributes.background == color) + && joining_color == color) rect_current->type = GUAC_CHAR_NOP; /* Next column */ @@ -700,7 +741,7 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, guac_color->red, guac_color->green, guac_color->blue, 0xFF); - } + } /* end if clear operation */ /* Next operation */ current++; From 7ea73559be143e1e0b73a814600ed3d51cdd8535 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 25 Mar 2013 02:56:59 -0700 Subject: [PATCH 049/169] Partial implementation of copy. --- protocols/ssh/src/terminal.c | 171 ++++++++++++++++++++++++++++++++++- 1 file changed, 169 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 0b0414cb..929f6cd8 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -575,7 +575,51 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, int dst_row, int dst_column, int src_row, int src_column, int w, int h) { - /* STUB */ + + int row, column; + + /* FIXME: Handle intersections between src and dst rects */ + + guac_terminal_operation* current_row = + &(delta->operations[dst_row*delta->width + dst_column]); + + guac_terminal_operation* src_current_row = + &(delta->operations[src_row*delta->width + src_column]); + + /* Set rectangle to copy operations */ + for (row=0; rowtype != GUAC_CHAR_NOP) + *current = *src_current; + + /* Store operation */ + else { + current->type = GUAC_CHAR_COPY; + current->row = src_row + row; + current->column = src_column + column; + } + + /* Next column */ + current++; + src_current++; + + } + + /* Next row */ + current_row += delta->width; + src_current_row += delta->width; + + } + + + } void guac_terminal_delta_set_rect(guac_terminal_delta* delta, @@ -610,7 +654,130 @@ void guac_terminal_delta_set_rect(guac_terminal_delta* delta, void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, guac_terminal* terminal) { - /* COPY */ + + guac_terminal_operation* current = delta->operations; + int row, col; + + /* For each operation */ + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { + + /* If operation is a copy operation */ + if (current->type == GUAC_CHAR_COPY) { + + /* The determined bounds of the rectangle of contiguous + * operations */ + int detected_right = -1; + int detected_bottom = row; + + /* The current row or column within a rectangle */ + int rect_row, rect_col; + + /* The dimensions of the rectangle as determined */ + int rect_width, rect_height; + + /* The expected row and column source for the next copy + * operation (if adjacent to current) */ + int expected_row, expected_col; + + /* Current row within a subrect */ + guac_terminal_operation* rect_current_row; + + /* Determine bounds of rectangle */ + rect_current_row = current; + expected_row = current->row; + for (rect_row=row; rect_rowheight; rect_row++) { + + guac_terminal_operation* rect_current = rect_current_row; + expected_col = current->column; + + /* Find width */ + for (rect_col=col; rect_colwidth; rect_col++) { + + /* If not identical operation, stop */ + if (rect_current->type != GUAC_CHAR_COPY + || rect_current->row != expected_row + || rect_current->column != expected_col) + break; + + /* Next column */ + rect_current++; + expected_col++; + + } + + /* If too small, cannot append row */ + if (rect_col-1 < detected_right) + break; + + /* As row has been accepted, update rect_row of rect */ + detected_bottom = rect_row; + + /* For now, only set rect_col bound if uninitialized */ + if (detected_right == -1) + detected_right = rect_col - 1; + + /* Next row */ + rect_current_row += delta->width; + expected_row++; + + } + + /* Calculate dimensions */ + rect_width = detected_right - col + 1; + rect_height = detected_bottom - row + 1; + + /* Mark rect as NOP (as it has been handled) */ + rect_current_row = current; + expected_row = current->row; + for (rect_row=0; rect_rowcolumn; + + for (rect_col=0; rect_coltype == GUAC_CHAR_COPY + && rect_current->row == expected_row + && rect_current->column == expected_col) + rect_current->type = GUAC_CHAR_NOP; + + /* Next column */ + rect_current++; + expected_col++; + + } + + /* Next row */ + rect_current_row += delta->width; + expected_row++; + + } + + /* Send copy */ + guac_protocol_send_copy(terminal->client->socket, + + GUAC_DEFAULT_LAYER, + current->column * terminal->char_width, + current->row * terminal->char_height, + rect_width * terminal->char_width, + rect_height * terminal->char_height, + + GUAC_COMP_OVER, + GUAC_DEFAULT_LAYER, + col * terminal->char_width, + row * terminal->char_height); + + + } /* end if copy operation */ + + /* Next operation */ + current++; + + } + } + } void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, From 17a4d141deff2fc5fd282887af783a20bf5bf1fd Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 25 Mar 2013 03:01:51 -0700 Subject: [PATCH 050/169] Remove cursor layer. --- protocols/ssh/include/terminal.h | 5 ----- protocols/ssh/src/ssh_client.c | 11 ----------- protocols/ssh/src/terminal.c | 1 - 3 files changed, 17 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 087615b2..f888f8a2 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -301,11 +301,6 @@ struct guac_terminal { */ int cursor_col; - /** - * Simple cursor layer until scrollback, etc. is implemented. - */ - guac_layer* cursor_layer; - /** * The attributes which will be applied to future characters. */ diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 2da7d481..b6d2b998 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -122,17 +122,6 @@ int guac_client_init(guac_client* client, int argc, char** argv) { term->char_width * term->term_width, term->char_height * term->term_height); - /* Cursor layer need only be one char */ - guac_protocol_send_size(socket, term->cursor_layer, term->char_width, term->char_height); - - /* Draw cursor */ - guac_protocol_send_rect(socket, term->cursor_layer, - 0, 0, term->char_width, term->char_height); - - guac_protocol_send_cfill(socket, - GUAC_COMP_OVER, term->cursor_layer, - 0x40, 0xFF, 0x80, 0x80); - guac_socket_flush(socket); /* Open SSH session */ diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 929f6cd8..05a94fc4 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -134,7 +134,6 @@ guac_terminal* guac_terminal_create(guac_client* client, term->cursor_row = 0; term->cursor_col = 0; - term->cursor_layer = guac_client_alloc_layer(client); term->term_width = width / term->char_width; term->term_height = height / term->char_height; From 87c36ca2a58703466f5e86317c39d00c99391f80 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 25 Mar 2013 03:04:14 -0700 Subject: [PATCH 051/169] Increate font size (needs to be arg). --- protocols/ssh/src/terminal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 05a94fc4..ca0f7c42 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -107,7 +107,7 @@ guac_terminal* guac_terminal_create(guac_client* client, term->font_desc = pango_font_description_new(); pango_font_description_set_family(term->font_desc, "monospace"); pango_font_description_set_weight(term->font_desc, PANGO_WEIGHT_NORMAL); - pango_font_description_set_size(term->font_desc, 10*PANGO_SCALE); + pango_font_description_set_size(term->font_desc, 12*PANGO_SCALE); font_map = pango_cairo_font_map_get_default(); context = pango_font_map_create_context(font_map); From 2c78613821589e1890ac9e342e77c4c997c249a9 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 27 Mar 2013 03:06:45 -0700 Subject: [PATCH 052/169] Refactor delta to display. --- protocols/ssh/include/terminal.h | 56 ++++--- protocols/ssh/src/ssh_handlers.c | 4 +- protocols/ssh/src/terminal.c | 256 ++++++++++++++++--------------- 3 files changed, 175 insertions(+), 141 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index f888f8a2..a5891edd 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -186,14 +186,33 @@ typedef struct guac_terminal_operation { } guac_terminal_operation; /** - * Set of all pending operations for the currently-visible screen area. + * A terminal character with two states - a current state, and a future state + * represented by a pairing of an operation with a terminal character. */ -typedef struct guac_terminal_delta { +typedef struct guac_terminal_display_char { /** - * Array of all operations pending for the visible screen area. + * The current state of this character. */ - guac_terminal_operation* operations; + guac_terminal_char current; + + /** + * The next state of this character, as an operation upon this + * character. + */ + guac_terminal_operation next; + +} guac_terminal_display_char; + +/** + * Set of all characters for the currently-visible screen area. + */ +typedef struct guac_terminal_display { + + /** + * Array of all characters within the visible screen area. + */ + guac_terminal_display_char* characters; /** * The width of the screen, in characters. @@ -205,7 +224,7 @@ typedef struct guac_terminal_delta { */ int height; -} guac_terminal_delta; +} guac_terminal_display; /** * Represents a terminal emulator which uses a given Guacamole client to @@ -319,10 +338,9 @@ struct guac_terminal { guac_terminal_char_handler* char_handler; /** - * The difference between the currently-rendered screen and the current - * state of the terminal. + * The current display state and pending state. */ - guac_terminal_delta* delta; + guac_terminal_display* display; }; @@ -384,32 +402,32 @@ int guac_terminal_scroll_down(guac_terminal* term, int start_row, int end_row, int amount); /** - * Allocates a new guac_terminal_delta. + * Allocates a new guac_terminal_display. */ -guac_terminal_delta* guac_terminal_delta_alloc(int width, int height); +guac_terminal_display* guac_terminal_display_alloc(int width, int height); /** - * Frees the given guac_terminal_delta. + * Frees the given guac_terminal_display. */ -void guac_terminal_delta_free(guac_terminal_delta* delta); +void guac_terminal_display_free(guac_terminal_display* display); /** - * Resizes the given guac_terminal_delta to the given dimensions. + * Resizes the given guac_terminal_display to the given dimensions. */ -void guac_terminal_delta_resize(guac_terminal_delta* delta, +void guac_terminal_display_resize(guac_terminal_display* display, int width, int height); /** * Stores a set operation at the given location. */ -void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, +void guac_terminal_display_set(guac_terminal_display* display, int r, int c, guac_terminal_char* character); /** * Stores a rectangle of copy operations, copying existing operations as * necessary. */ -void guac_terminal_delta_copy(guac_terminal_delta* delta, +void guac_terminal_display_copy(guac_terminal_display* display, int dst_row, int dst_column, int src_row, int src_column, int w, int h); @@ -417,15 +435,15 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, /** * Sets a rectangle of character data to the given character value. */ -void guac_terminal_delta_set_rect(guac_terminal_delta* delta, +void guac_terminal_display_set_rect(guac_terminal_display* display, int row, int column, int w, int h, guac_terminal_char* character); /** - * Flushes all pending operations within the given guac_client_delta to the + * Flushes all pending operations within the given guac_terminal_display to the * given guac_terminal. */ -void guac_terminal_delta_flush(guac_terminal_delta* delta, +void guac_terminal_display_flush(guac_terminal_display* display, guac_terminal* terminal); #endif diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 05e70d13..103192da 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -104,8 +104,8 @@ int ssh_guac_client_handle_messages(guac_client* client) { } } - /* Flush terminal delta */ - guac_terminal_delta_flush(client_data->term->delta, client_data->term); + /* Flush terminal display */ + guac_terminal_display_flush(client_data->term->display, client_data->term); return 0; } diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index ca0f7c42..010d58cd 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -163,8 +163,8 @@ guac_terminal* guac_terminal_create(guac_client* client, } - /* Init delta */ - term->delta = guac_terminal_delta_alloc(term->term_width, + /* Init display */ + term->display = guac_terminal_display_alloc(term->term_width, term->term_height); /* Clear with background color */ @@ -184,8 +184,8 @@ void guac_terminal_free(guac_terminal* term) { free(term->scrollback); - /* Free delta */ - guac_terminal_delta_free(term->delta); + /* Free display */ + guac_terminal_display_free(term->display); } @@ -353,7 +353,7 @@ int __guac_terminal_set_colors(guac_terminal* term, /** * Sends the given character to the terminal at the given row and column, - * rendering the charater immediately. This bypasses the guac_terminal_delta + * rendering the charater immediately. This bypasses the guac_terminal_display * mechanism and is intended for flushing of updates only. */ int __guac_terminal_set(guac_terminal* term, int row, int col, char c) { @@ -377,8 +377,8 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { guac_char.value = c; guac_char.attributes = term->current_attributes; - /* Set delta */ - guac_terminal_delta_set(term->delta, row, col, &guac_char); + /* Set display */ + guac_terminal_display_set(term->display, row, col, &guac_char); return 0; } @@ -398,8 +398,8 @@ int guac_terminal_copy(guac_terminal* term, int src_row, int src_col, int rows, int cols, int dst_row, int dst_col) { - /* Update delta */ - guac_terminal_delta_copy(term->delta, + /* Update display */ + guac_terminal_display_copy(term->display, dst_row, dst_col, src_row, src_col, cols, rows); @@ -419,7 +419,7 @@ int guac_terminal_clear(guac_terminal* term, character.attributes.background = background_color; /* Fill with color */ - guac_terminal_delta_set_rect(term->delta, + guac_terminal_display_set_rect(term->display, row, col, cols, rows, &character); return 0; @@ -513,64 +513,76 @@ int guac_terminal_clear_range(guac_terminal* term, } -guac_terminal_delta* guac_terminal_delta_alloc(int width, int height) { +guac_terminal_display* guac_terminal_display_alloc(int width, int height) { - guac_terminal_operation* current; + guac_terminal_display_char* current; int x, y; - /* Allocate delta */ - guac_terminal_delta* delta = malloc(sizeof(guac_terminal_delta)); + /* Allocate display */ + guac_terminal_display* display = malloc(sizeof(guac_terminal_display)); + + /* Initial value of all characters in the display */ + guac_terminal_char blank = { + .value = ' ', + .attributes = { + .background = 0 + } + }; /* Set width and height */ - delta->width = width; - delta->height = height; + display->width = width; + display->height = height; - /* Alloc operations */ - delta->operations = malloc(width * height * - sizeof(guac_terminal_operation)); + /* Alloc display */ + display->characters = malloc(width * height * + sizeof(guac_terminal_display_char)); - /* Init each operation buffer row */ - current = delta->operations; + /* Init each display buffer row */ + current = display->characters; for (y=0; ytype = GUAC_CHAR_NOP; + for (x=0; xcurrent = blank; + current->next.type = GUAC_CHAR_NOP; + current++; + } } - return delta; + return display; } -void guac_terminal_delta_free(guac_terminal_delta* delta) { +void guac_terminal_display_free(guac_terminal_display* display) { - /* Free operations buffer */ - free(delta->operations); + /* Free characters buffer */ + free(display->characters); - /* Free delta */ - free(delta); + /* Free display */ + free(display); } -void guac_terminal_delta_resize(guac_terminal_delta* delta, +void guac_terminal_display_resize(guac_terminal_display* display, int width, int height) { /* STUB */ } -void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, +void guac_terminal_display_set(guac_terminal_display* display, int r, int c, guac_terminal_char* character) { /* Get operation at coordinate */ - guac_terminal_operation* op = &(delta->operations[r*delta->width + c]); + guac_terminal_display_char* disp_char = + &(display->characters[r*display->width + c]); /* Store operation */ - op->type = GUAC_CHAR_SET; - op->character = *character; + disp_char->next.type = GUAC_CHAR_SET; + disp_char->next.character = *character; } -void guac_terminal_delta_copy(guac_terminal_delta* delta, +void guac_terminal_display_copy(guac_terminal_display* display, int dst_row, int dst_column, int src_row, int src_column, int w, int h) { @@ -579,30 +591,30 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, /* FIXME: Handle intersections between src and dst rects */ - guac_terminal_operation* current_row = - &(delta->operations[dst_row*delta->width + dst_column]); + guac_terminal_display_char* current_row = + &(display->characters[dst_row*display->width + dst_column]); - guac_terminal_operation* src_current_row = - &(delta->operations[src_row*delta->width + src_column]); + guac_terminal_display_char* src_current_row = + &(display->characters[src_row*display->width + src_column]); /* Set rectangle to copy operations */ for (row=0; rowtype != GUAC_CHAR_NOP) - *current = *src_current; + if (src_current->next.type != GUAC_CHAR_NOP) + current->next = src_current->next; /* Store operation */ else { - current->type = GUAC_CHAR_COPY; - current->row = src_row + row; - current->column = src_column + column; + current->next.type = GUAC_CHAR_COPY; + current->next.row = src_row + row; + current->next.column = src_column + column; } /* Next column */ @@ -612,8 +624,8 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, } /* Next row */ - current_row += delta->width; - src_current_row += delta->width; + current_row += display->width; + src_current_row += display->width; } @@ -621,23 +633,23 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, } -void guac_terminal_delta_set_rect(guac_terminal_delta* delta, +void guac_terminal_display_set_rect(guac_terminal_display* display, int row, int column, int w, int h, guac_terminal_char* character) { - guac_terminal_operation* current_row = - &(delta->operations[row*delta->width + column]); + guac_terminal_display_char* current_row = + &(display->characters[row*display->width + column]); /* Set rectangle contents to given character */ for (row=0; rowtype = GUAC_CHAR_SET; - current->character = *character; + current->next.type = GUAC_CHAR_SET; + current->next.character = *character; /* Next column */ current++; @@ -645,24 +657,24 @@ void guac_terminal_delta_set_rect(guac_terminal_delta* delta, } /* Next row */ - current_row += delta->width; + current_row += display->width; } } -void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, +void __guac_terminal_display_flush_copy(guac_terminal_display* display, guac_terminal* terminal) { - guac_terminal_operation* current = delta->operations; + guac_terminal_display_char* current = display->characters; int row, col; /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { /* If operation is a copy operation */ - if (current->type == GUAC_CHAR_COPY) { + if (current->next.type == GUAC_CHAR_COPY) { /* The determined bounds of the rectangle of contiguous * operations */ @@ -680,23 +692,23 @@ void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, int expected_row, expected_col; /* Current row within a subrect */ - guac_terminal_operation* rect_current_row; + guac_terminal_display_char* rect_current_row; /* Determine bounds of rectangle */ rect_current_row = current; - expected_row = current->row; - for (rect_row=row; rect_rowheight; rect_row++) { + expected_row = current->next.row; + for (rect_row=row; rect_rowheight; rect_row++) { - guac_terminal_operation* rect_current = rect_current_row; - expected_col = current->column; + guac_terminal_display_char* rect_current = rect_current_row; + expected_col = current->next.column; /* Find width */ - for (rect_col=col; rect_colwidth; rect_col++) { + for (rect_col=col; rect_colwidth; rect_col++) { /* If not identical operation, stop */ - if (rect_current->type != GUAC_CHAR_COPY - || rect_current->row != expected_row - || rect_current->column != expected_col) + if (rect_current->next.type != GUAC_CHAR_COPY + || rect_current->next.row != expected_row + || rect_current->next.column != expected_col) break; /* Next column */ @@ -717,7 +729,7 @@ void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, detected_right = rect_col - 1; /* Next row */ - rect_current_row += delta->width; + rect_current_row += display->width; expected_row++; } @@ -728,19 +740,19 @@ void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, /* Mark rect as NOP (as it has been handled) */ rect_current_row = current; - expected_row = current->row; + expected_row = current->next.row; for (rect_row=0; rect_rowcolumn; + guac_terminal_display_char* rect_current = rect_current_row; + expected_col = current->next.column; for (rect_col=0; rect_coltype == GUAC_CHAR_COPY - && rect_current->row == expected_row - && rect_current->column == expected_col) - rect_current->type = GUAC_CHAR_NOP; + if (rect_current->next.type == GUAC_CHAR_COPY + && rect_current->next.row == expected_row + && rect_current->next.column == expected_col) + rect_current->next.type = GUAC_CHAR_NOP; /* Next column */ rect_current++; @@ -749,7 +761,7 @@ void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, } /* Next row */ - rect_current_row += delta->width; + rect_current_row += display->width; expected_row++; } @@ -758,8 +770,8 @@ void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, guac_protocol_send_copy(terminal->client->socket, GUAC_DEFAULT_LAYER, - current->column * terminal->char_width, - current->row * terminal->char_height, + current->next.column * terminal->char_width, + current->next.row * terminal->char_height, rect_width * terminal->char_width, rect_height * terminal->char_height, @@ -779,19 +791,19 @@ void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, } -void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, +void __guac_terminal_display_flush_clear(guac_terminal_display* display, guac_terminal* terminal) { - guac_terminal_operation* current = delta->operations; + guac_terminal_display_char* current = display->characters; int row, col; /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { /* If operation is a cler operation (set to space) */ - if (current->type == GUAC_CHAR_SET && - current->character.value == ' ') { + if (current->next.type == GUAC_CHAR_SET && + current->next.character.value == ' ') { /* The determined bounds of the rectangle of contiguous * operations */ @@ -806,35 +818,37 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, /* Color of the rectangle to draw */ int color; - if (current->character.attributes.reverse) - color = current->character.attributes.foreground; + if (current->next.character.attributes.reverse) + color = current->next.character.attributes.foreground; else - color = current->character.attributes.background; + color = current->next.character.attributes.background; const guac_terminal_color* guac_color = &guac_terminal_palette[color]; /* Current row within a subrect */ - guac_terminal_operation* rect_current_row; + guac_terminal_display_char* rect_current_row; /* Determine bounds of rectangle */ rect_current_row = current; - for (rect_row=row; rect_rowheight; rect_row++) { + for (rect_row=row; rect_rowheight; rect_row++) { - guac_terminal_operation* rect_current = rect_current_row; + guac_terminal_display_char* rect_current = rect_current_row; /* Find width */ - for (rect_col=col; rect_colwidth; rect_col++) { + for (rect_col=col; rect_colwidth; rect_col++) { int joining_color; - if (rect_current->character.attributes.reverse) - joining_color = current->character.attributes.foreground; + if (rect_current->next.character.attributes.reverse) + joining_color = + current->next.character.attributes.foreground; else - joining_color = current->character.attributes.background; + joining_color = + current->next.character.attributes.background; /* If not identical operation, stop */ - if (rect_current->type != GUAC_CHAR_SET - || rect_current->character.value != ' ' + if (rect_current->next.type != GUAC_CHAR_SET + || rect_current->next.character.value != ' ' || joining_color != color) break; @@ -855,7 +869,7 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, detected_right = rect_col - 1; /* Next row */ - rect_current_row += delta->width; + rect_current_row += display->width; } @@ -867,21 +881,23 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, rect_current_row = current; for (rect_row=0; rect_rowcharacter.attributes.reverse) - joining_color = current->character.attributes.foreground; + if (rect_current->next.character.attributes.reverse) + joining_color = + current->next.character.attributes.foreground; else - joining_color = current->character.attributes.background; + joining_color = + current->next.character.attributes.background; /* Mark clear operations as NOP */ - if (rect_current->type == GUAC_CHAR_SET - && rect_current->character.value == ' ' + if (rect_current->next.type == GUAC_CHAR_SET + && rect_current->next.character.value == ' ' && joining_color == color) - rect_current->type = GUAC_CHAR_NOP; + rect_current->next.type = GUAC_CHAR_NOP; /* Next column */ rect_current++; @@ -889,7 +905,7 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, } /* Next row */ - rect_current_row += delta->width; + rect_current_row += display->width; } @@ -917,29 +933,29 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, } -void __guac_terminal_delta_flush_set(guac_terminal_delta* delta, +void __guac_terminal_display_flush_set(guac_terminal_display* display, guac_terminal* terminal) { - guac_terminal_operation* current = delta->operations; + guac_terminal_display_char* current = display->characters; int row, col; /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { /* Perform given operation */ - if (current->type == GUAC_CHAR_SET) { + if (current->next.type == GUAC_CHAR_SET) { /* Set attributes */ __guac_terminal_set_colors(terminal, - &(current->character.attributes)); + &(current->next.character.attributes)); /* Send character */ __guac_terminal_set(terminal, row, col, - current->character.value); + current->next.character.value); /* Mark operation as handled */ - current->type = GUAC_CHAR_NOP; + current->next.type = GUAC_CHAR_NOP; } @@ -951,17 +967,17 @@ void __guac_terminal_delta_flush_set(guac_terminal_delta* delta, } -void guac_terminal_delta_flush(guac_terminal_delta* delta, +void guac_terminal_display_flush(guac_terminal_display* display, guac_terminal* terminal) { /* Flush copy operations first */ - __guac_terminal_delta_flush_copy(delta, terminal); + __guac_terminal_display_flush_copy(display, terminal); /* Flush clear operations (as they're just rects) */ - __guac_terminal_delta_flush_clear(delta, terminal); + __guac_terminal_display_flush_clear(display, terminal); /* Flush set operations (the only operations remaining) */ - __guac_terminal_delta_flush_set(delta, terminal); + __guac_terminal_display_flush_set(display, terminal); } From 436387edaeb3c49d339afa0e73f64f495e012d20 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 27 Mar 2013 03:49:40 -0700 Subject: [PATCH 053/169] Keep current character status updated. --- protocols/ssh/src/terminal.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 010d58cd..96dd763f 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -578,7 +578,8 @@ void guac_terminal_display_set(guac_terminal_display* display, int r, int c, /* Store operation */ disp_char->next.type = GUAC_CHAR_SET; - disp_char->next.character = *character; + disp_char->next.character = + disp_char->current = *character; } @@ -617,6 +618,9 @@ void guac_terminal_display_copy(guac_terminal_display* display, current->next.column = src_column + column; } + /* Copy character data */ + current->current = src_current->current; + /* Next column */ current++; src_current++; @@ -649,7 +653,8 @@ void guac_terminal_display_set_rect(guac_terminal_display* display, /* Store operation */ current->next.type = GUAC_CHAR_SET; - current->next.character = *character; + current->next.character = + current->current = *character; /* Next column */ current++; From f1844ec555cf045167005310c4a3e2bc59b6c8b6 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 27 Mar 2013 04:03:54 -0700 Subject: [PATCH 054/169] Revert "Remove cursor layer." This reverts commit 16a77db63bcc1455b2ec3b9939f50a70b7e03c21. --- protocols/ssh/include/terminal.h | 5 +++++ protocols/ssh/src/ssh_client.c | 11 +++++++++++ protocols/ssh/src/terminal.c | 1 + 3 files changed, 17 insertions(+) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index a5891edd..662f839f 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -320,6 +320,11 @@ struct guac_terminal { */ int cursor_col; + /** + * Simple cursor layer until scrollback, etc. is implemented. + */ + guac_layer* cursor_layer; + /** * The attributes which will be applied to future characters. */ diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index b6d2b998..2da7d481 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -122,6 +122,17 @@ int guac_client_init(guac_client* client, int argc, char** argv) { term->char_width * term->term_width, term->char_height * term->term_height); + /* Cursor layer need only be one char */ + guac_protocol_send_size(socket, term->cursor_layer, term->char_width, term->char_height); + + /* Draw cursor */ + guac_protocol_send_rect(socket, term->cursor_layer, + 0, 0, term->char_width, term->char_height); + + guac_protocol_send_cfill(socket, + GUAC_COMP_OVER, term->cursor_layer, + 0x40, 0xFF, 0x80, 0x80); + guac_socket_flush(socket); /* Open SSH session */ diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 96dd763f..34e285a8 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -134,6 +134,7 @@ guac_terminal* guac_terminal_create(guac_client* client, term->cursor_row = 0; term->cursor_col = 0; + term->cursor_layer = guac_client_alloc_layer(client); term->term_width = width / term->char_width; term->term_height = height / term->char_height; From c2e80bda81358fa2010dd24cc1d0c4621502673e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 27 Mar 2013 04:11:56 -0700 Subject: [PATCH 055/169] Restore cursor redraw function. --- protocols/ssh/include/terminal.h | 5 +++++ protocols/ssh/src/ssh_handlers.c | 4 ++++ protocols/ssh/src/terminal.c | 15 +++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 662f839f..8f76a647 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -451,5 +451,10 @@ void guac_terminal_display_set_rect(guac_terminal_display* display, void guac_terminal_display_flush(guac_terminal_display* display, guac_terminal* terminal); +/** + * Update the cursor position and contents. + */ +int guac_terminal_redraw_cursor(guac_terminal* term); + #endif diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 103192da..60c8c3e2 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -106,6 +106,10 @@ int ssh_guac_client_handle_messages(guac_client* client) { /* Flush terminal display */ guac_terminal_display_flush(client_data->term->display, client_data->term); + + /* Update cursor */ + guac_terminal_redraw_cursor(client_data->term); + return 0; } diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 34e285a8..1135e4fb 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -987,3 +987,18 @@ void guac_terminal_display_flush(guac_terminal_display* display, } +int guac_terminal_redraw_cursor(guac_terminal* term) { + + guac_socket* socket = term->client->socket; + + /* Erase old cursor */ + return + guac_protocol_send_move(socket, + term->cursor_layer, + + GUAC_DEFAULT_LAYER, + term->char_width * term->cursor_col, + term->char_height * term->cursor_row, + 1); + +} From fa0522503b8fc5851fc09b1035d3640df7d691d4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 27 Mar 2013 12:43:21 -0700 Subject: [PATCH 056/169] Revert "Keep current character status updated." This reverts commit 449637998c1fa4dfcddc1ed0a88562bb0a81d14a. --- protocols/ssh/src/terminal.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 1135e4fb..ee94db30 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -579,8 +579,7 @@ void guac_terminal_display_set(guac_terminal_display* display, int r, int c, /* Store operation */ disp_char->next.type = GUAC_CHAR_SET; - disp_char->next.character = - disp_char->current = *character; + disp_char->next.character = *character; } @@ -619,9 +618,6 @@ void guac_terminal_display_copy(guac_terminal_display* display, current->next.column = src_column + column; } - /* Copy character data */ - current->current = src_current->current; - /* Next column */ current++; src_current++; @@ -654,8 +650,7 @@ void guac_terminal_display_set_rect(guac_terminal_display* display, /* Store operation */ current->next.type = GUAC_CHAR_SET; - current->next.character = - current->current = *character; + current->next.character = *character; /* Next column */ current++; From 38794ed94b469f22bd6e5c0da271c3cc5405e064 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 27 Mar 2013 12:44:40 -0700 Subject: [PATCH 057/169] Revert "Refactor delta to display." This reverts commit 4b34bbbf3feb2c0053a64f978e9890824b2e8320. Conflicts: src/ssh_handlers.c --- protocols/ssh/include/terminal.h | 56 +++---- protocols/ssh/src/ssh_handlers.c | 4 +- protocols/ssh/src/terminal.c | 256 +++++++++++++++---------------- 3 files changed, 141 insertions(+), 175 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 8f76a647..3b33507c 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -186,33 +186,14 @@ typedef struct guac_terminal_operation { } guac_terminal_operation; /** - * A terminal character with two states - a current state, and a future state - * represented by a pairing of an operation with a terminal character. + * Set of all pending operations for the currently-visible screen area. */ -typedef struct guac_terminal_display_char { +typedef struct guac_terminal_delta { /** - * The current state of this character. + * Array of all operations pending for the visible screen area. */ - guac_terminal_char current; - - /** - * The next state of this character, as an operation upon this - * character. - */ - guac_terminal_operation next; - -} guac_terminal_display_char; - -/** - * Set of all characters for the currently-visible screen area. - */ -typedef struct guac_terminal_display { - - /** - * Array of all characters within the visible screen area. - */ - guac_terminal_display_char* characters; + guac_terminal_operation* operations; /** * The width of the screen, in characters. @@ -224,7 +205,7 @@ typedef struct guac_terminal_display { */ int height; -} guac_terminal_display; +} guac_terminal_delta; /** * Represents a terminal emulator which uses a given Guacamole client to @@ -343,9 +324,10 @@ struct guac_terminal { guac_terminal_char_handler* char_handler; /** - * The current display state and pending state. + * The difference between the currently-rendered screen and the current + * state of the terminal. */ - guac_terminal_display* display; + guac_terminal_delta* delta; }; @@ -407,32 +389,32 @@ int guac_terminal_scroll_down(guac_terminal* term, int start_row, int end_row, int amount); /** - * Allocates a new guac_terminal_display. + * Allocates a new guac_terminal_delta. */ -guac_terminal_display* guac_terminal_display_alloc(int width, int height); +guac_terminal_delta* guac_terminal_delta_alloc(int width, int height); /** - * Frees the given guac_terminal_display. + * Frees the given guac_terminal_delta. */ -void guac_terminal_display_free(guac_terminal_display* display); +void guac_terminal_delta_free(guac_terminal_delta* delta); /** - * Resizes the given guac_terminal_display to the given dimensions. + * Resizes the given guac_terminal_delta to the given dimensions. */ -void guac_terminal_display_resize(guac_terminal_display* display, +void guac_terminal_delta_resize(guac_terminal_delta* delta, int width, int height); /** * Stores a set operation at the given location. */ -void guac_terminal_display_set(guac_terminal_display* display, int r, int c, +void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, guac_terminal_char* character); /** * Stores a rectangle of copy operations, copying existing operations as * necessary. */ -void guac_terminal_display_copy(guac_terminal_display* display, +void guac_terminal_delta_copy(guac_terminal_delta* delta, int dst_row, int dst_column, int src_row, int src_column, int w, int h); @@ -440,15 +422,15 @@ void guac_terminal_display_copy(guac_terminal_display* display, /** * Sets a rectangle of character data to the given character value. */ -void guac_terminal_display_set_rect(guac_terminal_display* display, +void guac_terminal_delta_set_rect(guac_terminal_delta* delta, int row, int column, int w, int h, guac_terminal_char* character); /** - * Flushes all pending operations within the given guac_terminal_display to the + * Flushes all pending operations within the given guac_client_delta to the * given guac_terminal. */ -void guac_terminal_display_flush(guac_terminal_display* display, +void guac_terminal_delta_flush(guac_terminal_delta* delta, guac_terminal* terminal); /** diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 60c8c3e2..6e045cc3 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -104,8 +104,8 @@ int ssh_guac_client_handle_messages(guac_client* client) { } } - /* Flush terminal display */ - guac_terminal_display_flush(client_data->term->display, client_data->term); + /* Flush terminal delta */ + guac_terminal_delta_flush(client_data->term->delta, client_data->term); /* Update cursor */ guac_terminal_redraw_cursor(client_data->term); diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index ee94db30..91fe2353 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -164,8 +164,8 @@ guac_terminal* guac_terminal_create(guac_client* client, } - /* Init display */ - term->display = guac_terminal_display_alloc(term->term_width, + /* Init delta */ + term->delta = guac_terminal_delta_alloc(term->term_width, term->term_height); /* Clear with background color */ @@ -185,8 +185,8 @@ void guac_terminal_free(guac_terminal* term) { free(term->scrollback); - /* Free display */ - guac_terminal_display_free(term->display); + /* Free delta */ + guac_terminal_delta_free(term->delta); } @@ -354,7 +354,7 @@ int __guac_terminal_set_colors(guac_terminal* term, /** * Sends the given character to the terminal at the given row and column, - * rendering the charater immediately. This bypasses the guac_terminal_display + * rendering the charater immediately. This bypasses the guac_terminal_delta * mechanism and is intended for flushing of updates only. */ int __guac_terminal_set(guac_terminal* term, int row, int col, char c) { @@ -378,8 +378,8 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { guac_char.value = c; guac_char.attributes = term->current_attributes; - /* Set display */ - guac_terminal_display_set(term->display, row, col, &guac_char); + /* Set delta */ + guac_terminal_delta_set(term->delta, row, col, &guac_char); return 0; } @@ -399,8 +399,8 @@ int guac_terminal_copy(guac_terminal* term, int src_row, int src_col, int rows, int cols, int dst_row, int dst_col) { - /* Update display */ - guac_terminal_display_copy(term->display, + /* Update delta */ + guac_terminal_delta_copy(term->delta, dst_row, dst_col, src_row, src_col, cols, rows); @@ -420,7 +420,7 @@ int guac_terminal_clear(guac_terminal* term, character.attributes.background = background_color; /* Fill with color */ - guac_terminal_display_set_rect(term->display, + guac_terminal_delta_set_rect(term->delta, row, col, cols, rows, &character); return 0; @@ -514,76 +514,64 @@ int guac_terminal_clear_range(guac_terminal* term, } -guac_terminal_display* guac_terminal_display_alloc(int width, int height) { +guac_terminal_delta* guac_terminal_delta_alloc(int width, int height) { - guac_terminal_display_char* current; + guac_terminal_operation* current; int x, y; - /* Allocate display */ - guac_terminal_display* display = malloc(sizeof(guac_terminal_display)); - - /* Initial value of all characters in the display */ - guac_terminal_char blank = { - .value = ' ', - .attributes = { - .background = 0 - } - }; + /* Allocate delta */ + guac_terminal_delta* delta = malloc(sizeof(guac_terminal_delta)); /* Set width and height */ - display->width = width; - display->height = height; + delta->width = width; + delta->height = height; - /* Alloc display */ - display->characters = malloc(width * height * - sizeof(guac_terminal_display_char)); + /* Alloc operations */ + delta->operations = malloc(width * height * + sizeof(guac_terminal_operation)); - /* Init each display buffer row */ - current = display->characters; + /* Init each operation buffer row */ + current = delta->operations; for (y=0; ycurrent = blank; - current->next.type = GUAC_CHAR_NOP; - current++; - } + for (x=0; xtype = GUAC_CHAR_NOP; } - return display; + return delta; } -void guac_terminal_display_free(guac_terminal_display* display) { +void guac_terminal_delta_free(guac_terminal_delta* delta) { - /* Free characters buffer */ - free(display->characters); + /* Free operations buffer */ + free(delta->operations); - /* Free display */ - free(display); + /* Free delta */ + free(delta); } -void guac_terminal_display_resize(guac_terminal_display* display, +void guac_terminal_delta_resize(guac_terminal_delta* delta, int width, int height) { /* STUB */ } -void guac_terminal_display_set(guac_terminal_display* display, int r, int c, +void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, guac_terminal_char* character) { /* Get operation at coordinate */ - guac_terminal_display_char* disp_char = - &(display->characters[r*display->width + c]); + guac_terminal_operation* op = &(delta->operations[r*delta->width + c]); /* Store operation */ - disp_char->next.type = GUAC_CHAR_SET; - disp_char->next.character = *character; + op->type = GUAC_CHAR_SET; + op->character = *character; } -void guac_terminal_display_copy(guac_terminal_display* display, +void guac_terminal_delta_copy(guac_terminal_delta* delta, int dst_row, int dst_column, int src_row, int src_column, int w, int h) { @@ -592,30 +580,30 @@ void guac_terminal_display_copy(guac_terminal_display* display, /* FIXME: Handle intersections between src and dst rects */ - guac_terminal_display_char* current_row = - &(display->characters[dst_row*display->width + dst_column]); + guac_terminal_operation* current_row = + &(delta->operations[dst_row*delta->width + dst_column]); - guac_terminal_display_char* src_current_row = - &(display->characters[src_row*display->width + src_column]); + guac_terminal_operation* src_current_row = + &(delta->operations[src_row*delta->width + src_column]); /* Set rectangle to copy operations */ for (row=0; rownext.type != GUAC_CHAR_NOP) - current->next = src_current->next; + if (src_current->type != GUAC_CHAR_NOP) + *current = *src_current; /* Store operation */ else { - current->next.type = GUAC_CHAR_COPY; - current->next.row = src_row + row; - current->next.column = src_column + column; + current->type = GUAC_CHAR_COPY; + current->row = src_row + row; + current->column = src_column + column; } /* Next column */ @@ -625,8 +613,8 @@ void guac_terminal_display_copy(guac_terminal_display* display, } /* Next row */ - current_row += display->width; - src_current_row += display->width; + current_row += delta->width; + src_current_row += delta->width; } @@ -634,23 +622,23 @@ void guac_terminal_display_copy(guac_terminal_display* display, } -void guac_terminal_display_set_rect(guac_terminal_display* display, +void guac_terminal_delta_set_rect(guac_terminal_delta* delta, int row, int column, int w, int h, guac_terminal_char* character) { - guac_terminal_display_char* current_row = - &(display->characters[row*display->width + column]); + guac_terminal_operation* current_row = + &(delta->operations[row*delta->width + column]); /* Set rectangle contents to given character */ for (row=0; rownext.type = GUAC_CHAR_SET; - current->next.character = *character; + current->type = GUAC_CHAR_SET; + current->character = *character; /* Next column */ current++; @@ -658,24 +646,24 @@ void guac_terminal_display_set_rect(guac_terminal_display* display, } /* Next row */ - current_row += display->width; + current_row += delta->width; } } -void __guac_terminal_display_flush_copy(guac_terminal_display* display, +void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, guac_terminal* terminal) { - guac_terminal_display_char* current = display->characters; + guac_terminal_operation* current = delta->operations; int row, col; /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { /* If operation is a copy operation */ - if (current->next.type == GUAC_CHAR_COPY) { + if (current->type == GUAC_CHAR_COPY) { /* The determined bounds of the rectangle of contiguous * operations */ @@ -693,23 +681,23 @@ void __guac_terminal_display_flush_copy(guac_terminal_display* display, int expected_row, expected_col; /* Current row within a subrect */ - guac_terminal_display_char* rect_current_row; + guac_terminal_operation* rect_current_row; /* Determine bounds of rectangle */ rect_current_row = current; - expected_row = current->next.row; - for (rect_row=row; rect_rowheight; rect_row++) { + expected_row = current->row; + for (rect_row=row; rect_rowheight; rect_row++) { - guac_terminal_display_char* rect_current = rect_current_row; - expected_col = current->next.column; + guac_terminal_operation* rect_current = rect_current_row; + expected_col = current->column; /* Find width */ - for (rect_col=col; rect_colwidth; rect_col++) { + for (rect_col=col; rect_colwidth; rect_col++) { /* If not identical operation, stop */ - if (rect_current->next.type != GUAC_CHAR_COPY - || rect_current->next.row != expected_row - || rect_current->next.column != expected_col) + if (rect_current->type != GUAC_CHAR_COPY + || rect_current->row != expected_row + || rect_current->column != expected_col) break; /* Next column */ @@ -730,7 +718,7 @@ void __guac_terminal_display_flush_copy(guac_terminal_display* display, detected_right = rect_col - 1; /* Next row */ - rect_current_row += display->width; + rect_current_row += delta->width; expected_row++; } @@ -741,19 +729,19 @@ void __guac_terminal_display_flush_copy(guac_terminal_display* display, /* Mark rect as NOP (as it has been handled) */ rect_current_row = current; - expected_row = current->next.row; + expected_row = current->row; for (rect_row=0; rect_rownext.column; + guac_terminal_operation* rect_current = rect_current_row; + expected_col = current->column; for (rect_col=0; rect_colnext.type == GUAC_CHAR_COPY - && rect_current->next.row == expected_row - && rect_current->next.column == expected_col) - rect_current->next.type = GUAC_CHAR_NOP; + if (rect_current->type == GUAC_CHAR_COPY + && rect_current->row == expected_row + && rect_current->column == expected_col) + rect_current->type = GUAC_CHAR_NOP; /* Next column */ rect_current++; @@ -762,7 +750,7 @@ void __guac_terminal_display_flush_copy(guac_terminal_display* display, } /* Next row */ - rect_current_row += display->width; + rect_current_row += delta->width; expected_row++; } @@ -771,8 +759,8 @@ void __guac_terminal_display_flush_copy(guac_terminal_display* display, guac_protocol_send_copy(terminal->client->socket, GUAC_DEFAULT_LAYER, - current->next.column * terminal->char_width, - current->next.row * terminal->char_height, + current->column * terminal->char_width, + current->row * terminal->char_height, rect_width * terminal->char_width, rect_height * terminal->char_height, @@ -792,19 +780,19 @@ void __guac_terminal_display_flush_copy(guac_terminal_display* display, } -void __guac_terminal_display_flush_clear(guac_terminal_display* display, +void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, guac_terminal* terminal) { - guac_terminal_display_char* current = display->characters; + guac_terminal_operation* current = delta->operations; int row, col; /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { /* If operation is a cler operation (set to space) */ - if (current->next.type == GUAC_CHAR_SET && - current->next.character.value == ' ') { + if (current->type == GUAC_CHAR_SET && + current->character.value == ' ') { /* The determined bounds of the rectangle of contiguous * operations */ @@ -819,37 +807,35 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display, /* Color of the rectangle to draw */ int color; - if (current->next.character.attributes.reverse) - color = current->next.character.attributes.foreground; + if (current->character.attributes.reverse) + color = current->character.attributes.foreground; else - color = current->next.character.attributes.background; + color = current->character.attributes.background; const guac_terminal_color* guac_color = &guac_terminal_palette[color]; /* Current row within a subrect */ - guac_terminal_display_char* rect_current_row; + guac_terminal_operation* rect_current_row; /* Determine bounds of rectangle */ rect_current_row = current; - for (rect_row=row; rect_rowheight; rect_row++) { + for (rect_row=row; rect_rowheight; rect_row++) { - guac_terminal_display_char* rect_current = rect_current_row; + guac_terminal_operation* rect_current = rect_current_row; /* Find width */ - for (rect_col=col; rect_colwidth; rect_col++) { + for (rect_col=col; rect_colwidth; rect_col++) { int joining_color; - if (rect_current->next.character.attributes.reverse) - joining_color = - current->next.character.attributes.foreground; + if (rect_current->character.attributes.reverse) + joining_color = current->character.attributes.foreground; else - joining_color = - current->next.character.attributes.background; + joining_color = current->character.attributes.background; /* If not identical operation, stop */ - if (rect_current->next.type != GUAC_CHAR_SET - || rect_current->next.character.value != ' ' + if (rect_current->type != GUAC_CHAR_SET + || rect_current->character.value != ' ' || joining_color != color) break; @@ -870,7 +856,7 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display, detected_right = rect_col - 1; /* Next row */ - rect_current_row += display->width; + rect_current_row += delta->width; } @@ -882,23 +868,21 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display, rect_current_row = current; for (rect_row=0; rect_rownext.character.attributes.reverse) - joining_color = - current->next.character.attributes.foreground; + if (rect_current->character.attributes.reverse) + joining_color = current->character.attributes.foreground; else - joining_color = - current->next.character.attributes.background; + joining_color = current->character.attributes.background; /* Mark clear operations as NOP */ - if (rect_current->next.type == GUAC_CHAR_SET - && rect_current->next.character.value == ' ' + if (rect_current->type == GUAC_CHAR_SET + && rect_current->character.value == ' ' && joining_color == color) - rect_current->next.type = GUAC_CHAR_NOP; + rect_current->type = GUAC_CHAR_NOP; /* Next column */ rect_current++; @@ -906,7 +890,7 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display, } /* Next row */ - rect_current_row += display->width; + rect_current_row += delta->width; } @@ -934,29 +918,29 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display, } -void __guac_terminal_display_flush_set(guac_terminal_display* display, +void __guac_terminal_delta_flush_set(guac_terminal_delta* delta, guac_terminal* terminal) { - guac_terminal_display_char* current = display->characters; + guac_terminal_operation* current = delta->operations; int row, col; /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { /* Perform given operation */ - if (current->next.type == GUAC_CHAR_SET) { + if (current->type == GUAC_CHAR_SET) { /* Set attributes */ __guac_terminal_set_colors(terminal, - &(current->next.character.attributes)); + &(current->character.attributes)); /* Send character */ __guac_terminal_set(terminal, row, col, - current->next.character.value); + current->character.value); /* Mark operation as handled */ - current->next.type = GUAC_CHAR_NOP; + current->type = GUAC_CHAR_NOP; } @@ -968,17 +952,17 @@ void __guac_terminal_display_flush_set(guac_terminal_display* display, } -void guac_terminal_display_flush(guac_terminal_display* display, +void guac_terminal_delta_flush(guac_terminal_delta* delta, guac_terminal* terminal) { /* Flush copy operations first */ - __guac_terminal_display_flush_copy(display, terminal); + __guac_terminal_delta_flush_copy(delta, terminal); /* Flush clear operations (as they're just rects) */ - __guac_terminal_display_flush_clear(display, terminal); + __guac_terminal_delta_flush_clear(delta, terminal); /* Flush set operations (the only operations remaining) */ - __guac_terminal_display_flush_set(display, terminal); + __guac_terminal_delta_flush_set(delta, terminal); } From 95923b2752d9b1f453aaf5b1e1cc34d64acf422a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 29 Mar 2013 01:56:27 -0700 Subject: [PATCH 058/169] Add terminal character buffer, update buffer with each operation. --- protocols/ssh/include/terminal.h | 68 ++++++++++++++++++++++++ protocols/ssh/src/terminal.c | 90 ++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 3b33507c..e7542383 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -207,6 +207,28 @@ typedef struct guac_terminal_delta { } guac_terminal_delta; +/** + * Dynamically-resizable character buffer. + */ +typedef struct guac_terminal_buffer { + + /** + * Array of characters. + */ + guac_terminal_char* characters; + + /** + * The width of this buffer in characters. + */ + int width; + + /** + * The height of this buffer in characters. + */ + int height; + +} guac_terminal_buffer; + /** * Represents a terminal emulator which uses a given Guacamole client to * render itself. @@ -329,6 +351,13 @@ struct guac_terminal { */ guac_terminal_delta* delta; + /** + * Current terminal display state. All characters present on the screen + * are within this buffer. This has nothing to do with the delta, which + * facilitates transfer of a set of changes to the remote display. + */ + guac_terminal_buffer* buffer; + }; /** @@ -438,5 +467,44 @@ void guac_terminal_delta_flush(guac_terminal_delta* delta, */ int guac_terminal_redraw_cursor(guac_terminal* term); +/** + * Allocates a new character buffer having the given dimensions. + */ +guac_terminal_buffer* guac_terminal_buffer_alloc(int width, int height); + +/** + * Resizes the given character buffer to the given dimensions. + */ +void guac_terminal_buffer_resize(guac_terminal_buffer* buffer, + int width, int height); + +/** + * Sets the character at the given location within the buffer to the given + * value. + */ +void guac_terminal_buffer_set(guac_terminal_buffer* buffer, int r, int c, + guac_terminal_char* character); + +/** + * Copies a rectangle of character data within the buffer. The source and + * destination may overlap. + */ +void guac_terminal_buffer_copy(guac_terminal_buffer* buffer, + int dst_row, int dst_column, + int src_row, int src_column, + int w, int h); + +/** + * Sets a rectangle of character data to the given character value. + */ +void guac_terminal_buffer_set_rect(guac_terminal_buffer* buffer, + int row, int column, int w, int h, + guac_terminal_char* character); + +/** + * Frees the given character buffer. + */ +void guac_terminal_buffer_free(guac_terminal_buffer* buffer); + #endif diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 91fe2353..bbcd683d 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -168,6 +168,10 @@ guac_terminal* guac_terminal_create(guac_client* client, term->delta = guac_terminal_delta_alloc(term->term_width, term->term_height); + /* Init buffer */ + term->buffer = guac_terminal_buffer_alloc(term->term_width, + term->term_height); + /* Clear with background color */ guac_terminal_clear(term, 0, 0, term->term_height, term->term_width, @@ -188,6 +192,9 @@ void guac_terminal_free(guac_terminal* term) { /* Free delta */ guac_terminal_delta_free(term->delta); + /* Free buffer */ + guac_terminal_buffer_free(term->buffer); + } /** @@ -380,6 +387,10 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { /* Set delta */ guac_terminal_delta_set(term->delta, row, col, &guac_char); + + /* Set buffer */ + guac_terminal_buffer_set(term->buffer, row, col, &guac_char); + return 0; } @@ -405,6 +416,12 @@ int guac_terminal_copy(guac_terminal* term, src_row, src_col, cols, rows); + /* Update buffer */ + guac_terminal_buffer_copy(term->buffer, + dst_row, dst_col, + src_row, src_col, + cols, rows); + return 0; } @@ -423,6 +440,9 @@ int guac_terminal_clear(guac_terminal* term, guac_terminal_delta_set_rect(term->delta, row, col, cols, rows, &character); + guac_terminal_buffer_set_rect(term->buffer, + row, col, cols, rows, &character); + return 0; } @@ -981,3 +1001,73 @@ int guac_terminal_redraw_cursor(guac_terminal* term) { 1); } + +guac_terminal_buffer* guac_terminal_buffer_alloc(int width, int height) { + + /* Allocate buffer */ + guac_terminal_buffer* buffer = malloc(sizeof(guac_terminal_buffer)); + + /* Set width and height */ + buffer->width = width; + buffer->height = height; + + /* Alloc characters */ + buffer->characters = malloc(width * height * + sizeof(guac_terminal_char)); + + return buffer; + +} + +void guac_terminal_buffer_resize(guac_terminal_buffer* buffer, + int width, int height) { + /* STUB */ +} + +void guac_terminal_buffer_free(guac_terminal_buffer* buffer) { + + /* Free characters */ + free(buffer->characters); + + /* Free buffer*/ + free(buffer); + +} + +void guac_terminal_buffer_set(guac_terminal_buffer* buffer, int r, int c, + guac_terminal_char* character) { + + /* Store character */ + buffer->characters[r * buffer->width + c] = *character; + +} + +void guac_terminal_buffer_copy(guac_terminal_buffer* buffer, + int dst_row, int dst_column, + int src_row, int src_column, + int w, int h) { + /* STUB */ +} + +void guac_terminal_buffer_set_rect(guac_terminal_buffer* buffer, + int row, int column, int w, int h, + guac_terminal_char* character) { + + guac_terminal_char* current_row = + &(buffer->characters[row*buffer->width + column]); + + /* Set rectangle contents to given character */ + for (row=0; rowwidth; + + } + +} + From c95c51a9c1d4b275638ac38016de6a01731b228b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 29 Mar 2013 02:51:31 -0700 Subject: [PATCH 059/169] Clear with current attributes (not just background color), use real cursor (not layer). --- protocols/ssh/include/terminal.h | 23 +++++------ protocols/ssh/src/ssh_client.c | 11 ----- protocols/ssh/src/ssh_handlers.c | 20 ++++++--- protocols/ssh/src/terminal.c | 58 ++++++++++++--------------- protocols/ssh/src/terminal_handlers.c | 24 ++++------- 5 files changed, 56 insertions(+), 80 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index e7542383..32bd7596 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -323,11 +323,6 @@ struct guac_terminal { */ int cursor_col; - /** - * Simple cursor layer until scrollback, etc. is implemented. - */ - guac_layer* cursor_layer; - /** * The attributes which will be applied to future characters. */ @@ -392,18 +387,18 @@ int guac_terminal_copy(guac_terminal* term, /** * Clears a rectangular region of characters, replacing them with the - * given background color. + * current background color and attributes. */ int guac_terminal_clear(guac_terminal* term, - int row, int col, int rows, int cols, int background_color); + int row, int col, int rows, int cols); /** * Clears the given region from right-to-left, top-to-bottom, replacing - * all characters with the given background color. + * all characters with the current background color and attributes. */ int guac_terminal_clear_range(guac_terminal* term, int start_row, int start_col, - int end_row, int end_col, int background_color); + int end_row, int end_col); /** * Scrolls the terminal's current scroll region up by one row. @@ -417,6 +412,11 @@ int guac_terminal_scroll_up(guac_terminal* term, int guac_terminal_scroll_down(guac_terminal* term, int start_row, int end_row, int amount); +/** + * Toggles the reverse attribute of the character at the given location. + */ +int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col); + /** * Allocates a new guac_terminal_delta. */ @@ -462,11 +462,6 @@ void guac_terminal_delta_set_rect(guac_terminal_delta* delta, void guac_terminal_delta_flush(guac_terminal_delta* delta, guac_terminal* terminal); -/** - * Update the cursor position and contents. - */ -int guac_terminal_redraw_cursor(guac_terminal* term); - /** * Allocates a new character buffer having the given dimensions. */ diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 2da7d481..b6d2b998 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -122,17 +122,6 @@ int guac_client_init(guac_client* client, int argc, char** argv) { term->char_width * term->term_width, term->char_height * term->term_height); - /* Cursor layer need only be one char */ - guac_protocol_send_size(socket, term->cursor_layer, term->char_width, term->char_height); - - /* Draw cursor */ - guac_protocol_send_rect(socket, term->cursor_layer, - 0, 0, term->char_width, term->char_height); - - guac_protocol_send_cfill(socket, - GUAC_COMP_OVER, term->cursor_layer, - 0x40, 0xFF, 0x80, 0x80); - guac_socket_flush(socket); /* Open SSH session */ diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 6e045cc3..3d06d256 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -85,6 +85,11 @@ int ssh_guac_client_handle_messages(guac_client* client) { int bytes_read = 0; + /* Clear cursor */ + guac_terminal_toggle_reverse(client_data->term, + client_data->term->cursor_row, + client_data->term->cursor_col); + /* While data available, write to terminal */ while (channel_is_open(client_data->term_channel) && !channel_is_eof(client_data->term_channel) @@ -102,14 +107,17 @@ int ssh_guac_client_handle_messages(guac_client* client) { guac_socket_flush(socket); return 1; } + + /* Draw cursor */ + guac_terminal_toggle_reverse(client_data->term, + client_data->term->cursor_row, + client_data->term->cursor_col); + + /* Flush terminal delta */ + guac_terminal_delta_flush(client_data->term->delta, client_data->term); + } - /* Flush terminal delta */ - guac_terminal_delta_flush(client_data->term->delta, client_data->term); - - /* Update cursor */ - guac_terminal_redraw_cursor(client_data->term); - return 0; } diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index bbcd683d..15982453 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -134,7 +134,6 @@ guac_terminal* guac_terminal_create(guac_client* client, term->cursor_row = 0; term->cursor_col = 0; - term->cursor_layer = guac_client_alloc_layer(client); term->term_width = width / term->char_width; term->term_height = height / term->char_height; @@ -174,8 +173,7 @@ guac_terminal* guac_terminal_create(guac_client* client, /* Clear with background color */ guac_terminal_clear(term, - 0, 0, term->term_height, term->term_width, - term->current_attributes.background); + 0, 0, term->term_height, term->term_width); return term; @@ -395,6 +393,22 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { } +int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col) { + + /* Get character from buffer */ + guac_terminal_char* guac_char = + &(term->buffer->characters[row*term->buffer->width + col]); + + /* Toggle reverse */ + guac_char->attributes.reverse = !(guac_char->attributes.reverse); + + /* Set delta */ + guac_terminal_delta_set(term->delta, row, col, guac_char); + + return 0; + +} + int guac_terminal_write(guac_terminal* term, const char* c, int size) { while (size > 0) { @@ -428,13 +442,12 @@ int guac_terminal_copy(guac_terminal* term, int guac_terminal_clear(guac_terminal* term, - int row, int col, int rows, int cols, int background_color) { + int row, int col, int rows, int cols) { /* Build space */ guac_terminal_char character; character.value = ' '; - character.attributes.reverse = false; - character.attributes.background = background_color; + character.attributes = term->current_attributes; /* Fill with color */ guac_terminal_delta_set_rect(term->delta, @@ -463,8 +476,7 @@ int guac_terminal_scroll_up(guac_terminal* term, /* Fill new rows with background */ || guac_terminal_clear(term, - end_row - amount + 1, 0, amount, term->term_width, - term->current_attributes.background); + end_row - amount + 1, 0, amount, term->term_width); } @@ -484,22 +496,20 @@ int guac_terminal_scroll_down(guac_terminal* term, /* Fill new rows with background */ || guac_terminal_clear(term, - start_row, 0, amount, term->term_width, - term->current_attributes.background); + start_row, 0, amount, term->term_width); } int guac_terminal_clear_range(guac_terminal* term, int start_row, int start_col, - int end_row, int end_col, int background_color) { + int end_row, int end_col) { /* If not at far left, must clear sub-region to far right */ if (start_col > 0) { /* Clear from start_col to far right */ if (guac_terminal_clear(term, - start_row, start_col, 1, term->term_width - start_col, - background_color)) + start_row, start_col, 1, term->term_width - start_col)) return 1; /* One less row to clear */ @@ -511,8 +521,7 @@ int guac_terminal_clear_range(guac_terminal* term, /* Clear from far left to end_col */ if (guac_terminal_clear(term, - end_row, 0, 1, end_col + 1, - background_color)) + end_row, 0, 1, end_col + 1)) return 1; /* One less row to clear */ @@ -524,8 +533,7 @@ int guac_terminal_clear_range(guac_terminal* term, if (start_row <= end_row) { if (guac_terminal_clear(term, - start_row, 0, end_row - start_row + 1, term->term_width, - background_color)) + start_row, 0, end_row - start_row + 1, term->term_width)) return 1; } @@ -986,22 +994,6 @@ void guac_terminal_delta_flush(guac_terminal_delta* delta, } -int guac_terminal_redraw_cursor(guac_terminal* term) { - - guac_socket* socket = term->client->socket; - - /* Erase old cursor */ - return - guac_protocol_send_move(socket, - term->cursor_layer, - - GUAC_DEFAULT_LAYER, - term->char_width * term->cursor_col, - term->char_height * term->cursor_row, - 1); - -} - guac_terminal_buffer* guac_terminal_buffer_alloc(int width, int height) { /* Allocate buffer */ diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 02082068..00aae120 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -348,21 +348,18 @@ int guac_terminal_csi(guac_terminal* term, char c) { if (argv[0] == 0) guac_terminal_clear_range(term, term->cursor_row, term->cursor_col, - term->term_height-1, term->term_width-1, - term->current_attributes.background); + term->term_height-1, term->term_width-1); /* Erase from start to cursor */ else if (argv[0] == 1) guac_terminal_clear_range(term, 0, 0, - term->cursor_row, term->cursor_col, - term->current_attributes.background); + term->cursor_row, term->cursor_col); /* Entire screen */ else if (argv[0] == 2) guac_terminal_clear(term, - 0, 0, term->term_height, term->term_width, - term->current_attributes.background); + 0, 0, term->term_height, term->term_width); break; @@ -373,23 +370,20 @@ int guac_terminal_csi(guac_terminal* term, char c) { if (argv[0] == 0) guac_terminal_clear(term, term->cursor_row, term->cursor_col, - 1, term->term_width - term->cursor_col, - term->current_attributes.background); + 1, term->term_width - term->cursor_col); /* Erase from start to cursor */ else if (argv[0] == 1) guac_terminal_clear(term, term->cursor_row, 0, - 1, term->cursor_col + 1, - term->current_attributes.background); + 1, term->cursor_col + 1); /* Erase line */ else if (argv[0] == 2) guac_terminal_clear(term, term->cursor_row, 0, - 1, term->term_width, - term->current_attributes.background); + 1, term->term_width); break; @@ -432,8 +426,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Clear right */ guac_terminal_clear(term, term->cursor_row, term->term_width - amount, - 1, amount, - term->current_attributes.background); + 1, amount); break; @@ -453,8 +446,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Clear left */ guac_terminal_clear(term, term->cursor_row, term->cursor_col, - 1, amount, - term->current_attributes.background); + 1, amount); break; From 8140a5cc3dfa5becd9780b964dd79baa20e7306d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 29 Mar 2013 03:14:08 -0700 Subject: [PATCH 060/169] Initial I-bar cursor. --- protocols/ssh/Makefile.am | 2 + protocols/ssh/include/ibar.h | 76 +++++++++++++++++++++++ protocols/ssh/src/ibar.c | 109 +++++++++++++++++++++++++++++++++ protocols/ssh/src/ssh_client.c | 4 ++ 4 files changed, 191 insertions(+) create mode 100644 protocols/ssh/include/ibar.h create mode 100644 protocols/ssh/src/ibar.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 310d7b02..7e9d36bb 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -41,12 +41,14 @@ ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libguac-client-ssh.la libguac_client_ssh_la_SOURCES = \ + src/ibar.c \ src/ssh_client.c \ src/ssh_handlers.c \ src/terminal.c \ src/terminal_handlers.c noinst_HEADERS = \ + include/ibar.h \ include/ssh_client.h \ include/ssh_handlers.h \ include/terminal.h \ diff --git a/protocols/ssh/include/ibar.h b/protocols/ssh/include/ibar.h new file mode 100644 index 00000000..e86c06a9 --- /dev/null +++ b/protocols/ssh/include/ibar.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-ssh. + * + * 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_SSH_IBAR_H +#define _GUAC_SSH_IBAR_H + +#include +#include + +/** + * Width of the embedded mouse cursor graphic. + */ +extern const int guac_ssh_ibar_width; + +/** + * Height of the embedded mouse cursor graphic. + */ +extern const int guac_ssh_ibar_height; + +/** + * Number of bytes in each row of the embedded mouse cursor graphic. + */ +extern const int guac_ssh_ibar_stride; + +/** + * The Cairo grapic format of the mouse cursor graphic. + */ +extern const cairo_format_t guac_ssh_ibar_format; + +/** + * Embedded mouse cursor graphic. + */ +extern unsigned char guac_ssh_ibar[]; + +/** + * Set the cursor of the remote display to the embedded cursor graphic. + * + * @param client The guac_client to send the cursor to. + */ +void guac_ssh_set_ibar(guac_client* client); + +#endif diff --git a/protocols/ssh/src/ibar.c b/protocols/ssh/src/ibar.c new file mode 100644 index 00000000..ab5d766c --- /dev/null +++ b/protocols/ssh/src/ibar.c @@ -0,0 +1,109 @@ + +/* ***** 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-ssh. + * + * 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_ssh_ibar_width = 11; +const int guac_ssh_ibar_height = 16; + +/* Format */ +const cairo_format_t guac_ssh_ibar_format = CAIRO_FORMAT_ARGB32; +const int guac_ssh_ibar_stride = 44; + +/* Embedded pointer graphic */ +unsigned char guac_ssh_ibar[] = { + + X,X,X,X,X,_,X,X,X,X,X, + X,O,O,O,O,X,O,O,O,O,X, + X,X,X,X,O,O,O,X,X,X,X, + _,_,_,_,X,O,X,_,_,_,_, + _,_,_,_,X,O,X,_,_,_,_, + _,_,_,_,X,O,X,_,_,_,_, + _,_,_,_,X,O,X,_,_,_,_, + _,_,_,_,X,O,X,_,_,_,_, + _,_,_,_,X,O,X,_,_,_,_, + _,_,_,_,X,O,X,_,_,_,_, + _,_,_,_,X,O,X,_,_,_,_, + _,_,_,_,X,O,X,_,_,_,_, + _,_,_,_,X,O,X,_,_,_,_, + X,X,X,X,O,O,O,X,X,X,X, + X,O,O,O,O,X,O,O,O,O,X, + X,X,X,X,X,_,X,X,X,X,X + +}; + + +void guac_ssh_set_ibar(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_ssh_ibar, + guac_ssh_ibar_format, + guac_ssh_ibar_width, + guac_ssh_ibar_height, + guac_ssh_ibar_stride); + + guac_protocol_send_png(socket, GUAC_COMP_SRC, cursor, 0, 0, graphic); + cairo_surface_destroy(graphic); + + /* Set cursor */ + guac_protocol_send_cursor(socket, + guac_ssh_ibar_width / 2, + guac_ssh_ibar_height / 2, + cursor, + 0, 0, + guac_ssh_ibar_width, + guac_ssh_ibar_height); + + /* Free buffer */ + guac_client_free_buffer(client, cursor); + +} + diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index b6d2b998..461548c6 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -48,6 +48,7 @@ #include "ssh_client.h" #include "ssh_handlers.h" #include "terminal.h" +#include "ibar.h" /* Client plugin arguments */ const char* GUAC_CLIENT_ARGS[] = { @@ -122,6 +123,9 @@ int guac_client_init(guac_client* client, int argc, char** argv) { term->char_width * term->term_width, term->char_height * term->term_height); + /* Send I-bar pointer */ + guac_ssh_set_ibar(client); + guac_socket_flush(socket); /* Open SSH session */ From bd45a8e5f622be4c8f4fe816004ca39ffec79514 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 29 Mar 2013 03:20:35 -0700 Subject: [PATCH 061/169] Hotspot in middle, halve width of I-bar, clean up edges, shade. --- protocols/ssh/src/ibar.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/protocols/ssh/src/ibar.c b/protocols/ssh/src/ibar.c index ab5d766c..ad17820e 100644 --- a/protocols/ssh/src/ibar.c +++ b/protocols/ssh/src/ibar.c @@ -42,36 +42,37 @@ /* Macros for prettying up the embedded image. */ #define X 0x00,0x00,0x00,0xFF +#define U 0x80,0x80,0x80,0xFF #define O 0xFF,0xFF,0xFF,0xFF #define _ 0x00,0x00,0x00,0x00 /* Dimensions */ -const int guac_ssh_ibar_width = 11; +const int guac_ssh_ibar_width = 7; const int guac_ssh_ibar_height = 16; /* Format */ const cairo_format_t guac_ssh_ibar_format = CAIRO_FORMAT_ARGB32; -const int guac_ssh_ibar_stride = 44; +const int guac_ssh_ibar_stride = 28; /* Embedded pointer graphic */ unsigned char guac_ssh_ibar[] = { - X,X,X,X,X,_,X,X,X,X,X, - X,O,O,O,O,X,O,O,O,O,X, - X,X,X,X,O,O,O,X,X,X,X, - _,_,_,_,X,O,X,_,_,_,_, - _,_,_,_,X,O,X,_,_,_,_, - _,_,_,_,X,O,X,_,_,_,_, - _,_,_,_,X,O,X,_,_,_,_, - _,_,_,_,X,O,X,_,_,_,_, - _,_,_,_,X,O,X,_,_,_,_, - _,_,_,_,X,O,X,_,_,_,_, - _,_,_,_,X,O,X,_,_,_,_, - _,_,_,_,X,O,X,_,_,_,_, - _,_,_,_,X,O,X,_,_,_,_, - X,X,X,X,O,O,O,X,X,X,X, - X,O,O,O,O,X,O,O,O,O,X, - X,X,X,X,X,_,X,X,X,X,X + X,X,X,X,X,X,X, + X,O,O,U,O,O,X, + X,X,X,O,X,X,X, + _,_,X,O,X,_,_, + _,_,X,O,X,_,_, + _,_,X,O,X,_,_, + _,_,X,O,X,_,_, + _,_,X,O,X,_,_, + _,_,X,O,X,_,_, + _,_,X,O,X,_,_, + _,_,X,O,X,_,_, + _,_,X,O,X,_,_, + _,_,X,O,X,_,_, + X,X,X,O,X,X,X, + X,O,O,U,O,O,X, + X,X,X,X,X,X,X }; From 320255ade1bf3992d7b91c29b36defcb85b41a3c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 1 Apr 2013 01:10:47 -0700 Subject: [PATCH 062/169] Partially implement copy for buffers, add logging to simulate scrollback push. --- protocols/ssh/src/terminal.c | 56 +++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 15982453..7faddc88 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -466,6 +466,28 @@ int guac_terminal_scroll_up(guac_terminal* term, /* Calculate height of scroll region */ int height = end_row - start_row + 1; + /* If scroll region is entire screen, push rows into scrollback */ + if (start_row == 0 && end_row == term->term_height-1) { + + /* STUB: Test, for sake of logging */ + char test_str[1024]; + int column; + + /* Generate test string */ + guac_terminal_char* current = term->buffer->characters; + for (column=0; column < term->buffer->width; column++) { + test_str[column] = current->value; + current++; + } + test_str[column] = 0; + + /* Log string version of row that WOULD have been scrolled into the + * scrollback */ + guac_client_log_info(term->client, + "scroll: %s", test_str); + + } + return /* Move rows within scroll region up by the given amount */ @@ -1038,7 +1060,39 @@ void guac_terminal_buffer_copy(guac_terminal_buffer* buffer, int dst_row, int dst_column, int src_row, int src_column, int w, int h) { - /* STUB */ + + int row, column; + + /* FIXME: Handle intersections between src and dst rects */ + + guac_terminal_char* current_row = + &(buffer->characters[dst_row*buffer->width + dst_column]); + + guac_terminal_char* src_current_row = + &(buffer->characters[src_row*buffer->width + src_column]); + + /* Set rectangle to copy operations */ + for (row=0; rowwidth; + src_current_row += buffer->width; + + } + } void guac_terminal_buffer_set_rect(guac_terminal_buffer* buffer, From 06fb3b5a2e3045eff853cf71166bb2515a6423ee Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 1 Apr 2013 01:59:15 -0700 Subject: [PATCH 063/169] Stub out scroll wheel handling. --- protocols/ssh/src/ssh_handlers.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 3d06d256..5c491d47 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -139,23 +139,35 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - /* Mouse just changed from down to up */ - int mouse_up = - (client_data->mouse_mask & GUAC_CLIENT_MOUSE_RIGHT) - && !(mask & GUAC_CLIENT_MOUSE_RIGHT); - + /* Determine which buttons were just released */ + int released_mask = client_data->mouse_mask & ~mask; client_data->mouse_mask = mask; /* Paste contents of clipboard on right mouse button up */ - if(mouse_up && client_data->clipboard_data != NULL) { + if ((released_mask & GUAC_CLIENT_MOUSE_RIGHT) + && client_data->clipboard_data != NULL) { int length = strlen(client_data->clipboard_data); + if (length) + return channel_write(client_data->term_channel, + client_data->clipboard_data, length); - if(length) - return channel_write(client_data->term_channel, client_data->clipboard_data, length); + } + + /* Scroll up if wheel moved up */ + if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP) { + /* STUB */ + guac_client_log_info(client, "stub: scroll up"); + } + + /* Scroll down if wheel moved down */ + if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN) { + /* STUB */ + guac_client_log_info(client, "stub: scroll down"); } return 0; + } int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { From f7143be78bfdc7714c63a215e4764b6c2de5e767 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Apr 2013 01:32:33 -0700 Subject: [PATCH 064/169] Initial scrollback implementation (missing buffer redraw and several necessary graphical ops). --- protocols/ssh/include/terminal.h | 101 ++++++++++- protocols/ssh/src/ssh_handlers.c | 7 +- protocols/ssh/src/terminal.c | 284 ++++++++++++++++++++++++++----- 3 files changed, 338 insertions(+), 54 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 32bd7596..13a54165 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -207,6 +207,63 @@ typedef struct guac_terminal_delta { } guac_terminal_delta; +/** + * A single variable-length row of terminal data. + */ +typedef struct guac_terminal_scrollback_row { + + /** + * Array of guac_terminal_char representing the contents of the row. + */ + guac_terminal_char* characters; + + /** + * The length of this row in characters. This is the number of initialized + * characters in the buffer, usually equal to the number of characters + * in the screen width at the time this row was created. + */ + int length; + + /** + * The number of elements in the characters array. After the length + * equals this value, the array must be resized. + */ + int available; + +} guac_terminal_scrollback_row; + +/** + * A scrollback buffer containing a constant number of arbitrary-length rows. + * New rows can be appended to the buffer, with the oldest row replaced with + * the new row. + */ +typedef struct guac_terminal_scrollback_buffer { + + /** + * Array of scrollback buffer rows. This array functions as a ring buffer. + * When a new row needs to be appended, the top reference is moved down + * and the old top row is replaced. + */ + guac_terminal_scrollback_row* scrollback; + + /** + * The number of rows in the scrollback buffer. This is the total capacity + * of the buffer. + */ + int rows; + + /** + * The row to replace when adding a new row to the scrollback. + */ + int top; + + /** + * The number of rows currently stored in the scrollback buffer. + */ + int length; + +} guac_terminal_scrollback_buffer; + /** * Dynamically-resizable character buffer. */ @@ -278,10 +335,16 @@ struct guac_terminal { guac_layer* filled_glyphs; /** - * Array of scrollback buffer rows, where each row is an array of - * characters. + * The scrollback buffer. */ - guac_terminal_char** scrollback; + guac_terminal_scrollback_buffer* scrollback; + + /** + * The relative offset of the display. A positive value indicates that + * many rows have been scrolled into view, zero indicates that no + * scrolling has occurred. Negative values are illegal. + */ + int scroll_offset; /** * The width of each character, in pixels. @@ -501,5 +564,37 @@ void guac_terminal_buffer_set_rect(guac_terminal_buffer* buffer, */ void guac_terminal_buffer_free(guac_terminal_buffer* buffer); +/** + * Allocates a new scrollback buffer having the given number of rows. + */ +guac_terminal_scrollback_buffer* + guac_terminal_scrollback_buffer_alloc(int rows); + +/** + * Frees the given scrollback buffer. + */ +void guac_terminal_scrollback_buffer_free( + guac_terminal_scrollback_buffer* buffer); + +/** + * Pushes the given number of rows into the scrollback, maintaining display + * position within the scrollback as possible. + */ +void guac_terminal_scrollback_buffer_append( + guac_terminal_scrollback_buffer* buffer, + guac_terminal* terminal, int rows); + +/** + * Scroll down the display by one row, replacing the new space with data from + * the scrollback. + */ +void guac_terminal_scroll_display_down(guac_terminal* terminal); + +/** + * Scroll up the display by one row, replacing the new space with data from + * either the scrollback or the terminal buffer. + */ +void guac_terminal_scroll_display_up(guac_terminal* terminal); + #endif diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 5c491d47..89e6922e 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -138,6 +138,7 @@ int ssh_guac_client_clipboard_handler(guac_client* client, char* data) { int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + guac_terminal* term = client_data->term; /* Determine which buttons were just released */ int released_mask = client_data->mouse_mask & ~mask; @@ -156,14 +157,12 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { /* Scroll up if wheel moved up */ if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP) { - /* STUB */ - guac_client_log_info(client, "stub: scroll up"); + guac_terminal_scroll_display_up(term); } /* Scroll down if wheel moved down */ if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN) { - /* STUB */ - guac_client_log_info(client, "stub: scroll down"); + guac_terminal_scroll_display_down(term); } return 0; diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 7faddc88..dc32bbd0 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -84,8 +84,6 @@ guac_terminal* guac_terminal_create(guac_client* client, .underscore = false }; - int row, col; - PangoFontMap* font_map; PangoFont* font; PangoFontMetrics* metrics; @@ -142,26 +140,9 @@ guac_terminal* guac_terminal_create(guac_client* client, term->scroll_start = 0; term->scroll_end = term->term_height - 1; - /* Create scrollback buffer */ - term->scrollback = malloc(term->term_height * sizeof(guac_terminal_char*)); - - /* Init buffer */ - for (row = 0; row < term->term_height; row++) { - - /* Create row */ - guac_terminal_char* current_row = - term->scrollback[row] = malloc(term->term_width * sizeof(guac_terminal_char)); - - /* Init row */ - for (col = 0; col < term->term_width; col++) { - - /* Empty character, default colors */ - current_row[col].value = '\0'; - current_row[col].attributes = term->default_attributes; - - } - - } + /* Init scrollback buffer */ + term->scrollback = guac_terminal_scrollback_buffer_alloc(1000); + term->scroll_offset = 0; /* Init delta */ term->delta = guac_terminal_delta_alloc(term->term_width, @@ -182,10 +163,7 @@ guac_terminal* guac_terminal_create(guac_client* client, void guac_terminal_free(guac_terminal* term) { /* Free scrollback buffer */ - for (int row = 0; row < term->term_height; row++) - free(term->scrollback[row]); - - free(term->scrollback); + guac_terminal_scrollback_buffer_free(term->scrollback); /* Free delta */ guac_terminal_delta_free(term->delta); @@ -467,26 +445,8 @@ int guac_terminal_scroll_up(guac_terminal* term, int height = end_row - start_row + 1; /* If scroll region is entire screen, push rows into scrollback */ - if (start_row == 0 && end_row == term->term_height-1) { - - /* STUB: Test, for sake of logging */ - char test_str[1024]; - int column; - - /* Generate test string */ - guac_terminal_char* current = term->buffer->characters; - for (column=0; column < term->buffer->width; column++) { - test_str[column] = current->value; - current++; - } - test_str[column] = 0; - - /* Log string version of row that WOULD have been scrolled into the - * scrollback */ - guac_client_log_info(term->client, - "scroll: %s", test_str); - - } + if (start_row == 0 && end_row == term->term_height-1) + guac_terminal_scrollback_buffer_append(term->scrollback, term, amount); return @@ -630,11 +590,16 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, /* FIXME: Handle intersections between src and dst rects */ + guac_terminal_operation* src_copy = malloc( + sizeof(guac_terminal_operation) * delta->width * delta->height); + memcpy(src_copy, delta->operations, + sizeof(guac_terminal_operation) * delta->width * delta->height); + guac_terminal_operation* current_row = &(delta->operations[dst_row*delta->width + dst_column]); guac_terminal_operation* src_current_row = - &(delta->operations[src_row*delta->width + src_column]); + &(src_copy[src_row*delta->width + src_column]); /* Set rectangle to copy operations */ for (row=0; rowrows = rows; + buffer->top = 0; + buffer->length = 0; + buffer->scrollback = malloc(sizeof(guac_terminal_scrollback_row) * + buffer->rows); + + /* Init scrollback rows */ + row = buffer->scrollback; + for (i=0; iavailable = 256; + row->length = 0; + row->characters = malloc(sizeof(guac_terminal_char) * row->available); + + /* Next row */ + row++; + + } + + return buffer; + +} + +void guac_terminal_scrollback_buffer_free( + guac_terminal_scrollback_buffer* buffer) { + + int i; + guac_terminal_scrollback_row* row = buffer->scrollback; + + /* Free all rows */ + for (i=0; irows; i++) { + free(row->characters); + row++; + } + + /* Free actual buffer */ + free(buffer->scrollback); + free(buffer); + +} + +void guac_terminal_scrollback_buffer_append( + guac_terminal_scrollback_buffer* buffer, + guac_terminal* terminal, int rows) { + + int row, column; + + /* Copy data into scrollback */ + guac_terminal_scrollback_row* scrollback_row = + &(buffer->scrollback[buffer->top]); + guac_terminal_char* current = terminal->buffer->characters; + + for (row=0; rowcharacters; + for (column=0; column < terminal->buffer->width; column++) + *(dest++) = *(current++); + + scrollback_row->length = terminal->buffer->width; + + /* Next scrollback row */ + scrollback_row++; + buffer->top++; + + /* Wrap around when bottom reached */ + if (buffer->top == buffer->rows) { + buffer->top = 0; + scrollback_row = buffer->scrollback; + } + + } /* end for each row */ + + /* Increment row count */ + buffer->length += rows; + if (buffer->length > buffer->rows) + buffer->length = buffer->rows; + + /* Log string version of row that WOULD have been scrolled into the + * scrollback */ + guac_client_log_info(terminal->client, + "scrollback->top=%i (length=%i/%i)", buffer->top, buffer->length, buffer->rows); + +} + +void guac_terminal_scroll_display_down(guac_terminal* terminal) { + + int scroll_amount = 3; + + int row, column; + + int scrollback_row_index; + guac_terminal_scrollback_row* scrollback_row; + + /* Limit scroll amount by size of scrollback buffer */ + if (scroll_amount > terminal->scroll_offset) + scroll_amount = terminal->scroll_offset; + + /* If not scrolling at all, don't bother trying */ + if (scroll_amount == 0) + return; + + /* Shift screen up */ + if (terminal->term_height > scroll_amount) + guac_terminal_delta_copy(terminal->delta, + 0, 0, /* Destination row, col */ + scroll_amount, 0, /* source row,col */ + terminal->term_width, terminal->term_height - scroll_amount); + + /* Advance by scroll amount */ + terminal->scroll_offset -= scroll_amount; + guac_client_log_info(terminal->client, "scrolling to %i", terminal->scroll_offset); + + /* Get corresponding scrollback row */ + scrollback_row_index = terminal->scrollback->top - terminal->scroll_offset + terminal->term_height - 1; + scrollback_row = &(terminal->scrollback->scrollback[scrollback_row_index]); + + /* Draw new rows from scrollback */ + for (row=terminal->term_height - scroll_amount; row < terminal->term_height; row++) { + + /* FIXME: Clear row first */ + + /* Draw row */ + guac_terminal_char* current = scrollback_row->characters; + for (column=0; columnlength; column++) + guac_terminal_delta_set(terminal->delta, row, column, current++); + + /* Next row */ + scrollback_row_index++; + + /* Wrap if at end of scrollback */ + if (scrollback_row_index == terminal->scrollback->rows) { + scrollback_row_index = 0; + scrollback_row = terminal->scrollback->scrollback; + } + + /* Otherwise, just advance to next row */ + else + scrollback_row++; + + } + + /* FIXME: Should flush somewhere more sensible */ + guac_terminal_delta_flush(terminal->delta, terminal); + guac_socket_flush(terminal->client->socket); + +} + +void guac_terminal_scroll_display_up(guac_terminal* terminal) { + + int scroll_amount = 3; + + int row, column; + + int scrollback_row_index; + guac_terminal_scrollback_row* scrollback_row; + + /* Limit scroll amount by size of scrollback buffer */ + if (terminal->scroll_offset + scroll_amount > terminal->scrollback->length) + scroll_amount = terminal->scrollback->length - terminal->scroll_offset; + + /* If not scrolling at all, don't bother trying */ + if (scroll_amount == 0) + return; + + /* Shift screen down */ + if (terminal->term_height > scroll_amount) + guac_terminal_delta_copy(terminal->delta, + scroll_amount, 0, /* Destination row,col */ + 0, 0, /* Source row, col */ + terminal->term_width, terminal->term_height - scroll_amount); + + /* Advance by scroll amount */ + terminal->scroll_offset += scroll_amount; + guac_client_log_info(terminal->client, "scrolling to %i", terminal->scroll_offset); + + /* Get corresponding scrollback row */ + scrollback_row_index = terminal->scrollback->top - terminal->scroll_offset; + scrollback_row = &(terminal->scrollback->scrollback[scrollback_row_index]); + + /* Draw new rows from scrollback */ + for (row=0; row < scroll_amount; row++) { + + /* FIXME: Clear row first */ + + /* Draw row */ + guac_terminal_char* current = scrollback_row->characters; + for (column=0; columnlength; column++) + guac_terminal_delta_set(terminal->delta, row, column, current++); + + /* Next row */ + scrollback_row_index++; + + /* Wrap if at end of scrollback */ + if (scrollback_row_index == terminal->scrollback->rows) { + scrollback_row_index = 0; + scrollback_row = terminal->scrollback->scrollback; + } + + /* Otherwise, just advance to next row */ + else + scrollback_row++; + + } + + /* FIXME: Should flush somewhere more sensible */ + guac_terminal_delta_flush(terminal->delta, terminal); + guac_socket_flush(terminal->client->socket); + +} + From 84d45097d0d3f1fd4e2a410c8038cdfefce806a8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Apr 2013 11:31:46 -0700 Subject: [PATCH 065/169] Simplify scrolling logic. --- protocols/ssh/include/terminal.h | 8 +++ protocols/ssh/src/terminal.c | 89 +++++++++++++++++--------------- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 13a54165..2b831f40 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -584,6 +584,14 @@ void guac_terminal_scrollback_buffer_append( guac_terminal_scrollback_buffer* buffer, guac_terminal* terminal, int rows); +/** + * Returns the row within the scrollback at the given location. The index + * of the row given is a negative number, denoting the number of rows into + * the past to look. + */ +guac_terminal_scrollback_row* guac_terminal_scrollback_buffer_get_row( + guac_terminal_scrollback_buffer* buffer, int row); + /** * Scroll down the display by one row, replacing the new space with data from * the scrollback. diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index dc32bbd0..392f7acb 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -1185,11 +1185,10 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal) { int scroll_amount = 3; + int start_row, end_row; + int dest_row; int row, column; - int scrollback_row_index; - guac_terminal_scrollback_row* scrollback_row; - /* Limit scroll amount by size of scrollback buffer */ if (scroll_amount > terminal->scroll_offset) scroll_amount = terminal->scroll_offset; @@ -1207,34 +1206,32 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal) { /* Advance by scroll amount */ terminal->scroll_offset -= scroll_amount; - guac_client_log_info(terminal->client, "scrolling to %i", terminal->scroll_offset); - /* Get corresponding scrollback row */ - scrollback_row_index = terminal->scrollback->top - terminal->scroll_offset + terminal->term_height - 1; - scrollback_row = &(terminal->scrollback->scrollback[scrollback_row_index]); + /* Get row range */ + end_row = terminal->term_height - terminal->scroll_offset - 1; + start_row = end_row - scroll_amount + 1; + dest_row = terminal->term_height - scroll_amount; + + guac_client_log_info(terminal->client, + "Scrolling rows %i through %i into view (scroll down)", + start_row, end_row); /* Draw new rows from scrollback */ - for (row=terminal->term_height - scroll_amount; row < terminal->term_height; row++) { + for (row=start_row; row<=end_row; row++) { - /* FIXME: Clear row first */ + /* Get row from scrollback */ + guac_terminal_scrollback_row* scrollback_row = + guac_terminal_scrollback_buffer_get_row(terminal->scrollback, row); /* Draw row */ + /* FIXME: Clear row first */ guac_terminal_char* current = scrollback_row->characters; for (column=0; columnlength; column++) - guac_terminal_delta_set(terminal->delta, row, column, current++); + guac_terminal_delta_set(terminal->delta, dest_row, column, + current++); /* Next row */ - scrollback_row_index++; - - /* Wrap if at end of scrollback */ - if (scrollback_row_index == terminal->scrollback->rows) { - scrollback_row_index = 0; - scrollback_row = terminal->scrollback->scrollback; - } - - /* Otherwise, just advance to next row */ - else - scrollback_row++; + dest_row++; } @@ -1248,10 +1245,10 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal) { int scroll_amount = 3; + int start_row, end_row; + int dest_row; int row, column; - int scrollback_row_index; - guac_terminal_scrollback_row* scrollback_row; /* Limit scroll amount by size of scrollback buffer */ if (terminal->scroll_offset + scroll_amount > terminal->scrollback->length) @@ -1270,34 +1267,32 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal) { /* Advance by scroll amount */ terminal->scroll_offset += scroll_amount; - guac_client_log_info(terminal->client, "scrolling to %i", terminal->scroll_offset); - /* Get corresponding scrollback row */ - scrollback_row_index = terminal->scrollback->top - terminal->scroll_offset; - scrollback_row = &(terminal->scrollback->scrollback[scrollback_row_index]); + /* Get row range */ + start_row = -terminal->scroll_offset; + end_row = start_row + scroll_amount - 1; + dest_row = 0; + + guac_client_log_info(terminal->client, + "Scrolling rows %i through %i into view (scroll up)", + start_row, end_row); /* Draw new rows from scrollback */ - for (row=0; row < scroll_amount; row++) { + for (row=start_row; row<=end_row; row++) { - /* FIXME: Clear row first */ + /* Get row from scrollback */ + guac_terminal_scrollback_row* scrollback_row = + guac_terminal_scrollback_buffer_get_row(terminal->scrollback, row); /* Draw row */ + /* FIXME: Clear row first */ guac_terminal_char* current = scrollback_row->characters; for (column=0; columnlength; column++) - guac_terminal_delta_set(terminal->delta, row, column, current++); + guac_terminal_delta_set(terminal->delta, dest_row, column, + current++); /* Next row */ - scrollback_row_index++; - - /* Wrap if at end of scrollback */ - if (scrollback_row_index == terminal->scrollback->rows) { - scrollback_row_index = 0; - scrollback_row = terminal->scrollback->scrollback; - } - - /* Otherwise, just advance to next row */ - else - scrollback_row++; + dest_row++; } @@ -1307,3 +1302,15 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal) { } +guac_terminal_scrollback_row* guac_terminal_scrollback_buffer_get_row( + guac_terminal_scrollback_buffer* buffer, int row) { + + /* Calculate scrollback row index */ + int index = buffer->top + row; + if (index < 0) index += buffer->rows; + + /* Return found row */ + return &(buffer->scrollback[index]); + +} + From 77b427a2f0312008faf2fa9541c388f610a15117 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Apr 2013 12:54:59 -0700 Subject: [PATCH 066/169] Pull from buffer if row non-negative. --- protocols/ssh/src/terminal.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 392f7acb..7af9b8a2 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -1219,16 +1219,34 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal) { /* Draw new rows from scrollback */ for (row=start_row; row<=end_row; row++) { - /* Get row from scrollback */ - guac_terminal_scrollback_row* scrollback_row = - guac_terminal_scrollback_buffer_get_row(terminal->scrollback, row); + /* If row in past, pull from scrollback */ + if (row < 0) { - /* Draw row */ - /* FIXME: Clear row first */ - guac_terminal_char* current = scrollback_row->characters; - for (column=0; columnlength; column++) - guac_terminal_delta_set(terminal->delta, dest_row, column, - current++); + /* Get row from scrollback */ + guac_terminal_scrollback_row* scrollback_row = + guac_terminal_scrollback_buffer_get_row(terminal->scrollback, + row); + + /* Draw row */ + /* FIXME: Clear row first */ + guac_terminal_char* current = scrollback_row->characters; + for (column=0; columnlength; column++) + guac_terminal_delta_set(terminal->delta, dest_row, column, + current++); + + } + + /* Otherwise, pull from buffer */ + else { + + guac_terminal_char* current = &(terminal->buffer->characters[ + terminal->buffer->width * row]); + + for (column=0; columnbuffer->width; column++) + guac_terminal_delta_set(terminal->delta, dest_row, column, + current++); + + } /* Next row */ dest_row++; From b8ec48d17945815a5c2b622f5be5a7a703ed1aae Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Apr 2013 13:12:18 -0700 Subject: [PATCH 067/169] Offset by scroll amount. --- protocols/ssh/src/terminal.c | 40 +++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 7af9b8a2..656c05bf 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -356,13 +356,16 @@ int __guac_terminal_set(guac_terminal* term, int row, int col, char c) { int guac_terminal_set(guac_terminal* term, int row, int col, char c) { + int scrolled_row = row + term->scroll_offset; + /* Build character with current attributes */ guac_terminal_char guac_char; guac_char.value = c; guac_char.attributes = term->current_attributes; /* Set delta */ - guac_terminal_delta_set(term->delta, row, col, &guac_char); + if (scrolled_row < term->delta->height) + guac_terminal_delta_set(term->delta, scrolled_row, col, &guac_char); /* Set buffer */ guac_terminal_buffer_set(term->buffer, row, col, &guac_char); @@ -373,6 +376,8 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col) { + int scrolled_row = row + term->scroll_offset; + /* Get character from buffer */ guac_terminal_char* guac_char = &(term->buffer->characters[row*term->buffer->width + col]); @@ -381,7 +386,8 @@ int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col) { guac_char->attributes.reverse = !(guac_char->attributes.reverse); /* Set delta */ - guac_terminal_delta_set(term->delta, row, col, guac_char); + if (scrolled_row < term->delta->height) + guac_terminal_delta_set(term->delta, scrolled_row, col, guac_char); return 0; @@ -402,10 +408,27 @@ int guac_terminal_copy(guac_terminal* term, int src_row, int src_col, int rows, int cols, int dst_row, int dst_col) { + int scrolled_src_row = src_row + term->scroll_offset; + int scrolled_dst_row = dst_row + term->scroll_offset; + + int scrolled_rows = rows; + + /* Adjust delta rect height if scrolled out of view */ + if (scrolled_src_row + scrolled_rows > term->delta->height) + scrolled_rows = term->delta->height - scrolled_src_row; + + if (scrolled_dst_row + scrolled_rows > term->delta->height) + scrolled_rows = term->delta->height - scrolled_dst_row; + + /* FIXME: If source (but not dest) is partially scrolled out of view, then + * the delta will not be updated properly. We need to pull the data + * from the buffer in such a case. + */ + /* Update delta */ guac_terminal_delta_copy(term->delta, - dst_row, dst_col, - src_row, src_col, + scrolled_dst_row, dst_col, + scrolled_src_row, src_col, cols, rows); /* Update buffer */ @@ -422,6 +445,13 @@ int guac_terminal_copy(guac_terminal* term, int guac_terminal_clear(guac_terminal* term, int row, int col, int rows, int cols) { + int scrolled_row = row + term->scroll_offset; + int scrolled_rows = rows; + + /* Adjust delta rect height if scrolled out of view */ + if (scrolled_row + scrolled_rows > term->delta->height) + scrolled_rows = term->delta->height - scrolled_row; + /* Build space */ guac_terminal_char character; character.value = ' '; @@ -429,7 +459,7 @@ int guac_terminal_clear(guac_terminal* term, /* Fill with color */ guac_terminal_delta_set_rect(term->delta, - row, col, cols, rows, &character); + scrolled_row, col, cols, scrolled_rows, &character); guac_terminal_buffer_set_rect(term->buffer, row, col, cols, rows, &character); From 43f42cbb4cf68c01075ec4df72211e3285cb2992 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 5 Apr 2013 13:15:19 -0700 Subject: [PATCH 068/169] Use pre-allocated scratch area, rather than constantly-reallocated scratch area. --- protocols/ssh/include/terminal.h | 6 ++++++ protocols/ssh/src/terminal.c | 12 +++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 2b831f40..1f07ee49 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -195,6 +195,12 @@ typedef struct guac_terminal_delta { */ guac_terminal_operation* operations; + /** + * Scratch area of same size as the operations buffer, facilitating copies + * of overlapping regions. + */ + guac_terminal_operation* scratch; + /** * The width of the screen, in characters. */ diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 656c05bf..40d72cfa 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -580,14 +580,18 @@ guac_terminal_delta* guac_terminal_delta_alloc(int width, int height) { } + /* Alloc scratch area */ + delta->scratch = malloc(width * height * sizeof(guac_terminal_operation)); + return delta; } void guac_terminal_delta_free(guac_terminal_delta* delta) { - /* Free operations buffer */ + /* Free operations buffers */ free(delta->operations); + free(delta->scratch); /* Free delta */ free(delta); @@ -620,16 +624,14 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, /* FIXME: Handle intersections between src and dst rects */ - guac_terminal_operation* src_copy = malloc( - sizeof(guac_terminal_operation) * delta->width * delta->height); - memcpy(src_copy, delta->operations, + memcpy(delta->scratch, delta->operations, sizeof(guac_terminal_operation) * delta->width * delta->height); guac_terminal_operation* current_row = &(delta->operations[dst_row*delta->width + dst_column]); guac_terminal_operation* src_current_row = - &(src_copy[src_row*delta->width + src_column]); + &(delta->scratch[src_row*delta->width + src_column]); /* Set rectangle to copy operations */ for (row=0; row Date: Sun, 7 Apr 2013 16:55:06 -0700 Subject: [PATCH 069/169] Exclude simultaneous access to the terminal structure by the input and output threads. --- protocols/ssh/include/terminal.h | 7 +++++++ protocols/ssh/src/ssh_handlers.c | 10 ++++++++++ protocols/ssh/src/terminal.c | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 1f07ee49..63e536ea 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -39,6 +39,7 @@ #define _SSH_GUAC_TERMINAL_H #include +#include #include @@ -303,6 +304,12 @@ struct guac_terminal { */ guac_client* client; + /** + * Lock which restricts simultaneous access to this terminal via the root + * guac_terminal_* functions. + */ + pthread_mutex_t lock; + /** * The description of the font to use for rendering. */ diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 89e6922e..11fd9e47 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -85,6 +85,9 @@ int ssh_guac_client_handle_messages(guac_client* client) { int bytes_read = 0; + /* Lock terminal access */ + pthread_mutex_lock(&(client_data->term->lock)); + /* Clear cursor */ guac_terminal_toggle_reverse(client_data->term, client_data->term->cursor_row, @@ -116,6 +119,9 @@ int ssh_guac_client_handle_messages(guac_client* client) { /* Flush terminal delta */ guac_terminal_delta_flush(client_data->term->delta, client_data->term); + /* Unlock terminal access */ + pthread_mutex_unlock(&(client_data->term->lock)); + } return 0; @@ -157,12 +163,16 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { /* Scroll up if wheel moved up */ if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP) { + pthread_mutex_lock(&(term->lock)); guac_terminal_scroll_display_up(term); + pthread_mutex_unlock(&(term->lock)); } /* Scroll down if wheel moved down */ if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN) { + pthread_mutex_lock(&(term->lock)); guac_terminal_scroll_display_down(term); + pthread_mutex_unlock(&(term->lock)); } return 0; diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 40d72cfa..3a2ad076 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -156,6 +157,9 @@ guac_terminal* guac_terminal_create(guac_client* client, guac_terminal_clear(term, 0, 0, term->term_height, term->term_width); + /* Init terminal lock */ + pthread_mutex_init(&(term->lock), NULL); + return term; } From 7897be9316735eb0e7b73bf225472d9499eba3c6 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 8 Apr 2013 00:14:25 -0700 Subject: [PATCH 070/169] Check write location on rect operations (copy, etc.) --- protocols/ssh/src/terminal.c | 47 +++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 3a2ad076..0da11890 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -417,23 +417,28 @@ int guac_terminal_copy(guac_terminal* term, int scrolled_rows = rows; - /* Adjust delta rect height if scrolled out of view */ - if (scrolled_src_row + scrolled_rows > term->delta->height) - scrolled_rows = term->delta->height - scrolled_src_row; - - if (scrolled_dst_row + scrolled_rows > term->delta->height) - scrolled_rows = term->delta->height - scrolled_dst_row; - /* FIXME: If source (but not dest) is partially scrolled out of view, then * the delta will not be updated properly. We need to pull the data * from the buffer in such a case. */ - /* Update delta */ - guac_terminal_delta_copy(term->delta, - scrolled_dst_row, dst_col, - scrolled_src_row, src_col, - cols, rows); + if (scrolled_src_row < term->delta->height && + scrolled_dst_row < term->delta->height) { + + /* Adjust delta rect height if scrolled out of view */ + if (scrolled_src_row + scrolled_rows > term->delta->height) + scrolled_rows = term->delta->height - scrolled_src_row; + + if (scrolled_dst_row + scrolled_rows > term->delta->height) + scrolled_rows = term->delta->height - scrolled_dst_row; + + /* Update delta */ + guac_terminal_delta_copy(term->delta, + scrolled_dst_row, dst_col, + scrolled_src_row, src_col, + cols, rows); + + } /* Update buffer */ guac_terminal_buffer_copy(term->buffer, @@ -452,18 +457,22 @@ int guac_terminal_clear(guac_terminal* term, int scrolled_row = row + term->scroll_offset; int scrolled_rows = rows; - /* Adjust delta rect height if scrolled out of view */ - if (scrolled_row + scrolled_rows > term->delta->height) - scrolled_rows = term->delta->height - scrolled_row; - /* Build space */ guac_terminal_char character; character.value = ' '; character.attributes = term->current_attributes; - /* Fill with color */ - guac_terminal_delta_set_rect(term->delta, - scrolled_row, col, cols, scrolled_rows, &character); + if (scrolled_row < term->delta->height) { + + /* Adjust delta rect height if scrolled out of view */ + if (scrolled_row + scrolled_rows > term->delta->height) + scrolled_rows = term->delta->height - scrolled_row; + + /* Fill with color */ + guac_terminal_delta_set_rect(term->delta, + scrolled_row, col, cols, scrolled_rows, &character); + + } guac_terminal_buffer_set_rect(term->buffer, row, col, cols, rows, &character); From c20fe79ace997dc6e8e84219e9481ef323bd4749 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 8 Apr 2013 00:47:08 -0700 Subject: [PATCH 071/169] Remove scroll logging, reset scroll upon typing. --- protocols/ssh/include/terminal.h | 16 ++++++++++------ protocols/ssh/src/ssh_handlers.c | 12 ++++++++++-- protocols/ssh/src/terminal.c | 23 ++++------------------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 63e536ea..ca0fd051 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -45,6 +45,8 @@ #include +#define GUAC_SSH_WHEEL_SCROLL_AMOUNT 3 + typedef struct guac_terminal guac_terminal; /** @@ -606,16 +608,18 @@ guac_terminal_scrollback_row* guac_terminal_scrollback_buffer_get_row( guac_terminal_scrollback_buffer* buffer, int row); /** - * Scroll down the display by one row, replacing the new space with data from - * the scrollback. + * Scroll down the display by the given amount, replacing the new space with + * data from the scrollback. If not enough data is available, the maximum + * amount will be scrolled. */ -void guac_terminal_scroll_display_down(guac_terminal* terminal); +void guac_terminal_scroll_display_down(guac_terminal* terminal, int amount); /** - * Scroll up the display by one row, replacing the new space with data from - * either the scrollback or the terminal buffer. + * Scroll up the display by the given amount, replacing the new space with data + * from either the scrollback or the terminal buffer. If not enough data is + * available, the maximum amount will be scrolled. */ -void guac_terminal_scroll_display_up(guac_terminal* terminal); +void guac_terminal_scroll_display_up(guac_terminal* terminal, int amount); #endif diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 11fd9e47..2edc915e 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -164,14 +164,14 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { /* Scroll up if wheel moved up */ if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP) { pthread_mutex_lock(&(term->lock)); - guac_terminal_scroll_display_up(term); + guac_terminal_scroll_display_up(term, GUAC_SSH_WHEEL_SCROLL_AMOUNT); pthread_mutex_unlock(&(term->lock)); } /* Scroll down if wheel moved down */ if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_DOWN) { pthread_mutex_lock(&(term->lock)); - guac_terminal_scroll_display_down(term); + guac_terminal_scroll_display_down(term, GUAC_SSH_WHEEL_SCROLL_AMOUNT); pthread_mutex_unlock(&(term->lock)); } @@ -182,6 +182,7 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + guac_terminal* term = client_data->term; /* Track modifiers */ if (keysym == 0xFFE3) { @@ -191,6 +192,13 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { /* If key pressed */ else if (pressed) { + /* Reset scroll */ + if (term->scroll_offset != 0) { + pthread_mutex_lock(&(term->lock)); + guac_terminal_scroll_display_down(term, term->scroll_offset); + pthread_mutex_unlock(&(term->lock)); + } + /* If simple ASCII key */ if (keysym >= 0x00 && keysym <= 0xFF) { char data = (char) keysym; diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 0da11890..6bdaea38 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -1219,16 +1219,10 @@ void guac_terminal_scrollback_buffer_append( if (buffer->length > buffer->rows) buffer->length = buffer->rows; - /* Log string version of row that WOULD have been scrolled into the - * scrollback */ - guac_client_log_info(terminal->client, - "scrollback->top=%i (length=%i/%i)", buffer->top, buffer->length, buffer->rows); - } -void guac_terminal_scroll_display_down(guac_terminal* terminal) { - - int scroll_amount = 3; +void guac_terminal_scroll_display_down(guac_terminal* terminal, + int scroll_amount) { int start_row, end_row; int dest_row; @@ -1257,10 +1251,6 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal) { start_row = end_row - scroll_amount + 1; dest_row = terminal->term_height - scroll_amount; - guac_client_log_info(terminal->client, - "Scrolling rows %i through %i into view (scroll down)", - start_row, end_row); - /* Draw new rows from scrollback */ for (row=start_row; row<=end_row; row++) { @@ -1304,9 +1294,8 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal) { } -void guac_terminal_scroll_display_up(guac_terminal* terminal) { - - int scroll_amount = 3; +void guac_terminal_scroll_display_up(guac_terminal* terminal, + int scroll_amount) { int start_row, end_row; int dest_row; @@ -1336,10 +1325,6 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal) { end_row = start_row + scroll_amount - 1; dest_row = 0; - guac_client_log_info(terminal->client, - "Scrolling rows %i through %i into view (scroll up)", - start_row, end_row); - /* Draw new rows from scrollback */ for (row=start_row; row<=end_row; row++) { From f74c4b851d02d4c851a88bf76bf0da33d86b8ed4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 8 Apr 2013 02:14:58 -0700 Subject: [PATCH 072/169] Implement CSI command 'X', clean up a bit. --- protocols/ssh/src/terminal_handlers.c | 28 ++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 00aae120..843e10d7 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -243,9 +243,6 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; - - - /* m: Set graphics rendition */ case 'm': @@ -430,6 +427,19 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; + /* X: Erase characters (no scroll) */ + case 'X': + + amount = argv[0]; + if (amount == 0) amount = 1; + + /* Clear characters */ + guac_terminal_clear(term, + term->cursor_row, term->cursor_col, + 1, amount); + + break; + /* @: Insert characters (scroll right) */ case '@': @@ -452,8 +462,16 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Warn of unhandled codes */ default: - if (c != ';') - guac_client_log_info(term->client, "Unhandled CSI sequence: %c", c); + if (c != ';') { + + guac_client_log_info(term->client, + "Unhandled CSI sequence: %c", c); + + for (i=0; iclient, + " -> argv[%i] = %i", i, argv[i]); + + } } From 21f109fdeb5f1cbf0631ffa405c0d95301d1a467 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 9 Apr 2013 13:54:23 -0700 Subject: [PATCH 073/169] Migrate to cacheable cursors. --- protocols/ssh/Makefile.am | 2 + protocols/ssh/include/cursor.h | 89 ++++++++++++++++++++++++++++++ protocols/ssh/include/ibar.h | 5 +- protocols/ssh/include/ssh_client.h | 11 ++++ protocols/ssh/src/cursor.c | 74 +++++++++++++++++++++++++ protocols/ssh/src/ibar.c | 26 ++++----- protocols/ssh/src/ssh_client.c | 6 +- 7 files changed, 196 insertions(+), 17 deletions(-) create mode 100644 protocols/ssh/include/cursor.h create mode 100644 protocols/ssh/src/cursor.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 7e9d36bb..283e8305 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -42,12 +42,14 @@ lib_LTLIBRARIES = libguac-client-ssh.la libguac_client_ssh_la_SOURCES = \ src/ibar.c \ + src/cursor.c \ src/ssh_client.c \ src/ssh_handlers.c \ src/terminal.c \ src/terminal_handlers.c noinst_HEADERS = \ + include/cursor.h \ include/ibar.h \ include/ssh_client.h \ include/ssh_handlers.h \ diff --git a/protocols/ssh/include/cursor.h b/protocols/ssh/include/cursor.h new file mode 100644 index 00000000..37d09bed --- /dev/null +++ b/protocols/ssh/include/cursor.h @@ -0,0 +1,89 @@ + +/* ***** 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-ssh. + * + * 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_SSH_CURSOR_H +#define _GUAC_SSH_CURSOR_H + +#include + +typedef struct guac_ssh_cursor { + + /** + * A buffer allocated with guac_client_alloc_buffer() that contains the + * cursor image. + */ + guac_layer* buffer; + + /** + * The width of the cursor in pixels. + */ + int width; + + /** + * The height of the cursor in pixels. + */ + int height; + + /** + * The X coordinate of the cursor hotspot. + */ + int hotspot_x; + + /** + * The Y coordinate of the cursor hotspot. + */ + int hotspot_y; + +} guac_ssh_cursor; + +/** + * Allocates a new cursor, pre-populating the cursor with a newly-allocated + * buffer. + */ +guac_ssh_cursor* guac_ssh_cursor_alloc(guac_client* client); + +/** + * Frees the buffer associated with this cursor as well as the cursor itself. + */ +void guac_ssh_cursor_free(guac_client* client, guac_ssh_cursor* cursor); + +/** + * Set the remote cursor. + */ +void guac_ssh_set_cursor(guac_client* client, guac_ssh_cursor* cursor); + +#endif diff --git a/protocols/ssh/include/ibar.h b/protocols/ssh/include/ibar.h index e86c06a9..66e4ff4f 100644 --- a/protocols/ssh/include/ibar.h +++ b/protocols/ssh/include/ibar.h @@ -67,10 +67,11 @@ extern const cairo_format_t guac_ssh_ibar_format; extern unsigned char guac_ssh_ibar[]; /** - * Set the cursor of the remote display to the embedded cursor graphic. + * Creates a new I-bar cursor, returning the corresponding cursor object. * * @param client The guac_client to send the cursor to. + * @return A new cursor which must be free'd via guac_ssh_cursor_free()/ */ -void guac_ssh_set_ibar(guac_client* client); +guac_ssh_cursor* guac_ssh_create_ibar(guac_client* client); #endif diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index 2dd3cebd..9da45237 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -46,6 +46,7 @@ #include "ssh_client.h" #include "ssh_handlers.h" #include "terminal.h" +#include "cursor.h" typedef struct ssh_guac_client_data { @@ -62,6 +63,16 @@ typedef struct ssh_guac_client_data { int mod_ctrl; int mouse_mask; + /** + * The cached I-bar cursor. + */ + guac_ssh_cursor* ibar_cursor; + + /** + * The current cursor, used to avoid re-setting the cursor. + */ + guac_ssh_cursor* current_cursor; + } ssh_guac_client_data; int ssh_guac_client_auth(guac_client* client, const char* password); diff --git a/protocols/ssh/src/cursor.c b/protocols/ssh/src/cursor.c new file mode 100644 index 00000000..ea6ad422 --- /dev/null +++ b/protocols/ssh/src/cursor.c @@ -0,0 +1,74 @@ + +/* ***** 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-ssh. + * + * 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 "cursor.h" + +guac_ssh_cursor* guac_ssh_cursor_alloc(guac_client* client) { + + /* Alloc new cursor, initialize buffer */ + guac_ssh_cursor* cursor = malloc(sizeof(guac_ssh_cursor)); + cursor->buffer = guac_client_alloc_buffer(client); + + return cursor; + +} + +void guac_ssh_cursor_free(guac_client* client, guac_ssh_cursor* cursor) { + + /* Free buffer */ + guac_client_free_buffer(client, cursor->buffer); + + /* Free cursor */ + free(cursor); + +} + +void guac_ssh_set_cursor(guac_client* client, guac_ssh_cursor* cursor) { + + /* Set cursor */ + guac_protocol_send_cursor(client->socket, + cursor->hotspot_x, cursor->hotspot_y, + cursor->buffer, + 0, 0, cursor->width, cursor->height); + +} + diff --git a/protocols/ssh/src/ibar.c b/protocols/ssh/src/ibar.c index ad17820e..70b969c0 100644 --- a/protocols/ssh/src/ibar.c +++ b/protocols/ssh/src/ibar.c @@ -40,6 +40,8 @@ #include #include +#include "cursor.h" + /* Macros for prettying up the embedded image. */ #define X 0x00,0x00,0x00,0xFF #define U 0x80,0x80,0x80,0xFF @@ -77,13 +79,12 @@ unsigned char guac_ssh_ibar[] = { }; -void guac_ssh_set_ibar(guac_client* client) { +guac_ssh_cursor* guac_ssh_create_ibar(guac_client* client) { guac_socket* socket = client->socket; + guac_ssh_cursor* cursor = guac_ssh_cursor_alloc(client); /* Draw to buffer */ - guac_layer* cursor = guac_client_alloc_buffer(client); - cairo_surface_t* graphic = cairo_image_surface_create_for_data( guac_ssh_ibar, guac_ssh_ibar_format, @@ -91,20 +92,17 @@ void guac_ssh_set_ibar(guac_client* client) { guac_ssh_ibar_height, guac_ssh_ibar_stride); - guac_protocol_send_png(socket, GUAC_COMP_SRC, cursor, 0, 0, graphic); + guac_protocol_send_png(socket, GUAC_COMP_SRC, cursor->buffer, + 0, 0, graphic); cairo_surface_destroy(graphic); - /* Set cursor */ - guac_protocol_send_cursor(socket, - guac_ssh_ibar_width / 2, - guac_ssh_ibar_height / 2, - cursor, - 0, 0, - guac_ssh_ibar_width, - guac_ssh_ibar_height); + /* Initialize cursor properties */ + cursor->width = guac_ssh_ibar_width; + cursor->height = guac_ssh_ibar_height; + cursor->hotspot_x = guac_ssh_ibar_width / 2; + cursor->hotspot_y = guac_ssh_ibar_height / 2; - /* Free buffer */ - guac_client_free_buffer(client, cursor); + return cursor; } diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 461548c6..10c567c6 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -116,6 +116,10 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->mod_ctrl = 0; client_data->clipboard_data = NULL; + /* Setup I-bar pointer */ + client_data->current_cursor = + client_data->ibar_cursor = guac_ssh_create_ibar(client); + /* Send name and dimensions */ guac_protocol_send_name(socket, "SSH TEST"); guac_protocol_send_size(socket, @@ -124,7 +128,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { term->char_height * term->term_height); /* Send I-bar pointer */ - guac_ssh_set_ibar(client); + guac_ssh_set_cursor(client, client_data->ibar_cursor); guac_socket_flush(socket); From a855af86085c3a9d9bca923c41d037049dadaa14 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 9 Apr 2013 13:58:55 -0700 Subject: [PATCH 074/169] Free cursors when done. --- protocols/ssh/src/ssh_handlers.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 2edc915e..7cdd9ba2 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -52,6 +52,7 @@ #include "ssh_handlers.h" #include "ssh_client.h" +#include "cursor.h" int ssh_guac_client_handle_messages(guac_client* client) { @@ -253,6 +254,9 @@ int ssh_guac_client_free_handler(guac_client* client) { /* Free clipboard data */ free(guac_client_data->clipboard_data); + /* Free cursors */ + guac_ssh_cursor_free(client, guac_client_data->ibar_cursor); + /* Free generic data struct */ free(client->data); From d3efaeee6e7cc533b826862520c44f1150106c50 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 9 Apr 2013 14:02:52 -0700 Subject: [PATCH 075/169] Add blank cursor. --- protocols/ssh/Makefile.am | 4 +- protocols/ssh/include/blank.h | 52 ++++++++++++++++++++++++ protocols/ssh/include/ssh_client.h | 5 +++ protocols/ssh/src/blank.c | 64 ++++++++++++++++++++++++++++++ protocols/ssh/src/ssh_client.c | 6 ++- protocols/ssh/src/ssh_handlers.c | 1 + 6 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 protocols/ssh/include/blank.h create mode 100644 protocols/ssh/src/blank.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 283e8305..532cf60c 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -41,14 +41,16 @@ ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libguac-client-ssh.la libguac_client_ssh_la_SOURCES = \ - src/ibar.c \ + src/blank.c \ src/cursor.c \ + src/ibar.c \ src/ssh_client.c \ src/ssh_handlers.c \ src/terminal.c \ src/terminal_handlers.c noinst_HEADERS = \ + include/blank.h \ include/cursor.h \ include/ibar.h \ include/ssh_client.h \ diff --git a/protocols/ssh/include/blank.h b/protocols/ssh/include/blank.h new file mode 100644 index 00000000..16995a27 --- /dev/null +++ b/protocols/ssh/include/blank.h @@ -0,0 +1,52 @@ + +/* ***** 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-ssh. + * + * 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_SSH_BLANK_H +#define _GUAC_SSH_BLANK_H + +#include +#include + +/** + * Creates a new blank cursor, returning the corresponding cursor object. + * + * @param client The guac_client to send the cursor to. + * @return A new cursor which must be free'd via guac_ssh_cursor_free()/ + */ +guac_ssh_cursor* guac_ssh_create_blank(guac_client* client); + +#endif diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index 9da45237..3ccbd376 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -68,6 +68,11 @@ typedef struct ssh_guac_client_data { */ guac_ssh_cursor* ibar_cursor; + /** + * The cached invisible (blank) cursor. + */ + guac_ssh_cursor* blank_cursor; + /** * The current cursor, used to avoid re-setting the cursor. */ diff --git a/protocols/ssh/src/blank.c b/protocols/ssh/src/blank.c new file mode 100644 index 00000000..9929e2bf --- /dev/null +++ b/protocols/ssh/src/blank.c @@ -0,0 +1,64 @@ + +/* ***** 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-ssh. + * + * 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 "cursor.h" + +guac_ssh_cursor* guac_ssh_create_blank(guac_client* client) { + + guac_socket* socket = client->socket; + guac_ssh_cursor* cursor = guac_ssh_cursor_alloc(client); + + /* Set buffer to a single 1x1 transparent rectangle */ + guac_protocol_send_rect(socket, cursor->buffer, 0, 0, 1, 1); + guac_protocol_send_cfill(socket, GUAC_COMP_SRC, cursor->buffer, + 0x00, 0x00, 0x00, 0x00); + + /* Initialize cursor properties */ + cursor->width = 1; + cursor->height = 1; + cursor->hotspot_x = 0; + cursor->hotspot_y = 0; + + return cursor; + +} + diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 10c567c6..ca95369d 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -48,6 +48,7 @@ #include "ssh_client.h" #include "ssh_handlers.h" #include "terminal.h" +#include "blank.h" #include "ibar.h" /* Client plugin arguments */ @@ -116,10 +117,13 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->mod_ctrl = 0; client_data->clipboard_data = NULL; - /* Setup I-bar pointer */ + /* Set up I-bar pointer */ client_data->current_cursor = client_data->ibar_cursor = guac_ssh_create_ibar(client); + /* Set up blank pointer */ + client_data->blank_cursor = guac_ssh_create_blank(client); + /* Send name and dimensions */ guac_protocol_send_name(socket, "SSH TEST"); guac_protocol_send_size(socket, diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 7cdd9ba2..86754911 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -256,6 +256,7 @@ int ssh_guac_client_free_handler(guac_client* client) { /* Free cursors */ guac_ssh_cursor_free(client, guac_client_data->ibar_cursor); + guac_ssh_cursor_free(client, guac_client_data->blank_cursor); /* Free generic data struct */ free(client->data); From 2eeb9263c5147d957594c97fde64b5e949c4a711 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 9 Apr 2013 14:09:41 -0700 Subject: [PATCH 076/169] Start with blank pointer, show pointer when moving mouse, hide pointer when typing. --- protocols/ssh/src/ssh_client.c | 8 ++++---- protocols/ssh/src/ssh_handlers.c | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index ca95369d..322d9459 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -118,8 +118,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->clipboard_data = NULL; /* Set up I-bar pointer */ - client_data->current_cursor = - client_data->ibar_cursor = guac_ssh_create_ibar(client); + client_data->ibar_cursor = guac_ssh_create_ibar(client); /* Set up blank pointer */ client_data->blank_cursor = guac_ssh_create_blank(client); @@ -131,8 +130,9 @@ int guac_client_init(guac_client* client, int argc, char** argv) { term->char_width * term->term_width, term->char_height * term->term_height); - /* Send I-bar pointer */ - guac_ssh_set_cursor(client, client_data->ibar_cursor); + /* Initialize pointer */ + client_data->current_cursor = client_data->blank_cursor; + guac_ssh_set_cursor(client, client_data->current_cursor); guac_socket_flush(socket); diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 86754911..55fdb00a 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -151,6 +151,17 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { int released_mask = client_data->mouse_mask & ~mask; client_data->mouse_mask = mask; + /* Show mouse cursor if not already shown */ + if (client_data->current_cursor != client_data->ibar_cursor) { + pthread_mutex_lock(&(term->lock)); + + client_data->current_cursor = client_data->ibar_cursor; + guac_ssh_set_cursor(client, client_data->ibar_cursor); + guac_socket_flush(client->socket); + + pthread_mutex_unlock(&(term->lock)); + } + /* Paste contents of clipboard on right mouse button up */ if ((released_mask & GUAC_CLIENT_MOUSE_RIGHT) && client_data->clipboard_data != NULL) { @@ -185,6 +196,17 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; guac_terminal* term = client_data->term; + /* Hide mouse cursor if not already hidden */ + if (client_data->current_cursor != client_data->blank_cursor) { + pthread_mutex_lock(&(term->lock)); + + client_data->current_cursor = client_data->blank_cursor; + guac_ssh_set_cursor(client, client_data->blank_cursor); + guac_socket_flush(client->socket); + + pthread_mutex_unlock(&(term->lock)); + } + /* Track modifiers */ if (keysym == 0xFFE3) { client_data->mod_ctrl = pressed; From 0dabb97f018163019c60ac5f83447b4c8d44f28f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 15 Apr 2013 01:22:05 -0700 Subject: [PATCH 077/169] Initial selection support - does not yet affect clipboard, nor work properly when scrolled. --- protocols/ssh/include/terminal.h | 51 +++++++ protocols/ssh/src/ssh_handlers.c | 34 ++++- protocols/ssh/src/terminal.c | 224 +++++++++++++++++++++++++++---- 3 files changed, 278 insertions(+), 31 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index ca0fd051..b2b04543 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -94,6 +94,11 @@ typedef struct guac_terminal_attributes { */ bool reverse; + /** + * Whether the associated character is selected. + */ + bool selected; + /** * Whether to render the character with underscore. */ @@ -431,6 +436,31 @@ struct guac_terminal { */ guac_terminal_buffer* buffer; + /** + * Whether text is being selected. + */ + bool text_selected; + + /** + * The row that the selection starts at. + */ + int selection_start_row; + + /** + * The column that the selection starts at. + */ + int selection_start_column; + + /** + * The row that the selection ends at. + */ + int selection_end_row; + + /** + * The column that the selection ends at. + */ + int selection_end_column; + }; /** @@ -621,5 +651,26 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, int amount); */ void guac_terminal_scroll_display_up(guac_terminal* terminal, int amount); +/** + * Marks the start of text selection at the given row and column. + */ +void guac_terminal_select_start(guac_terminal* terminal, int row, int column); + +/** + * Updates the end of text selection at the given row and column. + */ +void guac_terminal_select_update(guac_terminal* terminal, int row, int column); + +/** + * Ends text selection, removing any highlight. + */ +void guac_terminal_select_end(guac_terminal* terminal); + +/** + * Returns a row of character data, whether that data be from the scrollback buffer or the main backing buffer. The length + * parameter given here is a pointer to the int variable that should receive the length of the character array returned. + */ +guac_terminal_char* guac_terminal_get_row(guac_terminal* terminal, int row, int* length); + #endif diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 55fdb00a..991efea2 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -147,8 +147,10 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; guac_terminal* term = client_data->term; - /* Determine which buttons were just released */ - int released_mask = client_data->mouse_mask & ~mask; + /* Determine which buttons were just released and pressed */ + int released_mask = client_data->mouse_mask & ~mask; + int pressed_mask = ~client_data->mouse_mask & mask; + client_data->mouse_mask = mask; /* Show mouse cursor if not already shown */ @@ -173,6 +175,34 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { } + /* If text selected, change state based on left mouse mouse button */ + if (term->text_selected) { + pthread_mutex_lock(&(term->lock)); + + /* If mouse button released, stop selection */ + if (released_mask & GUAC_CLIENT_MOUSE_LEFT) + guac_terminal_select_end(term); + + /* Otherwise, just update */ + else + guac_terminal_select_update(term, + y / term->char_height - term->scroll_offset, + x / term->char_width); + + pthread_mutex_unlock(&(term->lock)); + } + + /* Otherwise, if mouse button pressed, start selection */ + else if (pressed_mask & GUAC_CLIENT_MOUSE_LEFT) { + pthread_mutex_lock(&(term->lock)); + + guac_terminal_select_start(term, + y / term->char_height - term->scroll_offset, + x / term->char_width); + + pthread_mutex_unlock(&(term->lock)); + } + /* Scroll up if wheel moved up */ if (released_mask & GUAC_CLIENT_MOUSE_SCROLL_UP) { pthread_mutex_lock(&(term->lock)); diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 6bdaea38..45758faa 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -141,6 +141,8 @@ guac_terminal* guac_terminal_create(guac_client* client, term->scroll_start = 0; term->scroll_end = term->term_height - 1; + term->text_selected = false; + /* Init scrollback buffer */ term->scrollback = guac_terminal_scrollback_buffer_alloc(1000); term->scroll_offset = 0; @@ -270,7 +272,7 @@ int __guac_terminal_set_colors(guac_terminal* term, int background, foreground; /* Handle reverse video */ - if (attributes->reverse) { + if (attributes->reverse != attributes->selected) { background = attributes->foreground; foreground = attributes->background; } @@ -867,7 +869,7 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, /* Color of the rectangle to draw */ int color; - if (current->character.attributes.reverse) + if (current->character.attributes.reverse != current->character.attributes.selected) color = current->character.attributes.foreground; else color = current->character.attributes.background; @@ -1221,6 +1223,22 @@ void guac_terminal_scrollback_buffer_append( } +guac_terminal_char* guac_terminal_get_row(guac_terminal* terminal, int row, int* length) { + + /* If row in past, pull from scrollback */ + if (row < 0) { + guac_terminal_scrollback_row* scrollback_row = + guac_terminal_scrollback_buffer_get_row(terminal->scrollback, row); + + *length = scrollback_row->length; + return scrollback_row->characters; + } + + *length = terminal->buffer->width; + return &(terminal->buffer->characters[terminal->buffer->width * row]); + +} + void guac_terminal_scroll_display_down(guac_terminal* terminal, int scroll_amount) { @@ -1254,34 +1272,12 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, /* Draw new rows from scrollback */ for (row=start_row; row<=end_row; row++) { - /* If row in past, pull from scrollback */ - if (row < 0) { + int length; + guac_terminal_char* current = guac_terminal_get_row(terminal, row, &length); - /* Get row from scrollback */ - guac_terminal_scrollback_row* scrollback_row = - guac_terminal_scrollback_buffer_get_row(terminal->scrollback, - row); - - /* Draw row */ - /* FIXME: Clear row first */ - guac_terminal_char* current = scrollback_row->characters; - for (column=0; columnlength; column++) - guac_terminal_delta_set(terminal->delta, dest_row, column, - current++); - - } - - /* Otherwise, pull from buffer */ - else { - - guac_terminal_char* current = &(terminal->buffer->characters[ - terminal->buffer->width * row]); - - for (column=0; columnbuffer->width; column++) - guac_terminal_delta_set(terminal->delta, dest_row, column, - current++); - - } + for (column=0; columndelta, dest_row, column, + current++); /* Next row */ dest_row++; @@ -1362,3 +1358,173 @@ guac_terminal_scrollback_row* guac_terminal_scrollback_buffer_get_row( } +void guac_terminal_select_start(guac_terminal* terminal, int row, int column) { + + guac_terminal_char* guac_char; + guac_terminal_operation* guac_operation; + + /* Update selection coordinates */ + terminal->selection_start_row = + terminal->selection_end_row = row; + terminal->selection_start_column = + terminal->selection_end_column = column; + terminal->text_selected = true; + + /* Get char and operation */ + guac_char = &(terminal->buffer->characters[terminal->buffer->width * row + column]); + guac_operation = &(terminal->delta->operations[terminal->delta->width * row + column]); + + /* Set character as selected */ + guac_char->attributes.selected = true; + guac_operation->type = GUAC_CHAR_SET; + guac_operation->character = *guac_char; + + guac_terminal_delta_flush(terminal->delta, terminal); + guac_socket_flush(terminal->client->socket); + +} + +void guac_terminal_select_update(guac_terminal* terminal, int row, int column) { + + int start_index = terminal->selection_start_row * terminal->buffer->width + + terminal->selection_start_column; + + int old_end_index = terminal->selection_end_row * terminal->buffer->width + + terminal->selection_end_column; + + int new_end_index = row * terminal->buffer->width + column; + + int old_index_a, old_index_b; + int new_index_a, new_index_b; + + int search_index_a, search_index_b; + + int i; + guac_terminal_char* guac_char; + guac_terminal_operation* guac_operation; + + /* If unchanged, do nothing */ + if (old_end_index == new_end_index) return; + + /* Calculate old selection range */ + if (start_index < old_end_index) { + old_index_a = start_index; + old_index_b = old_end_index; + } + else { + old_index_a = old_end_index; + old_index_b = start_index; + } + + /* Calculate new selection range */ + if (start_index < new_end_index) { + new_index_a = start_index; + new_index_b = new_end_index; + } + else { + new_index_a = new_end_index; + new_index_b = start_index; + } + + if (new_index_a < old_index_a) + search_index_a = new_index_a; + else + search_index_a = old_index_a; + + if (new_index_b > old_index_b) + search_index_b = new_index_b; + else + search_index_b = old_index_b; + + /* Get first character */ + guac_char = &(terminal->buffer->characters[search_index_a]); + guac_operation = &(terminal->delta->operations[search_index_a]); + + /* Invert modified area */ + for (i=search_index_a; i<=search_index_b; i++) { + + /* If now selected, mark as such */ + if (i >= new_index_a && i <= new_index_b && + (i < old_index_a || i > old_index_b)) { + + guac_char->attributes.selected = true; + guac_operation->type = GUAC_CHAR_SET; + guac_operation->character = *guac_char; + + } + + /* If now unselected, mark as such */ + else if (i >= old_index_a && i <= old_index_b && + (i < new_index_a || i > new_index_b)) { + + guac_char->attributes.selected = false; + guac_operation->type = GUAC_CHAR_SET; + guac_operation->character = *guac_char; + + } + + /* Next char */ + guac_char++; + guac_operation++; + + } + + terminal->selection_end_row = row; + terminal->selection_end_column = column; + + guac_terminal_delta_flush(terminal->delta, terminal); + guac_socket_flush(terminal->client->socket); + +} + +void guac_terminal_select_end(guac_terminal* terminal) { + + int index_a = terminal->selection_end_row * terminal->buffer->width + + terminal->selection_end_column; + + int index_b = terminal->selection_start_row * terminal->buffer->width + + terminal->selection_start_column; + + int i; + guac_terminal_char* guac_char; + guac_terminal_operation* guac_operation; + + /* The start and end indices of all characters in selection */ + int start_index; + int end_index; + + /* Order indices such that end is after start */ + if (index_a > index_b) { + start_index = index_b; + end_index = index_a; + } + else { + start_index = index_a; + end_index = index_b; + } + + /* Get first character */ + guac_char = &(terminal->buffer->characters[start_index]); + guac_operation = &(terminal->delta->operations[start_index]); + + /* Restore state from buffer */ + for (i=start_index; i<=end_index; i++) { + + /* Restore state */ + guac_char->attributes.selected = false; + guac_operation->type = GUAC_CHAR_SET; + guac_operation->character = *guac_char; + + /* Next char */ + guac_char++; + guac_operation++; + + } + + terminal->text_selected = false; + + guac_terminal_delta_flush(terminal->delta, terminal); + guac_socket_flush(terminal->client->socket); + +} + From cb4abb91158fadf509520ce0e57680725fdfeacf Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 15 Apr 2013 15:24:21 -0700 Subject: [PATCH 078/169] Assume UTF-8 for incoming bytes. --- protocols/ssh/src/terminal_handlers.c | 49 +++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 843e10d7..2acb74b3 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -42,7 +42,48 @@ int guac_terminal_echo(guac_terminal* term, char c) { - switch (c) { + static int bytes_remaining = 0; + static int codepoint = 0; + + /* 1-byte UTF-8 codepoint */ + if ((c & 0x80) == 0x00) { /* 0xxxxxxx */ + codepoint = c & 0x7F; + bytes_remaining = 0; + } + + /* 2-byte UTF-8 codepoint */ + else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */ + codepoint = c & 0x1F; + bytes_remaining = 1; + } + + /* 3-byte UTF-8 codepoint */ + else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */ + codepoint = c & 0x0F; + bytes_remaining = 2; + } + + /* 4-byte UTF-8 codepoint */ + else if ((c & 0xF8) == 0xF0) { /* 11110xxx */ + codepoint = c & 0x07; + bytes_remaining = 3; + } + + /* Continuation of UTF-8 codepoint */ + else if ((c & 0xC0) == 0x80) { /* 10xxxxxx */ + codepoint = (codepoint << 6) | (c & 0x3F); + bytes_remaining--; + } + + else { + /* FIXME: Handle */ + } + + /* If we need more bytes, wait for more bytes */ + if (bytes_remaining != 0) + return 0; + + switch (codepoint) { /* Bell */ case 0x07: @@ -98,11 +139,15 @@ int guac_terminal_echo(guac_terminal* term, char c) { } + /* For now, render all but basic latin as '?' */ + if (codepoint > 0x7F) + codepoint = '?'; + /* Write character */ guac_terminal_set(term, term->cursor_row, term->cursor_col, - c); + (char) codepoint); /* Advance cursor */ term->cursor_col++; From 396bba6ea3815960638429953583987573470970 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 25 Apr 2013 11:54:00 -0700 Subject: [PATCH 079/169] Initial stab at rearchitecting - will not compile as is. Partial. --- protocols/ssh/Makefile.am | 7 +- protocols/ssh/include/buffer.h | 132 +++++ protocols/ssh/include/delta.h | 228 +++++++++ protocols/ssh/include/terminal.h | 459 ++---------------- protocols/ssh/include/types.h | 122 +++++ protocols/ssh/src/buffer.c | 107 +++++ protocols/ssh/src/delta.c | 667 ++++++++++++++++++++++++++ protocols/ssh/src/ssh_client.c | 4 - protocols/ssh/src/ssh_handlers.c | 10 +- protocols/ssh/src/terminal.c | 798 +------------------------------ 10 files changed, 1321 insertions(+), 1213 deletions(-) create mode 100644 protocols/ssh/include/buffer.h create mode 100644 protocols/ssh/include/delta.h create mode 100644 protocols/ssh/include/types.h create mode 100644 protocols/ssh/src/buffer.c create mode 100644 protocols/ssh/src/delta.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 532cf60c..b8536b15 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -42,7 +42,9 @@ lib_LTLIBRARIES = libguac-client-ssh.la libguac_client_ssh_la_SOURCES = \ src/blank.c \ + src/buffer.c \ src/cursor.c \ + src/delta.c \ src/ibar.c \ src/ssh_client.c \ src/ssh_handlers.c \ @@ -51,12 +53,15 @@ libguac_client_ssh_la_SOURCES = \ noinst_HEADERS = \ include/blank.h \ + include/buffer.h \ include/cursor.h \ + include/delta.h \ include/ibar.h \ include/ssh_client.h \ include/ssh_handlers.h \ include/terminal.h \ - include/terminal_handlers.h + include/terminal_handlers.h \ + include/types.h libguac_client_ssh_la_CFLAGS = -Werror -Wall -pedantic -Iinclude @PANGO_CFLAGS@ @PANGOCAIRO_CFLAGS@ libguac_client_ssh_la_LIBADD = @PANGO_LIBS@ @PANGOCAIRO_LIBS@ diff --git a/protocols/ssh/include/buffer.h b/protocols/ssh/include/buffer.h new file mode 100644 index 00000000..6a9ce77f --- /dev/null +++ b/protocols/ssh/include/buffer.h @@ -0,0 +1,132 @@ + +/* ***** 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-ssh. + * + * 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 _SSH_GUAC_BUFFER_H +#define _SSH_GUAC_BUFFER_H + +#include "types.h" + +/** + * A single variable-length row of terminal data. + */ +typedef struct guac_terminal_buffer_row { + + /** + * Array of guac_terminal_char representing the contents of the row. + */ + guac_terminal_char* characters; + + /** + * The length of this row in characters. This is the number of initialized + * characters in the buffer, usually equal to the number of characters + * in the screen width at the time this row was created. + */ + int length; + + /** + * The number of elements in the characters array. After the length + * equals this value, the array must be resized. + */ + int available; + +} guac_terminal_buffer_row; + +/** + * A buffer containing a constant number of arbitrary-length rows. + * New rows can be appended to the buffer, with the oldest row replaced with + * the new row. + */ +typedef struct guac_terminal_buffer { + + /** + * Array of buffer rows. This array functions as a ring buffer. + * When a new row needs to be appended, the top reference is moved down + * and the old top row is replaced. + */ + guac_terminal_buffer_row* rows; + + /** + * The row to replace when adding a new row to the buffer. + */ + int top; + + /** + * The number of rows currently stored in the buffer. + */ + int length; + + /** + * The number of rows in the buffer. This is the total capacity + * of the buffer. + */ + int available; + +} guac_terminal_buffer; + +/** + * Allocates a new buffer having the given maximum number of rows. + */ +guac_terminal_buffer* guac_terminal_buffer_alloc(int rows); + +/** + * Frees the given buffer. + */ +void guac_terminal_buffer_free(guac_terminal_buffer* buffer); + +/** + * Copies the given range of columns to a new location, offset from + * the original by the given number of columns. + */ +void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row, + int start_column, int end_column, int offset); + +/** + * Copies the given range of rows to a new location, offset from the + * original by the given number of rows. + */ +void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int src_row, int rows, + int start_row, int end_row, int offset); + +/** + * Sets the given range of columns within the given row to the given + * character. + */ +void guac_terminal_buffer_set_columns(guac_terminal_buffer* buffer, int row, + int start_column, int end_column, guac_terminal_char* character); + +#endif + diff --git a/protocols/ssh/include/delta.h b/protocols/ssh/include/delta.h new file mode 100644 index 00000000..246306bb --- /dev/null +++ b/protocols/ssh/include/delta.h @@ -0,0 +1,228 @@ + +/* ***** 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-ssh. + * + * 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 _SSH_GUAC_DELTA_H +#define _SSH_GUAC_DELTA_H + +#include +#include + +#include "types.h" + +/** + * The available color palette. All integer colors within structures + * here are indices into this palette. + */ +extern const guac_terminal_color guac_terminal_palette[16]; + +/** + * All available terminal operations which affect character cells. + */ +typedef enum guac_terminal_operation_type { + + /** + * Operation which does nothing. + */ + GUAC_CHAR_NOP, + + /** + * Operation which copies a character from a given row/column coordinate. + */ + GUAC_CHAR_COPY, + + /** + * Operation which sets the character and attributes. + */ + GUAC_CHAR_SET + +} guac_terminal_operation_type; + +/** + * A pairing of a guac_terminal_operation_type and all parameters required by + * that operation type. + */ +typedef struct guac_terminal_operation { + + /** + * The type of operation to perform. + */ + guac_terminal_operation_type type; + + /** + * The character (and attributes) to set the current location to. This is + * only applicable to GUAC_CHAR_SET. + */ + guac_terminal_char character; + + /** + * The row to copy a character from. This is only applicable to + * GUAC_CHAR_COPY. + */ + int row; + + /** + * The column to copy a character from. This is only applicable to + * GUAC_CHAR_COPY. + */ + int column; + +} guac_terminal_operation; + +/** + * Set of all pending operations for the currently-visible screen area. + */ +typedef struct guac_terminal_delta { + + /** + * The Guacamole client this delta will use for rendering. + */ + guac_client* client; + + /** + * Array of all operations pending for the visible screen area. + */ + guac_terminal_operation* operations; + + /** + * Scratch area of same size as the operations buffer, facilitating copies + * of overlapping regions. + */ + guac_terminal_operation* scratch; + + /** + * The width of the screen, in characters. + */ + int width; + + /** + * The height of the screen, in characters. + */ + int height; + + /** + * The description of the font to use for rendering. + */ + PangoFontDescription* font_desc; + + /** + * The width of each character, in pixels. + */ + int char_width; + + /** + * The height of each character, in pixels. + */ + int char_height; + + /** + * Index of next glyph to create + */ + int next_glyph; + + /** + * Index of locations for each glyph in the stroke and fill layers. + */ + int glyphs[256]; + + /** + * Color of glyphs in copy buffer + */ + int glyph_foreground; + + /** + * Color of glyphs in copy buffer + */ + int glyph_background; + + /** + * A single wide layer holding each glyph, with each glyph only + * colored with foreground color (background remains transparent). + */ + guac_layer* glyph_stroke; + + /** + * A single wide layer holding each glyph, with each glyph properly + * colored with foreground and background color (no transparency at all). + */ + guac_layer* filled_glyphs; + +} guac_terminal_delta; + +/** + * Allocates a new delta having the given dimensions. + */ +guac_terminal_delta* guac_terminal_delta_alloc(guac_client* client, int width, int height, + int foreground, int background); + +/** + * Frees the given delta. + */ +void guac_terminal_delta_free(guac_terminal_delta* delta); + +/** + * Copies the given range of columns to a new location, offset from + * the original by the given number of columns. + */ +void guac_terminal_delta_copy_columns(guac_terminal_delta* delta, int row, + int start_column, int end_column, int offset); + +/** + * Copies the given range of rows to a new location, offset from the + * original by the given number of rows. + */ +void guac_terminal_delta_copy_rows(guac_terminal_delta* delta, int src_row, int rows, + int start_row, int end_row, int offset); + +/** + * Sets the given range of columns within the given row to the given + * character. + */ +void guac_terminal_delta_set_columns(guac_terminal_delta* delta, int row, + int start_column, int end_column, guac_terminal_char* character); + +/** + * Resize the terminal to the given dimensions. + */ +void guac_terminal_delta_resize(guac_terminal_delta* delta, int rows, int cols); + +/** + * Flushes all pending operations within the given guac_terminal_delta. + */ +void guac_terminal_delta_flush(guac_terminal_delta* delta); + +#endif + diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index b2b04543..4007b4f3 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -41,10 +41,12 @@ #include #include -#include - #include +#include "types.h" +#include "delta.h" +#include "buffer.h" + #define GUAC_SSH_WHEEL_SCROLL_AMOUNT 3 typedef struct guac_terminal guac_terminal; @@ -56,250 +58,6 @@ typedef struct guac_terminal guac_terminal; */ typedef int guac_terminal_char_handler(guac_terminal* term, char c); -/** - * An RGB color, where each component ranges from 0 to 255. - */ -typedef struct guac_terminal_color { - - /** - * The red component of this color. - */ - int red; - - /** - * The green component of this color. - */ - int green; - - /** - * The blue component of this color. - */ - int blue; - -} guac_terminal_color; - -/** - * Terminal attributes, as can be applied to a single character. - */ -typedef struct guac_terminal_attributes { - - /** - * Whether the character should be rendered bold. - */ - bool bold; - - /** - * Whether the character should be rendered with reversed colors - * (background becomes foreground and vice-versa). - */ - bool reverse; - - /** - * Whether the associated character is selected. - */ - bool selected; - - /** - * Whether to render the character with underscore. - */ - bool underscore; - - /** - * The foreground color of this character, as a palette index. - */ - int foreground; - - /** - * The background color of this character, as a palette index. - */ - int background; - -} guac_terminal_attributes; - -/** - * The available color palette. All integer colors within structures - * here are indices into this palette. - */ -extern const guac_terminal_color guac_terminal_palette[16]; - -/** - * Represents a single character for display in a terminal, including actual - * character value, foreground color, and background color. - */ -typedef struct guac_terminal_char { - - /** - * The character value of the character to display. - */ - char value; - - /** - * The attributes of the character to display. - */ - guac_terminal_attributes attributes; - -} guac_terminal_char; - -/** - * All available terminal operations which affect character cells. - */ -typedef enum guac_terminal_operation_type { - - /** - * Operation which does nothing. - */ - GUAC_CHAR_NOP, - - /** - * Operation which copies a character from a given row/column coordinate. - */ - GUAC_CHAR_COPY, - - /** - * Operation which sets the character and attributes. - */ - GUAC_CHAR_SET - -} guac_terminal_operation_type; - -/** - * A pairing of a guac_terminal_operation_type and all parameters required by - * that operation type. - */ -typedef struct guac_terminal_operation { - - /** - * The type of operation to perform. - */ - guac_terminal_operation_type type; - - /** - * The character (and attributes) to set the current location to. This is - * only applicable to GUAC_CHAR_SET. - */ - guac_terminal_char character; - - /** - * The row to copy a character from. This is only applicable to - * GUAC_CHAR_COPY. - */ - int row; - - /** - * The column to copy a character from. This is only applicable to - * GUAC_CHAR_COPY. - */ - int column; - -} guac_terminal_operation; - -/** - * Set of all pending operations for the currently-visible screen area. - */ -typedef struct guac_terminal_delta { - - /** - * Array of all operations pending for the visible screen area. - */ - guac_terminal_operation* operations; - - /** - * Scratch area of same size as the operations buffer, facilitating copies - * of overlapping regions. - */ - guac_terminal_operation* scratch; - - /** - * The width of the screen, in characters. - */ - int width; - - /** - * The height of the screen, in characters. - */ - int height; - -} guac_terminal_delta; - -/** - * A single variable-length row of terminal data. - */ -typedef struct guac_terminal_scrollback_row { - - /** - * Array of guac_terminal_char representing the contents of the row. - */ - guac_terminal_char* characters; - - /** - * The length of this row in characters. This is the number of initialized - * characters in the buffer, usually equal to the number of characters - * in the screen width at the time this row was created. - */ - int length; - - /** - * The number of elements in the characters array. After the length - * equals this value, the array must be resized. - */ - int available; - -} guac_terminal_scrollback_row; - -/** - * A scrollback buffer containing a constant number of arbitrary-length rows. - * New rows can be appended to the buffer, with the oldest row replaced with - * the new row. - */ -typedef struct guac_terminal_scrollback_buffer { - - /** - * Array of scrollback buffer rows. This array functions as a ring buffer. - * When a new row needs to be appended, the top reference is moved down - * and the old top row is replaced. - */ - guac_terminal_scrollback_row* scrollback; - - /** - * The number of rows in the scrollback buffer. This is the total capacity - * of the buffer. - */ - int rows; - - /** - * The row to replace when adding a new row to the scrollback. - */ - int top; - - /** - * The number of rows currently stored in the scrollback buffer. - */ - int length; - -} guac_terminal_scrollback_buffer; - -/** - * Dynamically-resizable character buffer. - */ -typedef struct guac_terminal_buffer { - - /** - * Array of characters. - */ - guac_terminal_char* characters; - - /** - * The width of this buffer in characters. - */ - int width; - - /** - * The height of this buffer in characters. - */ - int height; - -} guac_terminal_buffer; - /** * Represents a terminal emulator which uses a given Guacamole client to * render itself. @@ -317,48 +75,6 @@ struct guac_terminal { */ pthread_mutex_t lock; - /** - * The description of the font to use for rendering. - */ - PangoFontDescription* font_desc; - - /** - * Index of next glyph to create - */ - int next_glyph; - - /** - * Index of locations for each glyph in the stroke and fill layers. - */ - int glyphs[256]; - - /** - * Color of glyphs in copy buffer - */ - int glyph_foreground; - - /** - * Color of glyphs in copy buffer - */ - int glyph_background; - - /** - * A single wide layer holding each glyph, with each glyph only - * colored with foreground color (background remains transparent). - */ - guac_layer* glyph_stroke; - - /** - * A single wide layer holding each glyph, with each glyph properly - * colored with foreground and background color (no transparency at all). - */ - guac_layer* filled_glyphs; - - /** - * The scrollback buffer. - */ - guac_terminal_scrollback_buffer* scrollback; - /** * The relative offset of the display. A positive value indicates that * many rows have been scrolled into view, zero indicates that no @@ -366,16 +82,6 @@ struct guac_terminal { */ int scroll_offset; - /** - * The width of each character, in pixels. - */ - int char_width; - - /** - * The height of each character, in pixels. - */ - int char_height; - /** * The width of the terminal, in characters. */ @@ -485,14 +191,6 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size); */ int guac_terminal_set(guac_terminal* term, int row, int col, char c); -/** - * Copies a rectangular region of characters which may overlap with the - * destination. - */ -int guac_terminal_copy(guac_terminal* term, - int src_row, int src_col, int rows, int cols, - int dst_row, int dst_col); - /** * Clears a rectangular region of characters, replacing them with the * current background color and attributes. @@ -525,128 +223,16 @@ int guac_terminal_scroll_down(guac_terminal* term, */ int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col); -/** - * Allocates a new guac_terminal_delta. - */ -guac_terminal_delta* guac_terminal_delta_alloc(int width, int height); - -/** - * Frees the given guac_terminal_delta. - */ -void guac_terminal_delta_free(guac_terminal_delta* delta); - -/** - * Resizes the given guac_terminal_delta to the given dimensions. - */ -void guac_terminal_delta_resize(guac_terminal_delta* delta, - int width, int height); - -/** - * Stores a set operation at the given location. - */ -void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, - guac_terminal_char* character); - -/** - * Stores a rectangle of copy operations, copying existing operations as - * necessary. - */ -void guac_terminal_delta_copy(guac_terminal_delta* delta, - int dst_row, int dst_column, - int src_row, int src_column, - int w, int h); - -/** - * Sets a rectangle of character data to the given character value. - */ -void guac_terminal_delta_set_rect(guac_terminal_delta* delta, - int row, int column, int w, int h, - guac_terminal_char* character); - -/** - * Flushes all pending operations within the given guac_client_delta to the - * given guac_terminal. - */ -void guac_terminal_delta_flush(guac_terminal_delta* delta, - guac_terminal* terminal); - -/** - * Allocates a new character buffer having the given dimensions. - */ -guac_terminal_buffer* guac_terminal_buffer_alloc(int width, int height); - -/** - * Resizes the given character buffer to the given dimensions. - */ -void guac_terminal_buffer_resize(guac_terminal_buffer* buffer, - int width, int height); - -/** - * Sets the character at the given location within the buffer to the given - * value. - */ -void guac_terminal_buffer_set(guac_terminal_buffer* buffer, int r, int c, - guac_terminal_char* character); - -/** - * Copies a rectangle of character data within the buffer. The source and - * destination may overlap. - */ -void guac_terminal_buffer_copy(guac_terminal_buffer* buffer, - int dst_row, int dst_column, - int src_row, int src_column, - int w, int h); - -/** - * Sets a rectangle of character data to the given character value. - */ -void guac_terminal_buffer_set_rect(guac_terminal_buffer* buffer, - int row, int column, int w, int h, - guac_terminal_char* character); - -/** - * Frees the given character buffer. - */ -void guac_terminal_buffer_free(guac_terminal_buffer* buffer); - -/** - * Allocates a new scrollback buffer having the given number of rows. - */ -guac_terminal_scrollback_buffer* - guac_terminal_scrollback_buffer_alloc(int rows); - -/** - * Frees the given scrollback buffer. - */ -void guac_terminal_scrollback_buffer_free( - guac_terminal_scrollback_buffer* buffer); - -/** - * Pushes the given number of rows into the scrollback, maintaining display - * position within the scrollback as possible. - */ -void guac_terminal_scrollback_buffer_append( - guac_terminal_scrollback_buffer* buffer, - guac_terminal* terminal, int rows); - -/** - * Returns the row within the scrollback at the given location. The index - * of the row given is a negative number, denoting the number of rows into - * the past to look. - */ -guac_terminal_scrollback_row* guac_terminal_scrollback_buffer_get_row( - guac_terminal_scrollback_buffer* buffer, int row); - /** * Scroll down the display by the given amount, replacing the new space with - * data from the scrollback. If not enough data is available, the maximum + * data from the buffer. If not enough data is available, the maximum * amount will be scrolled. */ void guac_terminal_scroll_display_down(guac_terminal* terminal, int amount); /** * Scroll up the display by the given amount, replacing the new space with data - * from either the scrollback or the terminal buffer. If not enough data is + * from either the buffer or the terminal buffer. If not enough data is * available, the maximum amount will be scrolled. */ void guac_terminal_scroll_display_up(guac_terminal* terminal, int amount); @@ -666,11 +252,38 @@ void guac_terminal_select_update(guac_terminal* terminal, int row, int column); */ void guac_terminal_select_end(guac_terminal* terminal); +/* LOW-LEVEL TERMINAL OPERATIONS */ + /** - * Returns a row of character data, whether that data be from the scrollback buffer or the main backing buffer. The length - * parameter given here is a pointer to the int variable that should receive the length of the character array returned. + * Copies the given range of columns to a new location, offset from + * the original by the given number of columns. */ -guac_terminal_char* guac_terminal_get_row(guac_terminal* terminal, int row, int* length); +void guac_terminal_copy_columns(guac_terminal* terminal, int row, + int start_column, int end_column, int offset); + +/** + * Copies the given range of rows to a new location, offset from the + * original by the given number of rows. + */ +void guac_terminal_copy_rows(guac_terminal* terminal, int src_row, int rows, + int start_row, int end_row, int offset); + +/** + * Sets the given range of columns within the given row to the given + * character. + */ +void guac_terminal_set_columns(guac_terminal* terminal, int row, + int start_column, int end_column, guac_terminal_char* character); + +/** + * Resize the terminal to the given dimensions. + */ +void guac_terminal_resize(guac_terminal* term, int rows, int cols); + +/** + * Flushes all pending operations within the given guac_terminal. + */ +void guac_terminal_flush(guac_terminal* terminal); #endif diff --git a/protocols/ssh/include/types.h b/protocols/ssh/include/types.h new file mode 100644 index 00000000..cfe68c6c --- /dev/null +++ b/protocols/ssh/include/types.h @@ -0,0 +1,122 @@ + +/* ***** 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-ssh. + * + * 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 _SSH_GUAC_TYPES_H +#define _SSH_GUAC_TYPES_H + +#include + +/** + * An RGB color, where each component ranges from 0 to 255. + */ +typedef struct guac_terminal_color { + + /** + * The red component of this color. + */ + int red; + + /** + * The green component of this color. + */ + int green; + + /** + * The blue component of this color. + */ + int blue; + +} guac_terminal_color; + +/** + * Terminal attributes, as can be applied to a single character. + */ +typedef struct guac_terminal_attributes { + + /** + * Whether the character should be rendered bold. + */ + bool bold; + + /** + * Whether the character should be rendered with reversed colors + * (background becomes foreground and vice-versa). + */ + bool reverse; + + /** + * Whether the associated character is selected. + */ + bool selected; + + /** + * Whether to render the character with underscore. + */ + bool underscore; + + /** + * The foreground color of this character, as a palette index. + */ + int foreground; + + /** + * The background color of this character, as a palette index. + */ + int background; + +} guac_terminal_attributes; + +/** + * Represents a single character for display in a terminal, including actual + * character value, foreground color, and background color. + */ +typedef struct guac_terminal_char { + + /** + * The character value of the character to display. + */ + char value; + + /** + * The attributes of the character to display. + */ + guac_terminal_attributes attributes; + +} guac_terminal_char; + +#endif + diff --git a/protocols/ssh/src/buffer.c b/protocols/ssh/src/buffer.c new file mode 100644 index 00000000..43730304 --- /dev/null +++ b/protocols/ssh/src/buffer.c @@ -0,0 +1,107 @@ + +/* ***** 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-ssh. + * + * 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 "buffer.h" + +guac_terminal_buffer* guac_terminal_buffer_alloc(int rows) { + + /* Allocate scrollback */ + guac_terminal_buffer* buffer = + malloc(sizeof(guac_terminal_buffer)); + + int i; + guac_terminal_buffer_row* row; + + /* Init scrollback data */ + buffer->available = rows; + buffer->top = 0; + buffer->length = 0; + buffer->rows = malloc(sizeof(guac_terminal_buffer_row) * + buffer->available); + + /* Init scrollback rows */ + row = buffer->rows; + for (i=0; iavailable = 256; + row->length = 0; + row->characters = malloc(sizeof(guac_terminal_char) * row->available); + + /* Next row */ + row++; + + } + + return buffer; + +} + +void guac_terminal_buffer_free(guac_terminal_buffer* buffer) { + + int i; + guac_terminal_buffer_row* row = buffer->rows; + + /* Free all rows */ + for (i=0; iavailable; i++) { + free(row->characters); + row++; + } + + /* Free actual buffer */ + free(buffer->rows); + free(buffer); + +} + +void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row, + int start_column, int end_column, int offset) { + /* STUB */ +} + +void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int src_row, int rows, + int start_row, int end_row, int offset) { + /* STUB */ +} + +void guac_terminal_buffer_set_columns(guac_terminal_buffer* buffer, int row, + int start_column, int end_column, guac_terminal_char* character) { + /* STUB */ +} + diff --git a/protocols/ssh/src/delta.c b/protocols/ssh/src/delta.c new file mode 100644 index 00000000..c686c8f9 --- /dev/null +++ b/protocols/ssh/src/delta.c @@ -0,0 +1,667 @@ + +/* ***** 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-ssh. + * + * 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 "types.h" +#include "delta.h" + +const guac_terminal_color guac_terminal_palette[16] = { + + /* Normal colors */ + {0x00, 0x00, 0x00}, /* Black */ + {0x99, 0x3E, 0x3E}, /* Red */ + {0x3E, 0x99, 0x3E}, /* Green */ + {0x99, 0x99, 0x3E}, /* Brown */ + {0x3E, 0x3E, 0x99}, /* Blue */ + {0x99, 0x3E, 0x99}, /* Magenta */ + {0x3E, 0x99, 0x99}, /* Cyan */ + {0x99, 0x99, 0x99}, /* White */ + + /* Intense colors */ + {0x3E, 0x3E, 0x3E}, /* Black */ + {0xFF, 0x67, 0x67}, /* Red */ + {0x67, 0xFF, 0x67}, /* Green */ + {0xFF, 0xFF, 0x67}, /* Brown */ + {0x67, 0x67, 0xFF}, /* Blue */ + {0xFF, 0x67, 0xFF}, /* Magenta */ + {0x67, 0xFF, 0xFF}, /* Cyan */ + {0xFF, 0xFF, 0xFF}, /* White */ + +}; + +/** + * Returns the location of the given character in the glyph cache layer, + * sending it first if necessary. The location returned is in characters, + * and thus must be multiplied by the glyph width to obtain the actual + * location within the glyph cache layer. + */ +int __guac_terminal_get_glyph(guac_terminal_delta* delta, char c) { + + guac_socket* socket = delta->client->socket; + int location; + + /* Use foreground color */ + const guac_terminal_color* color = + &guac_terminal_palette[delta->glyph_foreground]; + + /* Use background color */ + const guac_terminal_color* background = + &guac_terminal_palette[delta->glyph_background]; + + cairo_surface_t* surface; + cairo_t* cairo; + + PangoLayout* layout; + + /* Return glyph if exists */ + if (delta->glyphs[(int) c]) + return delta->glyphs[(int) c] - 1; + + location = delta->next_glyph++; + + /* Otherwise, draw glyph */ + surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, + delta->char_width, delta->char_height); + cairo = cairo_create(surface); + + /* Get layout */ + layout = pango_cairo_create_layout(cairo); + pango_layout_set_font_description(layout, delta->font_desc); + pango_layout_set_text(layout, &c, 1); + + /* Draw */ + cairo_set_source_rgba(cairo, + color->red / 255.0, + color->green / 255.0, + color->blue / 255.0, + 1.0 /* alpha */ ); + + cairo_move_to(cairo, 0.0, 0.0); + pango_cairo_show_layout(cairo, layout); + + /* Free all */ + g_object_unref(layout); + cairo_destroy(cairo); + + /* Send glyph and update filled flyphs */ + guac_protocol_send_png(socket, GUAC_COMP_OVER, delta->glyph_stroke, location * delta->char_width, 0, surface); + + guac_protocol_send_rect(socket, delta->filled_glyphs, + location * delta->char_width, 0, + delta->char_width, delta->char_height); + + guac_protocol_send_cfill(socket, GUAC_COMP_OVER, delta->filled_glyphs, + background->red, + background->green, + background->blue, + 0xFF); + + guac_protocol_send_copy(socket, delta->glyph_stroke, + location * delta->char_width, 0, delta->char_width, delta->char_height, + GUAC_COMP_OVER, delta->filled_glyphs, location * delta->char_width, 0); + + delta->glyphs[(int) c] = location+1; + + cairo_surface_destroy(surface); + + /* Return glyph */ + return location; + +} + +/** + * Sets the attributes of the glyph cache layer such that future copies from + * this layer will display as expected. + */ +int __guac_terminal_set_colors(guac_terminal_delta* delta, + guac_terminal_attributes* attributes) { + + guac_socket* socket = delta->client->socket; + const guac_terminal_color* background_color; + int background, foreground; + + /* Handle reverse video */ + if (attributes->reverse != attributes->selected) { + background = attributes->foreground; + foreground = attributes->background; + } + else { + foreground = attributes->foreground; + background = attributes->background; + } + + /* Handle bold */ + if (attributes->bold && foreground <= 7) + foreground += 8; + + /* Get background color */ + background_color = &guac_terminal_palette[background]; + + /* If foreground different from current, colorize */ + if (foreground != delta->glyph_foreground) { + + /* Get color */ + const guac_terminal_color* color = + &guac_terminal_palette[foreground]; + + /* Colorize letter */ + guac_protocol_send_rect(socket, delta->glyph_stroke, + 0, 0, + delta->char_width * delta->next_glyph, delta->char_height); + + guac_protocol_send_cfill(socket, GUAC_COMP_ATOP, delta->glyph_stroke, + color->red, + color->green, + color->blue, + 255); + + } + + /* If any color change at all, update filled */ + if (foreground != delta->glyph_foreground + || background != delta->glyph_background) { + + /* Set background */ + guac_protocol_send_rect(socket, delta->filled_glyphs, + 0, 0, + delta->char_width * delta->next_glyph, delta->char_height); + + guac_protocol_send_cfill(socket, GUAC_COMP_OVER, delta->filled_glyphs, + background_color->red, + background_color->green, + background_color->blue, + 255); + + /* Copy stroke */ + guac_protocol_send_copy(socket, delta->glyph_stroke, + + 0, 0, + delta->char_width * delta->next_glyph, delta->char_height, + + GUAC_COMP_OVER, delta->filled_glyphs, + 0, 0); + + } + + delta->glyph_foreground = foreground; + delta->glyph_background = background; + + return 0; + +} + +/** + * Sends the given character to the terminal at the given row and column, + * rendering the charater immediately. This bypasses the guac_terminal_delta + * mechanism and is intended for flushing of updates only. + */ +int __guac_terminal_set(guac_terminal_delta* delta, int row, int col, char c) { + + guac_socket* socket = delta->client->socket; + int location = __guac_terminal_get_glyph(delta, c); + + return guac_protocol_send_copy(socket, + delta->filled_glyphs, + location * delta->char_width, 0, delta->char_width, delta->char_height, + GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, + delta->char_width * col, + delta->char_height * row); + +} + + +guac_terminal_delta* guac_terminal_delta_alloc(guac_client* client, int width, int height, + int foreground, int background) { + + guac_terminal_operation* current; + int x, y; + + PangoFontMap* font_map; + PangoFont* font; + PangoFontMetrics* metrics; + PangoContext* context; + + /* Allocate delta */ + guac_terminal_delta* delta = malloc(sizeof(guac_terminal_delta)); + delta->client = client; + + memset(delta->glyphs, 0, sizeof(delta->glyphs)); + delta->glyph_stroke = guac_client_alloc_buffer(client); + delta->filled_glyphs = guac_client_alloc_buffer(client); + + /* Get font */ + delta->font_desc = pango_font_description_new(); + pango_font_description_set_family(delta->font_desc, "monospace"); + pango_font_description_set_weight(delta->font_desc, PANGO_WEIGHT_NORMAL); + pango_font_description_set_size(delta->font_desc, 12*PANGO_SCALE); + + font_map = pango_cairo_font_map_get_default(); + context = pango_font_map_create_context(font_map); + + font = pango_font_map_load_font(font_map, context, delta->font_desc); + if (font == NULL) { + guac_client_log_error(delta->client, "Unable to get font."); + return NULL; + } + + metrics = pango_font_get_metrics(font, NULL); + if (metrics == NULL) { + guac_client_log_error(delta->client, "Unable to get font metrics."); + return NULL; + } + + delta->glyph_foreground = foreground; + delta->glyph_background = background; + + /* Calculate character dimensions */ + delta->char_width = + pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE; + delta->char_height = + (pango_font_metrics_get_descent(metrics) + + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; + + /* Set width and height */ + delta->width = width; + delta->height = height; + + /* Alloc operations */ + delta->operations = malloc(width * height * + sizeof(guac_terminal_operation)); + + /* Init each operation buffer row */ + current = delta->operations; + for (y=0; ytype = GUAC_CHAR_NOP; + + } + + /* Alloc scratch area */ + delta->scratch = malloc(width * height * sizeof(guac_terminal_operation)); + + /* Send initial display size */ + guac_protocol_send_size(client->socket, + GUAC_DEFAULT_LAYER, + delta->char_width * width, + delta->char_height * height); + + return delta; + +} + +void guac_terminal_delta_free(guac_terminal_delta* delta) { + + /* Free operations buffers */ + free(delta->operations); + free(delta->scratch); + + /* Free delta */ + free(delta); + +} + +void guac_terminal_delta_copy_columns(guac_terminal_delta* delta, int row, + int start_column, int end_column, int offset) { + /* STUB */ +} + +void guac_terminal_delta_copy_rows(guac_terminal_delta* delta, int src_row, int rows, + int start_row, int end_row, int offset) { + /* STUB */ +} + +void guac_terminal_delta_set_columns(guac_terminal_delta* delta, int row, + int start_column, int end_column, guac_terminal_char* character) { + /* STUB */ +} + +void guac_terminal_delta_resize(guac_terminal_delta* delta, int rows, int cols) { + /* STUB */ +} + +void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta) { + + guac_terminal_operation* current = delta->operations; + int row, col; + + /* For each operation */ + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { + + /* If operation is a copy operation */ + if (current->type == GUAC_CHAR_COPY) { + + /* The determined bounds of the rectangle of contiguous + * operations */ + int detected_right = -1; + int detected_bottom = row; + + /* The current row or column within a rectangle */ + int rect_row, rect_col; + + /* The dimensions of the rectangle as determined */ + int rect_width, rect_height; + + /* The expected row and column source for the next copy + * operation (if adjacent to current) */ + int expected_row, expected_col; + + /* Current row within a subrect */ + guac_terminal_operation* rect_current_row; + + /* Determine bounds of rectangle */ + rect_current_row = current; + expected_row = current->row; + for (rect_row=row; rect_rowheight; rect_row++) { + + guac_terminal_operation* rect_current = rect_current_row; + expected_col = current->column; + + /* Find width */ + for (rect_col=col; rect_colwidth; rect_col++) { + + /* If not identical operation, stop */ + if (rect_current->type != GUAC_CHAR_COPY + || rect_current->row != expected_row + || rect_current->column != expected_col) + break; + + /* Next column */ + rect_current++; + expected_col++; + + } + + /* If too small, cannot append row */ + if (rect_col-1 < detected_right) + break; + + /* As row has been accepted, update rect_row of rect */ + detected_bottom = rect_row; + + /* For now, only set rect_col bound if uninitialized */ + if (detected_right == -1) + detected_right = rect_col - 1; + + /* Next row */ + rect_current_row += delta->width; + expected_row++; + + } + + /* Calculate dimensions */ + rect_width = detected_right - col + 1; + rect_height = detected_bottom - row + 1; + + /* Mark rect as NOP (as it has been handled) */ + rect_current_row = current; + expected_row = current->row; + for (rect_row=0; rect_rowcolumn; + + for (rect_col=0; rect_coltype == GUAC_CHAR_COPY + && rect_current->row == expected_row + && rect_current->column == expected_col) + rect_current->type = GUAC_CHAR_NOP; + + /* Next column */ + rect_current++; + expected_col++; + + } + + /* Next row */ + rect_current_row += delta->width; + expected_row++; + + } + + /* Send copy */ + guac_protocol_send_copy(delta->client->socket, + + GUAC_DEFAULT_LAYER, + current->column * delta->char_width, + current->row * delta->char_height, + rect_width * delta->char_width, + rect_height * delta->char_height, + + GUAC_COMP_OVER, + GUAC_DEFAULT_LAYER, + col * delta->char_width, + row * delta->char_height); + + } /* end if copy operation */ + + /* Next operation */ + current++; + + } + } + +} + +void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta) { + + guac_terminal_operation* current = delta->operations; + int row, col; + + /* For each operation */ + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { + + /* If operation is a cler operation (set to space) */ + if (current->type == GUAC_CHAR_SET && + current->character.value == ' ') { + + /* The determined bounds of the rectangle of contiguous + * operations */ + int detected_right = -1; + int detected_bottom = row; + + /* The current row or column within a rectangle */ + int rect_row, rect_col; + + /* The dimensions of the rectangle as determined */ + int rect_width, rect_height; + + /* Color of the rectangle to draw */ + int color; + if (current->character.attributes.reverse != current->character.attributes.selected) + color = current->character.attributes.foreground; + else + color = current->character.attributes.background; + + const guac_terminal_color* guac_color = + &guac_terminal_palette[color]; + + /* Current row within a subrect */ + guac_terminal_operation* rect_current_row; + + /* Determine bounds of rectangle */ + rect_current_row = current; + for (rect_row=row; rect_rowheight; rect_row++) { + + guac_terminal_operation* rect_current = rect_current_row; + + /* Find width */ + for (rect_col=col; rect_colwidth; rect_col++) { + + int joining_color; + if (rect_current->character.attributes.reverse) + joining_color = current->character.attributes.foreground; + else + joining_color = current->character.attributes.background; + + /* If not identical operation, stop */ + if (rect_current->type != GUAC_CHAR_SET + || rect_current->character.value != ' ' + || joining_color != color) + break; + + /* Next column */ + rect_current++; + + } + + /* If too small, cannot append row */ + if (rect_col-1 < detected_right) + break; + + /* As row has been accepted, update rect_row of rect */ + detected_bottom = rect_row; + + /* For now, only set rect_col bound if uninitialized */ + if (detected_right == -1) + detected_right = rect_col - 1; + + /* Next row */ + rect_current_row += delta->width; + + } + + /* Calculate dimensions */ + rect_width = detected_right - col + 1; + rect_height = detected_bottom - row + 1; + + /* Mark rect as NOP (as it has been handled) */ + rect_current_row = current; + for (rect_row=0; rect_rowcharacter.attributes.reverse) + joining_color = current->character.attributes.foreground; + else + joining_color = current->character.attributes.background; + + /* Mark clear operations as NOP */ + if (rect_current->type == GUAC_CHAR_SET + && rect_current->character.value == ' ' + && joining_color == color) + rect_current->type = GUAC_CHAR_NOP; + + /* Next column */ + rect_current++; + + } + + /* Next row */ + rect_current_row += delta->width; + + } + + /* Send rect */ + guac_protocol_send_rect(delta->client->socket, + GUAC_DEFAULT_LAYER, + col * delta->char_width, + row * delta->char_height, + rect_width * delta->char_width, + rect_height * delta->char_height); + + guac_protocol_send_cfill(delta->client->socket, + GUAC_COMP_OVER, + GUAC_DEFAULT_LAYER, + guac_color->red, guac_color->green, guac_color->blue, + 0xFF); + + } /* end if clear operation */ + + /* Next operation */ + current++; + + } + } + +} + +void __guac_terminal_delta_flush_set(guac_terminal_delta* delta) { + + guac_terminal_operation* current = delta->operations; + int row, col; + + /* For each operation */ + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { + + /* Perform given operation */ + if (current->type == GUAC_CHAR_SET) { + + /* Set attributes */ + __guac_terminal_set_colors(delta, + &(current->character.attributes)); + + /* Send character */ + __guac_terminal_set(delta, row, col, + current->character.value); + + /* Mark operation as handled */ + current->type = GUAC_CHAR_NOP; + + } + + /* Next operation */ + current++; + + } + } + +} + +void guac_terminal_delta_flush(guac_terminal_delta* delta) { + + /* Flush operations, copies first, then clears, then sets. */ + __guac_terminal_delta_flush_copy(delta); + __guac_terminal_delta_flush_clear(delta); + __guac_terminal_delta_flush_set(delta); + +} + diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 322d9459..e2495e63 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -125,10 +125,6 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Send name and dimensions */ guac_protocol_send_name(socket, "SSH TEST"); - guac_protocol_send_size(socket, - GUAC_DEFAULT_LAYER, - term->char_width * term->term_width, - term->char_height * term->term_height); /* Initialize pointer */ client_data->current_cursor = client_data->blank_cursor; diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 991efea2..e425a2ca 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -118,7 +118,7 @@ int ssh_guac_client_handle_messages(guac_client* client) { client_data->term->cursor_col); /* Flush terminal delta */ - guac_terminal_delta_flush(client_data->term->delta, client_data->term); + guac_terminal_delta_flush(client_data->term->delta); /* Unlock terminal access */ pthread_mutex_unlock(&(client_data->term->lock)); @@ -186,8 +186,8 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { /* Otherwise, just update */ else guac_terminal_select_update(term, - y / term->char_height - term->scroll_offset, - x / term->char_width); + y / term->delta->char_height - term->scroll_offset, + x / term->delta->char_width); pthread_mutex_unlock(&(term->lock)); } @@ -197,8 +197,8 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { pthread_mutex_lock(&(term->lock)); guac_terminal_select_start(term, - y / term->char_height - term->scroll_offset, - x / term->char_width); + y / term->delta->char_height - term->scroll_offset, + x / term->delta->char_width); pthread_mutex_unlock(&(term->lock)); } diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 45758faa..fec36344 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -47,33 +47,12 @@ #include #include +#include "types.h" +#include "buffer.h" +#include "delta.h" #include "terminal.h" #include "terminal_handlers.h" -const guac_terminal_color guac_terminal_palette[16] = { - - /* Normal colors */ - {0x00, 0x00, 0x00}, /* Black */ - {0x99, 0x3E, 0x3E}, /* Red */ - {0x3E, 0x99, 0x3E}, /* Green */ - {0x99, 0x99, 0x3E}, /* Brown */ - {0x3E, 0x3E, 0x99}, /* Blue */ - {0x99, 0x3E, 0x99}, /* Magenta */ - {0x3E, 0x99, 0x99}, /* Cyan */ - {0x99, 0x99, 0x99}, /* White */ - - /* Intense colors */ - {0x3E, 0x3E, 0x3E}, /* Black */ - {0xFF, 0x67, 0x67}, /* Red */ - {0x67, 0xFF, 0x67}, /* Green */ - {0xFF, 0xFF, 0x67}, /* Brown */ - {0x67, 0x67, 0xFF}, /* Blue */ - {0xFF, 0x67, 0xFF}, /* Magenta */ - {0x67, 0xFF, 0xFF}, /* Cyan */ - {0xFF, 0xFF, 0xFF}, /* White */ - -}; - guac_terminal* guac_terminal_create(guac_client* client, int width, int height) { @@ -85,57 +64,28 @@ guac_terminal* guac_terminal_create(guac_client* client, .underscore = false }; - PangoFontMap* font_map; - PangoFont* font; - PangoFontMetrics* metrics; - PangoContext* context; - guac_terminal* term = malloc(sizeof(guac_terminal)); term->client = client; + /* Init buffer */ + term->buffer = guac_terminal_buffer_alloc(1000); + term->scroll_offset = 0; + + /* Init delta */ + term->delta = guac_terminal_delta_alloc(client, + term->term_width, term->term_height, + default_attributes.foreground, + default_attributes.background); + + /* Init terminal state */ term->current_attributes = term->default_attributes = default_attributes; - term->glyph_foreground = default_attributes.foreground; - term->glyph_background = default_attributes.background; - - memset(term->glyphs, 0, sizeof(term->glyphs)); - term->glyph_stroke = guac_client_alloc_buffer(client); - term->filled_glyphs = guac_client_alloc_buffer(client); - - /* Get font */ - term->font_desc = pango_font_description_new(); - pango_font_description_set_family(term->font_desc, "monospace"); - pango_font_description_set_weight(term->font_desc, PANGO_WEIGHT_NORMAL); - pango_font_description_set_size(term->font_desc, 12*PANGO_SCALE); - - font_map = pango_cairo_font_map_get_default(); - context = pango_font_map_create_context(font_map); - - font = pango_font_map_load_font(font_map, context, term->font_desc); - if (font == NULL) { - guac_client_log_error(term->client, "Unable to get font."); - return NULL; - } - - metrics = pango_font_get_metrics(font, NULL); - if (metrics == NULL) { - guac_client_log_error(term->client, "Unable to get font metrics."); - return NULL; - } - - /* Calculate character dimensions */ - term->char_width = - pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE; - term->char_height = - (pango_font_metrics_get_descent(metrics) - + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; - term->cursor_row = 0; term->cursor_col = 0; - term->term_width = width / term->char_width; - term->term_height = height / term->char_height; + term->term_width = width / term->delta->char_width; + term->term_height = height / term->delta->char_height; term->char_handler = guac_terminal_echo; term->scroll_start = 0; @@ -143,18 +93,6 @@ guac_terminal* guac_terminal_create(guac_client* client, term->text_selected = false; - /* Init scrollback buffer */ - term->scrollback = guac_terminal_scrollback_buffer_alloc(1000); - term->scroll_offset = 0; - - /* Init delta */ - term->delta = guac_terminal_delta_alloc(term->term_width, - term->term_height); - - /* Init buffer */ - term->buffer = guac_terminal_buffer_alloc(term->term_width, - term->term_height); - /* Clear with background color */ guac_terminal_clear(term, 0, 0, term->term_height, term->term_width); @@ -168,9 +106,6 @@ guac_terminal* guac_terminal_create(guac_client* client, void guac_terminal_free(guac_terminal* term) { - /* Free scrollback buffer */ - guac_terminal_scrollback_buffer_free(term->scrollback); - /* Free delta */ guac_terminal_delta_free(term->delta); @@ -179,187 +114,6 @@ void guac_terminal_free(guac_terminal* term) { } -/** - * Returns the location of the given character in the glyph cache layer, - * sending it first if necessary. The location returned is in characters, - * and thus must be multiplied by the glyph width to obtain the actual - * location within the glyph cache layer. - */ -int __guac_terminal_get_glyph(guac_terminal* term, char c) { - - guac_socket* socket = term->client->socket; - int location; - - /* Use foreground color */ - const guac_terminal_color* color = - &guac_terminal_palette[term->glyph_foreground]; - - /* Use background color */ - const guac_terminal_color* background = - &guac_terminal_palette[term->glyph_background]; - - cairo_surface_t* surface; - cairo_t* cairo; - - PangoLayout* layout; - - /* Return glyph if exists */ - if (term->glyphs[(int) c]) - return term->glyphs[(int) c] - 1; - - location = term->next_glyph++; - - /* Otherwise, draw glyph */ - surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, - term->char_width, term->char_height); - cairo = cairo_create(surface); - - /* Get layout */ - layout = pango_cairo_create_layout(cairo); - pango_layout_set_font_description(layout, term->font_desc); - pango_layout_set_text(layout, &c, 1); - - /* Draw */ - cairo_set_source_rgba(cairo, - color->red / 255.0, - color->green / 255.0, - color->blue / 255.0, - 1.0 /* alpha */ ); - - cairo_move_to(cairo, 0.0, 0.0); - pango_cairo_show_layout(cairo, layout); - - /* Free all */ - g_object_unref(layout); - cairo_destroy(cairo); - - /* Send glyph and update filled flyphs */ - guac_protocol_send_png(socket, GUAC_COMP_OVER, term->glyph_stroke, location * term->char_width, 0, surface); - - guac_protocol_send_rect(socket, term->filled_glyphs, - location * term->char_width, 0, - term->char_width, term->char_height); - - guac_protocol_send_cfill(socket, GUAC_COMP_OVER, term->filled_glyphs, - background->red, - background->green, - background->blue, - 0xFF); - - guac_protocol_send_copy(socket, term->glyph_stroke, - location * term->char_width, 0, term->char_width, term->char_height, - GUAC_COMP_OVER, term->filled_glyphs, location * term->char_width, 0); - - term->glyphs[(int) c] = location+1; - - cairo_surface_destroy(surface); - - /* Return glyph */ - return location; - -} - -/** - * Sets the attributes of the glyph cache layer such that future copies from - * this layer will display as expected. - */ -int __guac_terminal_set_colors(guac_terminal* term, - guac_terminal_attributes* attributes) { - - guac_socket* socket = term->client->socket; - const guac_terminal_color* background_color; - int background, foreground; - - /* Handle reverse video */ - if (attributes->reverse != attributes->selected) { - background = attributes->foreground; - foreground = attributes->background; - } - else { - foreground = attributes->foreground; - background = attributes->background; - } - - /* Handle bold */ - if (attributes->bold && foreground <= 7) - foreground += 8; - - /* Get background color */ - background_color = &guac_terminal_palette[background]; - - /* If foreground different from current, colorize */ - if (foreground != term->glyph_foreground) { - - /* Get color */ - const guac_terminal_color* color = - &guac_terminal_palette[foreground]; - - /* Colorize letter */ - guac_protocol_send_rect(socket, term->glyph_stroke, - 0, 0, - term->char_width * term->next_glyph, term->char_height); - - guac_protocol_send_cfill(socket, GUAC_COMP_ATOP, term->glyph_stroke, - color->red, - color->green, - color->blue, - 255); - - } - - /* If any color change at all, update filled */ - if (foreground != term->glyph_foreground - || background != term->glyph_background) { - - /* Set background */ - guac_protocol_send_rect(socket, term->filled_glyphs, - 0, 0, - term->char_width * term->next_glyph, term->char_height); - - guac_protocol_send_cfill(socket, GUAC_COMP_OVER, term->filled_glyphs, - background_color->red, - background_color->green, - background_color->blue, - 255); - - /* Copy stroke */ - guac_protocol_send_copy(socket, term->glyph_stroke, - - 0, 0, - term->char_width * term->next_glyph, term->char_height, - - GUAC_COMP_OVER, term->filled_glyphs, - 0, 0); - - } - - term->glyph_foreground = foreground; - term->glyph_background = background; - - return 0; - -} - -/** - * Sends the given character to the terminal at the given row and column, - * rendering the charater immediately. This bypasses the guac_terminal_delta - * mechanism and is intended for flushing of updates only. - */ -int __guac_terminal_set(guac_terminal* term, int row, int col, char c) { - - guac_socket* socket = term->client->socket; - int location = __guac_terminal_get_glyph(term, c); - - return guac_protocol_send_copy(socket, - term->filled_glyphs, - location * term->char_width, 0, term->char_width, term->char_height, - GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, - term->char_width * col, - term->char_height * row); - -} - int guac_terminal_set(guac_terminal* term, int row, int col, char c) { int scrolled_row = row + term->scroll_offset; @@ -371,10 +125,10 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { /* Set delta */ if (scrolled_row < term->delta->height) - guac_terminal_delta_set(term->delta, scrolled_row, col, &guac_char); + guac_terminal_delta_set_columns(term->delta, scrolled_row, col, col, &guac_char); /* Set buffer */ - guac_terminal_buffer_set(term->buffer, row, col, &guac_char); + guac_terminal_buffer_set_columns(term->buffer, row, col, col, &guac_char); return 0; @@ -569,54 +323,6 @@ int guac_terminal_clear_range(guac_terminal* term, } -guac_terminal_delta* guac_terminal_delta_alloc(int width, int height) { - - guac_terminal_operation* current; - int x, y; - - /* Allocate delta */ - guac_terminal_delta* delta = malloc(sizeof(guac_terminal_delta)); - - /* Set width and height */ - delta->width = width; - delta->height = height; - - /* Alloc operations */ - delta->operations = malloc(width * height * - sizeof(guac_terminal_operation)); - - /* Init each operation buffer row */ - current = delta->operations; - for (y=0; ytype = GUAC_CHAR_NOP; - - } - - /* Alloc scratch area */ - delta->scratch = malloc(width * height * sizeof(guac_terminal_operation)); - - return delta; - -} - -void guac_terminal_delta_free(guac_terminal_delta* delta) { - - /* Free operations buffers */ - free(delta->operations); - free(delta->scratch); - - /* Free delta */ - free(delta); - -} - -void guac_terminal_delta_resize(guac_terminal_delta* delta, - int width, int height) { - /* STUB */ -} void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, guac_terminal_char* character) { @@ -714,474 +420,6 @@ void guac_terminal_delta_set_rect(guac_terminal_delta* delta, } -void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta, - guac_terminal* terminal) { - - guac_terminal_operation* current = delta->operations; - int row, col; - - /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { - - /* If operation is a copy operation */ - if (current->type == GUAC_CHAR_COPY) { - - /* The determined bounds of the rectangle of contiguous - * operations */ - int detected_right = -1; - int detected_bottom = row; - - /* The current row or column within a rectangle */ - int rect_row, rect_col; - - /* The dimensions of the rectangle as determined */ - int rect_width, rect_height; - - /* The expected row and column source for the next copy - * operation (if adjacent to current) */ - int expected_row, expected_col; - - /* Current row within a subrect */ - guac_terminal_operation* rect_current_row; - - /* Determine bounds of rectangle */ - rect_current_row = current; - expected_row = current->row; - for (rect_row=row; rect_rowheight; rect_row++) { - - guac_terminal_operation* rect_current = rect_current_row; - expected_col = current->column; - - /* Find width */ - for (rect_col=col; rect_colwidth; rect_col++) { - - /* If not identical operation, stop */ - if (rect_current->type != GUAC_CHAR_COPY - || rect_current->row != expected_row - || rect_current->column != expected_col) - break; - - /* Next column */ - rect_current++; - expected_col++; - - } - - /* If too small, cannot append row */ - if (rect_col-1 < detected_right) - break; - - /* As row has been accepted, update rect_row of rect */ - detected_bottom = rect_row; - - /* For now, only set rect_col bound if uninitialized */ - if (detected_right == -1) - detected_right = rect_col - 1; - - /* Next row */ - rect_current_row += delta->width; - expected_row++; - - } - - /* Calculate dimensions */ - rect_width = detected_right - col + 1; - rect_height = detected_bottom - row + 1; - - /* Mark rect as NOP (as it has been handled) */ - rect_current_row = current; - expected_row = current->row; - for (rect_row=0; rect_rowcolumn; - - for (rect_col=0; rect_coltype == GUAC_CHAR_COPY - && rect_current->row == expected_row - && rect_current->column == expected_col) - rect_current->type = GUAC_CHAR_NOP; - - /* Next column */ - rect_current++; - expected_col++; - - } - - /* Next row */ - rect_current_row += delta->width; - expected_row++; - - } - - /* Send copy */ - guac_protocol_send_copy(terminal->client->socket, - - GUAC_DEFAULT_LAYER, - current->column * terminal->char_width, - current->row * terminal->char_height, - rect_width * terminal->char_width, - rect_height * terminal->char_height, - - GUAC_COMP_OVER, - GUAC_DEFAULT_LAYER, - col * terminal->char_width, - row * terminal->char_height); - - - } /* end if copy operation */ - - /* Next operation */ - current++; - - } - } - -} - -void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta, - guac_terminal* terminal) { - - guac_terminal_operation* current = delta->operations; - int row, col; - - /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { - - /* If operation is a cler operation (set to space) */ - if (current->type == GUAC_CHAR_SET && - current->character.value == ' ') { - - /* The determined bounds of the rectangle of contiguous - * operations */ - int detected_right = -1; - int detected_bottom = row; - - /* The current row or column within a rectangle */ - int rect_row, rect_col; - - /* The dimensions of the rectangle as determined */ - int rect_width, rect_height; - - /* Color of the rectangle to draw */ - int color; - if (current->character.attributes.reverse != current->character.attributes.selected) - color = current->character.attributes.foreground; - else - color = current->character.attributes.background; - - const guac_terminal_color* guac_color = - &guac_terminal_palette[color]; - - /* Current row within a subrect */ - guac_terminal_operation* rect_current_row; - - /* Determine bounds of rectangle */ - rect_current_row = current; - for (rect_row=row; rect_rowheight; rect_row++) { - - guac_terminal_operation* rect_current = rect_current_row; - - /* Find width */ - for (rect_col=col; rect_colwidth; rect_col++) { - - int joining_color; - if (rect_current->character.attributes.reverse) - joining_color = current->character.attributes.foreground; - else - joining_color = current->character.attributes.background; - - /* If not identical operation, stop */ - if (rect_current->type != GUAC_CHAR_SET - || rect_current->character.value != ' ' - || joining_color != color) - break; - - /* Next column */ - rect_current++; - - } - - /* If too small, cannot append row */ - if (rect_col-1 < detected_right) - break; - - /* As row has been accepted, update rect_row of rect */ - detected_bottom = rect_row; - - /* For now, only set rect_col bound if uninitialized */ - if (detected_right == -1) - detected_right = rect_col - 1; - - /* Next row */ - rect_current_row += delta->width; - - } - - /* Calculate dimensions */ - rect_width = detected_right - col + 1; - rect_height = detected_bottom - row + 1; - - /* Mark rect as NOP (as it has been handled) */ - rect_current_row = current; - for (rect_row=0; rect_rowcharacter.attributes.reverse) - joining_color = current->character.attributes.foreground; - else - joining_color = current->character.attributes.background; - - /* Mark clear operations as NOP */ - if (rect_current->type == GUAC_CHAR_SET - && rect_current->character.value == ' ' - && joining_color == color) - rect_current->type = GUAC_CHAR_NOP; - - /* Next column */ - rect_current++; - - } - - /* Next row */ - rect_current_row += delta->width; - - } - - /* Send rect */ - guac_protocol_send_rect(terminal->client->socket, - GUAC_DEFAULT_LAYER, - col * terminal->char_width, - row * terminal->char_height, - rect_width * terminal->char_width, - rect_height * terminal->char_height); - - guac_protocol_send_cfill(terminal->client->socket, - GUAC_COMP_OVER, - GUAC_DEFAULT_LAYER, - guac_color->red, guac_color->green, guac_color->blue, - 0xFF); - - } /* end if clear operation */ - - /* Next operation */ - current++; - - } - } - -} - -void __guac_terminal_delta_flush_set(guac_terminal_delta* delta, - guac_terminal* terminal) { - - guac_terminal_operation* current = delta->operations; - int row, col; - - /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { - - /* Perform given operation */ - if (current->type == GUAC_CHAR_SET) { - - /* Set attributes */ - __guac_terminal_set_colors(terminal, - &(current->character.attributes)); - - /* Send character */ - __guac_terminal_set(terminal, row, col, - current->character.value); - - /* Mark operation as handled */ - current->type = GUAC_CHAR_NOP; - - } - - /* Next operation */ - current++; - - } - } - -} - -void guac_terminal_delta_flush(guac_terminal_delta* delta, - guac_terminal* terminal) { - - /* Flush copy operations first */ - __guac_terminal_delta_flush_copy(delta, terminal); - - /* Flush clear operations (as they're just rects) */ - __guac_terminal_delta_flush_clear(delta, terminal); - - /* Flush set operations (the only operations remaining) */ - __guac_terminal_delta_flush_set(delta, terminal); - -} - -guac_terminal_buffer* guac_terminal_buffer_alloc(int width, int height) { - - /* Allocate buffer */ - guac_terminal_buffer* buffer = malloc(sizeof(guac_terminal_buffer)); - - /* Set width and height */ - buffer->width = width; - buffer->height = height; - - /* Alloc characters */ - buffer->characters = malloc(width * height * - sizeof(guac_terminal_char)); - - return buffer; - -} - -void guac_terminal_buffer_resize(guac_terminal_buffer* buffer, - int width, int height) { - /* STUB */ -} - -void guac_terminal_buffer_free(guac_terminal_buffer* buffer) { - - /* Free characters */ - free(buffer->characters); - - /* Free buffer*/ - free(buffer); - -} - -void guac_terminal_buffer_set(guac_terminal_buffer* buffer, int r, int c, - guac_terminal_char* character) { - - /* Store character */ - buffer->characters[r * buffer->width + c] = *character; - -} - -void guac_terminal_buffer_copy(guac_terminal_buffer* buffer, - int dst_row, int dst_column, - int src_row, int src_column, - int w, int h) { - - int row, column; - - /* FIXME: Handle intersections between src and dst rects */ - - guac_terminal_char* current_row = - &(buffer->characters[dst_row*buffer->width + dst_column]); - - guac_terminal_char* src_current_row = - &(buffer->characters[src_row*buffer->width + src_column]); - - /* Set rectangle to copy operations */ - for (row=0; rowwidth; - src_current_row += buffer->width; - - } - -} - -void guac_terminal_buffer_set_rect(guac_terminal_buffer* buffer, - int row, int column, int w, int h, - guac_terminal_char* character) { - - guac_terminal_char* current_row = - &(buffer->characters[row*buffer->width + column]); - - /* Set rectangle contents to given character */ - for (row=0; rowwidth; - - } - -} - -guac_terminal_scrollback_buffer* - guac_terminal_scrollback_buffer_alloc(int rows) { - - /* Allocate scrollback */ - guac_terminal_scrollback_buffer* buffer = - malloc(sizeof(guac_terminal_scrollback_buffer)); - - int i; - guac_terminal_scrollback_row* row; - - /* Init scrollback data */ - buffer->rows = rows; - buffer->top = 0; - buffer->length = 0; - buffer->scrollback = malloc(sizeof(guac_terminal_scrollback_row) * - buffer->rows); - - /* Init scrollback rows */ - row = buffer->scrollback; - for (i=0; iavailable = 256; - row->length = 0; - row->characters = malloc(sizeof(guac_terminal_char) * row->available); - - /* Next row */ - row++; - - } - - return buffer; - -} - -void guac_terminal_scrollback_buffer_free( - guac_terminal_scrollback_buffer* buffer) { - - int i; - guac_terminal_scrollback_row* row = buffer->scrollback; - - /* Free all rows */ - for (i=0; irows; i++) { - free(row->characters); - row++; - } - - /* Free actual buffer */ - free(buffer->scrollback); - free(buffer); - -} - void guac_terminal_scrollback_buffer_append( guac_terminal_scrollback_buffer* buffer, guac_terminal* terminal, int rows) { From 32e6a07f59ce0dadd16789a009f6b9c7dd72c150 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 25 Apr 2013 11:55:50 -0700 Subject: [PATCH 080/169] Rename delta to display. --- protocols/ssh/Makefile.am | 4 +- protocols/ssh/include/{delta.h => display.h} | 30 +-- protocols/ssh/include/terminal.h | 6 +- protocols/ssh/src/{delta.c => display.c} | 234 +++++++++---------- protocols/ssh/src/ssh_handlers.c | 12 +- protocols/ssh/src/terminal.c | 106 ++++----- 6 files changed, 196 insertions(+), 196 deletions(-) rename protocols/ssh/include/{delta.h => display.h} (86%) rename protocols/ssh/src/{delta.c => display.c} (69%) diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index b8536b15..a3c36d50 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -44,7 +44,7 @@ libguac_client_ssh_la_SOURCES = \ src/blank.c \ src/buffer.c \ src/cursor.c \ - src/delta.c \ + src/display.c \ src/ibar.c \ src/ssh_client.c \ src/ssh_handlers.c \ @@ -55,7 +55,7 @@ noinst_HEADERS = \ include/blank.h \ include/buffer.h \ include/cursor.h \ - include/delta.h \ + include/display.h \ include/ibar.h \ include/ssh_client.h \ include/ssh_handlers.h \ diff --git a/protocols/ssh/include/delta.h b/protocols/ssh/include/display.h similarity index 86% rename from protocols/ssh/include/delta.h rename to protocols/ssh/include/display.h index 246306bb..227b6792 100644 --- a/protocols/ssh/include/delta.h +++ b/protocols/ssh/include/display.h @@ -35,8 +35,8 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef _SSH_GUAC_DELTA_H -#define _SSH_GUAC_DELTA_H +#ifndef _SSH_GUAC_DISPLAY_H +#define _SSH_GUAC_DISPLAY_H #include #include @@ -105,10 +105,10 @@ typedef struct guac_terminal_operation { /** * Set of all pending operations for the currently-visible screen area. */ -typedef struct guac_terminal_delta { +typedef struct guac_terminal_display { /** - * The Guacamole client this delta will use for rendering. + * The Guacamole client this display will use for rendering. */ guac_client* client; @@ -180,49 +180,49 @@ typedef struct guac_terminal_delta { */ guac_layer* filled_glyphs; -} guac_terminal_delta; +} guac_terminal_display; /** - * Allocates a new delta having the given dimensions. + * Allocates a new display having the given dimensions. */ -guac_terminal_delta* guac_terminal_delta_alloc(guac_client* client, int width, int height, +guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int width, int height, int foreground, int background); /** - * Frees the given delta. + * Frees the given display. */ -void guac_terminal_delta_free(guac_terminal_delta* delta); +void guac_terminal_display_free(guac_terminal_display* display); /** * Copies the given range of columns to a new location, offset from * the original by the given number of columns. */ -void guac_terminal_delta_copy_columns(guac_terminal_delta* delta, int row, +void guac_terminal_display_copy_columns(guac_terminal_display* display, int row, int start_column, int end_column, int offset); /** * Copies the given range of rows to a new location, offset from the * original by the given number of rows. */ -void guac_terminal_delta_copy_rows(guac_terminal_delta* delta, int src_row, int rows, +void guac_terminal_display_copy_rows(guac_terminal_display* display, int src_row, int rows, int start_row, int end_row, int offset); /** * Sets the given range of columns within the given row to the given * character. */ -void guac_terminal_delta_set_columns(guac_terminal_delta* delta, int row, +void guac_terminal_display_set_columns(guac_terminal_display* display, int row, int start_column, int end_column, guac_terminal_char* character); /** * Resize the terminal to the given dimensions. */ -void guac_terminal_delta_resize(guac_terminal_delta* delta, int rows, int cols); +void guac_terminal_display_resize(guac_terminal_display* display, int rows, int cols); /** - * Flushes all pending operations within the given guac_terminal_delta. + * Flushes all pending operations within the given guac_terminal_display. */ -void guac_terminal_delta_flush(guac_terminal_delta* delta); +void guac_terminal_display_flush(guac_terminal_display* display); #endif diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 4007b4f3..6b9ecae8 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -44,7 +44,7 @@ #include #include "types.h" -#include "delta.h" +#include "display.h" #include "buffer.h" #define GUAC_SSH_WHEEL_SCROLL_AMOUNT 3 @@ -133,11 +133,11 @@ struct guac_terminal { * The difference between the currently-rendered screen and the current * state of the terminal. */ - guac_terminal_delta* delta; + guac_terminal_display* display; /** * Current terminal display state. All characters present on the screen - * are within this buffer. This has nothing to do with the delta, which + * are within this buffer. This has nothing to do with the display, which * facilitates transfer of a set of changes to the remote display. */ guac_terminal_buffer* buffer; diff --git a/protocols/ssh/src/delta.c b/protocols/ssh/src/display.c similarity index 69% rename from protocols/ssh/src/delta.c rename to protocols/ssh/src/display.c index c686c8f9..e1f24aaa 100644 --- a/protocols/ssh/src/delta.c +++ b/protocols/ssh/src/display.c @@ -41,7 +41,7 @@ #include #include "types.h" -#include "delta.h" +#include "display.h" const guac_terminal_color guac_terminal_palette[16] = { @@ -73,18 +73,18 @@ const guac_terminal_color guac_terminal_palette[16] = { * and thus must be multiplied by the glyph width to obtain the actual * location within the glyph cache layer. */ -int __guac_terminal_get_glyph(guac_terminal_delta* delta, char c) { +int __guac_terminal_get_glyph(guac_terminal_display* display, char c) { - guac_socket* socket = delta->client->socket; + guac_socket* socket = display->client->socket; int location; /* Use foreground color */ const guac_terminal_color* color = - &guac_terminal_palette[delta->glyph_foreground]; + &guac_terminal_palette[display->glyph_foreground]; /* Use background color */ const guac_terminal_color* background = - &guac_terminal_palette[delta->glyph_background]; + &guac_terminal_palette[display->glyph_background]; cairo_surface_t* surface; cairo_t* cairo; @@ -92,20 +92,20 @@ int __guac_terminal_get_glyph(guac_terminal_delta* delta, char c) { PangoLayout* layout; /* Return glyph if exists */ - if (delta->glyphs[(int) c]) - return delta->glyphs[(int) c] - 1; + if (display->glyphs[(int) c]) + return display->glyphs[(int) c] - 1; - location = delta->next_glyph++; + location = display->next_glyph++; /* Otherwise, draw glyph */ surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, - delta->char_width, delta->char_height); + display->char_width, display->char_height); cairo = cairo_create(surface); /* Get layout */ layout = pango_cairo_create_layout(cairo); - pango_layout_set_font_description(layout, delta->font_desc); + pango_layout_set_font_description(layout, display->font_desc); pango_layout_set_text(layout, &c, 1); /* Draw */ @@ -123,23 +123,23 @@ int __guac_terminal_get_glyph(guac_terminal_delta* delta, char c) { cairo_destroy(cairo); /* Send glyph and update filled flyphs */ - guac_protocol_send_png(socket, GUAC_COMP_OVER, delta->glyph_stroke, location * delta->char_width, 0, surface); + guac_protocol_send_png(socket, GUAC_COMP_OVER, display->glyph_stroke, location * display->char_width, 0, surface); - guac_protocol_send_rect(socket, delta->filled_glyphs, - location * delta->char_width, 0, - delta->char_width, delta->char_height); + guac_protocol_send_rect(socket, display->filled_glyphs, + location * display->char_width, 0, + display->char_width, display->char_height); - guac_protocol_send_cfill(socket, GUAC_COMP_OVER, delta->filled_glyphs, + guac_protocol_send_cfill(socket, GUAC_COMP_OVER, display->filled_glyphs, background->red, background->green, background->blue, 0xFF); - guac_protocol_send_copy(socket, delta->glyph_stroke, - location * delta->char_width, 0, delta->char_width, delta->char_height, - GUAC_COMP_OVER, delta->filled_glyphs, location * delta->char_width, 0); + guac_protocol_send_copy(socket, display->glyph_stroke, + location * display->char_width, 0, display->char_width, display->char_height, + GUAC_COMP_OVER, display->filled_glyphs, location * display->char_width, 0); - delta->glyphs[(int) c] = location+1; + display->glyphs[(int) c] = location+1; cairo_surface_destroy(surface); @@ -152,10 +152,10 @@ int __guac_terminal_get_glyph(guac_terminal_delta* delta, char c) { * Sets the attributes of the glyph cache layer such that future copies from * this layer will display as expected. */ -int __guac_terminal_set_colors(guac_terminal_delta* delta, +int __guac_terminal_set_colors(guac_terminal_display* display, guac_terminal_attributes* attributes) { - guac_socket* socket = delta->client->socket; + guac_socket* socket = display->client->socket; const guac_terminal_color* background_color; int background, foreground; @@ -177,18 +177,18 @@ int __guac_terminal_set_colors(guac_terminal_delta* delta, background_color = &guac_terminal_palette[background]; /* If foreground different from current, colorize */ - if (foreground != delta->glyph_foreground) { + if (foreground != display->glyph_foreground) { /* Get color */ const guac_terminal_color* color = &guac_terminal_palette[foreground]; /* Colorize letter */ - guac_protocol_send_rect(socket, delta->glyph_stroke, + guac_protocol_send_rect(socket, display->glyph_stroke, 0, 0, - delta->char_width * delta->next_glyph, delta->char_height); + display->char_width * display->next_glyph, display->char_height); - guac_protocol_send_cfill(socket, GUAC_COMP_ATOP, delta->glyph_stroke, + guac_protocol_send_cfill(socket, GUAC_COMP_ATOP, display->glyph_stroke, color->red, color->green, color->blue, @@ -197,33 +197,33 @@ int __guac_terminal_set_colors(guac_terminal_delta* delta, } /* If any color change at all, update filled */ - if (foreground != delta->glyph_foreground - || background != delta->glyph_background) { + if (foreground != display->glyph_foreground + || background != display->glyph_background) { /* Set background */ - guac_protocol_send_rect(socket, delta->filled_glyphs, + guac_protocol_send_rect(socket, display->filled_glyphs, 0, 0, - delta->char_width * delta->next_glyph, delta->char_height); + display->char_width * display->next_glyph, display->char_height); - guac_protocol_send_cfill(socket, GUAC_COMP_OVER, delta->filled_glyphs, + guac_protocol_send_cfill(socket, GUAC_COMP_OVER, display->filled_glyphs, background_color->red, background_color->green, background_color->blue, 255); /* Copy stroke */ - guac_protocol_send_copy(socket, delta->glyph_stroke, + guac_protocol_send_copy(socket, display->glyph_stroke, 0, 0, - delta->char_width * delta->next_glyph, delta->char_height, + display->char_width * display->next_glyph, display->char_height, - GUAC_COMP_OVER, delta->filled_glyphs, + GUAC_COMP_OVER, display->filled_glyphs, 0, 0); } - delta->glyph_foreground = foreground; - delta->glyph_background = background; + display->glyph_foreground = foreground; + display->glyph_background = background; return 0; @@ -231,25 +231,25 @@ int __guac_terminal_set_colors(guac_terminal_delta* delta, /** * Sends the given character to the terminal at the given row and column, - * rendering the charater immediately. This bypasses the guac_terminal_delta + * rendering the charater immediately. This bypasses the guac_terminal_display * mechanism and is intended for flushing of updates only. */ -int __guac_terminal_set(guac_terminal_delta* delta, int row, int col, char c) { +int __guac_terminal_set(guac_terminal_display* display, int row, int col, char c) { - guac_socket* socket = delta->client->socket; - int location = __guac_terminal_get_glyph(delta, c); + guac_socket* socket = display->client->socket; + int location = __guac_terminal_get_glyph(display, c); return guac_protocol_send_copy(socket, - delta->filled_glyphs, - location * delta->char_width, 0, delta->char_width, delta->char_height, + display->filled_glyphs, + location * display->char_width, 0, display->char_width, display->char_height, GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, - delta->char_width * col, - delta->char_height * row); + display->char_width * col, + display->char_height * row); } -guac_terminal_delta* guac_terminal_delta_alloc(guac_client* client, int width, int height, +guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int width, int height, int foreground, int background) { guac_terminal_operation* current; @@ -260,55 +260,55 @@ guac_terminal_delta* guac_terminal_delta_alloc(guac_client* client, int width, i PangoFontMetrics* metrics; PangoContext* context; - /* Allocate delta */ - guac_terminal_delta* delta = malloc(sizeof(guac_terminal_delta)); - delta->client = client; + /* Allocate display */ + guac_terminal_display* display = malloc(sizeof(guac_terminal_display)); + display->client = client; - memset(delta->glyphs, 0, sizeof(delta->glyphs)); - delta->glyph_stroke = guac_client_alloc_buffer(client); - delta->filled_glyphs = guac_client_alloc_buffer(client); + memset(display->glyphs, 0, sizeof(display->glyphs)); + display->glyph_stroke = guac_client_alloc_buffer(client); + display->filled_glyphs = guac_client_alloc_buffer(client); /* Get font */ - delta->font_desc = pango_font_description_new(); - pango_font_description_set_family(delta->font_desc, "monospace"); - pango_font_description_set_weight(delta->font_desc, PANGO_WEIGHT_NORMAL); - pango_font_description_set_size(delta->font_desc, 12*PANGO_SCALE); + display->font_desc = pango_font_description_new(); + pango_font_description_set_family(display->font_desc, "monospace"); + pango_font_description_set_weight(display->font_desc, PANGO_WEIGHT_NORMAL); + pango_font_description_set_size(display->font_desc, 12*PANGO_SCALE); font_map = pango_cairo_font_map_get_default(); context = pango_font_map_create_context(font_map); - font = pango_font_map_load_font(font_map, context, delta->font_desc); + font = pango_font_map_load_font(font_map, context, display->font_desc); if (font == NULL) { - guac_client_log_error(delta->client, "Unable to get font."); + guac_client_log_error(display->client, "Unable to get font."); return NULL; } metrics = pango_font_get_metrics(font, NULL); if (metrics == NULL) { - guac_client_log_error(delta->client, "Unable to get font metrics."); + guac_client_log_error(display->client, "Unable to get font metrics."); return NULL; } - delta->glyph_foreground = foreground; - delta->glyph_background = background; + display->glyph_foreground = foreground; + display->glyph_background = background; /* Calculate character dimensions */ - delta->char_width = + display->char_width = pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE; - delta->char_height = + display->char_height = (pango_font_metrics_get_descent(metrics) + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; /* Set width and height */ - delta->width = width; - delta->height = height; + display->width = width; + display->height = height; /* Alloc operations */ - delta->operations = malloc(width * height * + display->operations = malloc(width * height * sizeof(guac_terminal_operation)); /* Init each operation buffer row */ - current = delta->operations; + current = display->operations; for (y=0; yscratch = malloc(width * height * sizeof(guac_terminal_operation)); + display->scratch = malloc(width * height * sizeof(guac_terminal_operation)); /* Send initial display size */ guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER, - delta->char_width * width, - delta->char_height * height); + display->char_width * width, + display->char_height * height); - return delta; + return display; } -void guac_terminal_delta_free(guac_terminal_delta* delta) { +void guac_terminal_display_free(guac_terminal_display* display) { /* Free operations buffers */ - free(delta->operations); - free(delta->scratch); + free(display->operations); + free(display->scratch); - /* Free delta */ - free(delta); + /* Free display */ + free(display); } -void guac_terminal_delta_copy_columns(guac_terminal_delta* delta, int row, +void guac_terminal_display_copy_columns(guac_terminal_display* display, int row, int start_column, int end_column, int offset) { /* STUB */ } -void guac_terminal_delta_copy_rows(guac_terminal_delta* delta, int src_row, int rows, +void guac_terminal_display_copy_rows(guac_terminal_display* display, int src_row, int rows, int start_row, int end_row, int offset) { /* STUB */ } -void guac_terminal_delta_set_columns(guac_terminal_delta* delta, int row, +void guac_terminal_display_set_columns(guac_terminal_display* display, int row, int start_column, int end_column, guac_terminal_char* character) { /* STUB */ } -void guac_terminal_delta_resize(guac_terminal_delta* delta, int rows, int cols) { +void guac_terminal_display_resize(guac_terminal_display* display, int rows, int cols) { /* STUB */ } -void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta) { +void __guac_terminal_display_flush_copy(guac_terminal_display* display) { - guac_terminal_operation* current = delta->operations; + guac_terminal_operation* current = display->operations; int row, col; /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { /* If operation is a copy operation */ if (current->type == GUAC_CHAR_COPY) { @@ -393,13 +393,13 @@ void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta) { /* Determine bounds of rectangle */ rect_current_row = current; expected_row = current->row; - for (rect_row=row; rect_rowheight; rect_row++) { + for (rect_row=row; rect_rowheight; rect_row++) { guac_terminal_operation* rect_current = rect_current_row; expected_col = current->column; /* Find width */ - for (rect_col=col; rect_colwidth; rect_col++) { + for (rect_col=col; rect_colwidth; rect_col++) { /* If not identical operation, stop */ if (rect_current->type != GUAC_CHAR_COPY @@ -425,7 +425,7 @@ void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta) { detected_right = rect_col - 1; /* Next row */ - rect_current_row += delta->width; + rect_current_row += display->width; expected_row++; } @@ -457,24 +457,24 @@ void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta) { } /* Next row */ - rect_current_row += delta->width; + rect_current_row += display->width; expected_row++; } /* Send copy */ - guac_protocol_send_copy(delta->client->socket, + guac_protocol_send_copy(display->client->socket, GUAC_DEFAULT_LAYER, - current->column * delta->char_width, - current->row * delta->char_height, - rect_width * delta->char_width, - rect_height * delta->char_height, + current->column * display->char_width, + current->row * display->char_height, + rect_width * display->char_width, + rect_height * display->char_height, GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, - col * delta->char_width, - row * delta->char_height); + col * display->char_width, + row * display->char_height); } /* end if copy operation */ @@ -486,14 +486,14 @@ void __guac_terminal_delta_flush_copy(guac_terminal_delta* delta) { } -void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta) { +void __guac_terminal_display_flush_clear(guac_terminal_display* display) { - guac_terminal_operation* current = delta->operations; + guac_terminal_operation* current = display->operations; int row, col; /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { /* If operation is a cler operation (set to space) */ if (current->type == GUAC_CHAR_SET && @@ -525,12 +525,12 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta) { /* Determine bounds of rectangle */ rect_current_row = current; - for (rect_row=row; rect_rowheight; rect_row++) { + for (rect_row=row; rect_rowheight; rect_row++) { guac_terminal_operation* rect_current = rect_current_row; /* Find width */ - for (rect_col=col; rect_colwidth; rect_col++) { + for (rect_col=col; rect_colwidth; rect_col++) { int joining_color; if (rect_current->character.attributes.reverse) @@ -561,7 +561,7 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta) { detected_right = rect_col - 1; /* Next row */ - rect_current_row += delta->width; + rect_current_row += display->width; } @@ -595,19 +595,19 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta) { } /* Next row */ - rect_current_row += delta->width; + rect_current_row += display->width; } /* Send rect */ - guac_protocol_send_rect(delta->client->socket, + guac_protocol_send_rect(display->client->socket, GUAC_DEFAULT_LAYER, - col * delta->char_width, - row * delta->char_height, - rect_width * delta->char_width, - rect_height * delta->char_height); + col * display->char_width, + row * display->char_height, + rect_width * display->char_width, + rect_height * display->char_height); - guac_protocol_send_cfill(delta->client->socket, + guac_protocol_send_cfill(display->client->socket, GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, guac_color->red, guac_color->green, guac_color->blue, @@ -623,24 +623,24 @@ void __guac_terminal_delta_flush_clear(guac_terminal_delta* delta) { } -void __guac_terminal_delta_flush_set(guac_terminal_delta* delta) { +void __guac_terminal_display_flush_set(guac_terminal_display* display) { - guac_terminal_operation* current = delta->operations; + guac_terminal_operation* current = display->operations; int row, col; /* For each operation */ - for (row=0; rowheight; row++) { - for (col=0; colwidth; col++) { + for (row=0; rowheight; row++) { + for (col=0; colwidth; col++) { /* Perform given operation */ if (current->type == GUAC_CHAR_SET) { /* Set attributes */ - __guac_terminal_set_colors(delta, + __guac_terminal_set_colors(display, &(current->character.attributes)); /* Send character */ - __guac_terminal_set(delta, row, col, + __guac_terminal_set(display, row, col, current->character.value); /* Mark operation as handled */ @@ -656,12 +656,12 @@ void __guac_terminal_delta_flush_set(guac_terminal_delta* delta) { } -void guac_terminal_delta_flush(guac_terminal_delta* delta) { +void guac_terminal_display_flush(guac_terminal_display* display) { /* Flush operations, copies first, then clears, then sets. */ - __guac_terminal_delta_flush_copy(delta); - __guac_terminal_delta_flush_clear(delta); - __guac_terminal_delta_flush_set(delta); + __guac_terminal_display_flush_copy(display); + __guac_terminal_display_flush_clear(display); + __guac_terminal_display_flush_set(display); } diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index e425a2ca..26bba07a 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -117,8 +117,8 @@ int ssh_guac_client_handle_messages(guac_client* client) { client_data->term->cursor_row, client_data->term->cursor_col); - /* Flush terminal delta */ - guac_terminal_delta_flush(client_data->term->delta); + /* Flush terminal display */ + guac_terminal_display_flush(client_data->term->display); /* Unlock terminal access */ pthread_mutex_unlock(&(client_data->term->lock)); @@ -186,8 +186,8 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { /* Otherwise, just update */ else guac_terminal_select_update(term, - y / term->delta->char_height - term->scroll_offset, - x / term->delta->char_width); + y / term->display->char_height - term->scroll_offset, + x / term->display->char_width); pthread_mutex_unlock(&(term->lock)); } @@ -197,8 +197,8 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { pthread_mutex_lock(&(term->lock)); guac_terminal_select_start(term, - y / term->delta->char_height - term->scroll_offset, - x / term->delta->char_width); + y / term->display->char_height - term->scroll_offset, + x / term->display->char_width); pthread_mutex_unlock(&(term->lock)); } diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index fec36344..fc3a118d 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -49,7 +49,7 @@ #include "types.h" #include "buffer.h" -#include "delta.h" +#include "display.h" #include "terminal.h" #include "terminal_handlers.h" @@ -71,8 +71,8 @@ guac_terminal* guac_terminal_create(guac_client* client, term->buffer = guac_terminal_buffer_alloc(1000); term->scroll_offset = 0; - /* Init delta */ - term->delta = guac_terminal_delta_alloc(client, + /* Init display */ + term->display = guac_terminal_display_alloc(client, term->term_width, term->term_height, default_attributes.foreground, default_attributes.background); @@ -84,8 +84,8 @@ guac_terminal* guac_terminal_create(guac_client* client, term->cursor_row = 0; term->cursor_col = 0; - term->term_width = width / term->delta->char_width; - term->term_height = height / term->delta->char_height; + term->term_width = width / term->display->char_width; + term->term_height = height / term->display->char_height; term->char_handler = guac_terminal_echo; term->scroll_start = 0; @@ -106,8 +106,8 @@ guac_terminal* guac_terminal_create(guac_client* client, void guac_terminal_free(guac_terminal* term) { - /* Free delta */ - guac_terminal_delta_free(term->delta); + /* Free display */ + guac_terminal_display_free(term->display); /* Free buffer */ guac_terminal_buffer_free(term->buffer); @@ -123,9 +123,9 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { guac_char.value = c; guac_char.attributes = term->current_attributes; - /* Set delta */ - if (scrolled_row < term->delta->height) - guac_terminal_delta_set_columns(term->delta, scrolled_row, col, col, &guac_char); + /* Set display */ + if (scrolled_row < term->display->height) + guac_terminal_display_set_columns(term->display, scrolled_row, col, col, &guac_char); /* Set buffer */ guac_terminal_buffer_set_columns(term->buffer, row, col, col, &guac_char); @@ -145,9 +145,9 @@ int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col) { /* Toggle reverse */ guac_char->attributes.reverse = !(guac_char->attributes.reverse); - /* Set delta */ - if (scrolled_row < term->delta->height) - guac_terminal_delta_set(term->delta, scrolled_row, col, guac_char); + /* Set display */ + if (scrolled_row < term->display->height) + guac_terminal_display_set(term->display, scrolled_row, col, guac_char); return 0; @@ -174,22 +174,22 @@ int guac_terminal_copy(guac_terminal* term, int scrolled_rows = rows; /* FIXME: If source (but not dest) is partially scrolled out of view, then - * the delta will not be updated properly. We need to pull the data + * the display will not be updated properly. We need to pull the data * from the buffer in such a case. */ - if (scrolled_src_row < term->delta->height && - scrolled_dst_row < term->delta->height) { + if (scrolled_src_row < term->display->height && + scrolled_dst_row < term->display->height) { - /* Adjust delta rect height if scrolled out of view */ - if (scrolled_src_row + scrolled_rows > term->delta->height) - scrolled_rows = term->delta->height - scrolled_src_row; + /* Adjust display rect height if scrolled out of view */ + if (scrolled_src_row + scrolled_rows > term->display->height) + scrolled_rows = term->display->height - scrolled_src_row; - if (scrolled_dst_row + scrolled_rows > term->delta->height) - scrolled_rows = term->delta->height - scrolled_dst_row; + if (scrolled_dst_row + scrolled_rows > term->display->height) + scrolled_rows = term->display->height - scrolled_dst_row; - /* Update delta */ - guac_terminal_delta_copy(term->delta, + /* Update display */ + guac_terminal_display_copy(term->display, scrolled_dst_row, dst_col, scrolled_src_row, src_col, cols, rows); @@ -218,14 +218,14 @@ int guac_terminal_clear(guac_terminal* term, character.value = ' '; character.attributes = term->current_attributes; - if (scrolled_row < term->delta->height) { + if (scrolled_row < term->display->height) { - /* Adjust delta rect height if scrolled out of view */ - if (scrolled_row + scrolled_rows > term->delta->height) - scrolled_rows = term->delta->height - scrolled_row; + /* Adjust display rect height if scrolled out of view */ + if (scrolled_row + scrolled_rows > term->display->height) + scrolled_rows = term->display->height - scrolled_row; /* Fill with color */ - guac_terminal_delta_set_rect(term->delta, + guac_terminal_display_set_rect(term->display, scrolled_row, col, cols, scrolled_rows, &character); } @@ -324,11 +324,11 @@ int guac_terminal_clear_range(guac_terminal* term, } -void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, +void guac_terminal_display_set(guac_terminal_display* display, int r, int c, guac_terminal_char* character) { /* Get operation at coordinate */ - guac_terminal_operation* op = &(delta->operations[r*delta->width + c]); + guac_terminal_operation* op = &(display->operations[r*display->width + c]); /* Store operation */ op->type = GUAC_CHAR_SET; @@ -336,7 +336,7 @@ void guac_terminal_delta_set(guac_terminal_delta* delta, int r, int c, } -void guac_terminal_delta_copy(guac_terminal_delta* delta, +void guac_terminal_display_copy(guac_terminal_display* display, int dst_row, int dst_column, int src_row, int src_column, int w, int h) { @@ -345,14 +345,14 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, /* FIXME: Handle intersections between src and dst rects */ - memcpy(delta->scratch, delta->operations, - sizeof(guac_terminal_operation) * delta->width * delta->height); + memcpy(display->scratch, display->operations, + sizeof(guac_terminal_operation) * display->width * display->height); guac_terminal_operation* current_row = - &(delta->operations[dst_row*delta->width + dst_column]); + &(display->operations[dst_row*display->width + dst_column]); guac_terminal_operation* src_current_row = - &(delta->scratch[src_row*delta->width + src_column]); + &(display->scratch[src_row*display->width + src_column]); /* Set rectangle to copy operations */ for (row=0; rowtype != GUAC_CHAR_NOP) *current = *src_current; @@ -381,8 +381,8 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, } /* Next row */ - current_row += delta->width; - src_current_row += delta->width; + current_row += display->width; + src_current_row += display->width; } @@ -390,12 +390,12 @@ void guac_terminal_delta_copy(guac_terminal_delta* delta, } -void guac_terminal_delta_set_rect(guac_terminal_delta* delta, +void guac_terminal_display_set_rect(guac_terminal_display* display, int row, int column, int w, int h, guac_terminal_char* character) { guac_terminal_operation* current_row = - &(delta->operations[row*delta->width + column]); + &(display->operations[row*display->width + column]); /* Set rectangle contents to given character */ for (row=0; rowwidth; + current_row += display->width; } @@ -494,7 +494,7 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, /* Shift screen up */ if (terminal->term_height > scroll_amount) - guac_terminal_delta_copy(terminal->delta, + guac_terminal_display_copy(terminal->display, 0, 0, /* Destination row, col */ scroll_amount, 0, /* source row,col */ terminal->term_width, terminal->term_height - scroll_amount); @@ -514,7 +514,7 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, guac_terminal_char* current = guac_terminal_get_row(terminal, row, &length); for (column=0; columndelta, dest_row, column, + guac_terminal_display_set(terminal->display, dest_row, column, current++); /* Next row */ @@ -523,7 +523,7 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, } /* FIXME: Should flush somewhere more sensible */ - guac_terminal_delta_flush(terminal->delta, terminal); + guac_terminal_display_flush(terminal->display, terminal); guac_socket_flush(terminal->client->socket); } @@ -546,7 +546,7 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, /* Shift screen down */ if (terminal->term_height > scroll_amount) - guac_terminal_delta_copy(terminal->delta, + guac_terminal_display_copy(terminal->display, scroll_amount, 0, /* Destination row,col */ 0, 0, /* Source row, col */ terminal->term_width, terminal->term_height - scroll_amount); @@ -570,7 +570,7 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, /* FIXME: Clear row first */ guac_terminal_char* current = scrollback_row->characters; for (column=0; columnlength; column++) - guac_terminal_delta_set(terminal->delta, dest_row, column, + guac_terminal_display_set(terminal->display, dest_row, column, current++); /* Next row */ @@ -579,7 +579,7 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, } /* FIXME: Should flush somewhere more sensible */ - guac_terminal_delta_flush(terminal->delta, terminal); + guac_terminal_display_flush(terminal->display, terminal); guac_socket_flush(terminal->client->socket); } @@ -610,14 +610,14 @@ void guac_terminal_select_start(guac_terminal* terminal, int row, int column) { /* Get char and operation */ guac_char = &(terminal->buffer->characters[terminal->buffer->width * row + column]); - guac_operation = &(terminal->delta->operations[terminal->delta->width * row + column]); + guac_operation = &(terminal->display->operations[terminal->display->width * row + column]); /* Set character as selected */ guac_char->attributes.selected = true; guac_operation->type = GUAC_CHAR_SET; guac_operation->character = *guac_char; - guac_terminal_delta_flush(terminal->delta, terminal); + guac_terminal_display_flush(terminal->display, terminal); guac_socket_flush(terminal->client->socket); } @@ -676,7 +676,7 @@ void guac_terminal_select_update(guac_terminal* terminal, int row, int column) { /* Get first character */ guac_char = &(terminal->buffer->characters[search_index_a]); - guac_operation = &(terminal->delta->operations[search_index_a]); + guac_operation = &(terminal->display->operations[search_index_a]); /* Invert modified area */ for (i=search_index_a; i<=search_index_b; i++) { @@ -710,7 +710,7 @@ void guac_terminal_select_update(guac_terminal* terminal, int row, int column) { terminal->selection_end_row = row; terminal->selection_end_column = column; - guac_terminal_delta_flush(terminal->delta, terminal); + guac_terminal_display_flush(terminal->display, terminal); guac_socket_flush(terminal->client->socket); } @@ -743,7 +743,7 @@ void guac_terminal_select_end(guac_terminal* terminal) { /* Get first character */ guac_char = &(terminal->buffer->characters[start_index]); - guac_operation = &(terminal->delta->operations[start_index]); + guac_operation = &(terminal->display->operations[start_index]); /* Restore state from buffer */ for (i=start_index; i<=end_index; i++) { @@ -761,7 +761,7 @@ void guac_terminal_select_end(guac_terminal* terminal) { terminal->text_selected = false; - guac_terminal_delta_flush(terminal->delta, terminal); + guac_terminal_display_flush(terminal->display, terminal); guac_socket_flush(terminal->client->socket); } From bed877c2fe92461f897cd74d1accb013b074a965 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 25 Apr 2013 12:10:01 -0700 Subject: [PATCH 081/169] Add get_row function. --- protocols/ssh/include/buffer.h | 5 ++ protocols/ssh/src/buffer.c | 12 +++++ protocols/ssh/src/terminal.c | 87 +++++----------------------------- 3 files changed, 28 insertions(+), 76 deletions(-) diff --git a/protocols/ssh/include/buffer.h b/protocols/ssh/include/buffer.h index 6a9ce77f..7a4299e7 100644 --- a/protocols/ssh/include/buffer.h +++ b/protocols/ssh/include/buffer.h @@ -107,6 +107,11 @@ guac_terminal_buffer* guac_terminal_buffer_alloc(int rows); */ void guac_terminal_buffer_free(guac_terminal_buffer* buffer); +/** + * Returns the row at the given location. + */ +guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buffer, int row); + /** * Copies the given range of columns to a new location, offset from * the original by the given number of columns. diff --git a/protocols/ssh/src/buffer.c b/protocols/ssh/src/buffer.c index 43730304..f6df30e9 100644 --- a/protocols/ssh/src/buffer.c +++ b/protocols/ssh/src/buffer.c @@ -90,6 +90,18 @@ void guac_terminal_buffer_free(guac_terminal_buffer* buffer) { } +guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buffer, int row) { + + /* Calculate scrollback row index */ + int index = buffer->top + row; + if (index < 0) index += buffer->available; + + /* Return found row */ + return &(buffer->rows[index]); + +} + + void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row, int start_column, int end_column, int offset) { /* STUB */ diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index fc3a118d..0651ce19 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -420,63 +420,6 @@ void guac_terminal_display_set_rect(guac_terminal_display* display, } -void guac_terminal_scrollback_buffer_append( - guac_terminal_scrollback_buffer* buffer, - guac_terminal* terminal, int rows) { - - int row, column; - - /* Copy data into scrollback */ - guac_terminal_scrollback_row* scrollback_row = - &(buffer->scrollback[buffer->top]); - guac_terminal_char* current = terminal->buffer->characters; - - for (row=0; rowcharacters; - for (column=0; column < terminal->buffer->width; column++) - *(dest++) = *(current++); - - scrollback_row->length = terminal->buffer->width; - - /* Next scrollback row */ - scrollback_row++; - buffer->top++; - - /* Wrap around when bottom reached */ - if (buffer->top == buffer->rows) { - buffer->top = 0; - scrollback_row = buffer->scrollback; - } - - } /* end for each row */ - - /* Increment row count */ - buffer->length += rows; - if (buffer->length > buffer->rows) - buffer->length = buffer->rows; - -} - -guac_terminal_char* guac_terminal_get_row(guac_terminal* terminal, int row, int* length) { - - /* If row in past, pull from scrollback */ - if (row < 0) { - guac_terminal_scrollback_row* scrollback_row = - guac_terminal_scrollback_buffer_get_row(terminal->scrollback, row); - - *length = scrollback_row->length; - return scrollback_row->characters; - } - - *length = terminal->buffer->width; - return &(terminal->buffer->characters[terminal->buffer->width * row]); - -} - void guac_terminal_scroll_display_down(guac_terminal* terminal, int scroll_amount) { @@ -510,10 +453,14 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, /* Draw new rows from scrollback */ for (row=start_row; row<=end_row; row++) { - int length; - guac_terminal_char* current = guac_terminal_get_row(terminal, row, &length); + /* Get row from scrollback */ + guac_terminal_buffer_row* buffer_row = + guac_terminal_buffer_get_row(terminal, row); - for (column=0; columncharacters; + for (column=0; columnlength; column++) guac_terminal_display_set(terminal->display, dest_row, column, current++); @@ -563,13 +510,13 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, for (row=start_row; row<=end_row; row++) { /* Get row from scrollback */ - guac_terminal_scrollback_row* scrollback_row = - guac_terminal_scrollback_buffer_get_row(terminal->scrollback, row); + guac_terminal_buffer_row* buffer_row = + guac_terminal_buffer_get_row(terminal->buffer, row); /* Draw row */ /* FIXME: Clear row first */ - guac_terminal_char* current = scrollback_row->characters; - for (column=0; columnlength; column++) + guac_terminal_char* current = buffer_row->characters; + for (column=0; columnlength; column++) guac_terminal_display_set(terminal->display, dest_row, column, current++); @@ -584,18 +531,6 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, } -guac_terminal_scrollback_row* guac_terminal_scrollback_buffer_get_row( - guac_terminal_scrollback_buffer* buffer, int row) { - - /* Calculate scrollback row index */ - int index = buffer->top + row; - if (index < 0) index += buffer->rows; - - /* Return found row */ - return &(buffer->scrollback[index]); - -} - void guac_terminal_select_start(guac_terminal* terminal, int row, int column) { guac_terminal_char* guac_char; From d3db89d3b9f774b3c894e89a4e3da4543ffe357b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 26 Apr 2013 01:53:19 -0700 Subject: [PATCH 082/169] Fix compile errors. Add logs to stubs. --- protocols/ssh/include/buffer.h | 8 +- protocols/ssh/include/display.h | 2 +- protocols/ssh/include/terminal.h | 16 +- protocols/ssh/src/buffer.c | 25 +- protocols/ssh/src/display.c | 11 +- protocols/ssh/src/terminal.c | 383 ++++++-------------------- protocols/ssh/src/terminal_handlers.c | 58 ++-- 7 files changed, 151 insertions(+), 352 deletions(-) diff --git a/protocols/ssh/include/buffer.h b/protocols/ssh/include/buffer.h index 7a4299e7..83c05cce 100644 --- a/protocols/ssh/include/buffer.h +++ b/protocols/ssh/include/buffer.h @@ -112,6 +112,12 @@ void guac_terminal_buffer_free(guac_terminal_buffer* buffer); */ guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buffer, int row); +/** + * Ensures the given row has at least the given number of character spaces available. If new characters + * must be added, they are initialized with the given fill character. + */ +void guac_terminal_buffer_prepare_row(guac_terminal_buffer_row* row, int width, guac_terminal_char* fill); + /** * Copies the given range of columns to a new location, offset from * the original by the given number of columns. @@ -123,7 +129,7 @@ void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row, * Copies the given range of rows to a new location, offset from the * original by the given number of rows. */ -void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int src_row, int rows, +void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int start_row, int end_row, int offset); /** diff --git a/protocols/ssh/include/display.h b/protocols/ssh/include/display.h index 227b6792..7c01825e 100644 --- a/protocols/ssh/include/display.h +++ b/protocols/ssh/include/display.h @@ -204,7 +204,7 @@ void guac_terminal_display_copy_columns(guac_terminal_display* display, int row, * Copies the given range of rows to a new location, offset from the * original by the given number of rows. */ -void guac_terminal_display_copy_rows(guac_terminal_display* display, int src_row, int rows, +void guac_terminal_display_copy_rows(guac_terminal_display* display, int start_row, int end_row, int offset); /** diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 6b9ecae8..993be802 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -118,10 +118,11 @@ struct guac_terminal { guac_terminal_attributes current_attributes; /** - * The attributes which will be applied to characters by default, unless - * other attributes are explicitly specified. + * The character whose attributes dictate the default attributes + * of all characters. When new screen space is allocated, this + * character fills the gaps. */ - guac_terminal_attributes default_attributes; + guac_terminal_char default_char; /** * Handler which will receive all printed characters, updating the terminal @@ -192,11 +193,10 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size); int guac_terminal_set(guac_terminal* term, int row, int col, char c); /** - * Clears a rectangular region of characters, replacing them with the - * current background color and attributes. + * Clears the given region within a single row. */ -int guac_terminal_clear(guac_terminal* term, - int row, int col, int rows, int cols); +int guac_terminal_clear_columns(guac_terminal* term, + int row, int start_col, int end_col); /** * Clears the given region from right-to-left, top-to-bottom, replacing @@ -265,7 +265,7 @@ void guac_terminal_copy_columns(guac_terminal* terminal, int row, * Copies the given range of rows to a new location, offset from the * original by the given number of rows. */ -void guac_terminal_copy_rows(guac_terminal* terminal, int src_row, int rows, +void guac_terminal_copy_rows(guac_terminal* terminal, int start_row, int end_row, int offset); /** diff --git a/protocols/ssh/src/buffer.c b/protocols/ssh/src/buffer.c index f6df30e9..bfc3951c 100644 --- a/protocols/ssh/src/buffer.c +++ b/protocols/ssh/src/buffer.c @@ -101,13 +101,36 @@ guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buf } +void guac_terminal_buffer_prepare_row(guac_terminal_buffer_row* row, int width, guac_terminal_char* fill) { + + int i; + guac_terminal_char* first; + + /* If already wide enough, nothing to do. */ + if (width < row->length) + return; + + /* Expand if necessary */ + if (width > row->available) { + row->available = width*2; + row->characters = realloc(row->characters, sizeof(guac_terminal_char) * row->available); + } + + /* Initialize new part of row */ + first = &(row->characters[row->length]); + for (i=row->length; ilength = width; + +} void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row, int start_column, int end_column, int offset) { /* STUB */ } -void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int src_row, int rows, +void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int start_row, int end_row, int offset) { /* STUB */ } diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index e1f24aaa..91561b90 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -344,16 +344,25 @@ void guac_terminal_display_free(guac_terminal_display* display) { void guac_terminal_display_copy_columns(guac_terminal_display* display, int row, int start_column, int end_column, int offset) { /* STUB */ + guac_client_log_info(display->client, + "display_copy_columns: row=%i, start=%i, end=%i, offset=%i", + row, start_column, end_column, offset); } -void guac_terminal_display_copy_rows(guac_terminal_display* display, int src_row, int rows, +void guac_terminal_display_copy_rows(guac_terminal_display* display, int start_row, int end_row, int offset) { /* STUB */ + guac_client_log_info(display->client, + "display_copy_rows: start=%i, end=%i, offset=%i", + start_row, end_row, offset); } void guac_terminal_display_set_columns(guac_terminal_display* display, int row, int start_column, int end_column, guac_terminal_char* character) { /* STUB */ + guac_client_log_info(display->client, + "display_set_columns: row=%i, start=%i, end=%i, char='%c'", + row, start_column, end_column, character->value); } void guac_terminal_display_resize(guac_terminal_display* display, int rows, int cols) { diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 0651ce19..0ce482ef 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -56,13 +56,15 @@ guac_terminal* guac_terminal_create(guac_client* client, int width, int height) { - guac_terminal_attributes default_attributes = { - .foreground = 7, - .background = 0, - .bold = false, - .reverse = false, - .underscore = false - }; + guac_terminal_char default_char = { + .value = ' ', + .attributes = { + .foreground = 7, + .background = 0, + .bold = false, + .reverse = false, + .underscore = false + }}; guac_terminal* term = malloc(sizeof(guac_terminal)); term->client = client; @@ -74,12 +76,12 @@ guac_terminal* guac_terminal_create(guac_client* client, /* Init display */ term->display = guac_terminal_display_alloc(client, term->term_width, term->term_height, - default_attributes.foreground, - default_attributes.background); + default_char.attributes.foreground, + default_char.attributes.background); /* Init terminal state */ - term->current_attributes = - term->default_attributes = default_attributes; + term->current_attributes = default_char.attributes; + term->default_char = default_char; term->cursor_row = 0; term->cursor_col = 0; @@ -93,10 +95,6 @@ guac_terminal* guac_terminal_create(guac_client* client, term->text_selected = false; - /* Clear with background color */ - guac_terminal_clear(term, - 0, 0, term->term_height, term->term_width); - /* Init terminal lock */ pthread_mutex_init(&(term->lock), NULL); @@ -116,19 +114,12 @@ void guac_terminal_free(guac_terminal* term) { int guac_terminal_set(guac_terminal* term, int row, int col, char c) { - int scrolled_row = row + term->scroll_offset; - /* Build character with current attributes */ guac_terminal_char guac_char; guac_char.value = c; guac_char.attributes = term->current_attributes; - /* Set display */ - if (scrolled_row < term->display->height) - guac_terminal_display_set_columns(term->display, scrolled_row, col, col, &guac_char); - - /* Set buffer */ - guac_terminal_buffer_set_columns(term->buffer, row, col, col, &guac_char); + guac_terminal_set_columns(term, row, col, col, &guac_char); return 0; @@ -136,18 +127,20 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col) { + guac_terminal_char* guac_char; int scrolled_row = row + term->scroll_offset; /* Get character from buffer */ - guac_terminal_char* guac_char = - &(term->buffer->characters[row*term->buffer->width + col]); + guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(term->buffer, row); + guac_terminal_buffer_prepare_row(buffer_row, col+1, &term->default_char); /* Toggle reverse */ + guac_char = &(buffer_row->characters[col]); guac_char->attributes.reverse = !(guac_char->attributes.reverse); /* Set display */ if (scrolled_row < term->display->height) - guac_terminal_display_set(term->display, scrolled_row, col, guac_char); + guac_terminal_display_set_columns(term->display, scrolled_row, col, col, guac_char); return 0; @@ -164,120 +157,31 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size) { } -int guac_terminal_copy(guac_terminal* term, - int src_row, int src_col, int rows, int cols, - int dst_row, int dst_col) { - - int scrolled_src_row = src_row + term->scroll_offset; - int scrolled_dst_row = dst_row + term->scroll_offset; - - int scrolled_rows = rows; - - /* FIXME: If source (but not dest) is partially scrolled out of view, then - * the display will not be updated properly. We need to pull the data - * from the buffer in such a case. - */ - - if (scrolled_src_row < term->display->height && - scrolled_dst_row < term->display->height) { - - /* Adjust display rect height if scrolled out of view */ - if (scrolled_src_row + scrolled_rows > term->display->height) - scrolled_rows = term->display->height - scrolled_src_row; - - if (scrolled_dst_row + scrolled_rows > term->display->height) - scrolled_rows = term->display->height - scrolled_dst_row; - - /* Update display */ - guac_terminal_display_copy(term->display, - scrolled_dst_row, dst_col, - scrolled_src_row, src_col, - cols, rows); - - } - - /* Update buffer */ - guac_terminal_buffer_copy(term->buffer, - dst_row, dst_col, - src_row, src_col, - cols, rows); - - return 0; - -} - - -int guac_terminal_clear(guac_terminal* term, - int row, int col, int rows, int cols) { - - int scrolled_row = row + term->scroll_offset; - int scrolled_rows = rows; - - /* Build space */ - guac_terminal_char character; - character.value = ' '; - character.attributes = term->current_attributes; - - if (scrolled_row < term->display->height) { - - /* Adjust display rect height if scrolled out of view */ - if (scrolled_row + scrolled_rows > term->display->height) - scrolled_rows = term->display->height - scrolled_row; - - /* Fill with color */ - guac_terminal_display_set_rect(term->display, - scrolled_row, col, cols, scrolled_rows, &character); - - } - - guac_terminal_buffer_set_rect(term->buffer, - row, col, cols, rows, &character); - - return 0; - -} - int guac_terminal_scroll_up(guac_terminal* term, int start_row, int end_row, int amount) { - - /* Calculate height of scroll region */ - int height = end_row - start_row + 1; - - /* If scroll region is entire screen, push rows into scrollback */ - if (start_row == 0 && end_row == term->term_height-1) - guac_terminal_scrollback_buffer_append(term->scrollback, term, amount); - - return - - /* Move rows within scroll region up by the given amount */ - guac_terminal_copy(term, - start_row + amount, 0, - height - amount, term->term_width, - start_row, 0) - - /* Fill new rows with background */ - || guac_terminal_clear(term, - end_row - amount + 1, 0, amount, term->term_width); - + /* STUB */ + return 0; } int guac_terminal_scroll_down(guac_terminal* term, int start_row, int end_row, int amount) { + /* STUB */ + return 0; +} - /* Calculate height of scroll region */ - int height = end_row - start_row + 1; - - return +int guac_terminal_clear_columns(guac_terminal* term, + int row, int start_col, int end_col) { - /* Move rows within scroll region down by the given amount */ - guac_terminal_copy(term, - start_row, 0, - height - amount, term->term_width, - start_row + amount, 0) + /* Build space */ + guac_terminal_char blank; + blank.value = ' '; + blank.attributes = term->current_attributes; - /* Fill new rows with background */ - || guac_terminal_clear(term, - start_row, 0, amount, term->term_width); + /* Clear */ + guac_terminal_set_columns(term, + row, start_col, end_col, &blank); + + return 0; } @@ -289,9 +193,8 @@ int guac_terminal_clear_range(guac_terminal* term, if (start_col > 0) { /* Clear from start_col to far right */ - if (guac_terminal_clear(term, - start_row, start_col, 1, term->term_width - start_col)) - return 1; + guac_terminal_clear_columns(term, + start_row, start_col, term->term_width - 1); /* One less row to clear */ start_row++; @@ -301,9 +204,7 @@ int guac_terminal_clear_range(guac_terminal* term, if (end_col < term->term_width - 1) { /* Clear from far left to end_col */ - if (guac_terminal_clear(term, - end_row, 0, 1, end_col + 1)) - return 1; + guac_terminal_clear_columns(term, end_row, 0, end_col); /* One less row to clear */ end_row--; @@ -313,9 +214,13 @@ int guac_terminal_clear_range(guac_terminal* term, /* Remaining region now guaranteed rectangular. Clear, if possible */ if (start_row <= end_row) { - if (guac_terminal_clear(term, - start_row, 0, end_row - start_row + 1, term->term_width)) - return 1; + int row; + for (row=start_row; row<=end_row; row++) { + + /* Clear entire row */ + guac_terminal_clear_columns(term, row, 0, term->term_width - 1); + + } } @@ -455,7 +360,7 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, /* Get row from scrollback */ guac_terminal_buffer_row* buffer_row = - guac_terminal_buffer_get_row(terminal, row); + guac_terminal_buffer_get_row(terminal->buffer, row); /* Draw row */ /* FIXME: Clear row first */ @@ -470,7 +375,7 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, } /* FIXME: Should flush somewhere more sensible */ - guac_terminal_display_flush(terminal->display, terminal); + guac_terminal_display_flush(terminal->display); guac_socket_flush(terminal->client->socket); } @@ -484,8 +389,8 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, /* Limit scroll amount by size of scrollback buffer */ - if (terminal->scroll_offset + scroll_amount > terminal->scrollback->length) - scroll_amount = terminal->scrollback->length - terminal->scroll_offset; + if (terminal->scroll_offset + scroll_amount > terminal->buffer->length) + scroll_amount = terminal->buffer->length - terminal->scroll_offset; /* If not scrolling at all, don't bother trying */ if (scroll_amount == 0) @@ -526,178 +431,44 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, } /* FIXME: Should flush somewhere more sensible */ - guac_terminal_display_flush(terminal->display, terminal); + guac_terminal_display_flush(terminal->display); guac_socket_flush(terminal->client->socket); } void guac_terminal_select_start(guac_terminal* terminal, int row, int column) { - - guac_terminal_char* guac_char; - guac_terminal_operation* guac_operation; - - /* Update selection coordinates */ - terminal->selection_start_row = - terminal->selection_end_row = row; - terminal->selection_start_column = - terminal->selection_end_column = column; - terminal->text_selected = true; - - /* Get char and operation */ - guac_char = &(terminal->buffer->characters[terminal->buffer->width * row + column]); - guac_operation = &(terminal->display->operations[terminal->display->width * row + column]); - - /* Set character as selected */ - guac_char->attributes.selected = true; - guac_operation->type = GUAC_CHAR_SET; - guac_operation->character = *guac_char; - - guac_terminal_display_flush(terminal->display, terminal); - guac_socket_flush(terminal->client->socket); - + /* STUB */ } void guac_terminal_select_update(guac_terminal* terminal, int row, int column) { - - int start_index = terminal->selection_start_row * terminal->buffer->width - + terminal->selection_start_column; - - int old_end_index = terminal->selection_end_row * terminal->buffer->width - + terminal->selection_end_column; - - int new_end_index = row * terminal->buffer->width + column; - - int old_index_a, old_index_b; - int new_index_a, new_index_b; - - int search_index_a, search_index_b; - - int i; - guac_terminal_char* guac_char; - guac_terminal_operation* guac_operation; - - /* If unchanged, do nothing */ - if (old_end_index == new_end_index) return; - - /* Calculate old selection range */ - if (start_index < old_end_index) { - old_index_a = start_index; - old_index_b = old_end_index; - } - else { - old_index_a = old_end_index; - old_index_b = start_index; - } - - /* Calculate new selection range */ - if (start_index < new_end_index) { - new_index_a = start_index; - new_index_b = new_end_index; - } - else { - new_index_a = new_end_index; - new_index_b = start_index; - } - - if (new_index_a < old_index_a) - search_index_a = new_index_a; - else - search_index_a = old_index_a; - - if (new_index_b > old_index_b) - search_index_b = new_index_b; - else - search_index_b = old_index_b; - - /* Get first character */ - guac_char = &(terminal->buffer->characters[search_index_a]); - guac_operation = &(terminal->display->operations[search_index_a]); - - /* Invert modified area */ - for (i=search_index_a; i<=search_index_b; i++) { - - /* If now selected, mark as such */ - if (i >= new_index_a && i <= new_index_b && - (i < old_index_a || i > old_index_b)) { - - guac_char->attributes.selected = true; - guac_operation->type = GUAC_CHAR_SET; - guac_operation->character = *guac_char; - - } - - /* If now unselected, mark as such */ - else if (i >= old_index_a && i <= old_index_b && - (i < new_index_a || i > new_index_b)) { - - guac_char->attributes.selected = false; - guac_operation->type = GUAC_CHAR_SET; - guac_operation->character = *guac_char; - - } - - /* Next char */ - guac_char++; - guac_operation++; - - } - - terminal->selection_end_row = row; - terminal->selection_end_column = column; - - guac_terminal_display_flush(terminal->display, terminal); - guac_socket_flush(terminal->client->socket); - + /* STUB */ } void guac_terminal_select_end(guac_terminal* terminal) { - - int index_a = terminal->selection_end_row * terminal->buffer->width - + terminal->selection_end_column; - - int index_b = terminal->selection_start_row * terminal->buffer->width - + terminal->selection_start_column; - - int i; - guac_terminal_char* guac_char; - guac_terminal_operation* guac_operation; - - /* The start and end indices of all characters in selection */ - int start_index; - int end_index; - - /* Order indices such that end is after start */ - if (index_a > index_b) { - start_index = index_b; - end_index = index_a; - } - else { - start_index = index_a; - end_index = index_b; - } - - /* Get first character */ - guac_char = &(terminal->buffer->characters[start_index]); - guac_operation = &(terminal->display->operations[start_index]); - - /* Restore state from buffer */ - for (i=start_index; i<=end_index; i++) { - - /* Restore state */ - guac_char->attributes.selected = false; - guac_operation->type = GUAC_CHAR_SET; - guac_operation->character = *guac_char; - - /* Next char */ - guac_char++; - guac_operation++; - - } - - terminal->text_selected = false; - - guac_terminal_display_flush(terminal->display, terminal); - guac_socket_flush(terminal->client->socket); - + /* STUB */ +} + +void guac_terminal_copy_columns(guac_terminal* terminal, int row, + int start_column, int end_column, int offset) { + /* STUB */ + guac_client_log_info(terminal->client, + "terminal_copy_columns: row=%i, start=%i, end=%i, offset=%i", + row, start_column, end_column, offset); +} + +void guac_terminal_copy_rows(guac_terminal* terminal, + int start_row, int end_row, int offset) { + /* STUB */ + guac_client_log_info(terminal->client, + "terminal_copy_rows: start=%i, end=%i, offset=%i", + start_row, end_row, offset); +} + +void guac_terminal_set_columns(guac_terminal* terminal, int row, + int start_column, int end_column, guac_terminal_char* character) { + /* STUB */ + guac_client_log_info(terminal->client, + "terminal_set_columns: row=%i, start=%i, end=%i, char='%c'", + row, start_column, end_column, character->value); } diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 2acb74b3..619e1c89 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -297,7 +297,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Reset attributes */ if (value == 0) - term->current_attributes = term->default_attributes; + term->current_attributes = term->default_char.attributes; /* Bold */ else if (value == 1) @@ -319,20 +319,20 @@ int guac_terminal_csi(guac_terminal* term, char c) { else if (value == 38) { term->current_attributes.underscore = true; term->current_attributes.foreground = - term->default_attributes.foreground; + term->default_char.attributes.foreground; } /* Underscore off, default foreground */ else if (value == 39) { term->current_attributes.underscore = false; term->current_attributes.foreground = - term->default_attributes.foreground; + term->default_char.attributes.foreground; } /* Reset background */ else if (value == 49) term->current_attributes.background = - term->default_attributes.background; + term->default_char.attributes.background; /* Reverse video */ else if (value == 7) @@ -400,8 +400,8 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Entire screen */ else if (argv[0] == 2) - guac_terminal_clear(term, - 0, 0, term->term_height, term->term_width); + guac_terminal_clear_range(term, + 0, 0, term->term_height - 1, term->term_width - 1); break; @@ -410,22 +410,18 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Erase from cursor to end of line */ if (argv[0] == 0) - guac_terminal_clear(term, - term->cursor_row, term->cursor_col, - 1, term->term_width - term->cursor_col); - + guac_terminal_clear_columns(term, term->cursor_row, + term->cursor_col, term->term_width - 1); /* Erase from start to cursor */ else if (argv[0] == 1) - guac_terminal_clear(term, - term->cursor_row, 0, - 1, term->cursor_col + 1); + guac_terminal_clear_columns(term, term->cursor_row, + 0, term->cursor_col); /* Erase line */ else if (argv[0] == 2) - guac_terminal_clear(term, - term->cursor_row, 0, - 1, term->term_width); + guac_terminal_clear_columns(term, term->cursor_row, + 0, term->term_width - 1); break; @@ -459,16 +455,13 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Scroll left by amount */ if (term->cursor_col + amount < term->term_width) - guac_terminal_copy(term, - term->cursor_row, term->cursor_col + amount, - 1, - term->term_width - term->cursor_col - amount, - term->cursor_row, term->cursor_col); + guac_terminal_copy_columns(term, term->cursor_row, + term->cursor_col + amount, term->term_width - 1, + -amount); /* Clear right */ - guac_terminal_clear(term, - term->cursor_row, term->term_width - amount, - 1, amount); + guac_terminal_clear_columns(term, term->cursor_row, + term->term_width - amount, term->term_width - 1); break; @@ -479,9 +472,8 @@ int guac_terminal_csi(guac_terminal* term, char c) { if (amount == 0) amount = 1; /* Clear characters */ - guac_terminal_clear(term, - term->cursor_row, term->cursor_col, - 1, amount); + guac_terminal_clear_columns(term, term->cursor_row, + term->cursor_col, term->cursor_col + amount - 1); break; @@ -493,15 +485,13 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Scroll right by amount */ if (term->cursor_col + amount < term->term_width) - guac_terminal_copy(term, - term->cursor_row, term->cursor_col, - 1, term->term_width - term->cursor_col - amount, - term->cursor_row, term->cursor_col + amount); + guac_terminal_copy_columns(term, term->cursor_row, + term->cursor_col, term->term_width - amount - 1, + amount); /* Clear left */ - guac_terminal_clear(term, - term->cursor_row, term->cursor_col, - 1, amount); + guac_terminal_clear_columns(term, term->cursor_row, + term->cursor_col, term->cursor_col + amount - 1); break; From b64aaa72ee263ee00a055eb1fc20598a6b8f82d5 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 26 Apr 2013 02:29:30 -0700 Subject: [PATCH 083/169] Partial restoration of display functionality. Modify get_row to expand row as necessary. --- protocols/ssh/include/buffer.h | 21 +++++++------ protocols/ssh/src/buffer.c | 57 +++++++++++++++++++++------------- protocols/ssh/src/display.c | 20 +++++++++--- protocols/ssh/src/terminal.c | 21 +++++++------ 4 files changed, 74 insertions(+), 45 deletions(-) diff --git a/protocols/ssh/include/buffer.h b/protocols/ssh/include/buffer.h index 83c05cce..eb9edb48 100644 --- a/protocols/ssh/include/buffer.h +++ b/protocols/ssh/include/buffer.h @@ -72,6 +72,11 @@ typedef struct guac_terminal_buffer_row { */ typedef struct guac_terminal_buffer { + /** + * The character to assign to newly-allocated cells. + */ + guac_terminal_char default_character; + /** * Array of buffer rows. This array functions as a ring buffer. * When a new row needs to be appended, the top reference is moved down @@ -98,9 +103,10 @@ typedef struct guac_terminal_buffer { } guac_terminal_buffer; /** - * Allocates a new buffer having the given maximum number of rows. + * Allocates a new buffer having the given maximum number of rows. New character cells will + * be initialized to the given character. */ -guac_terminal_buffer* guac_terminal_buffer_alloc(int rows); +guac_terminal_buffer* guac_terminal_buffer_alloc(int rows, guac_terminal_char* default_character); /** * Frees the given buffer. @@ -108,15 +114,10 @@ guac_terminal_buffer* guac_terminal_buffer_alloc(int rows); void guac_terminal_buffer_free(guac_terminal_buffer* buffer); /** - * Returns the row at the given location. + * Returns the row at the given location. The row returned is guaranteed to be at least the given + * width. */ -guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buffer, int row); - -/** - * Ensures the given row has at least the given number of character spaces available. If new characters - * must be added, they are initialized with the given fill character. - */ -void guac_terminal_buffer_prepare_row(guac_terminal_buffer_row* row, int width, guac_terminal_char* fill); +guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buffer, int row, int width); /** * Copies the given range of columns to a new location, offset from diff --git a/protocols/ssh/src/buffer.c b/protocols/ssh/src/buffer.c index bfc3951c..bf4d4246 100644 --- a/protocols/ssh/src/buffer.c +++ b/protocols/ssh/src/buffer.c @@ -39,7 +39,7 @@ #include "buffer.h" -guac_terminal_buffer* guac_terminal_buffer_alloc(int rows) { +guac_terminal_buffer* guac_terminal_buffer_alloc(int rows, guac_terminal_char* default_character) { /* Allocate scrollback */ guac_terminal_buffer* buffer = @@ -49,6 +49,7 @@ guac_terminal_buffer* guac_terminal_buffer_alloc(int rows) { guac_terminal_buffer_row* row; /* Init scrollback data */ + buffer->default_character = *default_character; buffer->available = rows; buffer->top = 0; buffer->length = 0; @@ -90,38 +91,39 @@ void guac_terminal_buffer_free(guac_terminal_buffer* buffer) { } -guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buffer, int row) { +guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buffer, int row, int width) { + + int i; + guac_terminal_char* first; + guac_terminal_buffer_row* buffer_row; /* Calculate scrollback row index */ int index = buffer->top + row; if (index < 0) index += buffer->available; - /* Return found row */ - return &(buffer->rows[index]); + /* Get row */ + buffer_row = &(buffer->rows[index]); -} + /* If resizing is needed */ + if (width >= buffer_row->length) { -void guac_terminal_buffer_prepare_row(guac_terminal_buffer_row* row, int width, guac_terminal_char* fill) { + /* Expand if necessary */ + if (width > buffer_row->available) { + buffer_row->available = width*2; + buffer_row->characters = realloc(buffer_row->characters, sizeof(guac_terminal_char) * buffer_row->available); + } - int i; - guac_terminal_char* first; + /* Initialize new part of row */ + first = &(buffer_row->characters[buffer_row->length]); + for (i=buffer_row->length; idefault_character; - /* If already wide enough, nothing to do. */ - if (width < row->length) - return; + buffer_row->length = width; - /* Expand if necessary */ - if (width > row->available) { - row->available = width*2; - row->characters = realloc(row->characters, sizeof(guac_terminal_char) * row->available); } - /* Initialize new part of row */ - first = &(row->characters[row->length]); - for (i=row->length; ilength = width; + /* Return found row */ + return buffer_row; } @@ -137,6 +139,17 @@ void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, void guac_terminal_buffer_set_columns(guac_terminal_buffer* buffer, int row, int start_column, int end_column, guac_terminal_char* character) { - /* STUB */ + + int i; + guac_terminal_char* current; + + /* Get and expand row */ + guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(buffer, row, end_column+1); + + /* Set values */ + current = &(buffer_row->characters[start_column]); + for (i=start_column; i<=end_column; i++) + *(current++) = *character; + } diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 91561b90..e2028083 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -359,10 +359,22 @@ void guac_terminal_display_copy_rows(guac_terminal_display* display, void guac_terminal_display_set_columns(guac_terminal_display* display, int row, int start_column, int end_column, guac_terminal_char* character) { - /* STUB */ - guac_client_log_info(display->client, - "display_set_columns: row=%i, start=%i, end=%i, char='%c'", - row, start_column, end_column, character->value); + + int i; + guac_terminal_operation* current = + &(display->operations[row * display->width + start_column]); + + /* For each column in range */ + for (i=start_column; i<=end_column; i++) { + + /* Set operation */ + current->type = GUAC_CHAR_SET; + current->character = *character; + + /* Next column */ + current++; + } + } void guac_terminal_display_resize(guac_terminal_display* display, int rows, int cols) { diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 0ce482ef..641b85a0 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -70,12 +70,12 @@ guac_terminal* guac_terminal_create(guac_client* client, term->client = client; /* Init buffer */ - term->buffer = guac_terminal_buffer_alloc(1000); + term->buffer = guac_terminal_buffer_alloc(1000, &default_char); term->scroll_offset = 0; /* Init display */ term->display = guac_terminal_display_alloc(client, - term->term_width, term->term_height, + 80, 24, /*term->term_width, term->term_height,*/ default_char.attributes.foreground, default_char.attributes.background); @@ -131,8 +131,7 @@ int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col) { int scrolled_row = row + term->scroll_offset; /* Get character from buffer */ - guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(term->buffer, row); - guac_terminal_buffer_prepare_row(buffer_row, col+1, &term->default_char); + guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(term->buffer, row, col+1); /* Toggle reverse */ guac_char = &(buffer_row->characters[col]); @@ -360,7 +359,7 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, /* Get row from scrollback */ guac_terminal_buffer_row* buffer_row = - guac_terminal_buffer_get_row(terminal->buffer, row); + guac_terminal_buffer_get_row(terminal->buffer, row, 0); /* Draw row */ /* FIXME: Clear row first */ @@ -416,7 +415,7 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, /* Get row from scrollback */ guac_terminal_buffer_row* buffer_row = - guac_terminal_buffer_get_row(terminal->buffer, row); + guac_terminal_buffer_get_row(terminal->buffer, row, 0); /* Draw row */ /* FIXME: Clear row first */ @@ -467,8 +466,12 @@ void guac_terminal_copy_rows(guac_terminal* terminal, void guac_terminal_set_columns(guac_terminal* terminal, int row, int start_column, int end_column, guac_terminal_char* character) { /* STUB */ - guac_client_log_info(terminal->client, - "terminal_set_columns: row=%i, start=%i, end=%i, char='%c'", - row, start_column, end_column, character->value); + + guac_terminal_display_set_columns(terminal->display, row, + start_column, end_column, character); + + guac_terminal_buffer_set_columns(terminal->buffer, row, + start_column, end_column, character); + } From e1b45733a341451e4cc620a51162274a33df0ea7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 26 Apr 2013 10:36:02 -0700 Subject: [PATCH 084/169] Remove old functions, implement display copies. --- protocols/ssh/include/display.h | 6 -- protocols/ssh/src/display.c | 75 +++++++++++++++++++++---- protocols/ssh/src/terminal.c | 97 --------------------------------- 3 files changed, 63 insertions(+), 115 deletions(-) diff --git a/protocols/ssh/include/display.h b/protocols/ssh/include/display.h index 7c01825e..275bd21d 100644 --- a/protocols/ssh/include/display.h +++ b/protocols/ssh/include/display.h @@ -117,12 +117,6 @@ typedef struct guac_terminal_display { */ guac_terminal_operation* operations; - /** - * Scratch area of same size as the operations buffer, facilitating copies - * of overlapping regions. - */ - guac_terminal_operation* scratch; - /** * The width of the screen, in characters. */ diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index e2028083..b00cafca 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -317,9 +317,6 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int widt } - /* Alloc scratch area */ - display->scratch = malloc(width * height * sizeof(guac_terminal_operation)); - /* Send initial display size */ guac_protocol_send_size(client->socket, GUAC_DEFAULT_LAYER, @@ -334,7 +331,6 @@ void guac_terminal_display_free(guac_terminal_display* display) { /* Free operations buffers */ free(display->operations); - free(display->scratch); /* Free display */ free(display); @@ -343,18 +339,73 @@ void guac_terminal_display_free(guac_terminal_display* display) { void guac_terminal_display_copy_columns(guac_terminal_display* display, int row, int start_column, int end_column, int offset) { - /* STUB */ - guac_client_log_info(display->client, - "display_copy_columns: row=%i, start=%i, end=%i, offset=%i", - row, start_column, end_column, offset); + + int i; + guac_terminal_operation* src_current = + &(display->operations[row * display->width + start_column]); + + guac_terminal_operation* current = + &(display->operations[row * display->width + start_column + offset]); + + /* Move data */ + memmove(current, src_current, + (end_column - start_column + 1) * sizeof(guac_terminal_operation)); + + /* Update operations */ + for (i=start_column; i<=end_column; i++) { + + /* If no operation here, set as copy */ + if (current->type == GUAC_CHAR_NOP) { + current->type = GUAC_CHAR_COPY; + current->row = row; + current->column = i; + } + + /* Next column */ + current++; + + } + } void guac_terminal_display_copy_rows(guac_terminal_display* display, int start_row, int end_row, int offset) { - /* STUB */ - guac_client_log_info(display->client, - "display_copy_rows: start=%i, end=%i, offset=%i", - start_row, end_row, offset); + + int row, col; + + guac_terminal_operation* src_current_row = + &(display->operations[start_row * display->width]); + + guac_terminal_operation* current_row = + &(display->operations[(start_row + offset) * display->width]); + + /* Move data */ + memmove(current_row, src_current_row, + (end_row - start_row + 1) * sizeof(guac_terminal_operation) * display->width); + + /* Update operations */ + for (row=start_row; row<=end_row; row++) { + + guac_terminal_operation* current = current_row; + for (col=0; colwidth; col++) { + + /* If no operation here, set as copy */ + if (current->type == GUAC_CHAR_NOP) { + current->type = GUAC_CHAR_COPY; + current->row = row; + current->column = col; + } + + /* Next column */ + current++; + + } + + /* Next row */ + current_row += display->width; + + } + } void guac_terminal_display_set_columns(guac_terminal_display* display, int row, diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 641b85a0..b3521d43 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -227,103 +227,6 @@ int guac_terminal_clear_range(guac_terminal* term, } - -void guac_terminal_display_set(guac_terminal_display* display, int r, int c, - guac_terminal_char* character) { - - /* Get operation at coordinate */ - guac_terminal_operation* op = &(display->operations[r*display->width + c]); - - /* Store operation */ - op->type = GUAC_CHAR_SET; - op->character = *character; - -} - -void guac_terminal_display_copy(guac_terminal_display* display, - int dst_row, int dst_column, - int src_row, int src_column, - int w, int h) { - - int row, column; - - /* FIXME: Handle intersections between src and dst rects */ - - memcpy(display->scratch, display->operations, - sizeof(guac_terminal_operation) * display->width * display->height); - - guac_terminal_operation* current_row = - &(display->operations[dst_row*display->width + dst_column]); - - guac_terminal_operation* src_current_row = - &(display->scratch[src_row*display->width + src_column]); - - /* Set rectangle to copy operations */ - for (row=0; rowtype != GUAC_CHAR_NOP) - *current = *src_current; - - /* Store operation */ - else { - current->type = GUAC_CHAR_COPY; - current->row = src_row + row; - current->column = src_column + column; - } - - /* Next column */ - current++; - src_current++; - - } - - /* Next row */ - current_row += display->width; - src_current_row += display->width; - - } - - - -} - -void guac_terminal_display_set_rect(guac_terminal_display* display, - int row, int column, int w, int h, - guac_terminal_char* character) { - - guac_terminal_operation* current_row = - &(display->operations[row*display->width + column]); - - /* Set rectangle contents to given character */ - for (row=0; rowtype = GUAC_CHAR_SET; - current->character = *character; - - /* Next column */ - current++; - - } - - /* Next row */ - current_row += display->width; - - } - -} - void guac_terminal_scroll_display_down(guac_terminal* terminal, int scroll_amount) { From dd4862f59a875a0b0465a36bb8406acf2264107d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 26 Apr 2013 10:55:55 -0700 Subject: [PATCH 085/169] Implement basic terminal scroll. --- protocols/ssh/src/terminal.c | 54 +++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index b3521d43..0609d33f 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -86,8 +86,8 @@ guac_terminal* guac_terminal_create(guac_client* client, term->cursor_row = 0; term->cursor_col = 0; - term->term_width = width / term->display->char_width; - term->term_height = height / term->display->char_height; + term->term_width = 80; /*width / term->display->char_width;*/ + term->term_height = 24; /*height / term->display->char_height;*/ term->char_handler = guac_terminal_echo; term->scroll_start = 0; @@ -158,7 +158,15 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size) { int guac_terminal_scroll_up(guac_terminal* term, int start_row, int end_row, int amount) { - /* STUB */ + + /* Copy row data upwards */ + guac_terminal_copy_rows(term, start_row + amount, end_row, -amount); + + /* Clear new area */ + guac_terminal_clear_range(term, + end_row - amount + 1, 0, + end_row, term->term_width - 1); + return 0; } @@ -244,10 +252,9 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, /* Shift screen up */ if (terminal->term_height > scroll_amount) - guac_terminal_display_copy(terminal->display, - 0, 0, /* Destination row, col */ - scroll_amount, 0, /* source row,col */ - terminal->term_width, terminal->term_height - scroll_amount); + guac_terminal_display_copy_rows(terminal->display, + scroll_amount, terminal->term_height - 1, + -scroll_amount); /* Advance by scroll amount */ terminal->scroll_offset -= scroll_amount; @@ -268,8 +275,8 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, /* FIXME: Clear row first */ guac_terminal_char* current = buffer_row->characters; for (column=0; columnlength; column++) - guac_terminal_display_set(terminal->display, dest_row, column, - current++); + guac_terminal_display_set_columns(terminal->display, + dest_row, column, column, current++); /* Next row */ dest_row++; @@ -300,10 +307,9 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, /* Shift screen down */ if (terminal->term_height > scroll_amount) - guac_terminal_display_copy(terminal->display, - scroll_amount, 0, /* Destination row,col */ - 0, 0, /* Source row, col */ - terminal->term_width, terminal->term_height - scroll_amount); + guac_terminal_display_copy_rows(terminal->display, + 0, terminal->term_height - scroll_amount - 1, + scroll_amount); /* Advance by scroll amount */ terminal->scroll_offset += scroll_amount; @@ -324,8 +330,8 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, /* FIXME: Clear row first */ guac_terminal_char* current = buffer_row->characters; for (column=0; columnlength; column++) - guac_terminal_display_set(terminal->display, dest_row, column, - current++); + guac_terminal_display_set_columns(terminal->display, + dest_row, column, column, current++); /* Next row */ dest_row++; @@ -353,17 +359,25 @@ void guac_terminal_select_end(guac_terminal* terminal) { void guac_terminal_copy_columns(guac_terminal* terminal, int row, int start_column, int end_column, int offset) { /* STUB */ - guac_client_log_info(terminal->client, - "terminal_copy_columns: row=%i, start=%i, end=%i, offset=%i", - row, start_column, end_column, offset); + + guac_terminal_display_copy_columns(terminal->display, row, + start_column, end_column, offset); + + guac_terminal_buffer_copy_columns(terminal->buffer, row, + start_column, end_column, offset); + } void guac_terminal_copy_rows(guac_terminal* terminal, int start_row, int end_row, int offset) { /* STUB */ - guac_client_log_info(terminal->client, - "terminal_copy_rows: start=%i, end=%i, offset=%i", + + guac_terminal_display_copy_rows(terminal->display, start_row, end_row, offset); + + guac_terminal_buffer_copy_rows(terminal->buffer, + start_row, end_row, offset); + } void guac_terminal_set_columns(guac_terminal* terminal, int row, From 6092badb3b6d85d2cc566b4080f2336c63403f1e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 26 Apr 2013 14:14:19 -0700 Subject: [PATCH 086/169] Handle display size properly, start out empty. --- protocols/ssh/include/display.h | 8 ++-- protocols/ssh/include/terminal.h | 2 +- protocols/ssh/src/display.c | 70 ++++++++++++++++++-------------- protocols/ssh/src/terminal.c | 9 ++-- 4 files changed, 51 insertions(+), 38 deletions(-) diff --git a/protocols/ssh/include/display.h b/protocols/ssh/include/display.h index 275bd21d..0d9df406 100644 --- a/protocols/ssh/include/display.h +++ b/protocols/ssh/include/display.h @@ -177,10 +177,10 @@ typedef struct guac_terminal_display { } guac_terminal_display; /** - * Allocates a new display having the given dimensions. + * Allocates a new display having the given default foreground and background + * colors. */ -guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int width, int height, - int foreground, int background); +guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int foreground, int background); /** * Frees the given display. @@ -211,7 +211,7 @@ void guac_terminal_display_set_columns(guac_terminal_display* display, int row, /** * Resize the terminal to the given dimensions. */ -void guac_terminal_display_resize(guac_terminal_display* display, int rows, int cols); +void guac_terminal_display_resize(guac_terminal_display* display, int width, int height); /** * Flushes all pending operations within the given guac_terminal_display. diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 993be802..4992ba3b 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -278,7 +278,7 @@ void guac_terminal_set_columns(guac_terminal* terminal, int row, /** * Resize the terminal to the given dimensions. */ -void guac_terminal_resize(guac_terminal* term, int rows, int cols); +void guac_terminal_resize(guac_terminal* term, int width, int height); /** * Flushes all pending operations within the given guac_terminal. diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index b00cafca..40fde422 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -249,11 +249,7 @@ int __guac_terminal_set(guac_terminal_display* display, int row, int col, char c } -guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int width, int height, - int foreground, int background) { - - guac_terminal_operation* current; - int x, y; +guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int foreground, int background) { PangoFontMap* font_map; PangoFont* font; @@ -299,29 +295,10 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int widt (pango_font_metrics_get_descent(metrics) + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE; - /* Set width and height */ - display->width = width; - display->height = height; - - /* Alloc operations */ - display->operations = malloc(width * height * - sizeof(guac_terminal_operation)); - - /* Init each operation buffer row */ - current = display->operations; - for (y=0; ytype = GUAC_CHAR_NOP; - - } - - /* Send initial display size */ - guac_protocol_send_size(client->socket, - GUAC_DEFAULT_LAYER, - display->char_width * width, - display->char_height * height); + /* Initially empty */ + display->width = 0; + display->height = 0; + display->operations = NULL; return display; @@ -428,8 +405,41 @@ void guac_terminal_display_set_columns(guac_terminal_display* display, int row, } -void guac_terminal_display_resize(guac_terminal_display* display, int rows, int cols) { - /* STUB */ +void guac_terminal_display_resize(guac_terminal_display* display, int width, int height) { + + guac_terminal_operation* current; + int x, y; + + /* Set width and height */ + display->width = width; + display->height = height; + + /* Free old operations buffer */ + if (display->operations != NULL) + free(display->operations); + + /* Alloc operations */ + display->operations = malloc(width * height * + sizeof(guac_terminal_operation)); + + /* Init each operation buffer row */ + current = display->operations; + for (y=0; ytype = GUAC_CHAR_NOP; + + } + + /* Send initial display size */ + guac_protocol_send_size(display->client->socket, + GUAC_DEFAULT_LAYER, + display->char_width * width, + display->char_height * height); + + + } void __guac_terminal_display_flush_copy(guac_terminal_display* display) { diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 0609d33f..54f26cdb 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -75,7 +75,6 @@ guac_terminal* guac_terminal_create(guac_client* client, /* Init display */ term->display = guac_terminal_display_alloc(client, - 80, 24, /*term->term_width, term->term_height,*/ default_char.attributes.foreground, default_char.attributes.background); @@ -86,8 +85,8 @@ guac_terminal* guac_terminal_create(guac_client* client, term->cursor_row = 0; term->cursor_col = 0; - term->term_width = 80; /*width / term->display->char_width;*/ - term->term_height = 24; /*height / term->display->char_height;*/ + term->term_width = width / term->display->char_width; + term->term_height = height / term->display->char_height; term->char_handler = guac_terminal_echo; term->scroll_start = 0; @@ -95,6 +94,10 @@ guac_terminal* guac_terminal_create(guac_client* client, term->text_selected = false; + /* Size display */ + guac_terminal_display_resize(term->display, + term->term_width, term->term_height); + /* Init terminal lock */ pthread_mutex_init(&(term->lock), NULL); From 3a50c9572d90d553333d050e0c0af5be248e3bb8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 26 Apr 2013 14:52:51 -0700 Subject: [PATCH 087/169] Clear cells when resizing or scrolling. --- protocols/ssh/src/display.c | 34 +++++++++++++++++++++++++++------- protocols/ssh/src/terminal.c | 27 +++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 40fde422..ede7f16e 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -410,9 +410,14 @@ void guac_terminal_display_resize(guac_terminal_display* display, int width, int guac_terminal_operation* current; int x, y; - /* Set width and height */ - display->width = width; - display->height = height; + /* Fill with background color (index 0) */ + guac_terminal_char fill = { + .value = ' ', + .attributes = { + .foreground = 0, + .background = 0 + } + }; /* Free old operations buffer */ if (display->operations != NULL) @@ -427,19 +432,34 @@ void guac_terminal_display_resize(guac_terminal_display* display, int width, int for (y=0; ytype = GUAC_CHAR_NOP; + for (x=0; xwidth && y < display->height) + current->type = GUAC_CHAR_NOP; + + /* Otherwise, clear contents first */ + else { + current->type = GUAC_CHAR_SET; + current->character = fill; + } + + current++; + + } } + /* Set width and height */ + display->width = width; + display->height = height; + /* Send initial display size */ guac_protocol_send_size(display->client->socket, GUAC_DEFAULT_LAYER, display->char_width * width, display->char_height * height); - - } void __guac_terminal_display_flush_copy(guac_terminal_display* display) { diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 54f26cdb..8419b63e 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -162,8 +162,21 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size) { int guac_terminal_scroll_up(guac_terminal* term, int start_row, int end_row, int amount) { - /* Copy row data upwards */ - guac_terminal_copy_rows(term, start_row + amount, end_row, -amount); + /* If scrolling entire display, update scroll offset */ + if (start_row == 0 && end_row == term->term_height - 1) { + + /* Scroll up visibly */ + guac_terminal_display_copy_rows(term->display, start_row + amount, end_row, -amount); + + /* Advance by scroll amount */ + term->buffer->top += amount; + term->buffer->length += amount; + + } + + /* Otherwise, just copy row data upwards */ + else + guac_terminal_copy_rows(term, start_row + amount, end_row, -amount); /* Clear new area */ guac_terminal_clear_range(term, @@ -274,8 +287,11 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0); + /* Clear row */ + guac_terminal_display_set_columns(terminal->display, + dest_row, 0, terminal->display->width, &(terminal->default_char)); + /* Draw row */ - /* FIXME: Clear row first */ guac_terminal_char* current = buffer_row->characters; for (column=0; columnlength; column++) guac_terminal_display_set_columns(terminal->display, @@ -329,8 +345,11 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(terminal->buffer, row, 0); + /* Clear row */ + guac_terminal_display_set_columns(terminal->display, + dest_row, 0, terminal->display->width, &(terminal->default_char)); + /* Draw row */ - /* FIXME: Clear row first */ guac_terminal_char* current = buffer_row->characters; for (column=0; columnlength; column++) guac_terminal_display_set_columns(terminal->display, From 0c99978160bdb9828b4d4875877e6b6c29a87029 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 28 Apr 2013 01:28:49 -0700 Subject: [PATCH 088/169] Fix handling of length in buffer. --- protocols/ssh/src/buffer.c | 4 ++++ protocols/ssh/src/terminal.c | 13 +++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/protocols/ssh/src/buffer.c b/protocols/ssh/src/buffer.c index bf4d4246..6ea07b13 100644 --- a/protocols/ssh/src/buffer.c +++ b/protocols/ssh/src/buffer.c @@ -151,5 +151,9 @@ void guac_terminal_buffer_set_columns(guac_terminal_buffer* buffer, int row, for (i=start_column; i<=end_column; i++) *(current++) = *character; + /* Update length depending on row written */ + if (row >= buffer->length) + buffer->length = row+1; + } diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 8419b63e..dc146a70 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -170,7 +170,12 @@ int guac_terminal_scroll_up(guac_terminal* term, /* Advance by scroll amount */ term->buffer->top += amount; + if (term->buffer->top >= term->buffer->available) + term->buffer->top -= term->buffer->available; + term->buffer->length += amount; + if (term->buffer->length > term->buffer->available) + term->buffer->length = term->buffer->available; } @@ -263,7 +268,7 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, scroll_amount = terminal->scroll_offset; /* If not scrolling at all, don't bother trying */ - if (scroll_amount == 0) + if (scroll_amount <= 0) return; /* Shift screen up */ @@ -317,11 +322,11 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, /* Limit scroll amount by size of scrollback buffer */ - if (terminal->scroll_offset + scroll_amount > terminal->buffer->length) - scroll_amount = terminal->buffer->length - terminal->scroll_offset; + if (terminal->scroll_offset + scroll_amount > terminal->buffer->length - terminal->term_height) + scroll_amount = terminal->buffer->length - terminal->scroll_offset - terminal->term_height; /* If not scrolling at all, don't bother trying */ - if (scroll_amount == 0) + if (scroll_amount <= 0) return; /* Shift screen down */ From 1af2aa2f035a13d88ddb3460040141563599703e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 28 Apr 2013 01:33:42 -0700 Subject: [PATCH 089/169] Probably about time the title changed from SSH TEST. --- protocols/ssh/src/ssh_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index e2495e63..671ecb42 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -124,7 +124,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->blank_cursor = guac_ssh_create_blank(client); /* Send name and dimensions */ - guac_protocol_send_name(socket, "SSH TEST"); + guac_protocol_send_name(socket, "Terminal"); /* Initialize pointer */ client_data->current_cursor = client_data->blank_cursor; From ca17560328d58625aef81484739a5d470e4c3f30 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 30 Apr 2013 00:20:21 -0700 Subject: [PATCH 090/169] Limit display updates to visible area. --- protocols/ssh/Makefile.am | 2 ++ protocols/ssh/include/common.h | 48 ++++++++++++++++++++++++++++++++++ protocols/ssh/src/common.c | 46 ++++++++++++++++++++++++++++++++ protocols/ssh/src/display.c | 45 ++++++++++++++++++++++++------- protocols/ssh/src/terminal.c | 10 +++---- 5 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 protocols/ssh/include/common.h create mode 100644 protocols/ssh/src/common.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index a3c36d50..4411fb4f 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -43,6 +43,7 @@ lib_LTLIBRARIES = libguac-client-ssh.la libguac_client_ssh_la_SOURCES = \ src/blank.c \ src/buffer.c \ + src/common.c \ src/cursor.c \ src/display.c \ src/ibar.c \ @@ -54,6 +55,7 @@ libguac_client_ssh_la_SOURCES = \ noinst_HEADERS = \ include/blank.h \ include/buffer.h \ + include/common.h \ include/cursor.h \ include/display.h \ include/ibar.h \ diff --git a/protocols/ssh/include/common.h b/protocols/ssh/include/common.h new file mode 100644 index 00000000..eedbeb89 --- /dev/null +++ b/protocols/ssh/include/common.h @@ -0,0 +1,48 @@ + +/* ***** 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-ssh. + * + * 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 _SSH_GUAC_COMMON_H +#define _SSH_GUAC_COMMON_H + +/** + * Returns the closest value to the value given that is also + * within the given range. + */ +int guac_terminal_fit_to_range(int value, int min, int max); + +#endif + diff --git a/protocols/ssh/src/common.c b/protocols/ssh/src/common.c new file mode 100644 index 00000000..ca664666 --- /dev/null +++ b/protocols/ssh/src/common.c @@ -0,0 +1,46 @@ + +/* ***** 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-ssh. + * + * 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 ***** */ + +int guac_terminal_fit_to_range(int value, int min, int max) { + + if (value < min) return min; + if (value > max) return max; + + return value; + +} + diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index ede7f16e..f972e31f 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -40,6 +40,7 @@ #include #include +#include "common.h" #include "types.h" #include "display.h" @@ -318,11 +319,21 @@ void guac_terminal_display_copy_columns(guac_terminal_display* display, int row, int start_column, int end_column, int offset) { int i; - guac_terminal_operation* src_current = - &(display->operations[row * display->width + start_column]); + guac_terminal_operation* src_current; + guac_terminal_operation* current; - guac_terminal_operation* current = - &(display->operations[row * display->width + start_column + offset]); + /* Ignore operations outside display bounds */ + if (row < 0 || row >= display->height) + return; + + /* Fit range within bounds */ + start_column = guac_terminal_fit_to_range(start_column, 0, display->width - 1); + end_column = guac_terminal_fit_to_range(end_column, 0, display->width - 1); + start_column = guac_terminal_fit_to_range(start_column + offset, 0, display->width - 1) - offset; + end_column = guac_terminal_fit_to_range(end_column + offset, 0, display->width - 1) - offset; + + src_current = &(display->operations[row * display->width + start_column]); + current = &(display->operations[row * display->width + start_column + offset]); /* Move data */ memmove(current, src_current, @@ -349,12 +360,17 @@ void guac_terminal_display_copy_rows(guac_terminal_display* display, int start_row, int end_row, int offset) { int row, col; + guac_terminal_operation* src_current_row; + guac_terminal_operation* current_row; - guac_terminal_operation* src_current_row = - &(display->operations[start_row * display->width]); + /* Fit range within bounds */ + start_row = guac_terminal_fit_to_range(start_row, 0, display->height - 1); + end_row = guac_terminal_fit_to_range(end_row, 0, display->height - 1); + start_row = guac_terminal_fit_to_range(start_row + offset, 0, display->height - 1) - offset; + end_row = guac_terminal_fit_to_range(end_row + offset, 0, display->height - 1) - offset; - guac_terminal_operation* current_row = - &(display->operations[(start_row + offset) * display->width]); + src_current_row = &(display->operations[start_row * display->width]); + current_row = &(display->operations[(start_row + offset) * display->width]); /* Move data */ memmove(current_row, src_current_row, @@ -389,8 +405,17 @@ void guac_terminal_display_set_columns(guac_terminal_display* display, int row, int start_column, int end_column, guac_terminal_char* character) { int i; - guac_terminal_operation* current = - &(display->operations[row * display->width + start_column]); + guac_terminal_operation* current; + + /* Ignore operations outside display bounds */ + if (row < 0 || row >= display->height) + return; + + /* Fit range within bounds */ + start_column = guac_terminal_fit_to_range(start_column, 0, display->width - 1); + end_column = guac_terminal_fit_to_range(end_column, 0, display->width - 1); + + current = &(display->operations[row * display->width + start_column]); /* For each column in range */ for (i=start_column; i<=end_column; i++) { diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index dc146a70..c950449a 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -131,7 +131,6 @@ int guac_terminal_set(guac_terminal* term, int row, int col, char c) { int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col) { guac_terminal_char* guac_char; - int scrolled_row = row + term->scroll_offset; /* Get character from buffer */ guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(term->buffer, row, col+1); @@ -141,8 +140,7 @@ int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col) { guac_char->attributes.reverse = !(guac_char->attributes.reverse); /* Set display */ - if (scrolled_row < term->display->height) - guac_terminal_display_set_columns(term->display, scrolled_row, col, col, guac_char); + guac_terminal_display_set_columns(term->display, row + term->scroll_offset, col, col, guac_char); return 0; @@ -387,7 +385,7 @@ void guac_terminal_copy_columns(guac_terminal* terminal, int row, int start_column, int end_column, int offset) { /* STUB */ - guac_terminal_display_copy_columns(terminal->display, row, + guac_terminal_display_copy_columns(terminal->display, row + terminal->scroll_offset, start_column, end_column, offset); guac_terminal_buffer_copy_columns(terminal->buffer, row, @@ -400,7 +398,7 @@ void guac_terminal_copy_rows(guac_terminal* terminal, /* STUB */ guac_terminal_display_copy_rows(terminal->display, - start_row, end_row, offset); + start_row + terminal->scroll_offset, end_row + terminal->scroll_offset, offset); guac_terminal_buffer_copy_rows(terminal->buffer, start_row, end_row, offset); @@ -411,7 +409,7 @@ void guac_terminal_set_columns(guac_terminal* terminal, int row, int start_column, int end_column, guac_terminal_char* character) { /* STUB */ - guac_terminal_display_set_columns(terminal->display, row, + guac_terminal_display_set_columns(terminal->display, row + terminal->scroll_offset, start_column, end_column, character); guac_terminal_buffer_set_columns(terminal->buffer, row, From 8c81cae871fecb2183e44a79778e95b682de68ef Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 1 May 2013 16:54:29 -0700 Subject: [PATCH 091/169] Initial screen resize support. --- protocols/ssh/include/ssh_handlers.h | 1 + protocols/ssh/src/ssh_client.c | 1 + protocols/ssh/src/ssh_handlers.c | 30 ++++++++++++++++++++++++++++ protocols/ssh/src/terminal.c | 12 +++++++++++ 4 files changed, 44 insertions(+) diff --git a/protocols/ssh/include/ssh_handlers.h b/protocols/ssh/include/ssh_handlers.h index 300e93b0..d9f7ef18 100644 --- a/protocols/ssh/include/ssh_handlers.h +++ b/protocols/ssh/include/ssh_handlers.h @@ -45,6 +45,7 @@ int ssh_guac_client_handle_messages(guac_client* client); int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed); int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask); int ssh_guac_client_clipboard_handler(guac_client* client, char* data); +int ssh_guac_client_size_handler(guac_client* client, int width, int height); int ssh_guac_client_free_handler(guac_client* client); #endif diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 671ecb42..a58731fd 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -228,6 +228,7 @@ int ssh_guac_client_auth(guac_client* client, const char* password) { client->mouse_handler = ssh_guac_client_mouse_handler; client->key_handler = ssh_guac_client_key_handler; client->clipboard_handler = ssh_guac_client_clipboard_handler; + client->size_handler = ssh_guac_client_size_handler; /* Success */ diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 26bba07a..c182baa7 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -296,6 +296,36 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { } +int ssh_guac_client_size_handler(guac_client* client, int width, int height) { + + /* Get terminal */ + ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data; + guac_terminal* terminal = guac_client_data->term; + + /* Calculate dimensions */ + int rows = height / terminal->display->char_height; + int columns = width / terminal->display->char_width; + + pthread_mutex_lock(&(terminal->lock)); + + /* If size has changed */ + if (columns != terminal->term_width || rows != terminal->term_height) { + + /* Resize terminal */ + guac_terminal_resize(terminal, columns, rows); + channel_change_pty_size(guac_client_data->term_channel, + terminal->term_width, terminal->term_height); + + /* Reset scroll region */ + terminal->scroll_end = rows - 1; + + } + + pthread_mutex_unlock(&(terminal->lock)); + + return 0; +} + int ssh_guac_client_free_handler(guac_client* client) { ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data; diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index c950449a..c08c1bb7 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -417,3 +417,15 @@ void guac_terminal_set_columns(guac_terminal* terminal, int row, } +void guac_terminal_resize(guac_terminal* term, int width, int height) { + + /* Resize display */ + guac_terminal_display_flush(term->display); + guac_terminal_display_resize(term->display, width, height); + + /* Commit new dimensions */ + term->term_width = width; + term->term_height = height; + +} + From ed777d0a76c9c3d1c2a547042c84819b7890e126 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 2 May 2013 01:22:50 -0700 Subject: [PATCH 092/169] Remove stub status - no longer stubs. --- protocols/ssh/src/terminal.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index c08c1bb7..854c4924 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -395,7 +395,6 @@ void guac_terminal_copy_columns(guac_terminal* terminal, int row, void guac_terminal_copy_rows(guac_terminal* terminal, int start_row, int end_row, int offset) { - /* STUB */ guac_terminal_display_copy_rows(terminal->display, start_row + terminal->scroll_offset, end_row + terminal->scroll_offset, offset); @@ -407,7 +406,6 @@ void guac_terminal_copy_rows(guac_terminal* terminal, void guac_terminal_set_columns(guac_terminal* terminal, int row, int start_column, int end_column, guac_terminal_char* character) { - /* STUB */ guac_terminal_display_set_columns(terminal->display, row + terminal->scroll_offset, start_column, end_column, character); From 22ee4860191f4d6962a41985658a754255778790 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 2 May 2013 01:28:31 -0700 Subject: [PATCH 093/169] Remove more FIXME and STUB status. --- protocols/ssh/src/terminal.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 854c4924..9eb8eb43 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -305,7 +305,6 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal, } - /* FIXME: Should flush somewhere more sensible */ guac_terminal_display_flush(terminal->display); guac_socket_flush(terminal->client->socket); @@ -363,7 +362,6 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, } - /* FIXME: Should flush somewhere more sensible */ guac_terminal_display_flush(terminal->display); guac_socket_flush(terminal->client->socket); @@ -383,7 +381,6 @@ void guac_terminal_select_end(guac_terminal* terminal) { void guac_terminal_copy_columns(guac_terminal* terminal, int row, int start_column, int end_column, int offset) { - /* STUB */ guac_terminal_display_copy_columns(terminal->display, row + terminal->scroll_offset, start_column, end_column, offset); From ac52c8fb6408725db67e8317cd77df1b68f0ca6a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 2 May 2013 03:18:10 -0700 Subject: [PATCH 094/169] Proper handling of new rows and removed rows. --- protocols/ssh/src/terminal.c | 67 ++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 9eb8eb43..1c3ad517 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -414,10 +414,77 @@ void guac_terminal_set_columns(guac_terminal* terminal, int row, void guac_terminal_resize(guac_terminal* term, int width, int height) { + /* If height is decreasing, shift display up */ + if (height < term->term_height) { + + int shift_amount; + + /* Get number of rows actually occupying terminal space */ + int used_height = term->buffer->length; + if (used_height > term->term_height) + used_height = term->term_height; + + shift_amount = used_height - height; + + /* If the new terminal bottom covers N rows, shift up N rows */ + if (shift_amount > 0) { + + guac_terminal_display_copy_rows(term->display, + shift_amount, term->display->height - 1, -shift_amount); + + /* Update buffer top and cursor row based on shift */ + term->buffer->top += shift_amount; + term->cursor_row -= shift_amount; + + } + + } + /* Resize display */ guac_terminal_display_flush(term->display); guac_terminal_display_resize(term->display, width, height); + /* If height is increasing, shift display down */ + if (height > term->term_height) { + + /* If undisplayed rows exist in the buffer, shift them into view */ + if (term->term_height < term->buffer->length) { + + /* If the new terminal bottom reveals N rows, shift down N rows */ + int shift_amount = height - term->term_height; + + /* The maximum amount we can shift is the number of undisplayed rows */ + int max_shift = term->buffer->length - term->term_height; + + if (shift_amount > max_shift) + shift_amount = max_shift; + + /* Update buffer top and cursor row based on shift */ + term->buffer->top -= shift_amount; + term->cursor_row += shift_amount; + + /* If scrolled enough, use scroll to fulfill entire resize */ + if (term->scroll_offset >= shift_amount) + term->scroll_offset -= shift_amount; + + /* Otherwise, fulfill with as much scroll as possible */ + else { + + /* Attempt to fulfill part with scroll */ + shift_amount -= term->scroll_offset; + term->scroll_offset = 0; + + /* If anything remains, move screen as necessary */ + if (shift_amount > 0) + guac_terminal_display_copy_rows(term->display, + 0, term->display->height - shift_amount - 1, shift_amount); + + } + + } /* end if undisplayed rows exist */ + + } + /* Commit new dimensions */ term->term_width = width; term->term_height = height; From de5b945f7393ac998cf2bcb16367541d7887418a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 2 May 2013 12:35:20 -0700 Subject: [PATCH 095/169] Redraw from buffer as necessary during resize (finish resize support). --- protocols/ssh/src/buffer.c | 5 +++- protocols/ssh/src/terminal.c | 51 +++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/protocols/ssh/src/buffer.c b/protocols/ssh/src/buffer.c index 6ea07b13..5108ce77 100644 --- a/protocols/ssh/src/buffer.c +++ b/protocols/ssh/src/buffer.c @@ -99,7 +99,10 @@ guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buf /* Calculate scrollback row index */ int index = buffer->top + row; - if (index < 0) index += buffer->available; + if (index < 0) + index += buffer->available; + else if (index >= buffer->available) + index -= buffer->available; /* Get row */ buffer_row = &(buffer->rows[index]); diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 1c3ad517..88883806 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -412,6 +412,29 @@ void guac_terminal_set_columns(guac_terminal* terminal, int row, } +static void __guac_terminal_redraw_rect(guac_terminal* term, int start_row, int start_col, int end_row, int end_col) { + + int row, col; + + /* Redraw region */ + for (row=start_row; row<=end_row; row++) { + + guac_terminal_buffer_row* buffer_row = + guac_terminal_buffer_get_row(term->buffer, row - term->scroll_offset, 0); + + /* Clear row */ + guac_terminal_display_set_columns(term->display, + row, start_col, end_col, &(term->default_char)); + + /* Copy characters */ + for (col=start_col; col <= end_col && col < buffer_row->length; col++) + guac_terminal_display_set_columns(term->display, row, col, col, + &(buffer_row->characters[col])); + + } + +} + void guac_terminal_resize(guac_terminal* term, int width, int height) { /* If height is decreasing, shift display up */ @@ -436,6 +459,9 @@ void guac_terminal_resize(guac_terminal* term, int width, int height) { term->buffer->top += shift_amount; term->cursor_row -= shift_amount; + /* Redraw characters within old region */ + __guac_terminal_redraw_rect(term, height - shift_amount, 0, height-1, width-1); + } } @@ -444,6 +470,10 @@ void guac_terminal_resize(guac_terminal* term, int width, int height) { guac_terminal_display_flush(term->display); guac_terminal_display_resize(term->display, width, height); + /* Reraw any characters on right if widening */ + if (width > term->term_width) + __guac_terminal_redraw_rect(term, 0, term->term_width-1, height-1, width-1); + /* If height is increasing, shift display down */ if (height > term->term_height) { @@ -464,21 +494,36 @@ void guac_terminal_resize(guac_terminal* term, int width, int height) { term->cursor_row += shift_amount; /* If scrolled enough, use scroll to fulfill entire resize */ - if (term->scroll_offset >= shift_amount) + if (term->scroll_offset >= shift_amount) { + term->scroll_offset -= shift_amount; + /* Draw characters from scroll at bottom */ + __guac_terminal_redraw_rect(term, term->term_height, 0, term->term_height + shift_amount - 1, width-1); + + } + /* Otherwise, fulfill with as much scroll as possible */ else { - /* Attempt to fulfill part with scroll */ + /* Draw characters from scroll at bottom */ + __guac_terminal_redraw_rect(term, term->term_height, 0, term->term_height + term->scroll_offset - 1, width-1); + + /* Update shift_amount and scroll based on new rows */ shift_amount -= term->scroll_offset; term->scroll_offset = 0; /* If anything remains, move screen as necessary */ - if (shift_amount > 0) + if (shift_amount > 0) { + guac_terminal_display_copy_rows(term->display, 0, term->display->height - shift_amount - 1, shift_amount); + /* Draw characters at top from scroll */ + __guac_terminal_redraw_rect(term, 0, 0, shift_amount - 1, width-1); + + } + } } /* end if undisplayed rows exist */ From 356e3945e9271bc2b8b7d8a06b72c1bf842ffb17 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 2 May 2013 14:56:20 -0700 Subject: [PATCH 096/169] Unicode support. --- protocols/ssh/include/display.h | 21 +++++- protocols/ssh/include/terminal.h | 2 +- protocols/ssh/include/types.h | 4 +- protocols/ssh/src/display.c | 101 +++++++++++++++++++++++--- protocols/ssh/src/terminal.c | 4 +- protocols/ssh/src/terminal_handlers.c | 6 +- 6 files changed, 116 insertions(+), 22 deletions(-) diff --git a/protocols/ssh/include/display.h b/protocols/ssh/include/display.h index 0d9df406..0aa94528 100644 --- a/protocols/ssh/include/display.h +++ b/protocols/ssh/include/display.h @@ -102,6 +102,25 @@ typedef struct guac_terminal_operation { } guac_terminal_operation; + +/** + * A cached glyph. + */ +typedef struct guac_terminal_glyph { + + /** + * The location within the glyph layer that this glyph can be found. + */ + int location; + + /** + * The codepoint currently stored at that location. + */ + int codepoint; + +} guac_terminal_glyph; + + /** * Set of all pending operations for the currently-visible screen area. */ @@ -150,7 +169,7 @@ typedef struct guac_terminal_display { /** * Index of locations for each glyph in the stroke and fill layers. */ - int glyphs[256]; + guac_terminal_glyph glyphs[512]; /** * Color of glyphs in copy buffer diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 4992ba3b..59faa3a2 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -190,7 +190,7 @@ int guac_terminal_write(guac_terminal* term, const char* c, int size); /** * Sets the character at the given row and column to the specified value. */ -int guac_terminal_set(guac_terminal* term, int row, int col, char c); +int guac_terminal_set(guac_terminal* term, int row, int col, int codepoint); /** * Clears the given region within a single row. diff --git a/protocols/ssh/include/types.h b/protocols/ssh/include/types.h index cfe68c6c..d28a425e 100644 --- a/protocols/ssh/include/types.h +++ b/protocols/ssh/include/types.h @@ -107,9 +107,9 @@ typedef struct guac_terminal_attributes { typedef struct guac_terminal_char { /** - * The character value of the character to display. + * The Unicode codepoint of the character to display. */ - char value; + int value; /** * The attributes of the character to display. diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index f972e31f..4fc69ce5 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -68,17 +68,79 @@ const guac_terminal_color guac_terminal_palette[16] = { }; +/* Maps any codepoint onto a number between 0 and 511 inclusive */ +int __guac_terminal_hash_codepoint(int codepoint) { + + /* If within one byte, just return codepoint */ + if (codepoint <= 0xFF) + return codepoint; + + /* Otherwise, map to next 256 values */ + return (codepoint & 0xFF) + 0x100; + +} + +int __guac_terminal_encode_utf8(int codepoint, char* utf8) { + + int i; + int mask, bytes; + + /* Determine size and initial byte mask */ + if (codepoint <= 0x007F) { + mask = 0x00; + bytes = 1; + } + else if (codepoint <= 0x7FF) { + mask = 0xC0; + bytes = 2; + } + else if (codepoint <= 0xFFFF) { + mask = 0xE0; + bytes = 3; + } + else if (codepoint <= 0x1FFFFF) { + mask = 0xF0; + bytes = 4; + } + + /* Otherwise, invalid codepoint */ + else { + *(utf8++) = '?'; + *(utf8++) = 0; + return 1; + } + + /* Offset buffer by size */ + utf8 += bytes; + *(utf8--) = 0; + + /* Add trailing bytes, if any */ + for (i=1; i>= 6; + } + + /* Set initial byte */ + *utf8 = mask | codepoint; + + /* Done */ + return bytes; + +} + /** * Returns the location of the given character in the glyph cache layer, * sending it first if necessary. The location returned is in characters, * and thus must be multiplied by the glyph width to obtain the actual * location within the glyph cache layer. */ -int __guac_terminal_get_glyph(guac_terminal_display* display, char c) { +int __guac_terminal_get_glyph(guac_terminal_display* display, int codepoint) { guac_socket* socket = display->client->socket; int location; - + + char utf8[5]; + /* Use foreground color */ const guac_terminal_color* color = &guac_terminal_palette[display->glyph_foreground]; @@ -92,13 +154,29 @@ int __guac_terminal_get_glyph(guac_terminal_display* display, char c) { PangoLayout* layout; - /* Return glyph if exists */ - if (display->glyphs[(int) c]) - return display->glyphs[(int) c] - 1; + /* Get codepoint hash */ + int hashcode = __guac_terminal_hash_codepoint(codepoint); - location = display->next_glyph++; + /* If something already stored here, either same codepoint or collision */ + if (display->glyphs[hashcode].location) { - /* Otherwise, draw glyph */ + /* If match, return match. */ + if (display->glyphs[hashcode].codepoint == codepoint) + return display->glyphs[hashcode].location - 1; + + /* Otherwise, reuse location */ + location = display->glyphs[hashcode].location; + + } + + /* If no collision, allocate new glyph location */ + else + location = display->next_glyph++; + + /* Convert to UTF-8 */ + __guac_terminal_encode_utf8(codepoint, utf8); + + /* Prepare surface */ surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, display->char_width, display->char_height); @@ -107,7 +185,7 @@ int __guac_terminal_get_glyph(guac_terminal_display* display, char c) { /* Get layout */ layout = pango_cairo_create_layout(cairo); pango_layout_set_font_description(layout, display->font_desc); - pango_layout_set_text(layout, &c, 1); + pango_layout_set_text(layout, utf8, 1); /* Draw */ cairo_set_source_rgba(cairo, @@ -140,7 +218,8 @@ int __guac_terminal_get_glyph(guac_terminal_display* display, char c) { location * display->char_width, 0, display->char_width, display->char_height, GUAC_COMP_OVER, display->filled_glyphs, location * display->char_width, 0); - display->glyphs[(int) c] = location+1; + display->glyphs[hashcode].location = location+1; + display->glyphs[hashcode].codepoint = codepoint; cairo_surface_destroy(surface); @@ -235,10 +314,10 @@ int __guac_terminal_set_colors(guac_terminal_display* display, * rendering the charater immediately. This bypasses the guac_terminal_display * mechanism and is intended for flushing of updates only. */ -int __guac_terminal_set(guac_terminal_display* display, int row, int col, char c) { +int __guac_terminal_set(guac_terminal_display* display, int row, int col, int codepoint) { guac_socket* socket = display->client->socket; - int location = __guac_terminal_get_glyph(display, c); + int location = __guac_terminal_get_glyph(display, codepoint); return guac_protocol_send_copy(socket, display->filled_glyphs, diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 88883806..74d18697 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -115,11 +115,11 @@ void guac_terminal_free(guac_terminal* term) { } -int guac_terminal_set(guac_terminal* term, int row, int col, char c) { +int guac_terminal_set(guac_terminal* term, int row, int col, int codepoint) { /* Build character with current attributes */ guac_terminal_char guac_char; - guac_char.value = c; + guac_char.value = codepoint; guac_char.attributes = term->current_attributes; guac_terminal_set_columns(term, row, col, col, &guac_char); diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 619e1c89..c345f2c8 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -139,15 +139,11 @@ int guac_terminal_echo(guac_terminal* term, char c) { } - /* For now, render all but basic latin as '?' */ - if (codepoint > 0x7F) - codepoint = '?'; - /* Write character */ guac_terminal_set(term, term->cursor_row, term->cursor_col, - (char) codepoint); + codepoint); /* Advance cursor */ term->cursor_col++; From 5e51f361ee1731749fa3ccb8e5e34df74de38d39 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 2 May 2013 15:06:21 -0700 Subject: [PATCH 097/169] Fix unicode support (wrong lengths). --- protocols/ssh/src/display.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 4fc69ce5..d02d4d1f 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -106,13 +106,11 @@ int __guac_terminal_encode_utf8(int codepoint, char* utf8) { /* Otherwise, invalid codepoint */ else { *(utf8++) = '?'; - *(utf8++) = 0; return 1; } /* Offset buffer by size */ - utf8 += bytes; - *(utf8--) = 0; + utf8 += bytes - 1; /* Add trailing bytes, if any */ for (i=1; iclient->socket; int location; - char utf8[5]; + int bytes; + char utf8[4]; /* Use foreground color */ const guac_terminal_color* color = @@ -174,7 +173,7 @@ int __guac_terminal_get_glyph(guac_terminal_display* display, int codepoint) { location = display->next_glyph++; /* Convert to UTF-8 */ - __guac_terminal_encode_utf8(codepoint, utf8); + bytes = __guac_terminal_encode_utf8(codepoint, utf8); /* Prepare surface */ surface = cairo_image_surface_create( @@ -185,7 +184,7 @@ int __guac_terminal_get_glyph(guac_terminal_display* display, int codepoint) { /* Get layout */ layout = pango_cairo_create_layout(cairo); pango_layout_set_font_description(layout, display->font_desc); - pango_layout_set_text(layout, utf8, 1); + pango_layout_set_text(layout, utf8, bytes); /* Draw */ cairo_set_source_rgba(cairo, From edc4c223bb7e78c50ab4493e4200c55fa2715a2d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 2 May 2013 22:50:33 -0700 Subject: [PATCH 098/169] Restore scroll down. --- protocols/ssh/src/terminal.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 74d18697..30c5f333 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -191,7 +191,14 @@ int guac_terminal_scroll_up(guac_terminal* term, int guac_terminal_scroll_down(guac_terminal* term, int start_row, int end_row, int amount) { - /* STUB */ + + guac_terminal_copy_rows(term, start_row, end_row - amount, amount); + + /* Clear new area */ + guac_terminal_clear_range(term, + start_row, 0, + start_row + amount - 1, term->term_width - 1); + return 0; } From 3a32e7084c1ca8cde0b6adba9a2a5a9393d98b2a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 3 May 2013 02:50:05 -0700 Subject: [PATCH 099/169] Implement more console codes and reverse linefeed, de-stub buffer functions. --- protocols/ssh/src/buffer.c | 60 ++++++++++++++++++++++++++- protocols/ssh/src/terminal_handlers.c | 29 ++++++++++--- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/protocols/ssh/src/buffer.c b/protocols/ssh/src/buffer.c index 5108ce77..daeb412e 100644 --- a/protocols/ssh/src/buffer.c +++ b/protocols/ssh/src/buffer.c @@ -36,8 +36,10 @@ * ***** END LICENSE BLOCK ***** */ #include +#include #include "buffer.h" +#include "common.h" guac_terminal_buffer* guac_terminal_buffer_alloc(int rows, guac_terminal_char* default_character) { @@ -132,12 +134,66 @@ guac_terminal_buffer_row* guac_terminal_buffer_get_row(guac_terminal_buffer* buf void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row, int start_column, int end_column, int offset) { - /* STUB */ + + guac_terminal_char* src; + guac_terminal_char* dst; + + /* Get row */ + guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(buffer, row, end_column + offset + 1); + + /* Fit range within bounds */ + start_column = guac_terminal_fit_to_range(start_column, 0, buffer_row->length - 1); + end_column = guac_terminal_fit_to_range(end_column, 0, buffer_row->length - 1); + start_column = guac_terminal_fit_to_range(start_column + offset, 0, buffer_row->length - 1) - offset; + end_column = guac_terminal_fit_to_range(end_column + offset, 0, buffer_row->length - 1) - offset; + + /* Determine source and destination locations */ + src = &(buffer_row->characters[start_column]); + dst = &(buffer_row->characters[start_column + offset]); + + /* Copy data */ + memmove(dst, src, sizeof(guac_terminal_char) * (end_column - start_column + 1)); + } void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int start_row, int end_row, int offset) { - /* STUB */ + + int row; + int step; + + /* If shifting down, copy in reverse */ + if (offset > 0) { + + /* Swap start/end */ + int temp = end_row; + end_row = start_row; + start_row = temp; + + /* Iterate backwards */ + step = -1; + + } + + /* Otherwise, copy forwards */ + else + step = 1; + + /* Copy each row individually */ + for (row = start_row; row <= end_row; row += step) { + + guac_terminal_buffer_row* src_row; + guac_terminal_buffer_row* dst_row; + + /* Get source and destination rows */ + src_row = guac_terminal_buffer_get_row(buffer, row, 0); + dst_row = guac_terminal_buffer_get_row(buffer, row + offset, src_row->length); + + /* Copy data */ + memcpy(dst_row->characters, src_row->characters, sizeof(guac_terminal_char) * src_row->length); + + } + } void guac_terminal_buffer_set_columns(guac_terminal_buffer* buffer, int row, diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index c345f2c8..f3f9cd01 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -75,8 +75,10 @@ int guac_terminal_echo(guac_terminal* term, char c) { bytes_remaining--; } + /* Unrecognized prefix */ else { - /* FIXME: Handle */ + codepoint = '?'; + bytes_remaining = 0; } /* If we need more bytes, wait for more bytes */ @@ -170,6 +172,23 @@ int guac_terminal_escape(guac_terminal* term, char c) { term->char_handler = guac_terminal_csi; break; + case 'M': /* Reverse Linefeed */ + + term->cursor_row--; + + /* Scroll down if necessary */ + if (term->cursor_row < term->scroll_start) { + term->cursor_row = term->scroll_start; + + /* Scroll down by one row */ + guac_terminal_scroll_down(term, term->scroll_start, + term->scroll_end, 1); + + } + + term->char_handler = guac_terminal_echo; + break; + default: guac_client_log_info(term->client, "Unhandled ESC sequence: %c", c); term->char_handler = guac_terminal_echo; @@ -334,14 +353,14 @@ int guac_terminal_csi(guac_terminal* term, char c) { else if (value == 7) term->current_attributes.reverse = true; + /* Reset underscore */ + else if (value == 24) + term->current_attributes.underscore = false; + /* Reset reverse video */ else if (value == 27) term->current_attributes.reverse = false; - /* Reset intensity */ - else if (value == 27) - term->current_attributes.bold = false; - else guac_client_log_info(term->client, "Unhandled graphics rendition: %i", value); From 857498fc19db2c807b5dc2caca7b0cfa5be70be5 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 3 May 2013 11:49:08 -0700 Subject: [PATCH 100/169] Fix buffer copy_rows logic. --- protocols/ssh/src/buffer.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/protocols/ssh/src/buffer.c b/protocols/ssh/src/buffer.c index daeb412e..15fb6982 100644 --- a/protocols/ssh/src/buffer.c +++ b/protocols/ssh/src/buffer.c @@ -159,38 +159,34 @@ void guac_terminal_buffer_copy_columns(guac_terminal_buffer* buffer, int row, void guac_terminal_buffer_copy_rows(guac_terminal_buffer* buffer, int start_row, int end_row, int offset) { - int row; + int i, current_row; int step; /* If shifting down, copy in reverse */ if (offset > 0) { - - /* Swap start/end */ - int temp = end_row; - end_row = start_row; - start_row = temp; - - /* Iterate backwards */ + current_row = end_row; step = -1; - } /* Otherwise, copy forwards */ - else + else { + current_row = start_row; step = 1; + } - /* Copy each row individually */ - for (row = start_row; row <= end_row; row += step) { - - guac_terminal_buffer_row* src_row; - guac_terminal_buffer_row* dst_row; + /* Copy each current_row individually */ + for (i = start_row; i <= end_row; i++) { /* Get source and destination rows */ - src_row = guac_terminal_buffer_get_row(buffer, row, 0); - dst_row = guac_terminal_buffer_get_row(buffer, row + offset, src_row->length); + guac_terminal_buffer_row* src_row = guac_terminal_buffer_get_row(buffer, current_row, 0); + guac_terminal_buffer_row* dst_row = guac_terminal_buffer_get_row(buffer, current_row + offset, src_row->length); /* Copy data */ memcpy(dst_row->characters, src_row->characters, sizeof(guac_terminal_char) * src_row->length); + dst_row->length = src_row->length; + + /* Next current_row */ + current_row += step; } From 5df2f66fae853d2f9e28cc41b0c5018b0b117807 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 3 May 2013 11:53:06 -0700 Subject: [PATCH 101/169] Remove FIXME status. --- protocols/ssh/src/terminal_handlers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index f3f9cd01..f7467f66 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -214,7 +214,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { static int argv_length = 0; static char argv_buffer[256]; - /* FIXME: "The sequence of parameters may be preceded by a single question mark. */ + /* "The sequence of parameters may be preceded by a single question mark." */ if (c == '?') return 0; From 95810fbb7688e48a7443c11efec4ec378615b0a5 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 3 May 2013 12:10:38 -0700 Subject: [PATCH 102/169] Clear glyph region before sending PNG. --- protocols/ssh/src/display.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index d02d4d1f..3e7ddbcf 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -200,9 +200,18 @@ int __guac_terminal_get_glyph(guac_terminal_display* display, int codepoint) { g_object_unref(layout); cairo_destroy(cairo); - /* Send glyph and update filled flyphs */ + /* Clear existing glyph (if any) */ + guac_protocol_send_rect(socket, display->glyph_stroke, + location * display->char_width, 0, + display->char_width, display->char_height); + + guac_protocol_send_cfill(socket, GUAC_COMP_ROUT, display->glyph_stroke, + 0x00, 0x00, 0x00, 0xFF); + + /* Send glyph */ guac_protocol_send_png(socket, GUAC_COMP_OVER, display->glyph_stroke, location * display->char_width, 0, surface); + /* Update filled glyphs */ guac_protocol_send_rect(socket, display->filled_glyphs, location * display->char_width, 0, display->char_width, display->char_height); From cb2132274539d1cf57000f373b76fd97b33f6a2d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 3 May 2013 12:45:15 -0700 Subject: [PATCH 103/169] Fix wrong location value for collisions. --- protocols/ssh/src/display.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 3e7ddbcf..5e220276 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -159,12 +159,13 @@ int __guac_terminal_get_glyph(guac_terminal_display* display, int codepoint) { /* If something already stored here, either same codepoint or collision */ if (display->glyphs[hashcode].location) { + location = display->glyphs[hashcode].location - 1; + /* If match, return match. */ if (display->glyphs[hashcode].codepoint == codepoint) - return display->glyphs[hashcode].location - 1; + return location; /* Otherwise, reuse location */ - location = display->glyphs[hashcode].location; } From c29c43e056d107bc8359d9ad2d17d645c24c76ab Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 6 May 2013 01:02:23 -0700 Subject: [PATCH 104/169] Initial selection support. --- protocols/ssh/include/display.h | 11 ++++ protocols/ssh/src/display.c | 91 +++++++++++++++++++++++++++++++++ protocols/ssh/src/terminal.c | 32 +++++++++++- 3 files changed, 132 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/include/display.h b/protocols/ssh/include/display.h index 0aa94528..8b368b0f 100644 --- a/protocols/ssh/include/display.h +++ b/protocols/ssh/include/display.h @@ -181,6 +181,11 @@ typedef struct guac_terminal_display { */ int glyph_background; + /** + * Layer above default layer which highlights selected text. + */ + guac_layer* select_layer; + /** * A single wide layer holding each glyph, with each glyph only * colored with foreground color (background remains transparent). @@ -237,5 +242,11 @@ void guac_terminal_display_resize(guac_terminal_display* display, int width, int */ void guac_terminal_display_flush(guac_terminal_display* display); +/** + * Draws the text selection rectangle from the given coordinates to the given end coordinates. + */ +void guac_terminal_display_select(guac_terminal_display* display, + int start_row, int start_col, int end_row, int end_col); + #endif diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 5e220276..bfdfd8d2 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -353,6 +353,8 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int fore display->glyph_stroke = guac_client_alloc_buffer(client); display->filled_glyphs = guac_client_alloc_buffer(client); + display->select_layer = guac_client_alloc_layer(client); + /* Get font */ display->font_desc = pango_font_description_new(); pango_font_description_set_family(display->font_desc, "monospace"); @@ -573,6 +575,11 @@ void guac_terminal_display_resize(guac_terminal_display* display, int width, int display->char_width * width, display->char_height * height); + guac_protocol_send_size(display->client->socket, + display->select_layer, + display->char_width * width, + display->char_height * height); + } void __guac_terminal_display_flush_copy(guac_terminal_display* display) { @@ -880,3 +887,87 @@ void guac_terminal_display_flush(guac_terminal_display* display) { } +void guac_terminal_display_select(guac_terminal_display* display, + int start_row, int start_col, int end_row, int end_col) { + + guac_socket* socket = display->client->socket; + guac_layer* select_layer = display->select_layer; + + guac_client_log_info(display->client, "START_COL=%i", start_col); + + /* If single row, just need one rectangle */ + if (start_row == end_row) { + + /* Ensure proper ordering of columns */ + if (start_col > end_col) { + int temp = start_col; + start_col = end_col; + end_col = temp; + } + + /* Select characters between columns */ + guac_protocol_send_rect(socket, select_layer, + + start_col * display->char_width, + start_row * display->char_height, + + (end_col - start_col + 1) * display->char_width, + display->char_height); + + } + + /* Otherwise, need three */ + else { + + /* Ensure proper ordering of start and end coords */ + if (start_row > end_row) { + + int temp; + + temp = start_row; + start_row = end_row; + end_row = temp; + + temp = start_col; + start_col = end_col; + end_col = temp; + + } + + /* First row */ + guac_protocol_send_rect(socket, select_layer, + + start_col * display->char_width, + start_row * display->char_height, + + display->width * display->char_width, + display->char_height); + + /* Middle */ + guac_protocol_send_rect(socket, select_layer, + + 0, + (start_row + 1) * display->char_height, + + display->width * display->char_width, + (end_row - start_row - 1) * display->char_height); + + /* Last row */ + guac_protocol_send_rect(socket, select_layer, + + 0, + end_row * display->char_height, + + (end_col + 1) * display->char_width, + display->char_height); + + } + + /* Draw new selection, erasing old */ + guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer, + 0x00, 0xFF, 0x00, 0x80); + + guac_socket_flush(socket); + +} + diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 30c5f333..30373678 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -374,16 +374,44 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal, } +void guac_terminal_select_redraw(guac_terminal* terminal) { + + guac_terminal_display_select(terminal->display, + terminal->selection_start_row + terminal->scroll_offset, + terminal->selection_start_column, + terminal->selection_end_row + terminal->scroll_offset, + terminal->selection_end_column); + +} + void guac_terminal_select_start(guac_terminal* terminal, int row, int column) { - /* STUB */ + + terminal->selection_start_row = + terminal->selection_end_row = row; + + terminal->selection_start_column = + terminal->selection_end_column = column; + + terminal->text_selected = true; + + guac_terminal_select_redraw(terminal); + } void guac_terminal_select_update(guac_terminal* terminal, int row, int column) { - /* STUB */ + + if (row != terminal->selection_end_row || column != terminal->selection_end_column) { + terminal->selection_end_row = row; + terminal->selection_end_column = column; + + guac_terminal_select_redraw(terminal); + } + } void guac_terminal_select_end(guac_terminal* terminal) { /* STUB */ + terminal->text_selected = false; } void guac_terminal_copy_columns(guac_terminal* terminal, int row, From 3d1ca93b3adc094283ad5c351006a2a8bcce2649 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 6 May 2013 11:06:21 -0700 Subject: [PATCH 105/169] Initial actual support for copying of text. --- protocols/ssh/include/display.h | 5 +++ protocols/ssh/include/terminal.h | 5 ++- protocols/ssh/src/display.c | 17 +++++-- protocols/ssh/src/ssh_handlers.c | 17 ++++++- protocols/ssh/src/terminal.c | 76 +++++++++++++++++++++++++++++++- 5 files changed, 111 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/include/display.h b/protocols/ssh/include/display.h index 8b368b0f..a20503ea 100644 --- a/protocols/ssh/include/display.h +++ b/protocols/ssh/include/display.h @@ -248,5 +248,10 @@ void guac_terminal_display_flush(guac_terminal_display* display); void guac_terminal_display_select(guac_terminal_display* display, int start_row, int start_col, int end_row, int end_col); +/** + * Clears the select rectangle. + */ +void guac_terminal_display_clear_select(guac_terminal_display* display); + #endif diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 59faa3a2..545cdd89 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -248,9 +248,10 @@ void guac_terminal_select_start(guac_terminal* terminal, int row, int column); void guac_terminal_select_update(guac_terminal* terminal, int row, int column); /** - * Ends text selection, removing any highlight. + * Ends text selection, removing any highlight. Character data is stored in the + * string buffer provided. */ -void guac_terminal_select_end(guac_terminal* terminal); +void guac_terminal_select_end(guac_terminal* terminal, char* string); /* LOW-LEVEL TERMINAL OPERATIONS */ diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index bfdfd8d2..0beb3f3f 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -887,14 +887,25 @@ void guac_terminal_display_flush(guac_terminal_display* display) { } +void guac_terminal_display_clear_select(guac_terminal_display* display) { + + guac_socket* socket = display->client->socket; + guac_layer* select_layer = display->select_layer; + + guac_protocol_send_rect(socket, select_layer, 0, 0, 1, 1); + guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer, + 0x00, 0x00, 0x00, 0x00); + + guac_socket_flush(socket); + +} + void guac_terminal_display_select(guac_terminal_display* display, int start_row, int start_col, int end_row, int end_col) { guac_socket* socket = display->client->socket; guac_layer* select_layer = display->select_layer; - guac_client_log_info(display->client, "START_COL=%i", start_col); - /* If single row, just need one rectangle */ if (start_row == end_row) { @@ -965,7 +976,7 @@ void guac_terminal_display_select(guac_terminal_display* display, /* Draw new selection, erasing old */ guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer, - 0x00, 0xFF, 0x00, 0x80); + 0x00, 0x80, 0xFF, 0x60); guac_socket_flush(socket); diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index c182baa7..044baccd 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -180,8 +180,21 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { pthread_mutex_lock(&(term->lock)); /* If mouse button released, stop selection */ - if (released_mask & GUAC_CLIENT_MOUSE_LEFT) - guac_terminal_select_end(term); + if (released_mask & GUAC_CLIENT_MOUSE_LEFT) { + + /* End selection and get selected text */ + char* string = malloc(term->term_width * term->term_height * sizeof(char)); + guac_terminal_select_end(term, string); + + /* Store new data */ + free(client_data->clipboard_data); + client_data->clipboard_data = string; + + /* Send data */ + guac_protocol_send_clipboard(client->socket, string); + guac_socket_flush(client->socket); + + } /* Otherwise, just update */ else diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 30373678..fd2f75dd 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -409,9 +409,81 @@ void guac_terminal_select_update(guac_terminal* terminal, int row, int column) { } -void guac_terminal_select_end(guac_terminal* terminal) { - /* STUB */ +int __guac_terminal_buffer_string(guac_terminal_buffer_row* row, int start, int end, char* string) { + + int length = 0; + int i; + for (i=start; i<=end; i++) { + *(string++) = (char) row->characters[i].value; + length++; + } + + return length; + +} + +void guac_terminal_select_end(guac_terminal* terminal, char* string) { + + /* Deselect */ terminal->text_selected = false; + guac_terminal_display_clear_select(terminal->display); + + guac_terminal_buffer_row* buffer_row; + + int row; + + int start_row, start_col; + int end_row, end_col; + + /* Ensure proper ordering of start and end coords */ + if (terminal->selection_start_row <= terminal->selection_end_row) { + start_row = terminal->selection_start_row; + start_col = terminal->selection_start_column; + end_row = terminal->selection_end_row; + end_col = terminal->selection_end_column; + } + else { + end_row = terminal->selection_start_row; + end_col = terminal->selection_start_column; + start_row = terminal->selection_end_row; + start_col = terminal->selection_end_column; + } + + /* If only one row, simply copy */ + buffer_row = guac_terminal_buffer_get_row(terminal->buffer, start_row, 0); + if (end_row == start_row) { + if (buffer_row->length - 1 < end_col) + end_col = buffer_row->length - 1; + string += __guac_terminal_buffer_string(buffer_row, start_col, end_col, string); + } + + /* Otherwise, copy multiple rows */ + else { + + /* Store first row */ + string += __guac_terminal_buffer_string(buffer_row, start_col, buffer_row->length - 1, string); + + /* Store all middle rows */ + for (row=start_row+1; rowbuffer, row, 0); + + *(string++) = '\n'; + string += __guac_terminal_buffer_string(buffer_row, 0, buffer_row->length - 1, string); + + } + + /* Store last row */ + buffer_row = guac_terminal_buffer_get_row(terminal->buffer, end_row, 0); + if (buffer_row->length - 1 < end_col) + end_col = buffer_row->length - 1; + string += __guac_terminal_buffer_string(buffer_row, 0, end_col, string); + + } + + /* Null terminator */ + *string = 0; + } void guac_terminal_copy_columns(guac_terminal* terminal, int row, From 547966b63d239f6302d83f35e40955e4f555d2ce Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 6 May 2013 12:18:56 -0700 Subject: [PATCH 106/169] Add missing newline before last line of copied text. Add Unicode support to copied text. --- protocols/ssh/include/common.h | 6 +++++ protocols/ssh/src/common.c | 46 ++++++++++++++++++++++++++++++++ protocols/ssh/src/display.c | 48 +--------------------------------- protocols/ssh/src/terminal.c | 8 ++++-- 4 files changed, 59 insertions(+), 49 deletions(-) diff --git a/protocols/ssh/include/common.h b/protocols/ssh/include/common.h index eedbeb89..f945a558 100644 --- a/protocols/ssh/include/common.h +++ b/protocols/ssh/include/common.h @@ -44,5 +44,11 @@ */ int guac_terminal_fit_to_range(int value, int min, int max); +/** + * Encodes the given codepoint as UTF-8, storing the result within the + * provided buffer, and returning the number of bytes stored. + */ +int guac_terminal_encode_utf8(int codepoint, char* utf8); + #endif diff --git a/protocols/ssh/src/common.c b/protocols/ssh/src/common.c index ca664666..fcd5230a 100644 --- a/protocols/ssh/src/common.c +++ b/protocols/ssh/src/common.c @@ -44,3 +44,49 @@ int guac_terminal_fit_to_range(int value, int min, int max) { } +int guac_terminal_encode_utf8(int codepoint, char* utf8) { + + int i; + int mask, bytes; + + /* Determine size and initial byte mask */ + if (codepoint <= 0x007F) { + mask = 0x00; + bytes = 1; + } + else if (codepoint <= 0x7FF) { + mask = 0xC0; + bytes = 2; + } + else if (codepoint <= 0xFFFF) { + mask = 0xE0; + bytes = 3; + } + else if (codepoint <= 0x1FFFFF) { + mask = 0xF0; + bytes = 4; + } + + /* Otherwise, invalid codepoint */ + else { + *(utf8++) = '?'; + return 1; + } + + /* Offset buffer by size */ + utf8 += bytes - 1; + + /* Add trailing bytes, if any */ + for (i=1; i>= 6; + } + + /* Set initial byte */ + *utf8 = mask | codepoint; + + /* Done */ + return bytes; + +} + diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 0beb3f3f..2f81fad5 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -80,52 +80,6 @@ int __guac_terminal_hash_codepoint(int codepoint) { } -int __guac_terminal_encode_utf8(int codepoint, char* utf8) { - - int i; - int mask, bytes; - - /* Determine size and initial byte mask */ - if (codepoint <= 0x007F) { - mask = 0x00; - bytes = 1; - } - else if (codepoint <= 0x7FF) { - mask = 0xC0; - bytes = 2; - } - else if (codepoint <= 0xFFFF) { - mask = 0xE0; - bytes = 3; - } - else if (codepoint <= 0x1FFFFF) { - mask = 0xF0; - bytes = 4; - } - - /* Otherwise, invalid codepoint */ - else { - *(utf8++) = '?'; - return 1; - } - - /* Offset buffer by size */ - utf8 += bytes - 1; - - /* Add trailing bytes, if any */ - for (i=1; i>= 6; - } - - /* Set initial byte */ - *utf8 = mask | codepoint; - - /* Done */ - return bytes; - -} - /** * Returns the location of the given character in the glyph cache layer, * sending it first if necessary. The location returned is in characters, @@ -174,7 +128,7 @@ int __guac_terminal_get_glyph(guac_terminal_display* display, int codepoint) { location = display->next_glyph++; /* Convert to UTF-8 */ - bytes = __guac_terminal_encode_utf8(codepoint, utf8); + bytes = guac_terminal_encode_utf8(codepoint, utf8); /* Prepare surface */ surface = cairo_image_surface_create( diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index fd2f75dd..9f3e75f6 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -49,6 +49,7 @@ #include "types.h" #include "buffer.h" +#include "common.h" #include "display.h" #include "terminal.h" #include "terminal_handlers.h" @@ -414,8 +415,9 @@ int __guac_terminal_buffer_string(guac_terminal_buffer_row* row, int start, int int length = 0; int i; for (i=start; i<=end; i++) { - *(string++) = (char) row->characters[i].value; - length++; + int bytes = guac_terminal_encode_utf8(row->characters[i].value, string); + string += bytes; + length += bytes; } return length; @@ -477,6 +479,8 @@ void guac_terminal_select_end(guac_terminal* terminal, char* string) { buffer_row = guac_terminal_buffer_get_row(terminal->buffer, end_row, 0); if (buffer_row->length - 1 < end_col) end_col = buffer_row->length - 1; + + *(string++) = '\n'; string += __guac_terminal_buffer_string(buffer_row, 0, end_col, string); } From 0d41f4ecdef5a868773483a2441283495a0c22c7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 6 May 2013 16:12:37 -0700 Subject: [PATCH 107/169] Restore password prompt. --- protocols/ssh/src/ssh_client.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index a58731fd..31e8ceaf 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -71,6 +71,7 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Add to password */ client_data->password[client_data->password_length++] = keysym; guac_terminal_write(client_data->term, "*", 1); + guac_terminal_display_flush(client_data->term->display); guac_socket_flush(client->socket); } else if (keysym == 0xFF08) { @@ -80,6 +81,7 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Backspace */ guac_terminal_write(client_data->term, "\x08\x1B[K", 4); + guac_terminal_display_flush(client_data->term->display); guac_socket_flush(client->socket); } @@ -91,6 +93,7 @@ int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pr /* Clear screen */ guac_terminal_write(client_data->term, "\x1B[2J\x1B[1;1H", 10); + guac_terminal_display_flush(client_data->term->display); guac_socket_flush(client->socket); return ssh_guac_client_auth(client, client_data->password); @@ -151,6 +154,10 @@ int guac_client_init(guac_client* client, int argc, char** argv) { return 1; } + /* Set basic handlers */ + client->free_handler = ssh_guac_client_free_handler; + client->size_handler = ssh_guac_client_size_handler; + /* If password provided, authenticate now */ if (argv[2][0] != '\0') return ssh_guac_client_auth(client, argv[2]); @@ -160,6 +167,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->password_length = 0; guac_terminal_write(client_data->term, "Password: ", 10); + guac_terminal_display_flush(client_data->term->display); guac_socket_flush(client->socket); client->key_handler = ssh_guac_client_password_key_handler; @@ -224,12 +232,9 @@ int ssh_guac_client_auth(guac_client* client, const char* password) { /* Set handlers */ client->handle_messages = ssh_guac_client_handle_messages; - client->free_handler = ssh_guac_client_free_handler; client->mouse_handler = ssh_guac_client_mouse_handler; client->key_handler = ssh_guac_client_key_handler; client->clipboard_handler = ssh_guac_client_clipboard_handler; - client->size_handler = ssh_guac_client_size_handler; - /* Success */ return 0; From a7ba3f085fc3fbfd81f39ac9056c39fd43c82bad Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 9 May 2013 21:43:46 -0700 Subject: [PATCH 108/169] Username parameter should be "username", not "user". --- protocols/ssh/src/ssh_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 31e8ceaf..0ceed6df 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -54,7 +54,7 @@ /* Client plugin arguments */ const char* GUAC_CLIENT_ARGS[] = { "hostname", - "user", + "username", "password", NULL }; From 14bf8dd843232913f4bf9abc21e43cb1aecac3a9 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 13 May 2013 01:51:16 -0700 Subject: [PATCH 109/169] Handle typing of Unicode properly. --- protocols/ssh/src/ssh_handlers.c | 35 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 044baccd..536e4334 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -52,6 +52,7 @@ #include "ssh_handlers.h" #include "ssh_client.h" +#include "common.h" #include "cursor.h" int ssh_guac_client_handle_messages(guac_client* client) { @@ -265,22 +266,36 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { pthread_mutex_unlock(&(term->lock)); } - /* If simple ASCII key */ - if (keysym >= 0x00 && keysym <= 0xFF) { - char data = (char) keysym; + /* Translate Ctrl+letter to control code */ + if (client_data->mod_ctrl) { - /* Handle Ctrl modifier */ - if (client_data->mod_ctrl) { - if (keysym >= 'A' && keysym <= 'Z') - data = (char) (keysym - 'A' + 1); - else if (keysym >= 'a' && keysym <= 'z') - data = (char) (keysym - 'a' + 1); - } + char data; + + /* If valid control code, send it */ + if (keysym >= 'A' && keysym <= 'Z') + data = (char) (keysym - 'A' + 1); + else if (keysym >= 'a' && keysym <= 'z') + data = (char) (keysym - 'a' + 1); + + /* Otherwise ignore */ + else + return 0; return channel_write(client_data->term_channel, &data, 1); } + /* Translate Unicode to UTF-8 */ + else if ((keysym >= 0x00 && keysym <= 0xFF) || ((keysym & 0xFFFF0000) == 0x01000000)) { + + int length; + char data[5]; + + length = guac_terminal_encode_utf8(keysym & 0xFFFF, data); + return channel_write(client_data->term_channel, data, length); + + } + else { int length = 0; From d89453e342ce9c9c2bf658a3a2b85d2136808613 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 14 May 2013 13:26:22 -0700 Subject: [PATCH 110/169] Keep highlight displayed until text beneath highlight is touched (low-level logic is stubbed). --- protocols/ssh/include/display.h | 38 +++++++++++++++++- protocols/ssh/src/display.c | 68 +++++++++++++++++++++++++++------ protocols/ssh/src/terminal.c | 2 +- 3 files changed, 94 insertions(+), 14 deletions(-) diff --git a/protocols/ssh/include/display.h b/protocols/ssh/include/display.h index a20503ea..e5a2568e 100644 --- a/protocols/ssh/include/display.h +++ b/protocols/ssh/include/display.h @@ -198,6 +198,39 @@ typedef struct guac_terminal_display { */ guac_layer* filled_glyphs; + /** + * Whether text is being selected. + */ + bool text_selected; + + /** + * Whether the selection is finished, and will no longer be modified. A + * committed selection remains highlighted for reference, but the + * highlight will be removed when the display changes. + */ + bool selection_committed; + + /** + * The row that the selection starts at. + */ + int selection_start_row; + + /** + * The column that the selection starts at. + */ + int selection_start_column; + + /** + * The row that the selection ends at. + */ + int selection_end_row; + + /** + * The column that the selection ends at. + */ + int selection_end_column; + + } guac_terminal_display; /** @@ -249,9 +282,10 @@ void guac_terminal_display_select(guac_terminal_display* display, int start_row, int start_col, int end_row, int end_col); /** - * Clears the select rectangle. + * Commits the select rectangle, allowing the display to clear it when + * necessary. */ -void guac_terminal_display_clear_select(guac_terminal_display* display); +void guac_terminal_display_commit_select(guac_terminal_display* display); #endif diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 2f81fad5..f81be96b 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -68,6 +68,35 @@ const guac_terminal_color guac_terminal_palette[16] = { }; +/** + * Clears the currently-selected region, removing the highlight. + */ +static void __guac_terminal_display_clear_select(guac_terminal_display* display) { + + guac_socket* socket = display->client->socket; + guac_layer* select_layer = display->select_layer; + + guac_protocol_send_rect(socket, select_layer, 0, 0, 1, 1); + guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer, + 0x00, 0x00, 0x00, 0x00); + + guac_socket_flush(socket); + + /* Text is no longer selected */ + display->text_selected = + display->selection_committed = false; + +} + +/** + * Returns whether at least one character within the given range is selected. + */ +static bool __guac_terminal_display_contains_selected(guac_terminal_display* display, + int start_row, int start_column, int end_row, int end_column) { + /* STUB */ + return false; +} + /* Maps any codepoint onto a number between 0 and 511 inclusive */ int __guac_terminal_hash_codepoint(int codepoint) { @@ -345,6 +374,10 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int fore display->height = 0; display->operations = NULL; + /* Initially nothing selected */ + display->text_selected = + display->selection_committed = false; + return display; } @@ -398,6 +431,11 @@ void guac_terminal_display_copy_columns(guac_terminal_display* display, int row, } + /* If selection visible and committed, clear if update touches selection */ + if (display->text_selected && display->selection_committed && + __guac_terminal_display_contains_selected(display, row, row, start_column, end_column)) + __guac_terminal_display_clear_select(display); + } void guac_terminal_display_copy_rows(guac_terminal_display* display, @@ -443,6 +481,11 @@ void guac_terminal_display_copy_rows(guac_terminal_display* display, } + /* If selection visible and committed, clear if update touches selection */ + if (display->text_selected && display->selection_committed && + __guac_terminal_display_contains_selected(display, start_row, end_row, 0, display->width - 1)) + __guac_terminal_display_clear_select(display); + } void guac_terminal_display_set_columns(guac_terminal_display* display, int row, @@ -472,6 +515,11 @@ void guac_terminal_display_set_columns(guac_terminal_display* display, int row, current++; } + /* If selection visible and committed, clear if update touches selection */ + if (display->text_selected && display->selection_committed && + __guac_terminal_display_contains_selected(display, row, row, start_column, end_column)) + __guac_terminal_display_clear_select(display); + } void guac_terminal_display_resize(guac_terminal_display* display, int width, int height) { @@ -534,6 +582,10 @@ void guac_terminal_display_resize(guac_terminal_display* display, int width, int display->char_width * width, display->char_height * height); + /* If selection visible and committed, clear */ + if (display->text_selected && display->selection_committed) + __guac_terminal_display_clear_select(display); + } void __guac_terminal_display_flush_copy(guac_terminal_display* display) { @@ -841,17 +893,8 @@ void guac_terminal_display_flush(guac_terminal_display* display) { } -void guac_terminal_display_clear_select(guac_terminal_display* display) { - - guac_socket* socket = display->client->socket; - guac_layer* select_layer = display->select_layer; - - guac_protocol_send_rect(socket, select_layer, 0, 0, 1, 1); - guac_protocol_send_cfill(socket, GUAC_COMP_SRC, select_layer, - 0x00, 0x00, 0x00, 0x00); - - guac_socket_flush(socket); - +void guac_terminal_display_commit_select(guac_terminal_display* display) { + display->selection_committed = true; } void guac_terminal_display_select(guac_terminal_display* display, @@ -860,6 +903,9 @@ void guac_terminal_display_select(guac_terminal_display* display, guac_socket* socket = display->client->socket; guac_layer* select_layer = display->select_layer; + /* Text is now selected */ + display->text_selected = true; + /* If single row, just need one rectangle */ if (start_row == end_row) { diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 9f3e75f6..651c2056 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -428,7 +428,7 @@ void guac_terminal_select_end(guac_terminal* terminal, char* string) { /* Deselect */ terminal->text_selected = false; - guac_terminal_display_clear_select(terminal->display); + guac_terminal_display_commit_select(terminal->display); guac_terminal_buffer_row* buffer_row; From ce21f2c8835e3a469a53094f0d6073a568b613a4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 14 May 2013 17:19:08 -0700 Subject: [PATCH 111/169] Implement range check, fix parameters. --- protocols/ssh/src/display.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index f81be96b..9afbfba3 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -91,10 +91,28 @@ static void __guac_terminal_display_clear_select(guac_terminal_display* display) /** * Returns whether at least one character within the given range is selected. */ -static bool __guac_terminal_display_contains_selected(guac_terminal_display* display, +static bool __guac_terminal_display_selected_contains(guac_terminal_display* display, int start_row, int start_column, int end_row, int end_column) { - /* STUB */ - return false; + + /* If test range starts after highlight ends, does not intersect */ + if (start_row > display->selection_end_row) + return false; + + if (start_row == display->selection_end_row + && start_column > display->selection_end_column) + return false; + + /* If test range ends before highlight starts, does not intersect */ + if (end_row < display->selection_start_row) + return false; + + if (end_row == display->selection_start_row + && end_column < display->selection_start_column) + return false; + + /* Otherwise, does intersect */ + return true; + } /* Maps any codepoint onto a number between 0 and 511 inclusive */ @@ -433,7 +451,7 @@ void guac_terminal_display_copy_columns(guac_terminal_display* display, int row, /* If selection visible and committed, clear if update touches selection */ if (display->text_selected && display->selection_committed && - __guac_terminal_display_contains_selected(display, row, row, start_column, end_column)) + __guac_terminal_display_selected_contains(display, row, start_column, row, end_column)) __guac_terminal_display_clear_select(display); } @@ -483,7 +501,7 @@ void guac_terminal_display_copy_rows(guac_terminal_display* display, /* If selection visible and committed, clear if update touches selection */ if (display->text_selected && display->selection_committed && - __guac_terminal_display_contains_selected(display, start_row, end_row, 0, display->width - 1)) + __guac_terminal_display_selected_contains(display, start_row, 0, end_row, display->width - 1)) __guac_terminal_display_clear_select(display); } @@ -517,7 +535,7 @@ void guac_terminal_display_set_columns(guac_terminal_display* display, int row, /* If selection visible and committed, clear if update touches selection */ if (display->text_selected && display->selection_committed && - __guac_terminal_display_contains_selected(display, row, row, start_column, end_column)) + __guac_terminal_display_selected_contains(display, row, start_column, row, end_column)) __guac_terminal_display_clear_select(display); } @@ -906,6 +924,12 @@ void guac_terminal_display_select(guac_terminal_display* display, /* Text is now selected */ display->text_selected = true; + display->selection_start_row = start_row; + display->selection_start_column = start_col; + display->selection_end_row = end_row; + display->selection_end_column = end_col; + + /* If single row, just need one rectangle */ if (start_row == end_row) { From b5e3c2e721b19c0d95375d04a98786c94ed7d0b2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 15 May 2013 10:11:47 -0700 Subject: [PATCH 112/169] Clear with NULL character. Do not include NULLs in copied text. --- protocols/ssh/include/common.h | 8 ++++++++ protocols/ssh/src/common.c | 8 ++++++++ protocols/ssh/src/display.c | 8 ++++---- protocols/ssh/src/terminal.c | 17 ++++++++++++----- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/include/common.h b/protocols/ssh/include/common.h index f945a558..effcd67f 100644 --- a/protocols/ssh/include/common.h +++ b/protocols/ssh/include/common.h @@ -38,6 +38,8 @@ #ifndef _SSH_GUAC_COMMON_H #define _SSH_GUAC_COMMON_H +#include + /** * Returns the closest value to the value given that is also * within the given range. @@ -50,5 +52,11 @@ int guac_terminal_fit_to_range(int value, int min, int max); */ int guac_terminal_encode_utf8(int codepoint, char* utf8); +/** + * Returns whether a codepoint has a corresponding glyph, or is rendered + * as a blank space. + */ +bool guac_terminal_has_glyph(int codepoint); + #endif diff --git a/protocols/ssh/src/common.c b/protocols/ssh/src/common.c index fcd5230a..00d6bdec 100644 --- a/protocols/ssh/src/common.c +++ b/protocols/ssh/src/common.c @@ -35,6 +35,8 @@ * * ***** END LICENSE BLOCK ***** */ +#include + int guac_terminal_fit_to_range(int value, int min, int max) { if (value < min) return min; @@ -90,3 +92,9 @@ int guac_terminal_encode_utf8(int codepoint, char* utf8) { } +bool guac_terminal_has_glyph(int codepoint) { + return + codepoint != 0 + && codepoint != ' '; +} + diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 9afbfba3..9b0754db 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -547,7 +547,7 @@ void guac_terminal_display_resize(guac_terminal_display* display, int width, int /* Fill with background color (index 0) */ guac_terminal_char fill = { - .value = ' ', + .value = 0, .attributes = { .foreground = 0, .background = 0 @@ -743,7 +743,7 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) { /* If operation is a cler operation (set to space) */ if (current->type == GUAC_CHAR_SET && - current->character.value == ' ') { + !guac_terminal_has_glyph(current->character.value)) { /* The determined bounds of the rectangle of contiguous * operations */ @@ -786,7 +786,7 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) { /* If not identical operation, stop */ if (rect_current->type != GUAC_CHAR_SET - || rect_current->character.value != ' ' + || guac_terminal_has_glyph(rect_current->character.value) || joining_color != color) break; @@ -831,7 +831,7 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) { /* Mark clear operations as NOP */ if (rect_current->type == GUAC_CHAR_SET - && rect_current->character.value == ' ' + && !guac_terminal_has_glyph(rect_current->character.value) && joining_color == color) rect_current->type = GUAC_CHAR_NOP; diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 651c2056..cd175f80 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -58,7 +58,7 @@ guac_terminal* guac_terminal_create(guac_client* client, int width, int height) { guac_terminal_char default_char = { - .value = ' ', + .value = 0, .attributes = { .foreground = 7, .background = 0, @@ -208,7 +208,7 @@ int guac_terminal_clear_columns(guac_terminal* term, /* Build space */ guac_terminal_char blank; - blank.value = ' '; + blank.value = 0; blank.attributes = term->current_attributes; /* Clear */ @@ -415,9 +415,16 @@ int __guac_terminal_buffer_string(guac_terminal_buffer_row* row, int start, int int length = 0; int i; for (i=start; i<=end; i++) { - int bytes = guac_terminal_encode_utf8(row->characters[i].value, string); - string += bytes; - length += bytes; + + int codepoint = row->characters[i].value; + + /* If not null (blank), add to string */ + if (codepoint != 0) { + int bytes = guac_terminal_encode_utf8(codepoint, string); + string += bytes; + length += bytes; + } + } return length; From 337d79721e446be97174e2d5e30a03b1c17bc1b7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 15 May 2013 12:08:05 -0700 Subject: [PATCH 113/169] Do not start selection until mouse moves. --- protocols/ssh/src/ssh_handlers.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 536e4334..457ffa90 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -206,8 +206,9 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { pthread_mutex_unlock(&(term->lock)); } - /* Otherwise, if mouse button pressed, start selection */ - else if (pressed_mask & GUAC_CLIENT_MOUSE_LEFT) { + /* Otherwise, if mouse button pressed AND moved, start selection */ + else if (!(pressed_mask & GUAC_CLIENT_MOUSE_LEFT) && + mask & GUAC_CLIENT_MOUSE_LEFT) { pthread_mutex_lock(&(term->lock)); guac_terminal_select_start(term, From 9b0a210c124c5f1855cd0102c74249bb6f835ecb Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 15 May 2013 12:46:26 -0700 Subject: [PATCH 114/169] Improve cursor rendering, ensure display not affected if cursor does not move. Repurpose unused "selected" attribute for marking the cursor. --- protocols/ssh/include/terminal.h | 15 ++++++++-- protocols/ssh/include/types.h | 4 +-- protocols/ssh/src/display.c | 6 ++-- protocols/ssh/src/ssh_handlers.c | 11 ++----- protocols/ssh/src/terminal.c | 49 +++++++++++++++++++++++++------- 5 files changed, 58 insertions(+), 27 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 545cdd89..a99582d9 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -112,6 +112,16 @@ struct guac_terminal { */ int cursor_col; + /** + * The row of the rendered cursor. + */ + int visible_cursor_row; + + /** + * The column of the rendered cursor. + */ + int visible_cursor_col; + /** * The attributes which will be applied to future characters. */ @@ -219,9 +229,10 @@ int guac_terminal_scroll_down(guac_terminal* term, int start_row, int end_row, int amount); /** - * Toggles the reverse attribute of the character at the given location. + * Commits the current cursor location, updating the visible cursor + * on the screen. */ -int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col); +void guac_terminal_commit_cursor(guac_terminal* term); /** * Scroll down the display by the given amount, replacing the new space with diff --git a/protocols/ssh/include/types.h b/protocols/ssh/include/types.h index d28a425e..aba59bcf 100644 --- a/protocols/ssh/include/types.h +++ b/protocols/ssh/include/types.h @@ -79,9 +79,9 @@ typedef struct guac_terminal_attributes { bool reverse; /** - * Whether the associated character is selected. + * Whether the associated character is highlighted by the cursor. */ - bool selected; + bool cursor; /** * Whether to render the character with underscore. diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 9b0754db..6e9d2e8a 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -250,7 +250,7 @@ int __guac_terminal_set_colors(guac_terminal_display* display, int background, foreground; /* Handle reverse video */ - if (attributes->reverse != attributes->selected) { + if (attributes->reverse != attributes->cursor) { background = attributes->foreground; foreground = attributes->background; } @@ -758,7 +758,7 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) { /* Color of the rectangle to draw */ int color; - if (current->character.attributes.reverse != current->character.attributes.selected) + if (current->character.attributes.reverse != current->character.attributes.cursor) color = current->character.attributes.foreground; else color = current->character.attributes.background; @@ -824,7 +824,7 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) { for (rect_col=0; rect_colcharacter.attributes.reverse) + if (rect_current->character.attributes.reverse != rect_current->character.attributes.cursor) joining_color = current->character.attributes.foreground; else joining_color = current->character.attributes.background; diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 457ffa90..5439af69 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -90,11 +90,6 @@ int ssh_guac_client_handle_messages(guac_client* client) { /* Lock terminal access */ pthread_mutex_lock(&(client_data->term->lock)); - /* Clear cursor */ - guac_terminal_toggle_reverse(client_data->term, - client_data->term->cursor_row, - client_data->term->cursor_col); - /* While data available, write to terminal */ while (channel_is_open(client_data->term_channel) && !channel_is_eof(client_data->term_channel) @@ -113,10 +108,8 @@ int ssh_guac_client_handle_messages(guac_client* client) { return 1; } - /* Draw cursor */ - guac_terminal_toggle_reverse(client_data->term, - client_data->term->cursor_row, - client_data->term->cursor_col); + /* Update cursor */ + guac_terminal_commit_cursor(client_data->term); /* Flush terminal display */ guac_terminal_display_flush(client_data->term->display); diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index cd175f80..2c990fb1 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -83,8 +83,8 @@ guac_terminal* guac_terminal_create(guac_client* client, term->current_attributes = default_char.attributes; term->default_char = default_char; - term->cursor_row = 0; - term->cursor_col = 0; + term->cursor_row = term->visible_cursor_row = 0; + term->cursor_col = term->visible_cursor_col = 0; term->term_width = width / term->display->char_width; term->term_height = height / term->display->char_height; @@ -129,21 +129,37 @@ int guac_terminal_set(guac_terminal* term, int row, int col, int codepoint) { } -int guac_terminal_toggle_reverse(guac_terminal* term, int row, int col) { +void guac_terminal_commit_cursor(guac_terminal* term) { guac_terminal_char* guac_char; - /* Get character from buffer */ - guac_terminal_buffer_row* buffer_row = guac_terminal_buffer_get_row(term->buffer, row, col+1); + guac_terminal_buffer_row* old_row; + guac_terminal_buffer_row* new_row; - /* Toggle reverse */ - guac_char = &(buffer_row->characters[col]); - guac_char->attributes.reverse = !(guac_char->attributes.reverse); + /* If no change, done */ + if (term->visible_cursor_row == term->cursor_row && term->visible_cursor_col == term->cursor_col) + return; - /* Set display */ - guac_terminal_display_set_columns(term->display, row + term->scroll_offset, col, col, guac_char); + /* Get old and new rows with cursor */ + new_row = guac_terminal_buffer_get_row(term->buffer, term->cursor_row, term->cursor_col+1); + old_row = guac_terminal_buffer_get_row(term->buffer, term->visible_cursor_row, term->visible_cursor_col+1); - return 0; + /* Clear cursor */ + guac_char = &(old_row->characters[term->visible_cursor_col]); + guac_char->attributes.cursor = false; + guac_terminal_display_set_columns(term->display, term->visible_cursor_row + term->scroll_offset, + term->visible_cursor_col, term->visible_cursor_col, guac_char); + + /* Set cursor */ + guac_char = &(new_row->characters[term->cursor_col]); + guac_char->attributes.cursor = true; + guac_terminal_display_set_columns(term->display, term->cursor_row + term->scroll_offset, + term->cursor_col, term->cursor_col, guac_char); + + term->visible_cursor_row = term->cursor_row; + term->visible_cursor_col = term->cursor_col; + + return; } @@ -506,6 +522,12 @@ void guac_terminal_copy_columns(guac_terminal* terminal, int row, guac_terminal_buffer_copy_columns(terminal->buffer, row, start_column, end_column, offset); + /* Update cursor location if within region */ + if (row == terminal->visible_cursor_row && + terminal->visible_cursor_col >= start_column && + terminal->visible_cursor_col <= end_column) + terminal->visible_cursor_col += offset; + } void guac_terminal_copy_rows(guac_terminal* terminal, @@ -517,6 +539,11 @@ void guac_terminal_copy_rows(guac_terminal* terminal, guac_terminal_buffer_copy_rows(terminal->buffer, start_row, end_row, offset); + /* Update cursor location if within region */ + if (terminal->visible_cursor_row >= start_row && + terminal->visible_cursor_row <= end_row) + terminal->visible_cursor_row += offset; + } void guac_terminal_set_columns(guac_terminal* terminal, int row, From 791da3dc811436b57aa86451ce03bd07af828bf0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 15 May 2013 13:55:40 -0700 Subject: [PATCH 115/169] Properly update visible cursor row when terminal scrolled. --- protocols/ssh/src/terminal.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 2c990fb1..601b60a0 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -192,6 +192,11 @@ int guac_terminal_scroll_up(guac_terminal* term, if (term->buffer->length > term->buffer->available) term->buffer->length = term->buffer->available; + /* Update cursor location if within region */ + if (term->visible_cursor_row >= start_row && + term->visible_cursor_row <= end_row) + term->visible_cursor_row -= amount; + } /* Otherwise, just copy row data upwards */ From ec845a812a7fd5d5320fa7cdbbadf3f2c4ebea69 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 May 2013 20:20:51 -0700 Subject: [PATCH 116/169] Remove core SSH client code. Refactor message handler to handle pipe for STDOUT. Refactor key and clipboard handlers to handle pipe for STDIN. --- protocols/ssh/include/ssh_client.h | 35 ++++-- protocols/ssh/src/ssh_client.c | 170 +++++------------------------ protocols/ssh/src/ssh_handlers.c | 49 ++++----- 3 files changed, 79 insertions(+), 175 deletions(-) diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index 3ccbd376..03813836 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -48,21 +48,44 @@ #include "terminal.h" #include "cursor.h" +/** + * SSH-specific client data. + */ typedef struct ssh_guac_client_data { ssh_session session; ssh_channel term_channel; + /** + * The terminal which will render all output from the SSH client. + */ guac_terminal* term; - - char * clipboard_data; - - char password[1024]; - int password_length; + + /** + * The current contents of the clipboard. + */ + char* clipboard_data; + /** + * Whether the control key is currently being held down. + */ int mod_ctrl; + + /** + * The current mouse button state. + */ int mouse_mask; + /** + * Pipe which will be used to provide STDOUT to the SSH client. + */ + int stdout_pipe_fd[2]; + + /** + * Pipe which will be used to provide STDIN to the SSH client. + */ + int stdin_pipe_fd[2]; + /** * The cached I-bar cursor. */ @@ -80,7 +103,5 @@ typedef struct ssh_guac_client_data { } ssh_guac_client_data; -int ssh_guac_client_auth(guac_client* client, const char* password); - #endif diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 0ceed6df..9d10806e 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -38,12 +38,12 @@ #include #include - -#include +#include #include #include #include +#include #include "ssh_client.h" #include "ssh_handlers.h" @@ -59,53 +59,6 @@ const char* GUAC_CLIENT_ARGS[] = { NULL }; -int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pressed) { - - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - - /* If key pressed */ - if (pressed) { - - /* If simple ASCII key */ - if (keysym >= 0x00 && keysym <= 0xFF) { - /* Add to password */ - client_data->password[client_data->password_length++] = keysym; - guac_terminal_write(client_data->term, "*", 1); - guac_terminal_display_flush(client_data->term->display); - guac_socket_flush(client->socket); - } - else if (keysym == 0xFF08) { - - if (client_data->password_length > 0) { - client_data->password_length--; - - /* Backspace */ - guac_terminal_write(client_data->term, "\x08\x1B[K", 4); - guac_terminal_display_flush(client_data->term->display); - guac_socket_flush(client->socket); - } - - } - else if (keysym == 0xFF0D) { - - /* Finish password */ - client_data->password[client_data->password_length] = '\0'; - - /* Clear screen */ - guac_terminal_write(client_data->term, "\x1B[2J\x1B[1;1H", 10); - guac_terminal_display_flush(client_data->term->display); - guac_socket_flush(client->socket); - - return ssh_guac_client_auth(client, client_data->password); - - } - - } - - return 0; - -} - int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket* socket = client->socket; @@ -135,111 +88,44 @@ int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket_flush(socket); - /* Open SSH session */ - client_data->session = ssh_new(); - if (client_data->session == NULL) { - guac_protocol_send_error(socket, "Unable to create SSH session."); - guac_socket_flush(socket); + /* Open STDOUT pipe */ + if (pipe(client_data->stdout_pipe_fd)) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Unable to open pipe for STDOUT"; return 1; } - /* Set session options */ - ssh_options_set(client_data->session, SSH_OPTIONS_HOST, argv[0]); - ssh_options_set(client_data->session, SSH_OPTIONS_USER, argv[1]); + /* Redirect STDOUT to pipe */ + if (dup2(client_data->stdout_pipe_fd[1], STDOUT_FILENO) < 0) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Unable redirect STDOUT"; + return 1; + } - /* Connect */ - if (ssh_connect(client_data->session) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to connect via SSH."); - guac_socket_flush(socket); + /* Open STDIN pipe */ + if (pipe(client_data->stdin_pipe_fd)) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Unable to open pipe for STDIN"; + return 1; + } + + /* Redirect STDIN to pipe */ + if (dup2(client_data->stdin_pipe_fd[0], STDIN_FILENO) < 0) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Unable redirect STDIN"; return 1; } /* Set basic handlers */ - client->free_handler = ssh_guac_client_free_handler; - client->size_handler = ssh_guac_client_size_handler; - - /* If password provided, authenticate now */ - if (argv[2][0] != '\0') - return ssh_guac_client_auth(client, argv[2]); - - /* Otherwise, prompt for password */ - else { - - client_data->password_length = 0; - guac_terminal_write(client_data->term, "Password: ", 10); - guac_terminal_display_flush(client_data->term->display); - guac_socket_flush(client->socket); - - client->key_handler = ssh_guac_client_password_key_handler; - - } - - /* Success */ - return 0; - -} - -int ssh_guac_client_auth(guac_client* client, const char* password) { - - guac_socket* socket = client->socket; - ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - guac_terminal* term = client_data->term; - - /* Authenticate */ - if (ssh_userauth_password(client_data->session, NULL, password) != SSH_AUTH_SUCCESS) { - guac_protocol_send_error(socket, "SSH auth failed."); - guac_socket_flush(socket); - return 1; - } - - /* Open channel for terminal */ - client_data->term_channel = channel_new(client_data->session); - if (client_data->term_channel == NULL) { - guac_protocol_send_error(socket, "Unable to open channel."); - guac_socket_flush(socket); - return 1; - } - - /* Open session for channel */ - if (channel_open_session(client_data->term_channel) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to open channel session."); - guac_socket_flush(socket); - return 1; - } - - /* Request PTY */ - if (channel_request_pty(client_data->term_channel) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to allocate PTY for channel."); - guac_socket_flush(socket); - return 1; - } - - /* Request PTY size */ - if (channel_change_pty_size(client_data->term_channel, term->term_width, term->term_height) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to change PTY size."); - guac_socket_flush(socket); - return 1; - } - - /* Request shell */ - if (channel_request_shell(client_data->term_channel) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to associate shell with PTY."); - guac_socket_flush(socket); - return 1; - } - - guac_client_log_info(client, "SSH connection successful."); - - /* Set handlers */ - client->handle_messages = ssh_guac_client_handle_messages; - client->mouse_handler = ssh_guac_client_mouse_handler; - client->key_handler = ssh_guac_client_key_handler; + client->handle_messages = ssh_guac_client_handle_messages; client->clipboard_handler = ssh_guac_client_clipboard_handler; + client->key_handler = ssh_guac_client_key_handler; + client->mouse_handler = ssh_guac_client_mouse_handler; + client->size_handler = ssh_guac_client_size_handler; + client->free_handler = ssh_guac_client_free_handler; /* Success */ return 0; } - - diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/ssh_handlers.c index 5439af69..023e66b4 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/ssh_handlers.c @@ -41,14 +41,13 @@ #include #include -#include - #include #include #include #include #include +#include #include "ssh_handlers.h" #include "ssh_client.h" @@ -61,42 +60,32 @@ int ssh_guac_client_handle_messages(guac_client* client) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; char buffer[8192]; - ssh_channel channels[2], out_channels[2]; + int ret_val; + int fd = client_data->stdout_pipe_fd[0]; struct timeval timeout; fd_set fds; - int ssh_fd; - - /* Channels to read */ - channels[0] = client_data->term_channel; - channels[1] = NULL; - - /* Get SSH file descriptor */ - ssh_fd = ssh_get_fd(client_data->session); /* Build fd_set */ FD_ZERO(&fds); - FD_SET(ssh_fd, &fds); + FD_SET(fd, &fds); /* Time to wait */ timeout.tv_sec = 1; timeout.tv_usec = 0; /* Wait for data to be available */ - if (ssh_select(channels, out_channels, ssh_fd+1, &fds, &timeout) - == SSH_OK) { + ret_val = select(fd+1, &fds, NULL, NULL, &timeout); + if (ret_val > 0) { int bytes_read = 0; /* Lock terminal access */ pthread_mutex_lock(&(client_data->term->lock)); - /* While data available, write to terminal */ - while (channel_is_open(client_data->term_channel) - && !channel_is_eof(client_data->term_channel) - && (bytes_read = channel_read_nonblocking(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) { + /* Read data, write to terminal */ + if ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { - if (guac_terminal_write(client_data->term, buffer, bytes_read) - || guac_socket_flush(socket)) + if (guac_terminal_write(client_data->term, buffer, bytes_read)) return 1; } @@ -118,6 +107,11 @@ int ssh_guac_client_handle_messages(guac_client* client) { pthread_mutex_unlock(&(client_data->term->lock)); } + else if (ret_val < 0) { + guac_error_message = "Error waiting for pipe"; + guac_error = GUAC_STATUS_SEE_ERRNO; + return 1; + } return 0; @@ -164,7 +158,7 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { int length = strlen(client_data->clipboard_data); if (length) - return channel_write(client_data->term_channel, + return write(client_data->stdin_pipe_fd[1], client_data->clipboard_data, length); } @@ -234,6 +228,9 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; guac_terminal* term = client_data->term; + /* Get write end of STDIN pipe */ + int fd = client_data->stdin_pipe_fd[1]; + /* Hide mouse cursor if not already hidden */ if (client_data->current_cursor != client_data->blank_cursor) { pthread_mutex_lock(&(term->lock)); @@ -275,7 +272,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { else return 0; - return channel_write(client_data->term_channel, &data, 1); + return write(fd, &data, 1); } @@ -286,7 +283,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { char data[5]; length = guac_terminal_encode_utf8(keysym & 0xFFFF, data); - return channel_write(client_data->term_channel, data, length); + return write(fd, data, length); } @@ -309,7 +306,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { /* Ignore other keys */ else return 0; - return channel_write(client_data->term_channel, data, length); + return write(fd, data, length); } } @@ -335,8 +332,8 @@ int ssh_guac_client_size_handler(guac_client* client, int width, int height) { /* Resize terminal */ guac_terminal_resize(terminal, columns, rows); - channel_change_pty_size(guac_client_data->term_channel, - terminal->term_width, terminal->term_height); + + /* FIXME: Make resize call to SSH thread */ /* Reset scroll region */ terminal->scroll_end = rows - 1; From 0dbcdabe40714eb736e06e01f2287a64e273b511 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 May 2013 20:28:26 -0700 Subject: [PATCH 117/169] General naming cleanup. --- protocols/ssh/Makefile.am | 8 ++++---- protocols/ssh/configure.in | 4 ++-- protocols/ssh/include/{ssh_client.h => client.h} | 4 ---- protocols/ssh/include/{ssh_handlers.h => guac_handlers.h} | 0 protocols/ssh/src/{ssh_client.c => client.c} | 4 ++-- protocols/ssh/src/{ssh_handlers.c => guac_handlers.c} | 4 ++-- 6 files changed, 10 insertions(+), 14 deletions(-) rename protocols/ssh/include/{ssh_client.h => client.h} (97%) rename protocols/ssh/include/{ssh_handlers.h => guac_handlers.h} (100%) rename protocols/ssh/src/{ssh_client.c => client.c} (98%) rename protocols/ssh/src/{ssh_handlers.c => guac_handlers.c} (99%) diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 4411fb4f..e73d711e 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -43,24 +43,24 @@ lib_LTLIBRARIES = libguac-client-ssh.la libguac_client_ssh_la_SOURCES = \ src/blank.c \ src/buffer.c \ + src/client.c \ src/common.c \ src/cursor.c \ src/display.c \ + src/guac_handlers.c \ src/ibar.c \ - src/ssh_client.c \ - src/ssh_handlers.c \ src/terminal.c \ src/terminal_handlers.c noinst_HEADERS = \ include/blank.h \ include/buffer.h \ + include/client.h \ include/common.h \ include/cursor.h \ include/display.h \ + include/guac_handlers.h \ include/ibar.h \ - include/ssh_client.h \ - include/ssh_handlers.h \ include/terminal.h \ include/terminal_handlers.h \ include/types.h diff --git a/protocols/ssh/configure.in b/protocols/ssh/configure.in index 89e055e0..c18eba87 100644 --- a/protocols/ssh/configure.in +++ b/protocols/ssh/configure.in @@ -34,8 +34,8 @@ # # ***** END LICENSE BLOCK ***** -AC_INIT(src/ssh_client.c) -AM_INIT_AUTOMAKE([libguac-client-ssh], 0.6.0) +AC_INIT(src/client.c) +AM_INIT_AUTOMAKE([libguac-client-ssh], 0.8.0) AC_CONFIG_MACRO_DIR([m4]) # Checks for programs. diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/client.h similarity index 97% rename from protocols/ssh/include/ssh_client.h rename to protocols/ssh/include/client.h index 03813836..b827f621 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/client.h @@ -41,10 +41,6 @@ #include -#include - -#include "ssh_client.h" -#include "ssh_handlers.h" #include "terminal.h" #include "cursor.h" diff --git a/protocols/ssh/include/ssh_handlers.h b/protocols/ssh/include/guac_handlers.h similarity index 100% rename from protocols/ssh/include/ssh_handlers.h rename to protocols/ssh/include/guac_handlers.h diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/client.c similarity index 98% rename from protocols/ssh/src/ssh_client.c rename to protocols/ssh/src/client.c index 9d10806e..e704ff75 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/client.c @@ -45,8 +45,8 @@ #include #include -#include "ssh_client.h" -#include "ssh_handlers.h" +#include "client.h" +#include "guac_handlers.h" #include "terminal.h" #include "blank.h" #include "ibar.h" diff --git a/protocols/ssh/src/ssh_handlers.c b/protocols/ssh/src/guac_handlers.c similarity index 99% rename from protocols/ssh/src/ssh_handlers.c rename to protocols/ssh/src/guac_handlers.c index 023e66b4..294af139 100644 --- a/protocols/ssh/src/ssh_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -49,8 +49,8 @@ #include #include -#include "ssh_handlers.h" -#include "ssh_client.h" +#include "guac_handlers.h" +#include "client.h" #include "common.h" #include "cursor.h" From 96edfad7c03c4b0cfd13394ac315207050627a14 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 May 2013 20:47:05 -0700 Subject: [PATCH 118/169] Add stub client thread. --- protocols/ssh/Makefile.am | 2 ++ protocols/ssh/configure.in | 1 + protocols/ssh/include/client.h | 3 ++ protocols/ssh/include/ssh_client.h | 49 +++++++++++++++++++++++++++ protocols/ssh/src/client.c | 8 +++++ protocols/ssh/src/guac_handlers.c | 5 +++ protocols/ssh/src/ssh_client.c | 54 ++++++++++++++++++++++++++++++ 7 files changed, 122 insertions(+) create mode 100644 protocols/ssh/include/ssh_client.h create mode 100644 protocols/ssh/src/ssh_client.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index e73d711e..67ca8a7e 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -49,6 +49,7 @@ libguac_client_ssh_la_SOURCES = \ src/display.c \ src/guac_handlers.c \ src/ibar.c \ + src/ssh_client.c \ src/terminal.c \ src/terminal_handlers.c @@ -61,6 +62,7 @@ noinst_HEADERS = \ include/display.h \ include/guac_handlers.h \ include/ibar.h \ + include/ssh_client.h \ include/terminal.h \ include/terminal_handlers.h \ include/types.h diff --git a/protocols/ssh/configure.in b/protocols/ssh/configure.in index c18eba87..e64be7f5 100644 --- a/protocols/ssh/configure.in +++ b/protocols/ssh/configure.in @@ -47,6 +47,7 @@ AC_PROG_LIBTOOL 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([ssh], [ssh_new],, AC_MSG_ERROR("libssh is required")) +AC_CHECK_LIB([pthread], [pthread_create]) PKG_CHECK_MODULES([PANGO], pango); PKG_CHECK_MODULES([PANGOCAIRO], pangocairo); diff --git a/protocols/ssh/include/client.h b/protocols/ssh/include/client.h index b827f621..8a702b11 100644 --- a/protocols/ssh/include/client.h +++ b/protocols/ssh/include/client.h @@ -39,6 +39,7 @@ #ifndef _SSH_GUAC_CLIENT_H #define _SSH_GUAC_CLIENT_H +#include #include #include "terminal.h" @@ -49,6 +50,8 @@ */ typedef struct ssh_guac_client_data { + pthread_t client_thread; + ssh_session session; ssh_channel term_channel; diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h new file mode 100644 index 00000000..e964ba3f --- /dev/null +++ b/protocols/ssh/include/ssh_client.h @@ -0,0 +1,49 @@ + +/* ***** 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-ssh. + * + * 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 __SSH_CLIENT_H +#define __SSH_CLIENT_H + +#include + +/** + * Main SSH client thread, handling transfer of SSH output to STDOUT. + */ +void* ssh_client_thread(void* data); + +#endif + diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index e704ff75..423953bd 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,7 @@ #include "terminal.h" #include "blank.h" #include "ibar.h" +#include "ssh_client.h" /* Client plugin arguments */ const char* GUAC_CLIENT_ARGS[] = { @@ -124,6 +126,12 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client->size_handler = ssh_guac_client_size_handler; client->free_handler = ssh_guac_client_free_handler; + /* Start client thread */ + if (pthread_create(&(client_data->client_thread), NULL, ssh_client_thread, (void*) client)) { + guac_client_log_error(client, "Unable to SSH client thread"); + return -1; + } + /* Success */ return 0; diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index 294af139..f166917a 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -349,6 +349,11 @@ int ssh_guac_client_free_handler(guac_client* client) { ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data; + /* Close SSH client */ + close(STDOUT_FILENO); + close(STDIN_FILENO); + pthread_join(guac_client_data->client_thread, NULL); + /* Free terminal */ guac_terminal_free(guac_client_data->term); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c new file mode 100644 index 00000000..7c7673e5 --- /dev/null +++ b/protocols/ssh/src/ssh_client.c @@ -0,0 +1,54 @@ + +/* ***** 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-ssh. + * + * 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): + * James Muehlner + * + * 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 "client.h" + +void* ssh_client_thread(void* data) { + + /* STUB */ + printf("--- STUB! ---\n"); + fflush(stdout); + + return NULL; + +} + From 8f0c2f3723e90ef69cf22f35fd24f7feda708a3c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 May 2013 20:53:16 -0700 Subject: [PATCH 119/169] Use input/output threads for SSH client. --- protocols/ssh/include/client.h | 10 +++++++++- protocols/ssh/include/ssh_client.h | 7 ++++++- protocols/ssh/src/client.c | 12 +++++++++--- protocols/ssh/src/guac_handlers.c | 3 ++- protocols/ssh/src/ssh_client.c | 7 ++++++- 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/protocols/ssh/include/client.h b/protocols/ssh/include/client.h index 8a702b11..070c6ed1 100644 --- a/protocols/ssh/include/client.h +++ b/protocols/ssh/include/client.h @@ -50,7 +50,15 @@ */ typedef struct ssh_guac_client_data { - pthread_t client_thread; + /** + * SSH client output thread. + */ + pthread_t output_thread; + + /** + * SSH client input thread. + */ + pthread_t input_thread; ssh_session session; ssh_channel term_channel; diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index e964ba3f..6589943a 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -43,7 +43,12 @@ /** * Main SSH client thread, handling transfer of SSH output to STDOUT. */ -void* ssh_client_thread(void* data); +void* ssh_client_output_thread(void* data); + +/** + * Secondary SSH client thread, handling transfer of STDIN to SSH input. + */ +void* ssh_client_input_thread(void* data); #endif diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index 423953bd..614b0960 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -126,9 +126,15 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client->size_handler = ssh_guac_client_size_handler; client->free_handler = ssh_guac_client_free_handler; - /* Start client thread */ - if (pthread_create(&(client_data->client_thread), NULL, ssh_client_thread, (void*) client)) { - guac_client_log_error(client, "Unable to SSH client thread"); + /* Start client output thread */ + if (pthread_create(&(client_data->output_thread), NULL, ssh_client_output_thread, (void*) client)) { + guac_client_log_error(client, "Unable to start SSH client output thread"); + return -1; + } + + /* Start client input thread */ + if (pthread_create(&(client_data->input_thread), NULL, ssh_client_input_thread, (void*) client)) { + guac_client_log_error(client, "Unable to start SSH client input thread"); return -1; } diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index f166917a..28b01296 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -352,7 +352,8 @@ int ssh_guac_client_free_handler(guac_client* client) { /* Close SSH client */ close(STDOUT_FILENO); close(STDIN_FILENO); - pthread_join(guac_client_data->client_thread, NULL); + pthread_join(guac_client_data->output_thread, NULL); + pthread_join(guac_client_data->input_thread, NULL); /* Free terminal */ guac_terminal_free(guac_client_data->term); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 7c7673e5..f90112ca 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -42,7 +42,7 @@ #include "client.h" -void* ssh_client_thread(void* data) { +void* ssh_client_output_thread(void* data) { /* STUB */ printf("--- STUB! ---\n"); @@ -52,3 +52,8 @@ void* ssh_client_thread(void* data) { } +void* ssh_client_input_thread(void* data) { + /* STUB */ + return NULL; +} + From 5009d1c280ac63cd7e55fc2dac8c48ee69456cbf Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 May 2013 20:58:47 -0700 Subject: [PATCH 120/169] Revert "Use input/output threads for SSH client." Should actually have one main thread, which then spawns an input thread after connection successful. This reverts commit 6a099b4176fb732b7281128100fe66bb0a72da1a. --- protocols/ssh/include/client.h | 10 +--------- protocols/ssh/include/ssh_client.h | 7 +------ protocols/ssh/src/client.c | 12 +++--------- protocols/ssh/src/guac_handlers.c | 3 +-- protocols/ssh/src/ssh_client.c | 7 +------ 5 files changed, 7 insertions(+), 32 deletions(-) diff --git a/protocols/ssh/include/client.h b/protocols/ssh/include/client.h index 070c6ed1..8a702b11 100644 --- a/protocols/ssh/include/client.h +++ b/protocols/ssh/include/client.h @@ -50,15 +50,7 @@ */ typedef struct ssh_guac_client_data { - /** - * SSH client output thread. - */ - pthread_t output_thread; - - /** - * SSH client input thread. - */ - pthread_t input_thread; + pthread_t client_thread; ssh_session session; ssh_channel term_channel; diff --git a/protocols/ssh/include/ssh_client.h b/protocols/ssh/include/ssh_client.h index 6589943a..e964ba3f 100644 --- a/protocols/ssh/include/ssh_client.h +++ b/protocols/ssh/include/ssh_client.h @@ -43,12 +43,7 @@ /** * Main SSH client thread, handling transfer of SSH output to STDOUT. */ -void* ssh_client_output_thread(void* data); - -/** - * Secondary SSH client thread, handling transfer of STDIN to SSH input. - */ -void* ssh_client_input_thread(void* data); +void* ssh_client_thread(void* data); #endif diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index 614b0960..423953bd 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -126,15 +126,9 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client->size_handler = ssh_guac_client_size_handler; client->free_handler = ssh_guac_client_free_handler; - /* Start client output thread */ - if (pthread_create(&(client_data->output_thread), NULL, ssh_client_output_thread, (void*) client)) { - guac_client_log_error(client, "Unable to start SSH client output thread"); - return -1; - } - - /* Start client input thread */ - if (pthread_create(&(client_data->input_thread), NULL, ssh_client_input_thread, (void*) client)) { - guac_client_log_error(client, "Unable to start SSH client input thread"); + /* Start client thread */ + if (pthread_create(&(client_data->client_thread), NULL, ssh_client_thread, (void*) client)) { + guac_client_log_error(client, "Unable to SSH client thread"); return -1; } diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index 28b01296..f166917a 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -352,8 +352,7 @@ int ssh_guac_client_free_handler(guac_client* client) { /* Close SSH client */ close(STDOUT_FILENO); close(STDIN_FILENO); - pthread_join(guac_client_data->output_thread, NULL); - pthread_join(guac_client_data->input_thread, NULL); + pthread_join(guac_client_data->client_thread, NULL); /* Free terminal */ guac_terminal_free(guac_client_data->term); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index f90112ca..7c7673e5 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -42,7 +42,7 @@ #include "client.h" -void* ssh_client_output_thread(void* data) { +void* ssh_client_thread(void* data) { /* STUB */ printf("--- STUB! ---\n"); @@ -52,8 +52,3 @@ void* ssh_client_output_thread(void* data) { } -void* ssh_client_input_thread(void* data) { - /* STUB */ - return NULL; -} - From a53a5e2e1b3d06ad280253d6b005c54ee1cdf8cb Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 17 May 2013 22:53:13 -0700 Subject: [PATCH 121/169] Working input/output. --- protocols/ssh/src/guac_handlers.c | 2 +- protocols/ssh/src/ssh_client.c | 46 +++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index f166917a..6293df8b 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -294,7 +294,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { if (keysym == 0xFF08) { data = "\x08"; length = 1; } else if (keysym == 0xFF09) { data = "\x09"; length = 1; } - else if (keysym == 0xFF0D) { data = "\x0D"; length = 1; } + else if (keysym == 0xFF0D) { data = "\x0A"; length = 1; } else if (keysym == 0xFF1B) { data = "\x1B"; length = 1; } /* Arrow keys */ diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 7c7673e5..9f87c99a 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -37,16 +37,56 @@ * ***** END LICENSE BLOCK ***** */ #include +#include #include #include "client.h" +/** + * Similar to fgets(), reads a single line from STDIN. Unlike fgets(), this + * function does not include the trailing newline character, although the + * character is removed from the input stream. + * + * @param title The title of the prompt to display. + * @param str The buffer to read the result into. + * @param size The number of bytes available in the buffer. + * @return str, or NULL if the prompt failed. + */ +static char* prompt(const char* title, char* str, int size) { + + /* Print title */ + printf("%s", title); + fflush(stdout); + + /* Read input */ + str = fgets(str, size, stdin); + + /* Remove trailing newline, if any */ + if (str != NULL) { + int length = strlen(str); + if (str[length-1] == '\n') + str[length-1] = 0; + } + + return str; + +} + void* ssh_client_thread(void* data) { - /* STUB */ - printf("--- STUB! ---\n"); - fflush(stdout); + char username[1024]; + char password[1024]; + + /* Get username */ + if (prompt("Login as: ", username, sizeof(username)) == NULL) + return NULL; + + /* Get password */ + if (prompt("Password: ", password, sizeof(password)) == NULL) + return NULL; + + guac_client_log_info((guac_client*) data, "got: %s ... %s", username, password); return NULL; From 0057460c44129a490bcac4affc1e017179d78730 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 18 May 2013 22:24:09 -0700 Subject: [PATCH 122/169] Add echo flag. --- protocols/ssh/include/terminal.h | 8 ++++++++ protocols/ssh/src/terminal.c | 1 + 2 files changed, 9 insertions(+) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index a99582d9..d9ecd1fb 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -75,6 +75,14 @@ struct guac_terminal { */ pthread_mutex_t lock; + /** + * Whether input should be echoed when keys are pressed. Normally, the + * terminal on the side of the SSH server will handle this automatically, + * and this flag will need to be cleared. When SSH is not yet connected, + * this flag would need to be set for input to be visible. + */ + bool echo; + /** * The relative offset of the display. A positive value indicates that * many rows have been scrolled into view, zero indicates that no diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 601b60a0..af2533e0 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -94,6 +94,7 @@ guac_terminal* guac_terminal_create(guac_client* client, term->scroll_end = term->term_height - 1; term->text_selected = false; + term->echo = true; /* Size display */ guac_terminal_display_resize(term->display, From 0f978393a62f2767a21108b74acec45368c7a375 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 May 2013 00:33:17 -0700 Subject: [PATCH 123/169] Implement prompts, do not actually redirect real STDIN/STDOUT. --- protocols/ssh/include/terminal.h | 8 --- protocols/ssh/src/ssh_client.c | 97 +++++++++++++++++++++++++------- protocols/ssh/src/terminal.c | 1 - 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index d9ecd1fb..a99582d9 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -75,14 +75,6 @@ struct guac_terminal { */ pthread_mutex_t lock; - /** - * Whether input should be echoed when keys are pressed. Normally, the - * terminal on the side of the SSH server will handle this automatically, - * and this flag will need to be cleared. When SSH is not yet connected, - * this flag would need to be set for input to be visible. - */ - bool echo; - /** * The relative offset of the display. A positive value indicates that * many rows have been scrolled into view, zero indicates that no diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 9f87c99a..819c87b3 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -44,46 +44,101 @@ #include "client.h" /** - * Similar to fgets(), reads a single line from STDIN. Unlike fgets(), this - * function does not include the trailing newline character, although the - * character is removed from the input stream. - * - * @param title The title of the prompt to display. - * @param str The buffer to read the result into. - * @param size The number of bytes available in the buffer. - * @return str, or NULL if the prompt failed. + * Similar to write, but automatically retries the write operation until + * an error occurs. */ -static char* prompt(const char* title, char* str, int size) { +static int __write_all(int fd, const char* buffer, int size) { - /* Print title */ - printf("%s", title); - fflush(stdout); + int remaining = size; + while (remaining > 0) { - /* Read input */ - str = fgets(str, size, stdin); + /* Attempt to write data */ + int ret_val = write(fd, buffer, remaining); + if (ret_val <= 0) + return -1; + + /* If successful, contine with what data remains (if any) */ + remaining -= ret_val; + buffer += ret_val; - /* Remove trailing newline, if any */ - if (str != NULL) { - int length = strlen(str); - if (str[length-1] == '\n') - str[length-1] = 0; } + return size; + +} + +/** + * Reads a single line from STDIN. + */ +static char* prompt(guac_client* client, const char* title, char* str, int size, bool echo) { + + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + + int pos; + char in_byte; + + /* Get STDIN and STDOUT */ + int stdin_fd = client_data->stdin_pipe_fd[0]; + int stdout_fd = client_data->stdout_pipe_fd[1]; + + /* Print title */ + __write_all(stdout_fd, title, strlen(title)); + + /* Make room for null terminator */ + size--; + + /* Read bytes until newline */ + pos = 0; + while (pos < size && read(stdin_fd, &in_byte, 1) == 1) { + + /* Backspace */ + if (in_byte == 0x08) { + + if (pos > 0) { + __write_all(stdout_fd, "\b \b", 3); + pos--; + } + } + + /* Newline (end of input */ + else if (in_byte == 0x0A) { + __write_all(stdout_fd, "\r\n", 2); + break; + } + + else { + + /* Store character, update buffers */ + str[pos++] = in_byte; + + /* Print character if echoing */ + if (echo) + __write_all(stdout_fd, &in_byte, 1); + else + __write_all(stdout_fd, "*", 1); + + } + + } + + str[pos] = 0; return str; } void* ssh_client_thread(void* data) { + guac_client* client = (guac_client*) data; + char username[1024]; char password[1024]; /* Get username */ - if (prompt("Login as: ", username, sizeof(username)) == NULL) + if (prompt(client, "Login as: ", username, sizeof(username), true) == NULL) return NULL; /* Get password */ - if (prompt("Password: ", password, sizeof(password)) == NULL) + if (prompt(client, "Password: ", password, sizeof(password), false) == NULL) return NULL; guac_client_log_info((guac_client*) data, "got: %s ... %s", username, password); diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index af2533e0..601b60a0 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -94,7 +94,6 @@ guac_terminal* guac_terminal_create(guac_client* client, term->scroll_end = term->term_height - 1; term->text_selected = false; - term->echo = true; /* Size display */ guac_terminal_display_resize(term->display, From 80825072fed0a6d72eb56b198621792a2f2ae624 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 May 2013 01:23:21 -0700 Subject: [PATCH 124/169] Reinstate SSH client. --- protocols/ssh/include/client.h | 4 + protocols/ssh/src/client.c | 10 +++ protocols/ssh/src/ssh_client.c | 139 ++++++++++++++++++++++++++++++--- 3 files changed, 142 insertions(+), 11 deletions(-) diff --git a/protocols/ssh/include/client.h b/protocols/ssh/include/client.h index 8a702b11..d0301204 100644 --- a/protocols/ssh/include/client.h +++ b/protocols/ssh/include/client.h @@ -50,6 +50,10 @@ */ typedef struct ssh_guac_client_data { + char hostname[1024]; + char username[1024]; + char password[1024]; + pthread_t client_thread; ssh_session session; diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index 423953bd..37ea5bf6 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -75,6 +75,16 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->mod_ctrl = 0; client_data->clipboard_data = NULL; + if (argc != 3) { + guac_client_log_error(client, "Wrong number of arguments"); + return -1; + } + + /* Read parameters */ + strcpy(client_data->hostname, argv[0]); + strcpy(client_data->username, argv[1]); + strcpy(client_data->password, argv[2]); + /* Set up I-bar pointer */ client_data->ibar_cursor = guac_ssh_create_ibar(client); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 819c87b3..1be5001e 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -38,8 +38,11 @@ #include #include +#include #include +#include +#include #include "client.h" @@ -126,24 +129,138 @@ static char* prompt(guac_client* client, const char* title, char* str, int size, } -void* ssh_client_thread(void* data) { +void* ssh_input_thread(void* data) { guac_client* client = (guac_client*) data; + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; - char username[1024]; - char password[1024]; + char buffer[8192]; + int bytes_read; - /* Get username */ - if (prompt(client, "Login as: ", username, sizeof(username), true) == NULL) - return NULL; + int stdin_fd = client_data->stdin_pipe_fd[0]; - /* Get password */ - if (prompt(client, "Password: ", password, sizeof(password), false) == NULL) - return NULL; - - guac_client_log_info((guac_client*) data, "got: %s ... %s", username, password); + /* Write all data read */ + while ((bytes_read = read(stdin_fd, buffer, sizeof(buffer))) > 0) + channel_write(client_data->term_channel, buffer, bytes_read); return NULL; } +void* ssh_client_thread(void* data) { + + guac_client* client = (guac_client*) data; + ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + + guac_socket* socket = client->socket; + char buffer[8192]; + int bytes_read; + + int stdout_fd = client_data->stdout_pipe_fd[1]; + + pthread_t input_thread; + + /* Get username */ + if (client_data->username[0] == 0 && + prompt(client, "Login as: ", client_data->username, sizeof(client_data->username), true) == NULL) + return NULL; + + /* Get password */ + if (client_data->password[0] == 0 && + prompt(client, "Password: ", client_data->password, sizeof(client_data->password), false) == NULL) + return NULL; + + /* Open SSH session */ + client_data->session = ssh_new(); + if (client_data->session == NULL) { + guac_protocol_send_error(socket, "Unable to create SSH session."); + guac_socket_flush(socket); + return NULL; + } + + /* Set session options */ + ssh_options_set(client_data->session, SSH_OPTIONS_HOST, client_data->hostname); + ssh_options_set(client_data->session, SSH_OPTIONS_USER, client_data->username); + + /* Connect */ + if (ssh_connect(client_data->session) != SSH_OK) { + guac_protocol_send_error(socket, "Unable to connect via SSH."); + guac_socket_flush(socket); + return NULL; + } + + /* Authenticate */ + if (ssh_userauth_password(client_data->session, NULL, client_data->password) != SSH_AUTH_SUCCESS) { + guac_protocol_send_error(socket, "SSH auth failed."); + guac_socket_flush(socket); + return NULL; + } + + /* Open channel for terminal */ + client_data->term_channel = channel_new(client_data->session); + if (client_data->term_channel == NULL) { + guac_protocol_send_error(socket, "Unable to open channel."); + guac_socket_flush(socket); + return NULL; + } + + /* Open session for channel */ + if (channel_open_session(client_data->term_channel) != SSH_OK) { + guac_protocol_send_error(socket, "Unable to open channel session."); + guac_socket_flush(socket); + return NULL; + } + + /* Request PTY */ + if (channel_request_pty(client_data->term_channel) != SSH_OK) { + guac_protocol_send_error(socket, "Unable to allocate PTY for channel."); + guac_socket_flush(socket); + return NULL; + } + + /* Request PTY size */ + if (channel_change_pty_size(client_data->term_channel, + client_data->term->term_width, client_data->term->term_height) != SSH_OK) { + guac_protocol_send_error(socket, "Unable to change PTY size."); + guac_socket_flush(socket); + return NULL; + } + + /* Request shell */ + if (channel_request_shell(client_data->term_channel) != SSH_OK) { + guac_protocol_send_error(socket, "Unable to associate shell with PTY."); + guac_socket_flush(socket); + return NULL; + } + + /* Logged in */ + guac_client_log_info(client, "SSH connection successful."); + + /* Start input thread */ + if (pthread_create(&(input_thread), NULL, ssh_input_thread, (void*) client)) { + guac_client_log_error(client, "Unable to start SSH input thread"); + return NULL; + } + + /* While data available, write to terminal */ + while (channel_is_open(client_data->term_channel) + && !channel_is_eof(client_data->term_channel) + && (bytes_read = channel_read(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) { + + if (__write_all(stdout_fd, buffer, bytes_read) <= 0) + return NULL; + + } + + /* Notify on error */ + if (bytes_read < 0) { + guac_protocol_send_error(socket, "Error reading data."); + guac_socket_flush(socket); + return NULL; + } + + guac_client_log_info(client, "SSH connection ended."); + return NULL; + +} + From 5fd14b3b4d1728ba73209c2b47070b1d7064a02c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 May 2013 10:27:53 -0700 Subject: [PATCH 125/169] Handle resize when SSH not connected. Update visible cursor row in resize. --- protocols/ssh/src/client.c | 1 + protocols/ssh/src/guac_handlers.c | 7 ++++++- protocols/ssh/src/terminal.c | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index 37ea5bf6..bce1c6af 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -74,6 +74,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->term = term; client_data->mod_ctrl = 0; client_data->clipboard_data = NULL; + client_data->term_channel = NULL; if (argc != 3) { guac_client_log_error(client, "Wrong number of arguments"); diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index 6293df8b..be3b8e7d 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -333,11 +333,16 @@ int ssh_guac_client_size_handler(guac_client* client, int width, int height) { /* Resize terminal */ guac_terminal_resize(terminal, columns, rows); - /* FIXME: Make resize call to SSH thread */ + /* Update SSH pty size if connected */ + if (guac_client_data->term_channel != NULL) + channel_change_pty_size(guac_client_data->term_channel, + terminal->term_width, terminal->term_height); /* Reset scroll region */ terminal->scroll_end = rows - 1; + guac_terminal_display_flush(terminal->display); + guac_socket_flush(terminal->client->socket); } pthread_mutex_unlock(&(terminal->lock)); diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 601b60a0..1b60368e 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -608,6 +608,7 @@ void guac_terminal_resize(guac_terminal* term, int width, int height) { /* Update buffer top and cursor row based on shift */ term->buffer->top += shift_amount; term->cursor_row -= shift_amount; + term->visible_cursor_row -= shift_amount; /* Redraw characters within old region */ __guac_terminal_redraw_rect(term, height - shift_amount, 0, height-1, width-1); @@ -642,6 +643,7 @@ void guac_terminal_resize(guac_terminal* term, int width, int height) { /* Update buffer top and cursor row based on shift */ term->buffer->top -= shift_amount; term->cursor_row += shift_amount; + term->visible_cursor_row += shift_amount; /* If scrolled enough, use scroll to fulfill entire resize */ if (term->scroll_offset >= shift_amount) { From 639389ced898610c9d179dec29dade92a1264762 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 May 2013 10:44:43 -0700 Subject: [PATCH 126/169] Fix error when no data received (read returns SSH_AGAIN). --- protocols/ssh/src/ssh_client.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 1be5001e..f5f43feb 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -154,7 +154,7 @@ void* ssh_client_thread(void* data) { guac_socket* socket = client->socket; char buffer[8192]; - int bytes_read; + int bytes_read = -1234; int stdout_fd = client_data->stdout_pipe_fd[1]; @@ -244,11 +244,14 @@ void* ssh_client_thread(void* data) { /* While data available, write to terminal */ while (channel_is_open(client_data->term_channel) - && !channel_is_eof(client_data->term_channel) - && (bytes_read = channel_read(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) { + && !channel_is_eof(client_data->term_channel)) { - if (__write_all(stdout_fd, buffer, bytes_read) <= 0) - return NULL; + /* Repeat read if necessary */ + if ((bytes_read = channel_read(client_data->term_channel, buffer, sizeof(buffer), 0)) == SSH_AGAIN) + continue; + + if (bytes_read > 0) + __write_all(stdout_fd, buffer, bytes_read); } From c220a4875cbd5958ddd35b063961bc2c9faf5970 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 20 May 2013 10:52:47 -0700 Subject: [PATCH 127/169] Properly close pipe file descriptors. Wait for input thread to close in SSH thread. --- protocols/ssh/src/client.c | 14 -------------- protocols/ssh/src/guac_handlers.c | 11 ++++++++--- protocols/ssh/src/ssh_client.c | 3 +++ 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index bce1c6af..0bfbf088 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -108,13 +108,6 @@ int guac_client_init(guac_client* client, int argc, char** argv) { return 1; } - /* Redirect STDOUT to pipe */ - if (dup2(client_data->stdout_pipe_fd[1], STDOUT_FILENO) < 0) { - guac_error = GUAC_STATUS_SEE_ERRNO; - guac_error_message = "Unable redirect STDOUT"; - return 1; - } - /* Open STDIN pipe */ if (pipe(client_data->stdin_pipe_fd)) { guac_error = GUAC_STATUS_SEE_ERRNO; @@ -122,13 +115,6 @@ int guac_client_init(guac_client* client, int argc, char** argv) { return 1; } - /* Redirect STDIN to pipe */ - if (dup2(client_data->stdin_pipe_fd[0], STDIN_FILENO) < 0) { - guac_error = GUAC_STATUS_SEE_ERRNO; - guac_error_message = "Unable redirect STDIN"; - return 1; - } - /* Set basic handlers */ client->handle_messages = ssh_guac_client_handle_messages; client->clipboard_handler = ssh_guac_client_clipboard_handler; diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index be3b8e7d..5a16edb0 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -354,9 +354,14 @@ int ssh_guac_client_free_handler(guac_client* client) { ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data; - /* Close SSH client */ - close(STDOUT_FILENO); - close(STDIN_FILENO); + /* Close terminal output pipe */ + close(guac_client_data->stdout_pipe_fd[1]); + close(guac_client_data->stdout_pipe_fd[0]); + + /* Close user input pipe */ + close(guac_client_data->stdin_pipe_fd[1]); + close(guac_client_data->stdin_pipe_fd[0]); + pthread_join(guac_client_data->client_thread, NULL); /* Free terminal */ diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index f5f43feb..eb54a34a 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -262,6 +262,9 @@ void* ssh_client_thread(void* data) { return NULL; } + /* Wait for input thread to die */ + pthread_join(input_thread, NULL); + guac_client_log_info(client, "SSH connection ended."); return NULL; From b38412fd3d368a62c205a9673917d2d1683e410d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 00:19:53 -0700 Subject: [PATCH 128/169] Implement save/restore cursor. --- protocols/ssh/include/terminal.h | 10 ++++++++++ protocols/ssh/src/terminal.c | 4 ++-- protocols/ssh/src/terminal_handlers.c | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index a99582d9..92af6eab 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -122,6 +122,16 @@ struct guac_terminal { */ int visible_cursor_col; + /** + * The row of the saved cursor (ESC 7). + */ + int saved_cursor_row; + + /** + * The column of the saved cursor (ESC 7). + */ + int saved_cursor_col; + /** * The attributes which will be applied to future characters. */ diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 1b60368e..74e809c0 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -83,8 +83,8 @@ guac_terminal* guac_terminal_create(guac_client* client, term->current_attributes = default_char.attributes; term->default_char = default_char; - term->cursor_row = term->visible_cursor_row = 0; - term->cursor_col = term->visible_cursor_col = 0; + term->cursor_row = term->visible_cursor_row = term->saved_cursor_row = 0; + term->cursor_col = term->visible_cursor_col = term->saved_cursor_col = 0; term->term_width = width / term->display->char_width; term->term_height = height / term->display->char_height; diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index f7467f66..0e7d1870 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -172,6 +172,23 @@ int guac_terminal_escape(guac_terminal* term, char c) { term->char_handler = guac_terminal_csi; break; + case '7': /* Save Cursor (DECSC) */ + term->saved_cursor_row = term->cursor_row; + term->saved_cursor_col = term->cursor_col; + break; + + case '8': /* Restore Cursor (DECRC) */ + + term->cursor_row = term->saved_cursor_row; + if (term->cursor_row >= term->term_height) + term->cursor_row = term->term_height - 1; + + term->cursor_col = term->saved_cursor_col; + if (term->cursor_col >= term->term_width) + term->cursor_col = term->term_width - 1; + + break; + case 'M': /* Reverse Linefeed */ term->cursor_row--; From 7894346285382af1a6fbb0824fb65b3c3a51d40e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 00:29:19 -0700 Subject: [PATCH 129/169] Implement Index and Next Line. --- protocols/ssh/src/terminal_handlers.c | 47 +++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 0e7d1870..9900b5c5 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -172,12 +172,16 @@ int guac_terminal_escape(guac_terminal* term, char c) { term->char_handler = guac_terminal_csi; break; - case '7': /* Save Cursor (DECSC) */ + /* Save Cursor (DECSC) */ + case '7': term->saved_cursor_row = term->cursor_row; term->saved_cursor_col = term->cursor_col; + + term->char_handler = guac_terminal_echo; break; - case '8': /* Restore Cursor (DECRC) */ + /* Restore Cursor (DECRC) */ + case '8': term->cursor_row = term->saved_cursor_row; if (term->cursor_row >= term->term_height) @@ -187,9 +191,46 @@ int guac_terminal_escape(guac_terminal* term, char c) { if (term->cursor_col >= term->term_width) term->cursor_col = term->term_width - 1; + term->char_handler = guac_terminal_echo; break; - case 'M': /* Reverse Linefeed */ + /* Index (IND) */ + case 'D': + term->cursor_row++; + + /* Scroll up if necessary */ + if (term->cursor_row > term->scroll_end) { + term->cursor_row = term->scroll_end; + + /* Scroll up by one row */ + guac_terminal_scroll_up(term, term->scroll_start, + term->scroll_end, 1); + + } + + term->char_handler = guac_terminal_echo; + break; + + /* Next Line (NEL) */ + case 'E': + term->cursor_col = 0; + term->cursor_row++; + + /* Scroll up if necessary */ + if (term->cursor_row > term->scroll_end) { + term->cursor_row = term->scroll_end; + + /* Scroll up by one row */ + guac_terminal_scroll_up(term, term->scroll_start, + term->scroll_end, 1); + + } + + term->char_handler = guac_terminal_echo; + break; + + /* Reverse Linefeed */ + case 'M': term->cursor_row--; From c1b0e3bb3bae531c811648c1af74bea8554d9121 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 00:35:18 -0700 Subject: [PATCH 130/169] Alphabetize CSI handlers. --- protocols/ssh/src/terminal_handlers.c | 200 +++++++++++++------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 9900b5c5..8304b4ef 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -305,6 +305,24 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Handle CSI functions */ switch (c) { + /* @: Insert characters (scroll right) */ + case '@': + + amount = argv[0]; + if (amount == 0) amount = 1; + + /* Scroll right by amount */ + if (term->cursor_col + amount < term->term_width) + guac_terminal_copy_columns(term, term->cursor_row, + term->cursor_col, term->term_width - amount - 1, + amount); + + /* Clear left */ + guac_terminal_clear_columns(term, term->cursor_row, + term->cursor_col, term->cursor_col + amount - 1); + + break; + /* A: Move up */ case 'A': @@ -333,20 +351,6 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; - /* D: Move left */ - case 'D': - - /* Get move amount */ - amount = argv[0]; - if (amount == 0) amount = 1; - - /* Move cursor */ - term->cursor_col -= amount; - if (term->cursor_col < 0) - term->cursor_col = 0; - - break; - /* C: Move right */ case 'C': @@ -361,76 +365,18 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; - /* m: Set graphics rendition */ - case 'm': + /* D: Move left */ + case 'D': - for (i=0; icursor_col -= amount; + if (term->cursor_col < 0) + term->cursor_col = 0; - /* Reset attributes */ - if (value == 0) - term->current_attributes = term->default_char.attributes; - - /* Bold */ - else if (value == 1) - term->current_attributes.bold = true; - - /* Underscore on */ - else if (value == 4) - term->current_attributes.underscore = true; - - /* Foreground */ - else if (value >= 30 && value <= 37) - term->current_attributes.foreground = value - 30; - - /* Background */ - else if (value >= 40 && value <= 47) - term->current_attributes.background = value - 40; - - /* Underscore on, default foreground */ - else if (value == 38) { - term->current_attributes.underscore = true; - term->current_attributes.foreground = - term->default_char.attributes.foreground; - } - - /* Underscore off, default foreground */ - else if (value == 39) { - term->current_attributes.underscore = false; - term->current_attributes.foreground = - term->default_char.attributes.foreground; - } - - /* Reset background */ - else if (value == 49) - term->current_attributes.background = - term->default_char.attributes.background; - - /* Reverse video */ - else if (value == 7) - term->current_attributes.reverse = true; - - /* Reset underscore */ - else if (value == 24) - term->current_attributes.underscore = false; - - /* Reset reverse video */ - else if (value == 27) - term->current_attributes.reverse = false; - - else - guac_client_log_info(term->client, - "Unhandled graphics rendition: %i", value); - - } - - break; - - /* r: Set scrolling region */ - case 'r': - term->scroll_start = argv[0]-1; - term->scroll_end = argv[1]-1; break; /* H: Move cursor */ @@ -449,13 +395,6 @@ int guac_terminal_csi(guac_terminal* term, char c) { term->cursor_col = col; break; - /* d: Move cursor, current col */ - case 'd': - row = argv[0]; if (row != 0) row--; - term->cursor_row = row; - break; - - /* J: Erase display */ case 'J': @@ -550,22 +489,83 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; - /* @: Insert characters (scroll right) */ - case '@': - amount = argv[0]; - if (amount == 0) amount = 1; + /* d: Move cursor, current col */ + case 'd': + row = argv[0]; if (row != 0) row--; + term->cursor_row = row; + break; - /* Scroll right by amount */ - if (term->cursor_col + amount < term->term_width) - guac_terminal_copy_columns(term, term->cursor_row, - term->cursor_col, term->term_width - amount - 1, - amount); + /* m: Set graphics rendition */ + case 'm': - /* Clear left */ - guac_terminal_clear_columns(term, term->cursor_row, - term->cursor_col, term->cursor_col + amount - 1); + for (i=0; icurrent_attributes = term->default_char.attributes; + + /* Bold */ + else if (value == 1) + term->current_attributes.bold = true; + + /* Underscore on */ + else if (value == 4) + term->current_attributes.underscore = true; + + /* Foreground */ + else if (value >= 30 && value <= 37) + term->current_attributes.foreground = value - 30; + + /* Background */ + else if (value >= 40 && value <= 47) + term->current_attributes.background = value - 40; + + /* Underscore on, default foreground */ + else if (value == 38) { + term->current_attributes.underscore = true; + term->current_attributes.foreground = + term->default_char.attributes.foreground; + } + + /* Underscore off, default foreground */ + else if (value == 39) { + term->current_attributes.underscore = false; + term->current_attributes.foreground = + term->default_char.attributes.foreground; + } + + /* Reset background */ + else if (value == 49) + term->current_attributes.background = + term->default_char.attributes.background; + + /* Reverse video */ + else if (value == 7) + term->current_attributes.reverse = true; + + /* Reset underscore */ + else if (value == 24) + term->current_attributes.underscore = false; + + /* Reset reverse video */ + else if (value == 27) + term->current_attributes.reverse = false; + + else + guac_client_log_info(term->client, + "Unhandled graphics rendition: %i", value); + + } + + break; + + /* r: Set scrolling region */ + case 'r': + term->scroll_start = argv[0]-1; + term->scroll_end = argv[1]-1; break; /* Warn of unhandled codes */ From 4763f029a4f83cfa0de941a54df7276829e08893 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 00:47:55 -0700 Subject: [PATCH 131/169] ACTUALLY alphabetize... --- protocols/ssh/src/terminal_handlers.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 8304b4ef..92f78fed 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -379,6 +379,12 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; + /* G: Move cursor, current row */ + case 'G': + col = argv[0]; if (col != 0) col--; + term->cursor_col = col; + break; + /* H: Move cursor */ case 'H': @@ -389,12 +395,6 @@ int guac_terminal_csi(guac_terminal* term, char c) { term->cursor_col = col; break; - /* G: Move cursor, current row */ - case 'G': - col = argv[0]; if (col != 0) col--; - term->cursor_col = col; - break; - /* J: Erase display */ case 'J': From 438ac8e9bb8d5d06bd3604e2fea9bdaae5f234d6 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 00:55:46 -0700 Subject: [PATCH 132/169] VT and FF should behave exactly as LF. --- protocols/ssh/src/terminal_handlers.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 92f78fed..255f6743 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -102,8 +102,10 @@ int guac_terminal_echo(guac_terminal* term, char c) { term->cursor_col = 0; break; - /* Line feed */ + /* Line feed / VT / FF */ case '\n': + case '0x0B': /* VT */ + case '0x0C': /* FF */ term->cursor_row++; /* Scroll up if necessary */ From 225377f197f49a505b106098fdd04622bf1671e7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 00:57:44 -0700 Subject: [PATCH 133/169] Fix VT/FF. Implement DEL and CSI. --- protocols/ssh/src/terminal_handlers.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 255f6743..a2f91cb2 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -104,8 +104,8 @@ int guac_terminal_echo(guac_terminal* term, char c) { /* Line feed / VT / FF */ case '\n': - case '0x0B': /* VT */ - case '0x0C': /* FF */ + case 0x0B: /* VT */ + case 0x0C: /* FF */ term->cursor_row++; /* Scroll up if necessary */ @@ -124,6 +124,15 @@ int guac_terminal_echo(guac_terminal* term, char c) { term->char_handler = guac_terminal_escape; break; + /* CSI */ + case 0x9B: + term->char_handler = guac_terminal_csi; + break; + + /* DEL (ignored) */ + case 0x7F: + break; + /* Displayable chars */ default: From 5a0b8b2ea7b70944bcc46e017e316a08878af6c1 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 22:02:11 -0700 Subject: [PATCH 134/169] Move pipes to terminal scope. --- protocols/ssh/include/client.h | 10 ---------- protocols/ssh/include/terminal.h | 16 ++++++++++++++++ protocols/ssh/src/client.c | 14 -------------- protocols/ssh/src/guac_handlers.c | 17 ++++------------- protocols/ssh/src/ssh_client.c | 8 ++++---- protocols/ssh/src/terminal.c | 25 +++++++++++++++++++++++++ 6 files changed, 49 insertions(+), 41 deletions(-) diff --git a/protocols/ssh/include/client.h b/protocols/ssh/include/client.h index d0301204..bb51d0a6 100644 --- a/protocols/ssh/include/client.h +++ b/protocols/ssh/include/client.h @@ -79,16 +79,6 @@ typedef struct ssh_guac_client_data { */ int mouse_mask; - /** - * Pipe which will be used to provide STDOUT to the SSH client. - */ - int stdout_pipe_fd[2]; - - /** - * Pipe which will be used to provide STDIN to the SSH client. - */ - int stdin_pipe_fd[2]; - /** * The cached I-bar cursor. */ diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 92af6eab..6dc0bc0c 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -75,6 +75,22 @@ struct guac_terminal { */ pthread_mutex_t lock; + /** + * Pipe which should be written to (and read from) to provide output to + * this terminal. Another thread should read from this pipe when writing + * data to the terminal. It would make sense for the terminal to provide + * this thread, but for simplicity, that logic is left to the guac + * message handler (to give the message handler something to block with). + */ + int stdout_pipe_fd[2]; + + /** + * Pipe which will be the source of user input. When a terminal code + * generates synthesized user input, that data will be written to + * this pipe. + */ + int stdin_pipe_fd[2]; + /** * The relative offset of the display. A positive value indicates that * many rows have been scrolled into view, zero indicates that no diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index 0bfbf088..8332049d 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -101,20 +101,6 @@ int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket_flush(socket); - /* Open STDOUT pipe */ - if (pipe(client_data->stdout_pipe_fd)) { - guac_error = GUAC_STATUS_SEE_ERRNO; - guac_error_message = "Unable to open pipe for STDOUT"; - return 1; - } - - /* Open STDIN pipe */ - if (pipe(client_data->stdin_pipe_fd)) { - guac_error = GUAC_STATUS_SEE_ERRNO; - guac_error_message = "Unable to open pipe for STDIN"; - return 1; - } - /* Set basic handlers */ client->handle_messages = ssh_guac_client_handle_messages; client->clipboard_handler = ssh_guac_client_clipboard_handler; diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index 5a16edb0..60438cdc 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -61,7 +61,7 @@ int ssh_guac_client_handle_messages(guac_client* client) { char buffer[8192]; int ret_val; - int fd = client_data->stdout_pipe_fd[0]; + int fd = client_data->term->stdout_pipe_fd[0]; struct timeval timeout; fd_set fds; @@ -158,7 +158,7 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { int length = strlen(client_data->clipboard_data); if (length) - return write(client_data->stdin_pipe_fd[1], + return write(term->stdin_pipe_fd[1], client_data->clipboard_data, length); } @@ -229,7 +229,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { guac_terminal* term = client_data->term; /* Get write end of STDIN pipe */ - int fd = client_data->stdin_pipe_fd[1]; + int fd = term->stdin_pipe_fd[1]; /* Hide mouse cursor if not already hidden */ if (client_data->current_cursor != client_data->blank_cursor) { @@ -354,18 +354,9 @@ int ssh_guac_client_free_handler(guac_client* client) { ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data; - /* Close terminal output pipe */ - close(guac_client_data->stdout_pipe_fd[1]); - close(guac_client_data->stdout_pipe_fd[0]); - - /* Close user input pipe */ - close(guac_client_data->stdin_pipe_fd[1]); - close(guac_client_data->stdin_pipe_fd[0]); - - pthread_join(guac_client_data->client_thread, NULL); - /* Free terminal */ guac_terminal_free(guac_client_data->term); + pthread_join(guac_client_data->client_thread, NULL); /* Free clipboard data */ free(guac_client_data->clipboard_data); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index eb54a34a..564ee8cf 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -81,8 +81,8 @@ static char* prompt(guac_client* client, const char* title, char* str, int size, char in_byte; /* Get STDIN and STDOUT */ - int stdin_fd = client_data->stdin_pipe_fd[0]; - int stdout_fd = client_data->stdout_pipe_fd[1]; + int stdin_fd = client_data->term->stdin_pipe_fd[0]; + int stdout_fd = client_data->term->stdout_pipe_fd[1]; /* Print title */ __write_all(stdout_fd, title, strlen(title)); @@ -137,7 +137,7 @@ void* ssh_input_thread(void* data) { char buffer[8192]; int bytes_read; - int stdin_fd = client_data->stdin_pipe_fd[0]; + int stdin_fd = client_data->term->stdin_pipe_fd[0]; /* Write all data read */ while ((bytes_read = read(stdin_fd, buffer, sizeof(buffer))) > 0) @@ -156,7 +156,7 @@ void* ssh_client_thread(void* data) { char buffer[8192]; int bytes_read = -1234; - int stdout_fd = client_data->stdout_pipe_fd[1]; + int stdout_fd = client_data->term->stdout_pipe_fd[1]; pthread_t input_thread; diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 74e809c0..22374405 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "types.h" #include "buffer.h" @@ -95,6 +96,22 @@ guac_terminal* guac_terminal_create(guac_client* client, term->text_selected = false; + /* Open STDOUT pipe */ + if (pipe(term->stdout_pipe_fd)) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Unable to open pipe for STDOUT"; + free(term); + return NULL; + } + + /* Open STDIN pipe */ + if (pipe(term->stdin_pipe_fd)) { + guac_error = GUAC_STATUS_SEE_ERRNO; + guac_error_message = "Unable to open pipe for STDIN"; + free(term); + return NULL; + } + /* Size display */ guac_terminal_display_resize(term->display, term->term_width, term->term_height); @@ -108,6 +125,14 @@ guac_terminal* guac_terminal_create(guac_client* client, void guac_terminal_free(guac_terminal* term) { + /* Close terminal output pipe */ + close(term->stdout_pipe_fd[1]); + close(term->stdout_pipe_fd[0]); + + /* Close user input pipe */ + close(term->stdin_pipe_fd[1]); + close(term->stdin_pipe_fd[0]); + /* Free display */ guac_terminal_display_free(term->display); From 142b526a97e6d9e1bcc8007dc3e739f6caa2185e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 22:09:42 -0700 Subject: [PATCH 135/169] Move write_all convenience function to common. --- protocols/ssh/include/common.h | 6 ++++++ protocols/ssh/src/common.c | 21 +++++++++++++++++++ protocols/ssh/src/ssh_client.c | 37 +++++++--------------------------- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/protocols/ssh/include/common.h b/protocols/ssh/include/common.h index effcd67f..d6b224da 100644 --- a/protocols/ssh/include/common.h +++ b/protocols/ssh/include/common.h @@ -58,5 +58,11 @@ int guac_terminal_encode_utf8(int codepoint, char* utf8); */ bool guac_terminal_has_glyph(int codepoint); +/** + * Similar to write, but automatically retries the write operation until + * an error occurs. + */ +int guac_terminal_write_all(int fd, const char* buffer, int size); + #endif diff --git a/protocols/ssh/src/common.c b/protocols/ssh/src/common.c index 00d6bdec..cdd069e4 100644 --- a/protocols/ssh/src/common.c +++ b/protocols/ssh/src/common.c @@ -36,6 +36,7 @@ * ***** END LICENSE BLOCK ***** */ #include +#include int guac_terminal_fit_to_range(int value, int min, int max) { @@ -98,3 +99,23 @@ bool guac_terminal_has_glyph(int codepoint) { && codepoint != ' '; } +int guac_terminal_write_all(int fd, const char* buffer, int size) { + + int remaining = size; + while (remaining > 0) { + + /* Attempt to write data */ + int ret_val = write(fd, buffer, remaining); + if (ret_val <= 0) + return -1; + + /* If successful, contine with what data remains (if any) */ + remaining -= ret_val; + buffer += ret_val; + + } + + return size; + +} + diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 564ee8cf..90ecb820 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -45,30 +45,7 @@ #include #include "client.h" - -/** - * Similar to write, but automatically retries the write operation until - * an error occurs. - */ -static int __write_all(int fd, const char* buffer, int size) { - - int remaining = size; - while (remaining > 0) { - - /* Attempt to write data */ - int ret_val = write(fd, buffer, remaining); - if (ret_val <= 0) - return -1; - - /* If successful, contine with what data remains (if any) */ - remaining -= ret_val; - buffer += ret_val; - - } - - return size; - -} +#include "common.h" /** * Reads a single line from STDIN. @@ -85,7 +62,7 @@ static char* prompt(guac_client* client, const char* title, char* str, int size, int stdout_fd = client_data->term->stdout_pipe_fd[1]; /* Print title */ - __write_all(stdout_fd, title, strlen(title)); + guac_terminal_write_all(stdout_fd, title, strlen(title)); /* Make room for null terminator */ size--; @@ -98,14 +75,14 @@ static char* prompt(guac_client* client, const char* title, char* str, int size, if (in_byte == 0x08) { if (pos > 0) { - __write_all(stdout_fd, "\b \b", 3); + guac_terminal_write_all(stdout_fd, "\b \b", 3); pos--; } } /* Newline (end of input */ else if (in_byte == 0x0A) { - __write_all(stdout_fd, "\r\n", 2); + guac_terminal_write_all(stdout_fd, "\r\n", 2); break; } @@ -116,9 +93,9 @@ static char* prompt(guac_client* client, const char* title, char* str, int size, /* Print character if echoing */ if (echo) - __write_all(stdout_fd, &in_byte, 1); + guac_terminal_write_all(stdout_fd, &in_byte, 1); else - __write_all(stdout_fd, "*", 1); + guac_terminal_write_all(stdout_fd, "*", 1); } @@ -251,7 +228,7 @@ void* ssh_client_thread(void* data) { continue; if (bytes_read > 0) - __write_all(stdout_fd, buffer, bytes_read); + guac_terminal_write_all(stdout_fd, buffer, bytes_read); } From c62bba9e15d19f082e956434847b8fa2a2c32b90 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 22:37:53 -0700 Subject: [PATCH 136/169] Fix scroll region set CSI (no parameters should reset region) --- protocols/ssh/src/terminal_handlers.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index a2f91cb2..0aa9a6a8 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -37,6 +37,7 @@ #include +#include "common.h" #include "terminal.h" #include "terminal_handlers.h" @@ -500,6 +501,10 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; + /* c: Identify */ + case 'c': + guac_terminal_write_all(term->stdin_pipe_fd[1], "\x1B[?6c", 5); + break; /* d: Move cursor, current col */ case 'd': @@ -575,8 +580,19 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* r: Set scrolling region */ case 'r': - term->scroll_start = argv[0]-1; - term->scroll_end = argv[1]-1; + + /* If parameters given, set region */ + if (argc == 2) { + term->scroll_start = argv[0]-1; + term->scroll_end = argv[1]-1; + } + + /* Otherwise, reset scrolling region */ + else { + term->scroll_start = 0; + term->scroll_end = term->term_height - 1; + } + break; /* Warn of unhandled codes */ From af700542b01c62f3ace1912276a85c152bbe9700 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 23:20:13 -0700 Subject: [PATCH 137/169] Implement CSI E and F. --- protocols/ssh/src/terminal_handlers.c | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 0aa9a6a8..16a63d38 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -391,6 +391,40 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; + /* E: Move cursor down given number rows, column 1 */ + case 'E': + + /* Get move amount */ + amount = argv[0]; + if (amount == 0) amount = 1; + + /* Move cursor */ + term->cursor_row += amount; + if (term->cursor_row >= term->term_height) + term->cursor_row = term->term_height - 1; + + /* Reset to column 1 */ + term->cursor_col = 0; + + break; + + /* F: Move cursor up given number rows, column 1 */ + case 'F': + + /* Get move amount */ + amount = argv[0]; + if (amount == 0) amount = 1; + + /* Move cursor */ + term->cursor_row -= amount; + if (term->cursor_row < 0) + term->cursor_row = 0; + + /* Reset to column 1 */ + term->cursor_col = 0; + + break; + /* G: Move cursor, current row */ case 'G': col = argv[0]; if (col != 0) col--; From dd936b4873af9023e6a47aee210222c71efd306d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 23:27:34 -0700 Subject: [PATCH 138/169] Implement CSI e and f. --- protocols/ssh/src/terminal_handlers.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 16a63d38..d626b696 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -350,6 +350,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; /* B: Move down */ + case 'e': case 'B': /* Get move amount */ @@ -432,6 +433,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; /* H: Move cursor */ + case 'f': case 'H': row = argv[0]; if (row != 0) row--; From 266f4e8d1b974d1d00ca595c1879aba0aa9f0d99 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 23:38:35 -0700 Subject: [PATCH 139/169] Implement DECALGN (fill screen with E's) --- protocols/ssh/include/terminal_handlers.h | 1 + protocols/ssh/src/terminal_handlers.c | 31 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/protocols/ssh/include/terminal_handlers.h b/protocols/ssh/include/terminal_handlers.h index 0a5a1e87..ec796085 100644 --- a/protocols/ssh/include/terminal_handlers.h +++ b/protocols/ssh/include/terminal_handlers.h @@ -45,6 +45,7 @@ int guac_terminal_escape(guac_terminal* term, char c); int guac_terminal_charset(guac_terminal* term, char c); int guac_terminal_csi(guac_terminal* term, char c); int guac_terminal_osc(guac_terminal* term, char c); +int guac_terminal_ctrl_func(guac_terminal* term, char c); #endif diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index d626b696..686fc975 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -184,6 +184,10 @@ int guac_terminal_escape(guac_terminal* term, char c) { term->char_handler = guac_terminal_csi; break; + case '#': + term->char_handler = guac_terminal_ctrl_func; + break; + /* Save Cursor (DECSC) */ case '7': term->saved_cursor_row = term->cursor_row; @@ -672,3 +676,30 @@ int guac_terminal_osc(guac_terminal* term, char c) { return 0; } +int guac_terminal_ctrl_func(guac_terminal* term, char c) { + + int row; + + /* Build character with current attributes */ + guac_terminal_char guac_char; + guac_char.value = 'E'; + guac_char.attributes = term->current_attributes; + + switch (c) { + + /* Alignment test (fill screen with E's) */ + case '8': + + for (row=0; rowterm_height; row++) + guac_terminal_set_columns(term, row, 0, term->term_width-1, &guac_char); + + break; + + } + + term->char_handler = guac_terminal_echo; + + return 0; + +} + From b66d8f2e9a8426fea59cda1604b3339901566fe7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 21 May 2013 23:57:55 -0700 Subject: [PATCH 140/169] Implement CSI 'a' --- protocols/ssh/src/terminal_handlers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 686fc975..5cee5642 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -369,6 +369,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; /* C: Move right */ + case 'a': case 'C': /* Get move amount */ From 334d6cb08befd54f11f748403ed91dbdca3c37e1 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 22 May 2013 03:26:51 -0700 Subject: [PATCH 141/169] Fix background filling algorithm. --- protocols/ssh/src/display.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 6e9d2e8a..11d98e03 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -779,10 +779,10 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) { for (rect_col=col; rect_colwidth; rect_col++) { int joining_color; - if (rect_current->character.attributes.reverse) - joining_color = current->character.attributes.foreground; + if (rect_current->character.attributes.reverse != rect_current->character.attributes.cursor) + joining_color = rect_current->character.attributes.foreground; else - joining_color = current->character.attributes.background; + joining_color = rect_current->character.attributes.background; /* If not identical operation, stop */ if (rect_current->type != GUAC_CHAR_SET @@ -825,9 +825,9 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) { int joining_color; if (rect_current->character.attributes.reverse != rect_current->character.attributes.cursor) - joining_color = current->character.attributes.foreground; + joining_color = rect_current->character.attributes.foreground; else - joining_color = current->character.attributes.background; + joining_color = rect_current->character.attributes.background; /* Mark clear operations as NOP */ if (rect_current->type == GUAC_CHAR_SET From b1622413a9dbd28bccfb6753dc322d5c8a624014 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 22 May 2013 11:08:38 -0700 Subject: [PATCH 142/169] Implement mode set/reset. Implement DECCKM mode. --- protocols/ssh/include/terminal.h | 6 +++++ protocols/ssh/src/guac_handlers.c | 28 ++++++++++++++++++---- protocols/ssh/src/terminal.c | 1 + protocols/ssh/src/terminal_handlers.c | 34 +++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 6dc0bc0c..438e18e1 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -204,6 +204,12 @@ struct guac_terminal { */ int selection_end_column; + /** + * Whether the cursor (arrow) keys should send cursor sequences + * or application sequences (DECCKM). + */ + bool application_cursor_keys; + }; /** diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index 60438cdc..673442bd 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -298,10 +298,30 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { else if (keysym == 0xFF1B) { data = "\x1B"; length = 1; } /* Arrow keys */ - else if (keysym == 0xFF52) { data = "\x1B\x5B""A"; length = 3; } - else if (keysym == 0xFF54) { data = "\x1B\x5B""B"; length = 3; } - else if (keysym == 0xFF53) { data = "\x1B\x5B""C"; length = 3; } - else if (keysym == 0xFF51) { data = "\x1B\x5B""D"; length = 3; } + else if (keysym == 0xFF52) { + if (term->application_cursor_keys) data = "\x1BOA"; + else data = "\x1B[A"; + length = 3; + } + + else if (keysym == 0xFF54) { + if (term->application_cursor_keys) data = "\x1BOB"; + else data = "\x1B[B"; + length = 3; + } + + else if (keysym == 0xFF53) { + if (term->application_cursor_keys) data = "\x1BOC"; + else data = "\x1B[C"; + length = 3; + } + + else if (keysym == 0xFF51) { + if (term->application_cursor_keys) data = "\x1BOD"; + else data = "\x1B[D"; + length = 3; + } + /* Ignore other keys */ else return 0; diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 22374405..1f5b7a4f 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -95,6 +95,7 @@ guac_terminal* guac_terminal_create(guac_client* client, term->scroll_end = term->term_height - 1; term->text_selected = false; + term->application_cursor_keys = false; /* Open STDOUT pipe */ if (pipe(term->stdout_pipe_fd)) { diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 5cee5642..f293fe76 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -284,6 +284,9 @@ int guac_terminal_csi(guac_terminal* term, char c) { static int argc = 0; static int argv[16] = {0}; + /* Whether the sequence started with a question mark */ + static bool initial_question_mark = false; + /* Argument building counter and buffer */ static int argv_length = 0; static char argv_buffer[256]; @@ -553,6 +556,34 @@ int guac_terminal_csi(guac_terminal* term, char c) { term->cursor_row = row; break; + /* h: Set Mode */ + case 'h': + + /* DECCKM */ + if (argv[0] == 1) + term->application_cursor_keys = true; + + else + guac_client_log_info(term->client, + "Unhandled mode set: mode=%i, initial_question_mark=%i", + argv[0], initial_question_mark); + + break; + + /* l: Reset Mode */ + case 'l': + + /* DECCKM */ + if (argv[0] == 1) + term->application_cursor_keys = false; + + else + guac_client_log_info(term->client, + "Unhandled mode reset: mode=%i, initial_question_mark=%i", + argv[0], initial_question_mark); + + break; + /* m: Set graphics rendition */ case 'm': @@ -659,6 +690,9 @@ int guac_terminal_csi(guac_terminal* term, char c) { for (i=0; i Date: Wed, 22 May 2013 11:38:39 -0700 Subject: [PATCH 143/169] Fix handling of CSI sequences. --- protocols/ssh/src/terminal_handlers.c | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index f293fe76..0a94b632 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -284,17 +284,13 @@ int guac_terminal_csi(guac_terminal* term, char c) { static int argc = 0; static int argv[16] = {0}; - /* Whether the sequence started with a question mark */ - static bool initial_question_mark = false; + /* Sequence prefix, if any */ + static char private_mode_character = 0; /* Argument building counter and buffer */ static int argv_length = 0; static char argv_buffer[256]; - /* "The sequence of parameters may be preceded by a single question mark." */ - if (c == '?') - return 0; - /* Digits get concatenated into argv */ if (c >= '0' && c <= '9') { @@ -304,8 +300,8 @@ int guac_terminal_csi(guac_terminal* term, char c) { } - /* Any non-digit stops the parameter, and possibly the sequence */ - else { + /* Specific non-digits stop the parameter, and possibly the sequence */ + else if ((c >= 0x40 && c <= 0x7E) || c == ';') { int i, row, col, amount; @@ -560,13 +556,13 @@ int guac_terminal_csi(guac_terminal* term, char c) { case 'h': /* DECCKM */ - if (argv[0] == 1) + if (argv[0] == 1 && private_mode_character == '?') term->application_cursor_keys = true; else guac_client_log_info(term->client, - "Unhandled mode set: mode=%i, initial_question_mark=%i", - argv[0], initial_question_mark); + "Unhandled mode set: mode=%i, private_mode_character=0x%0x", + argv[0], private_mode_character); break; @@ -574,13 +570,13 @@ int guac_terminal_csi(guac_terminal* term, char c) { case 'l': /* DECCKM */ - if (argv[0] == 1) + if (argv[0] == 1 && private_mode_character == '?') term->application_cursor_keys = false; else guac_client_log_info(term->client, - "Unhandled mode reset: mode=%i, initial_question_mark=%i", - argv[0], initial_question_mark); + "Unhandled mode reset: mode=%i, private_mode_character=0x%0x", + argv[0], private_mode_character); break; @@ -690,8 +686,8 @@ int guac_terminal_csi(guac_terminal* term, char c) { for (i=0; i= 0x3A && c <= 0x3F && private_mode_character == 0) + private_mode_character = c; + return 0; } From 88dfb1517df075ebcee5e92575759fba050fba4f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 22 May 2013 11:51:01 -0700 Subject: [PATCH 144/169] Preserve cursor attribute when cursor row is touched. --- protocols/ssh/src/terminal.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 1f5b7a4f..6c02eefa 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -586,6 +586,23 @@ void guac_terminal_set_columns(guac_terminal* terminal, int row, guac_terminal_buffer_set_columns(terminal->buffer, row, start_column, end_column, character); + /* If visible cursor in current row, preserve state */ + if (row == terminal->visible_cursor_row + && terminal->visible_cursor_col >= start_column + && terminal->visible_cursor_col <= end_column) { + + /* Create copy of character with cursor attribute set */ + guac_terminal_char cursor_character = *character; + cursor_character.attributes.cursor = true; + + guac_terminal_display_set_columns(terminal->display, row + terminal->scroll_offset, + terminal->visible_cursor_col, terminal->visible_cursor_col, &cursor_character); + + guac_terminal_buffer_set_columns(terminal->buffer, row, + terminal->visible_cursor_col, terminal->visible_cursor_col, &cursor_character); + + } + } static void __guac_terminal_redraw_rect(guac_terminal* term, int start_row, int start_col, int end_row, int end_col) { From ae7959c6a4afff20f009b01d735fec1d9cb74f43 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 22 May 2013 11:54:28 -0700 Subject: [PATCH 145/169] Clear screen after prompts. --- protocols/ssh/src/ssh_client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 90ecb820..10f76067 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -147,6 +147,9 @@ void* ssh_client_thread(void* data) { prompt(client, "Password: ", client_data->password, sizeof(client_data->password), false) == NULL) return NULL; + /* Clear screen */ + guac_terminal_write_all(stdout_fd, "\x1B[H\x1B[J", 6); + /* Open SSH session */ client_data->session = ssh_new(); if (client_data->session == NULL) { From f4475b4f0073e105b07a297b759371196262d84f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 23 May 2013 23:12:01 -0700 Subject: [PATCH 146/169] Move flags to generic function, add charset handler stubs. --- protocols/ssh/include/terminal_handlers.h | 5 +- protocols/ssh/src/terminal_handlers.c | 84 ++++++++++++++++++++--- 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/protocols/ssh/include/terminal_handlers.h b/protocols/ssh/include/terminal_handlers.h index ec796085..3b1faf68 100644 --- a/protocols/ssh/include/terminal_handlers.h +++ b/protocols/ssh/include/terminal_handlers.h @@ -42,7 +42,10 @@ int guac_terminal_echo(guac_terminal* term, char c); int guac_terminal_escape(guac_terminal* term, char c); -int guac_terminal_charset(guac_terminal* term, char c); +int guac_terminal_g0_charset(guac_terminal* term, char c); +int guac_terminal_g1_charset(guac_terminal* term, char c); +int guac_terminal_g2_charset(guac_terminal* term, char c); +int guac_terminal_g3_charset(guac_terminal* term, char c); int guac_terminal_csi(guac_terminal* term, char c); int guac_terminal_osc(guac_terminal* term, char c); int guac_terminal_ctrl_func(guac_terminal* term, char c); diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 0a94b632..212bd78c 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -173,7 +173,19 @@ int guac_terminal_escape(guac_terminal* term, char c) { switch (c) { case '(': - term->char_handler = guac_terminal_charset; + term->char_handler = guac_terminal_g0_charset; + break; + + case ')': + term->char_handler = guac_terminal_g1_charset; + break; + + case '*': + term->char_handler = guac_terminal_g2_charset; + break; + + case '+': + term->char_handler = guac_terminal_g3_charset; break; case ']': @@ -273,9 +285,58 @@ int guac_terminal_escape(guac_terminal* term, char c) { } -int guac_terminal_charset(guac_terminal* term, char c) { +int guac_terminal_g0_charset(guac_terminal* term, char c) { + + /* STUB */ + guac_client_log_info(term->client, "Ignoring G0 charset: 0x%02x", c); term->char_handler = guac_terminal_echo; return 0; + +} + +int guac_terminal_g1_charset(guac_terminal* term, char c) { + + /* STUB */ + guac_client_log_info(term->client, "Ignoring G1 charset: 0x%02x", c); + term->char_handler = guac_terminal_echo; + return 0; + +} + +int guac_terminal_g2_charset(guac_terminal* term, char c) { + + /* STUB */ + guac_client_log_info(term->client, "Ignoring G2 charset: 0x%02x", c); + term->char_handler = guac_terminal_echo; + return 0; + +} + +int guac_terminal_g3_charset(guac_terminal* term, char c) { + + /* STUB */ + guac_client_log_info(term->client, "Ignoring G3 charset: 0x%02x", c); + term->char_handler = guac_terminal_echo; + return 0; + +} + +/** + * Looks up the flag specified by the given number and mode. Used by the Set/Reset Mode + * functions of the terminal. + */ +static bool* __guac_terminal_get_flag(guac_terminal* term, int num, char private_mode) { + + if (private_mode == '?') { + switch (num) { + case 1: return &(term->application_cursor_keys); /* DECCKM */ + } + } + + + /* Unknown flag */ + return NULL; + } int guac_terminal_csi(guac_terminal* term, char c) { @@ -304,6 +365,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { else if ((c >= 0x40 && c <= 0x7E) || c == ';') { int i, row, col, amount; + bool* flag; /* At most 16 parameters */ if (argc < 16) { @@ -554,10 +616,11 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* h: Set Mode */ case 'h': - - /* DECCKM */ - if (argv[0] == 1 && private_mode_character == '?') - term->application_cursor_keys = true; + + /* Look up flag and set */ + flag = __guac_terminal_get_flag(term, argv[0], private_mode_character); + if (flag != NULL) + *flag = true; else guac_client_log_info(term->client, @@ -568,10 +631,11 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* l: Reset Mode */ case 'l': - - /* DECCKM */ - if (argv[0] == 1 && private_mode_character == '?') - term->application_cursor_keys = false; + + /* Look up flag and clear */ + flag = __guac_terminal_get_flag(term, argv[0], private_mode_character); + if (flag != NULL) + *flag = false; else guac_client_log_info(term->client, From e3f89052e5619c9759486bbd5fc6dd4646160121 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 23 May 2013 23:43:35 -0700 Subject: [PATCH 147/169] Specify terminal type. --- protocols/ssh/src/ssh_client.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 10f76067..5f304a04 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -192,20 +192,13 @@ void* ssh_client_thread(void* data) { } /* Request PTY */ - if (channel_request_pty(client_data->term_channel) != SSH_OK) { + if (channel_request_pty_size(client_data->term_channel, "linux", + client_data->term->term_width, client_data->term->term_height) != SSH_OK) { guac_protocol_send_error(socket, "Unable to allocate PTY for channel."); guac_socket_flush(socket); return NULL; } - /* Request PTY size */ - if (channel_change_pty_size(client_data->term_channel, - client_data->term->term_width, client_data->term->term_height) != SSH_OK) { - guac_protocol_send_error(socket, "Unable to change PTY size."); - guac_socket_flush(socket); - return NULL; - } - /* Request shell */ if (channel_request_shell(client_data->term_channel) != SSH_OK) { guac_protocol_send_error(socket, "Unable to associate shell with PTY."); From b513e4ba939e26e89495fa549ed9189cfe5385e1 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 24 May 2013 13:33:32 -0700 Subject: [PATCH 148/169] Add LF/NL mode. --- protocols/ssh/include/terminal.h | 5 +++++ protocols/ssh/src/terminal.c | 1 + protocols/ssh/src/terminal_handlers.c | 19 ++++++++++++++----- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 438e18e1..8138a812 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -210,6 +210,11 @@ struct guac_terminal { */ bool application_cursor_keys; + /** + * Whether a CR should automatically follow a LF, VT, or FF. + */ + bool automatic_carriage_return; + }; /** diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 6c02eefa..afc00ef3 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -96,6 +96,7 @@ guac_terminal* guac_terminal_create(guac_client* client, term->text_selected = false; term->application_cursor_keys = false; + term->automatic_carriage_return = false; /* Open STDOUT pipe */ if (pipe(term->stdout_pipe_fd)) { diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 212bd78c..392db46a 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -98,11 +98,6 @@ int guac_terminal_echo(guac_terminal* term, char c) { term->cursor_col--; break; - /* Carriage return */ - case '\r': - term->cursor_col = 0; - break; - /* Line feed / VT / FF */ case '\n': case 0x0B: /* VT */ @@ -118,6 +113,14 @@ int guac_terminal_echo(guac_terminal* term, char c) { term->scroll_end, 1); } + + /* If automatic carriage return, fall through to CR handler */ + if (!term->automatic_carriage_return) + break; + + /* Carriage return */ + case '\r': + term->cursor_col = 0; break; /* ESC */ @@ -333,6 +336,12 @@ static bool* __guac_terminal_get_flag(guac_terminal* term, int num, char private } } + else if (private_mode == 0) { + switch (num) { + case 20: return &(term->automatic_carriage_return); /* LF/NL */ + } + } + /* Unknown flag */ return NULL; From e0c46a41a2eba6e46074ea66cedf9f77bd5b6fb3 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 24 May 2013 13:44:51 -0700 Subject: [PATCH 149/169] Interpret ESC[3J as if ESC[2J --- protocols/ssh/src/terminal_handlers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 392db46a..93092b0e 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -534,7 +534,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { term->cursor_row, term->cursor_col); /* Entire screen */ - else if (argv[0] == 2) + else if (argv[0] == 2 || argv[0] == 3) guac_terminal_clear_range(term, 0, 0, term->term_height - 1, term->term_width - 1); From 7216f734b5546f9c95871a2a758f3fadc90a8c49 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 24 May 2013 15:00:54 -0700 Subject: [PATCH 150/169] Add some Linux-specific missing codes, reorder SGR. Add explicit ignores for unapplicable features. --- protocols/ssh/src/terminal_handlers.c | 67 ++++++++++++++++++++------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 93092b0e..a7891c2d 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -502,6 +502,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; /* G: Move cursor, current row */ + case '`': case 'G': col = argv[0]; if (col != 0) col--; term->cursor_col = col; @@ -612,6 +613,11 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; + /* ]: Linux Private CSI */ + case ']': + /* Explicitly ignored */ + break; + /* c: Identify */ case 'c': guac_terminal_write_all(term->stdin_pipe_fd[1], "\x1B[?6c", 5); @@ -672,14 +678,26 @@ int guac_terminal_csi(guac_terminal* term, char c) { else if (value == 4) term->current_attributes.underscore = true; + /* Reverse video */ + else if (value == 7) + term->current_attributes.reverse = true; + + /* Normal intensity (not bold) */ + else if (value == 21 || value == 22) + term->current_attributes.bold = false; + + /* Reset underscore */ + else if (value == 24) + term->current_attributes.underscore = false; + + /* Reset reverse video */ + else if (value == 27) + term->current_attributes.reverse = false; + /* Foreground */ else if (value >= 30 && value <= 37) term->current_attributes.foreground = value - 30; - /* Background */ - else if (value >= 40 && value <= 47) - term->current_attributes.background = value - 40; - /* Underscore on, default foreground */ else if (value == 38) { term->current_attributes.underscore = true; @@ -694,23 +712,15 @@ int guac_terminal_csi(guac_terminal* term, char c) { term->default_char.attributes.foreground; } + /* Background */ + else if (value >= 40 && value <= 47) + term->current_attributes.background = value - 40; + /* Reset background */ else if (value == 49) term->current_attributes.background = term->default_char.attributes.background; - /* Reverse video */ - else if (value == 7) - term->current_attributes.reverse = true; - - /* Reset underscore */ - else if (value == 24) - term->current_attributes.underscore = false; - - /* Reset reverse video */ - else if (value == 27) - term->current_attributes.reverse = false; - else guac_client_log_info(term->client, "Unhandled graphics rendition: %i", value); @@ -719,6 +729,11 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; + /* q: Set keyboard LEDs */ + case 'q': + /* Explicitly ignored */ + break; + /* r: Set scrolling region */ case 'r': @@ -736,6 +751,26 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; + /* Save Cursor */ + case 's': + term->saved_cursor_row = term->cursor_row; + term->saved_cursor_col = term->cursor_col; + break; + + /* Restore Cursor */ + case 'u': + + term->cursor_row = term->saved_cursor_row; + if (term->cursor_row >= term->term_height) + term->cursor_row = term->term_height - 1; + + term->cursor_col = term->saved_cursor_col; + if (term->cursor_col >= term->term_width) + term->cursor_col = term->term_width - 1; + + break; + + /* Warn of unhandled codes */ default: if (c != ';') { From 94ee3c87fc23ab4b9cc19aaa3b2079ffe4f8143e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 24 May 2013 16:29:43 -0700 Subject: [PATCH 151/169] Implement ESC-Z identify and ESC-c reset. --- protocols/ssh/include/terminal.h | 5 +++ protocols/ssh/src/terminal.c | 48 +++++++++++++++++++-------- protocols/ssh/src/terminal_handlers.c | 11 ++++++ 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 8138a812..d9006eeb 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -224,6 +224,11 @@ struct guac_terminal { guac_terminal* guac_terminal_create(guac_client* client, int width, int height); +/** + * Resets the state of the given terminal, as if it were just allocated. + */ +void guac_terminal_reset(guac_terminal* term); + /** * Frees all resources associated with the given terminal. */ diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index afc00ef3..b7229877 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -55,6 +55,35 @@ #include "terminal.h" #include "terminal_handlers.h" +void guac_terminal_reset(guac_terminal* term) { + + int row; + + /* Set current state */ + term->char_handler = guac_terminal_echo; + + /* Reset cursor location */ + term->cursor_row = term->visible_cursor_row = term->saved_cursor_row = 0; + term->cursor_col = term->visible_cursor_col = term->saved_cursor_col = 0; + + /* Clear scrollback, buffer, and scoll region */ + term->buffer->top = 0; + term->buffer->length = 0; + term->scroll_start = 0; + term->scroll_end = term->term_height - 1; + term->scroll_offset = 0; + + /* Reset flags */ + term->text_selected = false; + term->application_cursor_keys = false; + term->automatic_carriage_return = false; + + /* Clear terminal */ + for (row=0; rowterm_height; row++) + guac_terminal_set_columns(term, row, 0, term->term_width, &(term->default_char)); + +} + guac_terminal* guac_terminal_create(guac_client* client, int width, int height) { @@ -73,7 +102,6 @@ guac_terminal* guac_terminal_create(guac_client* client, /* Init buffer */ term->buffer = guac_terminal_buffer_alloc(1000, &default_char); - term->scroll_offset = 0; /* Init display */ term->display = guac_terminal_display_alloc(client, @@ -84,19 +112,8 @@ guac_terminal* guac_terminal_create(guac_client* client, term->current_attributes = default_char.attributes; term->default_char = default_char; - term->cursor_row = term->visible_cursor_row = term->saved_cursor_row = 0; - term->cursor_col = term->visible_cursor_col = term->saved_cursor_col = 0; - term->term_width = width / term->display->char_width; term->term_height = height / term->display->char_height; - term->char_handler = guac_terminal_echo; - - term->scroll_start = 0; - term->scroll_end = term->term_height - 1; - - term->text_selected = false; - term->application_cursor_keys = false; - term->automatic_carriage_return = false; /* Open STDOUT pipe */ if (pipe(term->stdout_pipe_fd)) { @@ -114,12 +131,15 @@ guac_terminal* guac_terminal_create(guac_client* client, return NULL; } + /* Init terminal lock */ + pthread_mutex_init(&(term->lock), NULL); + /* Size display */ guac_terminal_display_resize(term->display, term->term_width, term->term_height); - /* Init terminal lock */ - pthread_mutex_init(&(term->lock), NULL); + /* Init terminal */ + guac_terminal_reset(term); return term; diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index a7891c2d..72e737b3 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -278,6 +278,17 @@ int guac_terminal_escape(guac_terminal* term, char c) { term->char_handler = guac_terminal_echo; break; + /* DEC Identify */ + case 'Z': + guac_terminal_write_all(term->stdin_pipe_fd[1], "\x1B[?6c", 5); + term->char_handler = guac_terminal_echo; + break; + + /* Reset */ + case 'c': + guac_terminal_reset(term); + break; + default: guac_client_log_info(term->client, "Unhandled ESC sequence: %c", c); term->char_handler = guac_terminal_echo; From 0f5b5d39b4b5223904444cb4855f82959a4f7302 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 24 May 2013 21:18:47 -0700 Subject: [PATCH 152/169] Initial support for G0/G1 character sets. --- protocols/ssh/Makefile.am | 2 + protocols/ssh/include/char_mappings.h | 63 +++++++++++++++++++ protocols/ssh/include/terminal.h | 13 ++++ protocols/ssh/src/char_mappings.c | 88 +++++++++++++++++++++++++++ protocols/ssh/src/terminal.c | 4 ++ protocols/ssh/src/terminal_handlers.c | 14 ++++- 6 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 protocols/ssh/include/char_mappings.h create mode 100644 protocols/ssh/src/char_mappings.c diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index 67ca8a7e..de2a9e16 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -43,6 +43,7 @@ lib_LTLIBRARIES = libguac-client-ssh.la libguac_client_ssh_la_SOURCES = \ src/blank.c \ src/buffer.c \ + src/char_mappings.c \ src/client.c \ src/common.c \ src/cursor.c \ @@ -56,6 +57,7 @@ libguac_client_ssh_la_SOURCES = \ noinst_HEADERS = \ include/blank.h \ include/buffer.h \ + include/char_mappings.h \ include/client.h \ include/common.h \ include/cursor.h \ diff --git a/protocols/ssh/include/char_mappings.h b/protocols/ssh/include/char_mappings.h new file mode 100644 index 00000000..dfbd1ebf --- /dev/null +++ b/protocols/ssh/include/char_mappings.h @@ -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-ssh. + * + * 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_SSH_CHAR_MAPPINGS_H +#define _GUAC_SSH_CHAR_MAPPINGS_H + +/** + * VT100 graphics mapping. Each entry is the corresponding Unicode codepoint + * for the character N+32, where N is the index of the element in the array. + * All characters less than 32 are universally mapped to themselves. + */ +extern const int vt100_map[]; + +/** + * Null graphics mapping. Each entry is the corresponding Unicode codepoint + * for the character N+32, where N is the index of the element in the array. + * All characters less than 32 are universally mapped to themselves. + */ +extern const int null_map[]; + +/** + * User graphics mapping. Each entry is the corresponding Unicode codepoint + * for the character N+32, where N is the index of the element in the array. + * All characters less than 32 are universally mapped to themselves. + */ +extern const int user_map[]; + +#endif + diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index d9006eeb..805ce684 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -179,6 +179,19 @@ struct guac_terminal { */ guac_terminal_buffer* buffer; + /** + * Array of arrays of mapped characters, where the character N is located at the N-32 + * position within the array. Each element in a contained array is the corresponding Unicode + * codepoint. If NULL, a direct mapping from Unicode is used. The entries of the main array + * correspond to the character set in use (G0, G1, etc.) + */ + const int* char_mapping[2]; + + /** + * The active character set. For example, 0 for G0, 1 for G1, etc. + */ + int active_char_set; + /** * Whether text is being selected. */ diff --git a/protocols/ssh/src/char_mappings.c b/protocols/ssh/src/char_mappings.c new file mode 100644 index 00000000..483091b2 --- /dev/null +++ b/protocols/ssh/src/char_mappings.c @@ -0,0 +1,88 @@ + +/* ***** 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-ssh. + * + * 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 ***** */ + +const int vt100_map[] = { + ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + 0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x00B0, 0x00B1, 0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA, + 0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00B7, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF +}; + +const int null_map[] = { + ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x007F, + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x000A +}; + +const int user_map[] = { + ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', + 0x00A7, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x00C4, 0x00D6, 0x00DC, '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0x00E4, 0x00F6, 0x00FC, 0x00DF, 0x007F, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF +}; + diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index b7229877..7160090a 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -54,6 +54,7 @@ #include "display.h" #include "terminal.h" #include "terminal_handlers.h" +#include "char_mappings.h" void guac_terminal_reset(guac_terminal* term) { @@ -61,6 +62,9 @@ void guac_terminal_reset(guac_terminal* term) { /* Set current state */ term->char_handler = guac_terminal_echo; + term->active_char_set = 0; + term->char_mapping[0] = + term->char_mapping[1] = NULL; /* Reset cursor location */ term->cursor_row = term->visible_cursor_row = term->saved_cursor_row = 0; diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 72e737b3..904b7a58 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -46,8 +46,16 @@ int guac_terminal_echo(guac_terminal* term, char c) { static int bytes_remaining = 0; static int codepoint = 0; + const int* char_mapping = term->char_mapping[term->active_char_set]; + + /* If using non-Unicode mapping, just map straight bytes */ + if (char_mapping != NULL) { + codepoint = c; + bytes_remaining = 0; + } + /* 1-byte UTF-8 codepoint */ - if ((c & 0x80) == 0x00) { /* 0xxxxxxx */ + else if ((c & 0x80) == 0x00) { /* 0xxxxxxx */ codepoint = c & 0x7F; bytes_remaining = 0; } @@ -140,6 +148,10 @@ int guac_terminal_echo(guac_terminal* term, char c) { /* Displayable chars */ default: + /* Translate mappable codepoints to whatever codepoint is mapped */ + if (codepoint >= 0x20 && codepoint <= 0xFF && char_mapping != NULL) + codepoint = char_mapping[codepoint - 0x20]; + /* Wrap if necessary */ if (term->cursor_col >= term->term_width) { term->cursor_col = 0; From 43eddc14c0b1645b58681f9b2745d6544f477ce8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 24 May 2013 21:28:14 -0700 Subject: [PATCH 153/169] Implement G0/G1 switching. --- protocols/ssh/src/terminal.c | 1 - protocols/ssh/src/terminal_handlers.c | 62 ++++++++++++++------------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 7160090a..76d74623 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -54,7 +54,6 @@ #include "display.h" #include "terminal.h" #include "terminal_handlers.h" -#include "char_mappings.h" void guac_terminal_reset(guac_terminal* term) { diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 904b7a58..fc48fea2 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -40,6 +40,7 @@ #include "common.h" #include "terminal.h" #include "terminal_handlers.h" +#include "char_mappings.h" int guac_terminal_echo(guac_terminal* term, char c) { @@ -131,6 +132,16 @@ int guac_terminal_echo(guac_terminal* term, char c) { term->cursor_col = 0; break; + /* SO (activates character set G1) */ + case 0x0E: + term->active_char_set = 1; + break; + + /* SI (activates character set G0) */ + case 0x0F: + term->active_char_set = 0; + break; + /* ESC */ case 0x1B: term->char_handler = guac_terminal_escape; @@ -195,14 +206,6 @@ int guac_terminal_escape(guac_terminal* term, char c) { term->char_handler = guac_terminal_g1_charset; break; - case '*': - term->char_handler = guac_terminal_g2_charset; - break; - - case '+': - term->char_handler = guac_terminal_g3_charset; - break; - case ']': term->char_handler = guac_terminal_osc; break; @@ -311,10 +314,28 @@ int guac_terminal_escape(guac_terminal* term, char c) { } +/** + * Given a character mapping specifier (such as B, 0, U, or K), + * returns the corresponding character mapping. + */ +static const int* __guac_terminal_get_char_mapping(char c) { + + /* Translate character specifier to actual mapping */ + switch (c) { + case 'B': return NULL; + case '0': return vt100_map; + case 'U': return null_map; + case 'K': return user_map; + } + + /* Default to Unicode */ + return NULL; + +} + int guac_terminal_g0_charset(guac_terminal* term, char c) { - /* STUB */ - guac_client_log_info(term->client, "Ignoring G0 charset: 0x%02x", c); + term->char_mapping[0] = __guac_terminal_get_char_mapping(c); term->char_handler = guac_terminal_echo; return 0; @@ -322,26 +343,7 @@ int guac_terminal_g0_charset(guac_terminal* term, char c) { int guac_terminal_g1_charset(guac_terminal* term, char c) { - /* STUB */ - guac_client_log_info(term->client, "Ignoring G1 charset: 0x%02x", c); - term->char_handler = guac_terminal_echo; - return 0; - -} - -int guac_terminal_g2_charset(guac_terminal* term, char c) { - - /* STUB */ - guac_client_log_info(term->client, "Ignoring G2 charset: 0x%02x", c); - term->char_handler = guac_terminal_echo; - return 0; - -} - -int guac_terminal_g3_charset(guac_terminal* term, char c) { - - /* STUB */ - guac_client_log_info(term->client, "Ignoring G3 charset: 0x%02x", c); + term->char_mapping[1] = __guac_terminal_get_char_mapping(c); term->char_handler = guac_terminal_echo; return 0; From 8967afefb6872eab5094666bb06227635358d5e8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 24 May 2013 22:26:24 -0700 Subject: [PATCH 154/169] Only send response to ESC[0c. --- protocols/ssh/src/terminal_handlers.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index fc48fea2..3b320135 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -645,7 +645,8 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* c: Identify */ case 'c': - guac_terminal_write_all(term->stdin_pipe_fd[1], "\x1B[?6c", 5); + if (argv[0] == 0) + guac_terminal_write_all(term->stdin_pipe_fd[1], "\x1B[?6c", 5); break; /* d: Move cursor, current col */ From b823192f0301e4c030b740b3a6204d254f49a590 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 24 May 2013 22:54:56 -0700 Subject: [PATCH 155/169] Ignore unhandled control codes. Implement ENQ. --- protocols/ssh/src/terminal_handlers.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 3b320135..89b06bd8 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -97,6 +97,11 @@ int guac_terminal_echo(guac_terminal* term, char c) { switch (codepoint) { + /* Enquiry */ + case 0x05: + guac_terminal_write_all(term->stdin_pipe_fd[1], "GUACAMOLE", 9); + break; + /* Bell */ case 0x07: break; @@ -159,6 +164,10 @@ int guac_terminal_echo(guac_terminal* term, char c) { /* Displayable chars */ default: + /* Don't bother handling control chars if unknown */ + if (codepoint < 0x20) + break; + /* Translate mappable codepoints to whatever codepoint is mapped */ if (codepoint >= 0x20 && codepoint <= 0xFF && char_mapping != NULL) codepoint = char_mapping[codepoint - 0x20]; From f2520ca91c670fe8e649ee185a0dcb4c71e1892d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 25 May 2013 16:09:28 -0700 Subject: [PATCH 156/169] Send 0x0D for return. --- protocols/ssh/src/guac_handlers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index 673442bd..24be1caa 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -294,7 +294,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { if (keysym == 0xFF08) { data = "\x08"; length = 1; } else if (keysym == 0xFF09) { data = "\x09"; length = 1; } - else if (keysym == 0xFF0D) { data = "\x0A"; length = 1; } + else if (keysym == 0xFF0D) { data = "\x0D"; length = 1; } else if (keysym == 0xFF1B) { data = "\x1B"; length = 1; } /* Arrow keys */ From 3a14c864a6704bec8f81266500031ed456a3cea1 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 25 May 2013 18:20:35 -0700 Subject: [PATCH 157/169] Only send DA response if no private mode. --- protocols/ssh/src/terminal_handlers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 89b06bd8..1347f5c2 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -654,7 +654,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* c: Identify */ case 'c': - if (argv[0] == 0) + if (argv[0] == 0 && private_mode_character == 0) guac_terminal_write_all(term->stdin_pipe_fd[1], "\x1B[?6c", 5); break; From 88ca59a5b34c423fc98a0e124936bd5e8ae3cd1a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 25 May 2013 20:24:44 -0700 Subject: [PATCH 158/169] Implement terminal reporting features. --- protocols/ssh/src/terminal_handlers.c | 60 +++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 1347f5c2..b8fa4bb2 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -36,12 +36,53 @@ * ***** END LICENSE BLOCK ***** */ #include +#include #include "common.h" #include "terminal.h" #include "terminal_handlers.h" #include "char_mappings.h" +/** + * Response string sent when identification is requested. + */ +#define GUAC_TERMINAL_VT102_ID "\x1B[?6c" + +/** + * Arbitrary response to ENQ control character. + */ +#define GUAC_TERMINAL_ANSWERBACK "GUACAMOLE" + +/** + * Response which indicates the terminal is alive. + */ +#define GUAC_TERMINAL_OK "\x1B[0n" + + +/** + * Sends data through STDIN as if typed by the user, using the format + * string given and any args (similar to printf). + */ +static int guac_terminal_respond(guac_terminal* term, const char* format, ...) { + + int written; + + va_list ap; + char buffer[1024]; + + /* Print to buffer */ + va_start(ap, format); + written = vsnprintf(buffer, sizeof(buffer)-1, format, ap); + va_end(ap); + + if (written < 0) + return written; + + /* Write to STDIN */ + return guac_terminal_write_all(term->stdin_pipe_fd[1], buffer, written); + +} + int guac_terminal_echo(guac_terminal* term, char c) { static int bytes_remaining = 0; @@ -99,7 +140,7 @@ int guac_terminal_echo(guac_terminal* term, char c) { /* Enquiry */ case 0x05: - guac_terminal_write_all(term->stdin_pipe_fd[1], "GUACAMOLE", 9); + guac_terminal_respond(term, "%s", GUAC_TERMINAL_ANSWERBACK); break; /* Bell */ @@ -304,7 +345,7 @@ int guac_terminal_escape(guac_terminal* term, char c) { /* DEC Identify */ case 'Z': - guac_terminal_write_all(term->stdin_pipe_fd[1], "\x1B[?6c", 5); + guac_terminal_respond(term, "%s", GUAC_TERMINAL_VT102_ID); term->char_handler = guac_terminal_echo; break; @@ -655,7 +696,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* c: Identify */ case 'c': if (argv[0] == 0 && private_mode_character == 0) - guac_terminal_write_all(term->stdin_pipe_fd[1], "\x1B[?6c", 5); + guac_terminal_respond(term, "%s", GUAC_TERMINAL_VT102_ID); break; /* d: Move cursor, current col */ @@ -764,6 +805,19 @@ int guac_terminal_csi(guac_terminal* term, char c) { break; + /* n: Status report */ + case 'n': + + /* Device status report */ + if (argv[0] == 5 && private_mode_character == 0) + guac_terminal_respond(term, "%s", GUAC_TERMINAL_OK); + + /* Cursor position report */ + else if (argv[0] == 6 && private_mode_character == 0) + guac_terminal_respond(term, "\x1B[%i;%iR", term->cursor_row+1, term->cursor_col+1); + + break; + /* q: Set keyboard LEDs */ case 'q': /* Explicitly ignored */ From 763ed371793af2717c4e90c95b5486c0f9e7d50a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 25 May 2013 22:45:26 -0700 Subject: [PATCH 159/169] Add remaining keys, fix backspace. Add insert flag. --- protocols/ssh/include/client.h | 5 ++ protocols/ssh/include/terminal.h | 21 +++++++ protocols/ssh/src/client.c | 1 + protocols/ssh/src/guac_handlers.c | 83 +++++++++++++++------------ protocols/ssh/src/terminal.c | 31 ++++++++++ protocols/ssh/src/terminal_handlers.c | 37 ++---------- 6 files changed, 110 insertions(+), 68 deletions(-) diff --git a/protocols/ssh/include/client.h b/protocols/ssh/include/client.h index bb51d0a6..2a38c1c6 100644 --- a/protocols/ssh/include/client.h +++ b/protocols/ssh/include/client.h @@ -69,6 +69,11 @@ typedef struct ssh_guac_client_data { */ char* clipboard_data; + /** + * Whether the alt key is currently being held down. + */ + int mod_alt; + /** * Whether the control key is currently being held down. */ diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 805ce684..ac50b616 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -228,6 +228,11 @@ struct guac_terminal { */ bool automatic_carriage_return; + /** + * Whether insert mode is enabled (DECIM). + */ + bool insert_mode; + }; /** @@ -352,5 +357,21 @@ void guac_terminal_resize(guac_terminal* term, int width, int height); */ void guac_terminal_flush(guac_terminal* terminal); +/** + * Sends the given string as if typed by the user. + */ +int guac_terminal_send_data(guac_terminal* term, const char* data, int length); + +/** + * Sends the given string as if typed by the user. + */ +int guac_terminal_send_string(guac_terminal* term, const char* data); + +/** + * Sends data through STDIN as if typed by the user, using the format + * string given and any args (similar to printf). + */ +int guac_terminal_sendf(guac_terminal* term, const char* format, ...); + #endif diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index 8332049d..8f680ac0 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -72,6 +72,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Init client data */ client->data = client_data; client_data->term = term; + client_data->mod_alt = 0; client_data->mod_ctrl = 0; client_data->clipboard_data = NULL; client_data->term_channel = NULL; diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index 24be1caa..99896887 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -158,7 +158,7 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { int length = strlen(client_data->clipboard_data); if (length) - return write(term->stdin_pipe_fd[1], + return guac_terminal_send_data(term, client_data->clipboard_data, length); } @@ -228,9 +228,6 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; guac_terminal* term = client_data->term; - /* Get write end of STDIN pipe */ - int fd = term->stdin_pipe_fd[1]; - /* Hide mouse cursor if not already hidden */ if (client_data->current_cursor != client_data->blank_cursor) { pthread_mutex_lock(&(term->lock)); @@ -243,9 +240,10 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { } /* Track modifiers */ - if (keysym == 0xFFE3) { + if (keysym == 0xFFE3) client_data->mod_ctrl = pressed; - } + else if (keysym == 0xFFE9) + client_data->mod_alt = pressed; /* If key pressed */ else if (pressed) { @@ -257,6 +255,10 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { pthread_mutex_unlock(&(term->lock)); } + /* If alt being held, also send escape character */ + if (client_data->mod_alt) + return guac_terminal_send_string(term, "\x1B"); + /* Translate Ctrl+letter to control code */ if (client_data->mod_ctrl) { @@ -272,7 +274,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { else return 0; - return write(fd, &data, 1); + return guac_terminal_send_data(term, &data, 1); } @@ -283,50 +285,57 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { char data[5]; length = guac_terminal_encode_utf8(keysym & 0xFFFF, data); - return write(fd, data, length); + return guac_terminal_send_data(term, data, length); } + /* Non-printable keys */ else { - int length = 0; - const char* data = NULL; + if (keysym == 0xFF08) return guac_terminal_send_string(term, "\x7F"); /* Backspace */ + if (keysym == 0xFF09) return guac_terminal_send_string(term, "\x09"); /* Tab */ + if (keysym == 0xFF0D) return guac_terminal_send_string(term, "\x0D"); /* Enter */ + if (keysym == 0xFF1B) return guac_terminal_send_string(term, "\x1B"); /* Esc */ - if (keysym == 0xFF08) { data = "\x08"; length = 1; } - else if (keysym == 0xFF09) { data = "\x09"; length = 1; } - else if (keysym == 0xFF0D) { data = "\x0D"; length = 1; } - else if (keysym == 0xFF1B) { data = "\x1B"; length = 1; } + if (keysym == 0xFF50) return guac_terminal_send_string(term, "\x1BOH"); /* Home */ - /* Arrow keys */ - else if (keysym == 0xFF52) { - if (term->application_cursor_keys) data = "\x1BOA"; - else data = "\x1B[A"; - length = 3; + /* Arrow keys w/ application cursor */ + if (term->application_cursor_keys) { + if (keysym == 0xFF51) return guac_terminal_send_string(term, "\x1BOD"); /* Left */ + if (keysym == 0xFF52) return guac_terminal_send_string(term, "\x1BOA"); /* Up */ + if (keysym == 0xFF53) return guac_terminal_send_string(term, "\x1BOC"); /* Right */ + if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1BOB"); /* Down */ + } + else { + if (keysym == 0xFF51) return guac_terminal_send_string(term, "\x1B[D"); /* Left */ + if (keysym == 0xFF52) return guac_terminal_send_string(term, "\x1B[A"); /* Up */ + if (keysym == 0xFF53) return guac_terminal_send_string(term, "\x1B[C"); /* Right */ + if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1B[B"); /* Down */ } - else if (keysym == 0xFF54) { - if (term->application_cursor_keys) data = "\x1BOB"; - else data = "\x1B[B"; - length = 3; - } + if (keysym == 0xFF55) return guac_terminal_send_string(term, "\x1B[5;3~"); /* Page up */ + if (keysym == 0xFF56) return guac_terminal_send_string(term, "\x1B[6;3~"); /* Page down */ + if (keysym == 0xFF57) return guac_terminal_send_string(term, "\x1BOF"); /* End */ - else if (keysym == 0xFF53) { - if (term->application_cursor_keys) data = "\x1BOC"; - else data = "\x1B[C"; - length = 3; - } + if (keysym == 0xFF63) return guac_terminal_send_string(term, "\x1B[2~"); /* Insert */ - else if (keysym == 0xFF51) { - if (term->application_cursor_keys) data = "\x1BOD"; - else data = "\x1B[D"; - length = 3; - } + if (keysym == 0xFFBE) return guac_terminal_send_string(term, "\x1BOP"); /* F1 */ + if (keysym == 0xFFBF) return guac_terminal_send_string(term, "\x1BOQ"); /* F2 */ + if (keysym == 0xFFC0) return guac_terminal_send_string(term, "\x1BOR"); /* F3 */ + if (keysym == 0xFFC1) return guac_terminal_send_string(term, "\x1BOS"); /* F4 */ + if (keysym == 0xFFC2) return guac_terminal_send_string(term, "\x1B[15~"); /* F5 */ + if (keysym == 0xFFC3) return guac_terminal_send_string(term, "\x1B[17~"); /* F6 */ + if (keysym == 0xFFC4) return guac_terminal_send_string(term, "\x1B[18~"); /* F7 */ + if (keysym == 0xFFC5) return guac_terminal_send_string(term, "\x1B[19~"); /* F8 */ + if (keysym == 0xFFC6) return guac_terminal_send_string(term, "\x1B[20~"); /* F9 */ + if (keysym == 0xFFC7) return guac_terminal_send_string(term, "\x1B[21~"); /* F10 */ + if (keysym == 0xFFC8) return guac_terminal_send_string(term, "\x1B[22~"); /* F11 */ + if (keysym == 0xFFC9) return guac_terminal_send_string(term, "\x1B[23~"); /* F12 */ - /* Ignore other keys */ - else return 0; + if (keysym == 0xFFFF) return guac_terminal_send_string(term, "\x1B[3~"); /* Delete */ - return write(fd, data, length); + /* Ignore unknown keys */ } } diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index 76d74623..f7bd7fc0 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -37,6 +37,7 @@ * ***** END LICENSE BLOCK ***** */ #include +#include #include #include @@ -80,6 +81,7 @@ void guac_terminal_reset(guac_terminal* term) { term->text_selected = false; term->application_cursor_keys = false; term->automatic_carriage_return = false; + term->insert_mode = false; /* Clear terminal */ for (row=0; rowterm_height; row++) @@ -755,3 +757,32 @@ void guac_terminal_resize(guac_terminal* term, int width, int height) { } +int guac_terminal_send_data(guac_terminal* term, const char* data, int length) { + return guac_terminal_write_all(term->stdin_pipe_fd[1], data, length); +} + +int guac_terminal_send_string(guac_terminal* term, const char* data) { + return guac_terminal_write_all(term->stdin_pipe_fd[1], data, strlen(data)); +} + +int guac_terminal_sendf(guac_terminal* term, const char* format, ...) { + + int written; + + va_list ap; + char buffer[1024]; + + /* Print to buffer */ + va_start(ap, format); + written = vsnprintf(buffer, sizeof(buffer)-1, format, ap); + va_end(ap); + + if (written < 0) + return written; + + /* Write to STDIN */ + return guac_terminal_write_all(term->stdin_pipe_fd[1], buffer, written); + +} + + diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index b8fa4bb2..002f7981 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -36,7 +36,6 @@ * ***** END LICENSE BLOCK ***** */ #include -#include #include "common.h" #include "terminal.h" @@ -58,31 +57,6 @@ */ #define GUAC_TERMINAL_OK "\x1B[0n" - -/** - * Sends data through STDIN as if typed by the user, using the format - * string given and any args (similar to printf). - */ -static int guac_terminal_respond(guac_terminal* term, const char* format, ...) { - - int written; - - va_list ap; - char buffer[1024]; - - /* Print to buffer */ - va_start(ap, format); - written = vsnprintf(buffer, sizeof(buffer)-1, format, ap); - va_end(ap); - - if (written < 0) - return written; - - /* Write to STDIN */ - return guac_terminal_write_all(term->stdin_pipe_fd[1], buffer, written); - -} - int guac_terminal_echo(guac_terminal* term, char c) { static int bytes_remaining = 0; @@ -140,7 +114,7 @@ int guac_terminal_echo(guac_terminal* term, char c) { /* Enquiry */ case 0x05: - guac_terminal_respond(term, "%s", GUAC_TERMINAL_ANSWERBACK); + guac_terminal_send_string(term, GUAC_TERMINAL_ANSWERBACK); break; /* Bell */ @@ -345,7 +319,7 @@ int guac_terminal_escape(guac_terminal* term, char c) { /* DEC Identify */ case 'Z': - guac_terminal_respond(term, "%s", GUAC_TERMINAL_VT102_ID); + guac_terminal_send_string(term, GUAC_TERMINAL_VT102_ID); term->char_handler = guac_terminal_echo; break; @@ -413,6 +387,7 @@ static bool* __guac_terminal_get_flag(guac_terminal* term, int num, char private else if (private_mode == 0) { switch (num) { + case 4: return &(term->insert_mode); /* DECIM */ case 20: return &(term->automatic_carriage_return); /* LF/NL */ } } @@ -696,7 +671,7 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* c: Identify */ case 'c': if (argv[0] == 0 && private_mode_character == 0) - guac_terminal_respond(term, "%s", GUAC_TERMINAL_VT102_ID); + guac_terminal_send_string(term, GUAC_TERMINAL_VT102_ID); break; /* d: Move cursor, current col */ @@ -810,11 +785,11 @@ int guac_terminal_csi(guac_terminal* term, char c) { /* Device status report */ if (argv[0] == 5 && private_mode_character == 0) - guac_terminal_respond(term, "%s", GUAC_TERMINAL_OK); + guac_terminal_send_string(term, GUAC_TERMINAL_OK); /* Cursor position report */ else if (argv[0] == 6 && private_mode_character == 0) - guac_terminal_respond(term, "\x1B[%i;%iR", term->cursor_row+1, term->cursor_col+1); + guac_terminal_sendf(term, "\x1B[%i;%iR", term->cursor_row+1, term->cursor_col+1); break; From cf8ec8dbc24af6ebc0fd50914405a240c022fbfe Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 25 May 2013 23:05:58 -0700 Subject: [PATCH 160/169] Properly cleanup SSH sessions. --- protocols/ssh/src/guac_handlers.c | 12 ++++++++++++ protocols/ssh/src/ssh_client.c | 17 ++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index 99896887..de658067 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -49,6 +49,8 @@ #include #include +#include + #include "guac_handlers.h" #include "client.h" #include "common.h" @@ -383,10 +385,20 @@ int ssh_guac_client_free_handler(guac_client* client) { ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data; + /* Close SSH channel */ + ssh_channel_close(guac_client_data->term_channel); + ssh_channel_send_eof(guac_client_data->term_channel); + /* Free terminal */ guac_terminal_free(guac_client_data->term); pthread_join(guac_client_data->client_thread, NULL); + /* Free channels */ + ssh_channel_free(guac_client_data->term_channel); + + /* Free session */ + ssh_free(guac_client_data->session); + /* Free clipboard data */ free(guac_client_data->clipboard_data); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 5f304a04..a0fa9810 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -44,6 +44,8 @@ #include #include +#include + #include "client.h" #include "common.h" @@ -223,18 +225,15 @@ void* ssh_client_thread(void* data) { if ((bytes_read = channel_read(client_data->term_channel, buffer, sizeof(buffer), 0)) == SSH_AGAIN) continue; - if (bytes_read > 0) - guac_terminal_write_all(stdout_fd, buffer, bytes_read); + /* Attempt to write data received. Exit on failure. */ + if (bytes_read > 0) { + int written = guac_terminal_write_all(stdout_fd, buffer, bytes_read); + if (written < 0) + break; + } } - /* Notify on error */ - if (bytes_read < 0) { - guac_protocol_send_error(socket, "Error reading data."); - guac_socket_flush(socket); - return NULL; - } - /* Wait for input thread to die */ pthread_join(input_thread, NULL); From 6d33be152cf174debf20f248417f09c8e1b92100 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 25 May 2013 23:15:55 -0700 Subject: [PATCH 161/169] Fix prompt() handling of backspace/enter. Format name based on username and hostname. Add port parameter (not yet used). Determine parameter indices based on enum. --- protocols/ssh/src/client.c | 21 +++++++++++++++------ protocols/ssh/src/ssh_client.c | 13 ++++++++++--- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index 8f680ac0..76697e10 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -56,11 +56,20 @@ /* Client plugin arguments */ const char* GUAC_CLIENT_ARGS[] = { "hostname", + "port", "username", "password", NULL }; +enum __SSH_ARGS_IDX { + IDX_HOSTNAME, + IDX_PORT, + IDX_USERNAME, + IDX_PASSWORD, + SSH_ARGS_COUNT +}; + int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket* socket = client->socket; @@ -77,15 +86,15 @@ int guac_client_init(guac_client* client, int argc, char** argv) { client_data->clipboard_data = NULL; client_data->term_channel = NULL; - if (argc != 3) { + if (argc != SSH_ARGS_COUNT) { guac_client_log_error(client, "Wrong number of arguments"); return -1; } /* Read parameters */ - strcpy(client_data->hostname, argv[0]); - strcpy(client_data->username, argv[1]); - strcpy(client_data->password, argv[2]); + strcpy(client_data->hostname, argv[IDX_HOSTNAME]); + strcpy(client_data->username, argv[IDX_USERNAME]); + strcpy(client_data->password, argv[IDX_PASSWORD]); /* Set up I-bar pointer */ client_data->ibar_cursor = guac_ssh_create_ibar(client); @@ -93,8 +102,8 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Set up blank pointer */ client_data->blank_cursor = guac_ssh_create_blank(client); - /* Send name and dimensions */ - guac_protocol_send_name(socket, "Terminal"); + /* Send initial name */ + guac_protocol_send_name(socket, client_data->hostname); /* Initialize pointer */ client_data->current_cursor = client_data->blank_cursor; diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index a0fa9810..960eedec 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -74,7 +74,7 @@ static char* prompt(guac_client* client, const char* title, char* str, int size, while (pos < size && read(stdin_fd, &in_byte, 1) == 1) { /* Backspace */ - if (in_byte == 0x08) { + if (in_byte == 0x7F) { if (pos > 0) { guac_terminal_write_all(stdout_fd, "\b \b", 3); @@ -82,8 +82,8 @@ static char* prompt(guac_client* client, const char* title, char* str, int size, } } - /* Newline (end of input */ - else if (in_byte == 0x0A) { + /* CR (end of input */ + else if (in_byte == 0x0D) { guac_terminal_write_all(stdout_fd, "\r\n", 2); break; } @@ -131,6 +131,8 @@ void* ssh_client_thread(void* data) { guac_client* client = (guac_client*) data; ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data; + char name[1024]; + guac_socket* socket = client->socket; char buffer[8192]; int bytes_read = -1234; @@ -144,11 +146,16 @@ void* ssh_client_thread(void* data) { prompt(client, "Login as: ", client_data->username, sizeof(client_data->username), true) == NULL) return NULL; + /* Send new name */ + snprintf(name, sizeof(name)-1, "%s@%s", client_data->username, client_data->hostname); + guac_protocol_send_name(socket, name); + /* Get password */ if (client_data->password[0] == 0 && prompt(client, "Password: ", client_data->password, sizeof(client_data->password), false) == NULL) return NULL; + /* Clear screen */ guac_terminal_write_all(stdout_fd, "\x1B[H\x1B[J", 6); From 2a6de3aaed6ee6ce8e0a3b6439f4b24626feab1d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 25 May 2013 23:50:13 -0700 Subject: [PATCH 162/169] Add port and font options. --- protocols/ssh/include/client.h | 4 ++ protocols/ssh/include/display.h | 4 +- protocols/ssh/include/terminal.h | 1 + protocols/ssh/src/client.c | 65 ++++++++++++++++++++++++++++--- protocols/ssh/src/display.c | 12 +++--- protocols/ssh/src/guac_handlers.c | 6 ++- protocols/ssh/src/ssh_client.c | 1 + protocols/ssh/src/terminal.c | 2 + 8 files changed, 81 insertions(+), 14 deletions(-) diff --git a/protocols/ssh/include/client.h b/protocols/ssh/include/client.h index 2a38c1c6..09cffac5 100644 --- a/protocols/ssh/include/client.h +++ b/protocols/ssh/include/client.h @@ -51,9 +51,13 @@ typedef struct ssh_guac_client_data { char hostname[1024]; + int port; char username[1024]; char password[1024]; + char font_name[1024]; + int font_size; + pthread_t client_thread; ssh_session session; diff --git a/protocols/ssh/include/display.h b/protocols/ssh/include/display.h index e5a2568e..c0f5c520 100644 --- a/protocols/ssh/include/display.h +++ b/protocols/ssh/include/display.h @@ -237,7 +237,9 @@ typedef struct guac_terminal_display { * Allocates a new display having the given default foreground and background * colors. */ -guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int foreground, int background); +guac_terminal_display* guac_terminal_display_alloc(guac_client* client, + const char* font_name, int font_size, + int foreground, int background); /** * Frees the given display. diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index ac50b616..2208aa78 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -240,6 +240,7 @@ struct guac_terminal { * rendering to the given client. */ guac_terminal* guac_terminal_create(guac_client* client, + const char* font_name, int font_size, int width, int height); /** diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index 76697e10..b23dbdc5 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -53,20 +53,53 @@ #include "ibar.h" #include "ssh_client.h" +#define GUAC_SSH_DEFAULT_FONT_NAME "monospace" +#define GUAC_SSH_DEFAULT_FONT_SIZE 12 +#define GUAC_SSH_DEFAULT_PORT 22 + /* Client plugin arguments */ const char* GUAC_CLIENT_ARGS[] = { "hostname", "port", "username", "password", + "font-name", + "font-size", NULL }; enum __SSH_ARGS_IDX { + + /** + * The hostname to connect to. Required. + */ IDX_HOSTNAME, + + /** + * The port to connect to. Optional. + */ IDX_PORT, + + /** + * The name of the user to login as. Optional. + */ IDX_USERNAME, + + /** + * The password to use when logging in. Optional. + */ IDX_PASSWORD, + + /** + * The name of the font to use within the terminal. + */ + IDX_FONT_NAME, + + /** + * The size of the font to use within the terminal, in points. + */ + IDX_FONT_SIZE, + SSH_ARGS_COUNT }; @@ -75,12 +108,9 @@ int guac_client_init(guac_client* client, int argc, char** argv) { guac_socket* socket = client->socket; ssh_guac_client_data* client_data = malloc(sizeof(ssh_guac_client_data)); - guac_terminal* term = guac_terminal_create(client, - client->info.optimal_width, client->info.optimal_height); /* Init client data */ client->data = client_data; - client_data->term = term; client_data->mod_alt = 0; client_data->mod_ctrl = 0; client_data->clipboard_data = NULL; @@ -92,9 +122,32 @@ int guac_client_init(guac_client* client, int argc, char** argv) { } /* Read parameters */ - strcpy(client_data->hostname, argv[IDX_HOSTNAME]); - strcpy(client_data->username, argv[IDX_USERNAME]); - strcpy(client_data->password, argv[IDX_PASSWORD]); + strcpy(client_data->hostname, argv[IDX_HOSTNAME]); + strcpy(client_data->username, argv[IDX_USERNAME]); + strcpy(client_data->password, argv[IDX_PASSWORD]); + + /* Read font name */ + if (argv[IDX_FONT_NAME][0] != 0) + strcpy(client_data->font_name, argv[IDX_FONT_NAME]); + else + strcpy(client_data->font_name, GUAC_SSH_DEFAULT_FONT_NAME ); + + /* Read font size */ + if (argv[IDX_FONT_SIZE][0] != 0) + client_data->font_size = atoi(argv[IDX_FONT_SIZE]); + else + client_data->font_size = GUAC_SSH_DEFAULT_FONT_SIZE; + + /* Read port */ + if (argv[IDX_PORT][0] != 0) + client_data->port = atoi(argv[IDX_PORT]); + else + client_data->port = GUAC_SSH_DEFAULT_PORT; + + /* Create terminal */ + client_data->term = guac_terminal_create(client, + client_data->font_name, client_data->font_size, + client->info.optimal_width, client->info.optimal_height); /* Set up I-bar pointer */ client_data->ibar_cursor = guac_ssh_create_ibar(client); diff --git a/protocols/ssh/src/display.c b/protocols/ssh/src/display.c index 11d98e03..5cd4c2a5 100644 --- a/protocols/ssh/src/display.c +++ b/protocols/ssh/src/display.c @@ -339,7 +339,9 @@ int __guac_terminal_set(guac_terminal_display* display, int row, int col, int co } -guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int foreground, int background) { +guac_terminal_display* guac_terminal_display_alloc(guac_client* client, + const char* font_name, int font_size, + int foreground, int background) { PangoFontMap* font_map; PangoFont* font; @@ -358,22 +360,22 @@ guac_terminal_display* guac_terminal_display_alloc(guac_client* client, int fore /* Get font */ display->font_desc = pango_font_description_new(); - pango_font_description_set_family(display->font_desc, "monospace"); + pango_font_description_set_family(display->font_desc, font_name); pango_font_description_set_weight(display->font_desc, PANGO_WEIGHT_NORMAL); - pango_font_description_set_size(display->font_desc, 12*PANGO_SCALE); + pango_font_description_set_size(display->font_desc, font_size*PANGO_SCALE); font_map = pango_cairo_font_map_get_default(); context = pango_font_map_create_context(font_map); font = pango_font_map_load_font(font_map, context, display->font_desc); if (font == NULL) { - guac_client_log_error(display->client, "Unable to get font."); + guac_client_log_error(display->client, "Unable to get font \"%s\"", font_name); return NULL; } metrics = pango_font_get_metrics(font, NULL); if (metrics == NULL) { - guac_client_log_error(display->client, "Unable to get font metrics."); + guac_client_log_error(display->client, "Unable to get font metrics for font \"%s\"", font_name); return NULL; } diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index de658067..d57c7251 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -386,8 +386,10 @@ int ssh_guac_client_free_handler(guac_client* client) { ssh_guac_client_data* guac_client_data = (ssh_guac_client_data*) client->data; /* Close SSH channel */ - ssh_channel_close(guac_client_data->term_channel); - ssh_channel_send_eof(guac_client_data->term_channel); + if (guac_client_data->term_channel != NULL) { + ssh_channel_close(guac_client_data->term_channel); + ssh_channel_send_eof(guac_client_data->term_channel); + } /* Free terminal */ guac_terminal_free(guac_client_data->term); diff --git a/protocols/ssh/src/ssh_client.c b/protocols/ssh/src/ssh_client.c index 960eedec..3d352d29 100644 --- a/protocols/ssh/src/ssh_client.c +++ b/protocols/ssh/src/ssh_client.c @@ -169,6 +169,7 @@ void* ssh_client_thread(void* data) { /* Set session options */ ssh_options_set(client_data->session, SSH_OPTIONS_HOST, client_data->hostname); + ssh_options_set(client_data->session, SSH_OPTIONS_PORT, &(client_data->port)); ssh_options_set(client_data->session, SSH_OPTIONS_USER, client_data->username); /* Connect */ diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index f7bd7fc0..fc3c50b1 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -90,6 +90,7 @@ void guac_terminal_reset(guac_terminal* term) { } guac_terminal* guac_terminal_create(guac_client* client, + const char* font_name, int font_size, int width, int height) { guac_terminal_char default_char = { @@ -110,6 +111,7 @@ guac_terminal* guac_terminal_create(guac_client* client, /* Init display */ term->display = guac_terminal_display_alloc(client, + font_name, font_size, default_char.attributes.foreground, default_char.attributes.background); From 2f7e0eec41dcd9c51408ff28e9c4302bf956c01b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 25 May 2013 23:56:59 -0700 Subject: [PATCH 163/169] Add comments to client data. --- protocols/ssh/include/client.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/protocols/ssh/include/client.h b/protocols/ssh/include/client.h index 09cffac5..a0fc5198 100644 --- a/protocols/ssh/include/client.h +++ b/protocols/ssh/include/client.h @@ -50,17 +50,49 @@ */ typedef struct ssh_guac_client_data { + /** + * The hostname of the SSH server to connect to. + */ char hostname[1024]; + + /** + * The port of the SSH server to connect to. + */ int port; + + /** + * The name of the user to login as. + */ char username[1024]; + + /** + * The password to give when authenticating. + */ char password[1024]; + /** + * The name of the font to use for display rendering. + */ char font_name[1024]; + + /** + * The size of the font to use, in points. + */ int font_size; + /** + * The SSH client thread. + */ pthread_t client_thread; + /** + * SSH session, used by the SSH client thread. + */ ssh_session session; + + /** + * SSH terminal channel, used by the SSH client thread. + */ ssh_channel term_channel; /** From 417642eb8ea8397fdb62723d28e3c4222b7e5098 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 26 May 2013 00:01:47 -0700 Subject: [PATCH 164/169] Implement insert mode. --- protocols/ssh/src/terminal_handlers.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 002f7981..47bfa87c 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -203,6 +203,11 @@ int guac_terminal_echo(guac_terminal* term, char c) { } + /* If insert mode, shift other characters right by 1 */ + if (term->insert_mode) + guac_terminal_copy_columns(term, term->cursor_row, + term->cursor_col, term->term_width-2, 1); + /* Write character */ guac_terminal_set(term, term->cursor_row, From d583dbb9906fb66a69a6eaaa8bbacc0f2a13a80d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 26 May 2013 00:17:31 -0700 Subject: [PATCH 165/169] Add keyboard shortcuts for paste and scrolling. Allow middle click for paste. --- protocols/ssh/include/client.h | 5 ++++ protocols/ssh/src/client.c | 5 ++-- protocols/ssh/src/guac_handlers.c | 46 +++++++++++++++++++++++++------ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/protocols/ssh/include/client.h b/protocols/ssh/include/client.h index a0fc5198..08437d03 100644 --- a/protocols/ssh/include/client.h +++ b/protocols/ssh/include/client.h @@ -115,6 +115,11 @@ typedef struct ssh_guac_client_data { */ int mod_ctrl; + /** + * Whether the shift key is currently being held down. + */ + int mod_shift; + /** * The current mouse button state. */ diff --git a/protocols/ssh/src/client.c b/protocols/ssh/src/client.c index b23dbdc5..4a0e6457 100644 --- a/protocols/ssh/src/client.c +++ b/protocols/ssh/src/client.c @@ -111,8 +111,9 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Init client data */ client->data = client_data; - client_data->mod_alt = 0; - client_data->mod_ctrl = 0; + client_data->mod_alt = + client_data->mod_ctrl = + client_data->mod_shift = 0; client_data->clipboard_data = NULL; client_data->term_channel = NULL; diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index d57c7251..bbe741b8 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -154,15 +154,12 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) { pthread_mutex_unlock(&(term->lock)); } - /* Paste contents of clipboard on right mouse button up */ - if ((released_mask & GUAC_CLIENT_MOUSE_RIGHT) - && client_data->clipboard_data != NULL) { - - int length = strlen(client_data->clipboard_data); - if (length) - return guac_terminal_send_data(term, - client_data->clipboard_data, length); - + /* Paste contents of clipboard on right or middle mouse button up */ + if ((released_mask & GUAC_CLIENT_MOUSE_RIGHT) || (released_mask & GUAC_CLIENT_MOUSE_MIDDLE)) { + if (client_data->clipboard_data != NULL) + return guac_terminal_send_string(term, client_data->clipboard_data); + else + return 0; } /* If text selected, change state based on left mouse mouse button */ @@ -246,10 +243,41 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { client_data->mod_ctrl = pressed; else if (keysym == 0xFFE9) client_data->mod_alt = pressed; + else if (keysym == 0xFFE1) + client_data->mod_shift = pressed; /* If key pressed */ else if (pressed) { + /* Ctrl+Shift+V shortcut for paste */ + if (keysym == 'V' && client_data->mod_ctrl) { + if (client_data->clipboard_data != NULL) + return guac_terminal_send_string(term, client_data->clipboard_data); + else + return 0; + } + + /* Shift+PgUp / Shift+PgDown shortcuts for scrolling */ + if (client_data->mod_shift) { + + /* Page up */ + if (keysym == 0xFF55) { + pthread_mutex_lock(&(term->lock)); + guac_terminal_scroll_display_up(term, term->term_height); + pthread_mutex_unlock(&(term->lock)); + return 0; + } + + /* Page down */ + if (keysym == 0xFF56) { + pthread_mutex_lock(&(term->lock)); + guac_terminal_scroll_display_down(term, term->term_height); + pthread_mutex_unlock(&(term->lock)); + return 0; + } + + } + /* Reset scroll */ if (term->scroll_offset != 0) { pthread_mutex_lock(&(term->lock)); From 8019063214894514e7dfeb7ee952c1e67790e18d Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 26 May 2013 00:49:06 -0700 Subject: [PATCH 166/169] Fix home/end and function keys. --- protocols/ssh/src/guac_handlers.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index bbe741b8..639dffb0 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -327,7 +327,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { if (keysym == 0xFF0D) return guac_terminal_send_string(term, "\x0D"); /* Enter */ if (keysym == 0xFF1B) return guac_terminal_send_string(term, "\x1B"); /* Esc */ - if (keysym == 0xFF50) return guac_terminal_send_string(term, "\x1BOH"); /* Home */ + if (keysym == 0xFF50) return guac_terminal_send_string(term, "\x1B[1~"); /* Home */ /* Arrow keys w/ application cursor */ if (term->application_cursor_keys) { @@ -343,18 +343,18 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) { if (keysym == 0xFF54) return guac_terminal_send_string(term, "\x1B[B"); /* Down */ } - if (keysym == 0xFF55) return guac_terminal_send_string(term, "\x1B[5;3~"); /* Page up */ - if (keysym == 0xFF56) return guac_terminal_send_string(term, "\x1B[6;3~"); /* Page down */ - if (keysym == 0xFF57) return guac_terminal_send_string(term, "\x1BOF"); /* End */ + if (keysym == 0xFF55) return guac_terminal_send_string(term, "\x1B[5~"); /* Page up */ + if (keysym == 0xFF56) return guac_terminal_send_string(term, "\x1B[6~"); /* Page down */ + if (keysym == 0xFF57) return guac_terminal_send_string(term, "\x1B[4~"); /* End */ if (keysym == 0xFF63) return guac_terminal_send_string(term, "\x1B[2~"); /* Insert */ - if (keysym == 0xFFBE) return guac_terminal_send_string(term, "\x1BOP"); /* F1 */ - if (keysym == 0xFFBF) return guac_terminal_send_string(term, "\x1BOQ"); /* F2 */ - if (keysym == 0xFFC0) return guac_terminal_send_string(term, "\x1BOR"); /* F3 */ - if (keysym == 0xFFC1) return guac_terminal_send_string(term, "\x1BOS"); /* F4 */ + if (keysym == 0xFFBE) return guac_terminal_send_string(term, "\x1B[[A"); /* F1 */ + if (keysym == 0xFFBF) return guac_terminal_send_string(term, "\x1B[[B"); /* F2 */ + if (keysym == 0xFFC0) return guac_terminal_send_string(term, "\x1B[[C"); /* F3 */ + if (keysym == 0xFFC1) return guac_terminal_send_string(term, "\x1B[[D"); /* F4 */ + if (keysym == 0xFFC2) return guac_terminal_send_string(term, "\x1B[[E"); /* F5 */ - if (keysym == 0xFFC2) return guac_terminal_send_string(term, "\x1B[15~"); /* F5 */ if (keysym == 0xFFC3) return guac_terminal_send_string(term, "\x1B[17~"); /* F6 */ if (keysym == 0xFFC4) return guac_terminal_send_string(term, "\x1B[18~"); /* F7 */ if (keysym == 0xFFC5) return guac_terminal_send_string(term, "\x1B[19~"); /* F8 */ From 8f0be20b35b47bc6f1a16e87505bc15578cb05a7 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 26 May 2013 01:49:47 -0700 Subject: [PATCH 167/169] Implement tab setting/resetting. --- protocols/ssh/include/terminal.h | 41 +++++++++++++++++ protocols/ssh/src/terminal.c | 66 +++++++++++++++++++++++++++ protocols/ssh/src/terminal_handlers.c | 23 ++++++++++ 3 files changed, 130 insertions(+) diff --git a/protocols/ssh/include/terminal.h b/protocols/ssh/include/terminal.h index 2208aa78..569cce46 100644 --- a/protocols/ssh/include/terminal.h +++ b/protocols/ssh/include/terminal.h @@ -47,6 +47,14 @@ #include "display.h" #include "buffer.h" +/** + * The maximum number of custom tab stops. + */ +#define GUAC_TERMINAL_MAX_TABS 16 + +/** + * The number of rows to scroll per scroll wheel event. + */ #define GUAC_SSH_WHEEL_SCROLL_AMOUNT 3 typedef struct guac_terminal guac_terminal; @@ -179,6 +187,18 @@ struct guac_terminal { */ guac_terminal_buffer* buffer; + /** + * Automatically place a tabstop every N characters. If zero, then no + * tabstops exist automatically. + */ + int tab_interval; + + /** + * Array of all tabs set. Each entry is the column number of a tab + 1, + * or 0 if that tab cell is unset. + */ + int custom_tabs[GUAC_TERMINAL_MAX_TABS]; + /** * Array of arrays of mapped characters, where the character N is located at the N-32 * position within the array. Each element in a contained array is the corresponding Unicode @@ -374,5 +394,26 @@ int guac_terminal_send_string(guac_terminal* term, const char* data); */ int guac_terminal_sendf(guac_terminal* term, const char* format, ...); +/** + * Sets a tabstop in the given column. + */ +void guac_terminal_set_tab(guac_terminal* term, int column); + +/** + * Removes the tabstop at the given column. + */ +void guac_terminal_unset_tab(guac_terminal* term, int column); + +/** + * Removes all tabstops. + */ +void guac_terminal_clear_tabs(guac_terminal* term); + +/** + * Given a column within the given terminal, returns the location of the + * next tabstop (or the rightmost character, if no more tabstops exist). + */ +int guac_terminal_next_tab(guac_terminal* term, int column); + #endif diff --git a/protocols/ssh/src/terminal.c b/protocols/ssh/src/terminal.c index fc3c50b1..56a9afa6 100644 --- a/protocols/ssh/src/terminal.c +++ b/protocols/ssh/src/terminal.c @@ -83,6 +83,10 @@ void guac_terminal_reset(guac_terminal* term) { term->automatic_carriage_return = false; term->insert_mode = false; + /* Reset tabs */ + term->tab_interval = 8; + memset(term->custom_tabs, 0, sizeof(term->custom_tabs)); + /* Clear terminal */ for (row=0; rowterm_height; row++) guac_terminal_set_columns(term, row, 0, term->term_width, &(term->default_char)); @@ -787,4 +791,66 @@ int guac_terminal_sendf(guac_terminal* term, const char* format, ...) { } +void guac_terminal_set_tab(guac_terminal* term, int column) { + + int i; + + /* Search for available space, set if available */ + for (i=0; icustom_tabs[i] == 0) { + term->custom_tabs[i] = column+1; + break; + } + + } + +} + +void guac_terminal_unset_tab(guac_terminal* term, int column) { + + int i; + + /* Search for given tab, unset if found */ + for (i=0; icustom_tabs[i] == column+1) { + term->custom_tabs[i] = 0; + break; + } + + } + +} + +void guac_terminal_clear_tabs(guac_terminal* term) { + term->tab_interval = 0; + memset(term->custom_tabs, 0, sizeof(term->custom_tabs)); +} + +int guac_terminal_next_tab(guac_terminal* term, int column) { + + int i; + + /* Determine tab stop from interval */ + int tabstop; + if (term->tab_interval != 0) + tabstop = (column / term->tab_interval + 1) * term->tab_interval; + else + tabstop = term->term_width - 1; + + /* Walk custom tabs, trying to find an earlier occurrence */ + for (i=0; icustom_tabs[i] - 1; + if (custom_tabstop != -1 && custom_tabstop > column && custom_tabstop < tabstop) + tabstop = custom_tabstop; + + } + + return tabstop; +} + diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index 47bfa87c..bd18adb2 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -127,6 +127,11 @@ int guac_terminal_echo(guac_terminal* term, char c) { term->cursor_col--; break; + /* Tab */ + case 0x09: + term->cursor_col = guac_terminal_next_tab(term, term->cursor_col); + break; + /* Line feed / VT / FF */ case '\n': case 0x0B: /* VT */ @@ -304,6 +309,11 @@ int guac_terminal_escape(guac_terminal* term, char c) { term->char_handler = guac_terminal_echo; break; + /* Set Tab (HTS) */ + case 'H': + guac_terminal_set_tab(term, term->cursor_col); + break; + /* Reverse Linefeed */ case 'M': @@ -685,6 +695,19 @@ int guac_terminal_csi(guac_terminal* term, char c) { term->cursor_row = row; break; + /* g: Clear tab */ + case 'g': + + /* Clear tab at current location */ + if (argv[0] == 0) + guac_terminal_unset_tab(term, term->cursor_col); + + /* Clear all tabs */ + else if (argv[0] == 3) + guac_terminal_clear_tabs(term); + + break; + /* h: Set Mode */ case 'h': From 48bd2148689817a18ad5c4a3cda1de5dbe0ecb99 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 26 May 2013 02:03:54 -0700 Subject: [PATCH 168/169] Remove unnecessary logging. --- protocols/ssh/src/terminal_handlers.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/protocols/ssh/src/terminal_handlers.c b/protocols/ssh/src/terminal_handlers.c index bd18adb2..e555b003 100644 --- a/protocols/ssh/src/terminal_handlers.c +++ b/protocols/ssh/src/terminal_handlers.c @@ -716,11 +716,6 @@ int guac_terminal_csi(guac_terminal* term, char c) { if (flag != NULL) *flag = true; - else - guac_client_log_info(term->client, - "Unhandled mode set: mode=%i, private_mode_character=0x%0x", - argv[0], private_mode_character); - break; /* l: Reset Mode */ @@ -731,11 +726,6 @@ int guac_terminal_csi(guac_terminal* term, char c) { if (flag != NULL) *flag = false; - else - guac_client_log_info(term->client, - "Unhandled mode reset: mode=%i, private_mode_character=0x%0x", - argv[0], private_mode_character); - break; /* m: Set graphics rendition */ @@ -800,10 +790,6 @@ int guac_terminal_csi(guac_terminal* term, char c) { term->current_attributes.background = term->default_char.attributes.background; - else - guac_client_log_info(term->client, - "Unhandled graphics rendition: %i", value); - } break; From d07830ea33965182dc75d3ef3c7bcd04a51b609f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 31 May 2013 09:54:06 -0700 Subject: [PATCH 169/169] Implement compatibility with older libssh. --- protocols/ssh/Makefile.am | 1 + protocols/ssh/configure.in | 5 +++ protocols/ssh/include/libssh_compat.h | 57 +++++++++++++++++++++++++++ protocols/ssh/src/guac_handlers.c | 1 + 4 files changed, 64 insertions(+) create mode 100644 protocols/ssh/include/libssh_compat.h diff --git a/protocols/ssh/Makefile.am b/protocols/ssh/Makefile.am index de2a9e16..850b3a25 100644 --- a/protocols/ssh/Makefile.am +++ b/protocols/ssh/Makefile.am @@ -64,6 +64,7 @@ noinst_HEADERS = \ include/display.h \ include/guac_handlers.h \ include/ibar.h \ + include/libssh_compat.h \ include/ssh_client.h \ include/terminal.h \ include/terminal_handlers.h \ diff --git a/protocols/ssh/configure.in b/protocols/ssh/configure.in index e64be7f5..65a164af 100644 --- a/protocols/ssh/configure.in +++ b/protocols/ssh/configure.in @@ -51,6 +51,11 @@ AC_CHECK_LIB([pthread], [pthread_create]) PKG_CHECK_MODULES([PANGO], pango); PKG_CHECK_MODULES([PANGOCAIRO], pangocairo); +# Check for SSH functions +AC_CHECK_FUNC(ssh_channel_close, AC_DEFINE(HAVE_SSH_CHANNEL_CLOSE)) +AC_CHECK_FUNC(ssh_channel_send_eof, AC_DEFINE(HAVE_SSH_CHANNEL_SEND_EOF)) +AC_CHECK_FUNC(ssh_channel_free, AC_DEFINE(HAVE_SSH_CHANNEL_FREE)) + # Checks for header files. AC_CHECK_HEADERS([guacamole/client.h guacamole/guacio.h guacamole/protocol.h]) diff --git a/protocols/ssh/include/libssh_compat.h b/protocols/ssh/include/libssh_compat.h new file mode 100644 index 00000000..82205d7a --- /dev/null +++ b/protocols/ssh/include/libssh_compat.h @@ -0,0 +1,57 @@ + +/* ***** 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-ssh. + * + * 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 _SSH_GUAC_LIBSSH_COMPAT_H +#define _SSH_GUAC_LIBSSH_COMPAT_H + +/* Define ssh_channel_close() if undefined */ +#ifndef HAVE_SSH_CHANNEL_CLOSE +#define ssh_channel_close channel_close +#endif + +/* Define ssh_channel_send_eof() if undefined */ +#ifndef HAVE_SSH_CHANNEL_SEND_EOF +#define ssh_channel_send_eof channel_send_eof +#endif + +/* Define ssh_channel_free() if undefined */ +#ifndef HAVE_SSH_CHANNEL_FREE +#define ssh_channel_free channel_free +#endif + +#endif + diff --git a/protocols/ssh/src/guac_handlers.c b/protocols/ssh/src/guac_handlers.c index 639dffb0..50778afc 100644 --- a/protocols/ssh/src/guac_handlers.c +++ b/protocols/ssh/src/guac_handlers.c @@ -50,6 +50,7 @@ #include #include +#include "libssh_compat.h" #include "guac_handlers.h" #include "client.h"