diff options
author | Kenny Root <kenny@the-b.org> | 2007-11-21 05:32:39 +0000 |
---|---|---|
committer | Kenny Root <kenny@the-b.org> | 2007-11-21 05:32:39 +0000 |
commit | d6764b7763e7857273b65048b4eb37a3c65efade (patch) | |
tree | 4e84d4d9327d8a8de685fd0f181208c82e1cb9ad | |
parent | 4d94be3c05a341c18396f45945d86d61ca86dec4 (diff) | |
download | connectbot-d6764b7763e7857273b65048b4eb37a3c65efade.tar.gz connectbot-d6764b7763e7857273b65048b4eb37a3c65efade.tar.bz2 connectbot-d6764b7763e7857273b65048b4eb37a3c65efade.zip |
Merging back in the jcterm branch
25 files changed, 7739 insertions, 356 deletions
@@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/res/layout/secure_shell.xml b/res/layout/secure_shell.xml index 0f47c70..9af5081 100644 --- a/res/layout/secure_shell.xml +++ b/res/layout/secure_shell.xml @@ -3,10 +3,16 @@ android:layout_width="fill_parent" android:layout_height="fill_parent"> + <org.theb.ssh.TerminalView id="@+id/terminal" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:textSize="8sp" + android:typeface="monospace" /> +<!-- <TextView id="@+id/output" android:layout_width="fill_parent" android:layout_height="fill_parent" android:textSize="8sp" android:typeface="monospace" /> - + --> </RelativeLayout> diff --git a/src/com/jcraft/jcterm/Emulator.java b/src/com/jcraft/jcterm/Emulator.java new file mode 100644 index 0000000..7723738 --- /dev/null +++ b/src/com/jcraft/jcterm/Emulator.java @@ -0,0 +1,416 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* JCTerm + * Copyright (C) 2002,2007 ymnk, JCraft,Inc. + * + * Written by: ymnk<ymnk@jcaft.com> + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jcterm; + +import java.io.InputStream; +import java.io.IOException; + +public abstract class Emulator{ + Term term=null; + InputStream in=null; + + public Emulator(Term term, InputStream in){ + this.term=term; + this.in=in; + } + + public abstract void start(); + + public abstract byte[] getCodeENTER(); + + public abstract byte[] getCodeUP(); + + public abstract byte[] getCodeDOWN(); + + public abstract byte[] getCodeRIGHT(); + + public abstract byte[] getCodeLEFT(); + + public abstract byte[] getCodeF1(); + + public abstract byte[] getCodeF2(); + + public abstract byte[] getCodeF3(); + + public abstract byte[] getCodeF4(); + + public abstract byte[] getCodeF5(); + + public abstract byte[] getCodeF6(); + + public abstract byte[] getCodeF7(); + + public abstract byte[] getCodeF8(); + + public abstract byte[] getCodeF9(); + + public abstract byte[] getCodeF10(); + + public abstract byte[] getCodeTAB(); + + public void reset(){ + term_width=term.getColumnCount(); + term_height=term.getRowCount(); + char_width=term.getCharWidth(); + char_height=term.getCharHeight(); + region_y1=1; + region_y2=term_height; + } + + byte[] buf=new byte[1024]; + int bufs=0; + int buflen=0; + + byte getChar() throws java.io.IOException{ + if(buflen==0){ + fillBuf(); + } + buflen--; + + // System.out.println("getChar: "+new Character((char)buf[bufs])+"["+Integer.toHexString(buf[bufs]&0xff)+"]"); + + return buf[bufs++]; + } + + void fillBuf() throws java.io.IOException{ + buflen=bufs=0; + buflen=in.read(buf, bufs, buf.length-bufs); + /* + System.out.println("fillBuf: "); + for(int i=0; i<buflen; i++){ + byte b=buf[i]; + System.out.print(new Character((char)b)+"["+Integer.toHexString(b&0xff)+"], "); + } + System.out.println(""); + */ + if(buflen<=0){ + buflen=0; + throw new IOException("fillBuf"); + } + } + + void pushChar(byte foo) throws java.io.IOException{ + //System.out.println("pushChar: "+new Character((char)foo)+"["+Integer.toHexString(foo&0xff)+"]"); + buflen++; + buf[--bufs]=foo; + } + + int getASCII(int len) throws java.io.IOException{ + //System.out.println("bufs="+bufs+", buflen="+buflen+", len="+len); + if(buflen==0){ + fillBuf(); + } + if(len>buflen) + len=buflen; + int foo=len; + byte tmp; + while(len>0){ + tmp=buf[bufs++]; + if(0x20<=tmp&&tmp<=0x7f){ + buflen--; + len--; + continue; + } + bufs--; + break; + } + //System.out.println(" return "+(foo-len)); + return foo-len; + } + + protected int term_width=80; + protected int term_height=24; + + protected int x=0; + protected int y=0; + + protected int char_width; + protected int char_height; + + private int region_y2; + private int region_y1; + + protected int tab=8; + + // Reverse scroll + protected void scroll_reverse(){ + term.draw_cursor(); + term.scroll_area(0, (region_y1-1)*char_height, term_width*char_width, + (region_y2-region_y1)*char_height, 0, char_height); + term.clear_area(x, y-char_height, term_width*char_width, y); + term.redraw(0, 0, term_width*char_width, term_height*char_height + -char_height); + //term.setCursor(x, y); + term.draw_cursor(); + } + + // Normal scroll one line + protected void scroll_forward(){ + term.draw_cursor(); + term.scroll_area(0, (region_y1-1)*char_height, term_width*char_width, + (region_y2-region_y1+1)*char_height, 0, -char_height); + term.clear_area(0, region_y2*char_height-char_height, + term_width*char_width, region_y2*char_height); + term.redraw(0, (region_y1-1)*char_height, term_width*char_width, (region_y2 + -region_y1+1) + *char_height); + term.draw_cursor(); + } + + // Save cursor position + protected void save_cursor(){ + // TODO + //System.out.println("save current cursor position"); + } + + // Enable alternate character set + protected void ena_acs(){ + // TODO + //System.out.println("enable alterate char set"); + } + + protected void exit_alt_charset_mode(){ + // TODO + //System.out.println("end alternate character set (P)"); + } + + protected void enter_alt_charset_mode(){ + // TODO + //System.out.println("start alternate character set (P)"); + } + + protected void reset_2string(){ + // TODO + // rs2(reset string) + } + + protected void exit_attribute_mode(){ + // TODO + //System.out.println("turn off all attributes"); + term.resetAllAttributes(); + } + + protected void exit_standout_mode(){ + term.resetAllAttributes(); + } + + protected void exit_underline_mode(){ + // TODO + } + + protected void enter_bold_mode(){ + term.setBold(); + } + + protected void enter_underline_mode(){ + term.setUnderline(); + } + + protected void enter_reverse_mode(){ + term.setReverse(); + } + + protected void change_scroll_region(int y1, int y2){ + region_y1=y1; + region_y2=y2; + } + + protected void cursor_address(int r, int c){ + term.draw_cursor(); + x=(c-1)*char_width; + y=r*char_height; + //System.out.println("setCourosr: "+x+" "+y); + term.setCursor(x, y); + term.draw_cursor(); + } + + protected void parm_down_cursor(int lines){ + term.draw_cursor(); + y+=(lines)*char_height; + term.setCursor(x, y); + term.draw_cursor(); + } + + protected void parm_left_cursor(int chars){ + term.draw_cursor(); + x-=(chars)*char_width; + term.setCursor(x, y); + term.draw_cursor(); + } + + protected void parm_right_cursor(int chars){ + term.draw_cursor(); + x+=(chars)*char_width; + term.setCursor(x, y); + term.draw_cursor(); + } + + protected void clr_eol(){ + term.draw_cursor(); + term.clear_area(x, y-char_height, term_width*char_width, y); + term.redraw(x, y-char_height, (term_width)*char_width-x, char_height); + term.draw_cursor(); + } + + protected void clr_bol(){ + term.draw_cursor(); + term.clear_area(0, y-char_height, x, y); + term.redraw(0, y-char_height, x, char_height); + term.draw_cursor(); + } + + protected void clr_eos(){ + term.draw_cursor(); + term.clear_area(x, y-char_height, term_width*char_width, term_height + *char_height); + term.redraw(x, y-char_height, term_width*char_width-x, term_height + *char_height-y+char_height); + term.draw_cursor(); + } + + protected void parm_up_cursor(int lines){ + term.draw_cursor(); + // x=0; + // y-=char_height; + y-=(lines)*char_height; + term.setCursor(x, y); + term.draw_cursor(); + } + + protected void bell(){ + term.beep(); + } + + protected void tab(){ + term.draw_cursor(); + x=(((x/char_width)/tab+1)*tab*char_width); + if(x>=term_width*char_width){ + x=0; + y+=char_height; + } + term.setCursor(x, y); + term.draw_cursor(); + } + + protected void carriage_return(){ + term.draw_cursor(); + x=0; + term.setCursor(x, y); + term.draw_cursor(); + } + + protected void cursor_left(){ + term.draw_cursor(); + x-=char_width; + if(x<0){ + y-=char_height; + x=term_width*char_width-char_width; + } + term.setCursor(x, y); + term.draw_cursor(); + } + + protected void cursor_down(){ + term.draw_cursor(); + y+=char_height; + term.setCursor(x, y); + term.draw_cursor(); + + check_region(); + } + + private byte[] b2=new byte[2]; + private byte[] b1=new byte[1]; + + protected void draw_text() throws java.io.IOException{ + + int rx; + int ry; + int w; + int h; + + check_region(); + + rx=x; + ry=y; + + byte b=getChar(); + term.draw_cursor(); + //System.out.print(new Character((char)b)+"["+Integer.toHexString(b&0xff)+"]"); + if((b&0x80)!=0){ + term.clear_area(x, y-char_height, x+char_width*2, y); + b2[0]=b; + b2[1]=getChar(); + term.drawString(new String(b2, 0, 2, "EUC-JP"), x, y); + x+=char_width; + x+=char_width; + w=char_width*2; + h=char_height; + } + else{ + pushChar(b); + int foo=getASCII(term_width-(x/char_width)); + if(foo!=0){ + //System.out.println("foo="+foo+" "+x+", "+(y-char_height)+" "+(x+foo*char_width)+" "+y+" "+buf+" "+bufs+" "+b+" "+buf[bufs-foo]); + //System.out.println("foo="+foo+" ["+new String(buf, bufs-foo, foo)); + term.clear_area(x, y-char_height, x+foo*char_width, y); + term.drawBytes(buf, bufs-foo, foo, x, y); + } + else{ + foo=1; + term.clear_area(x, y-char_height, x+foo*char_width, y); + b1[0]=getChar(); + term.drawBytes(b1, 0, foo, x, y); + //System.out.print("["+Integer.toHexString(bar[0]&0xff)+"]"); + } + x+=(char_width*foo); + w=char_width*foo; + h=char_height; + } + term.redraw(rx, ry-char_height, w, h); + term.setCursor(x, y); + term.draw_cursor(); + } + + private void check_region(){ + if(x>=term_width*char_width){ + //System.out.println("!! "+new Character((char)b)+"["+Integer.toHexString(b&0xff)+"]"); + x=0; + y+=char_height; + //System.out.println("@1: ry="+ry); + } + + if(y>region_y2*char_height){ + while(y>region_y2*char_height){ + y-=char_height; + } + term.draw_cursor(); + term.scroll_area(0, region_y1*char_height, term_width*char_width, + (region_y2-region_y1)*char_height, 0, -char_height); + term.clear_area(0, y-char_height, term_width*char_width, y); + term.redraw(0, 0, term_width*char_width, region_y2*char_height); + term.setCursor(x, y); + term.draw_cursor(); + } + } +} diff --git a/src/com/jcraft/jcterm/EmulatorVT100.java b/src/com/jcraft/jcterm/EmulatorVT100.java new file mode 100644 index 0000000..cf2f96a --- /dev/null +++ b/src/com/jcraft/jcterm/EmulatorVT100.java @@ -0,0 +1,634 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* JCTerm + * Copyright (C) 2002,2007 ymnk, JCraft,Inc. + * + * Written by: ymnk<ymnk@jcaft.com> + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jcterm; + +import java.io.InputStream; + +public class EmulatorVT100 extends Emulator{ + + public EmulatorVT100(Term term, InputStream in){ + super(term, in); + } + + public void setInputStream(InputStream in){ + this.in=in; + } + + public void setTerm(Term term){ + this.term=term; + } + + public void start(){ + reset(); + + int[] intarg=new int[10]; + int intargi=0; + + x=0; + y=char_height; + + byte b; + + try{ + while(true){ + + b=getChar(); + + //System.out.println("@0: "+ new Character((char)b)+"["+Integer.toHexString(b&0xff)+"]"); + + //System.out.println("@0: ry="+ry); + + /* + outputs from infocmp on RedHat8.0 + # Reconstructed via infocmp from file: /usr/share/terminfo/v/vt100 + vt100|vt100-am|dec vt100 (w/advanced video), + am, msgr, xenl, xon, + cols#80, it#8, lines#24, vt#3, + acsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + bel=^G, blink=\E[5m$<2>, bold=\E[1m$<2>, + clear=\E[H\E[J$<50>, cr=^M, csr=\E[%i%p1%d;%p2%dr, + cub=\E[%p1%dD, cub1=^H, cud=\E[%p1%dB, cud1=^J, + cuf=\E[%p1%dC, cuf1=\E[C$<2>, + cup=\E[%i%p1%d;%p2%dH$<5>, cuu=\E[%p1%dA, + cuu1=\E[A$<2>, ed=\E[J$<50>, el=\E[K$<3>, el1=\E[1K$<3>, + enacs=\E(B\E)0, home=\E[H, ht=^I, hts=\EH, ind=^J, ka1=\EOq, + ka3=\EOs, kb2=\EOr, kbs=^H, kc1=\EOp, kc3=\EOn, kcub1=\EOD, + kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, kent=\EOM, kf0=\EOy, + kf1=\EOP, kf10=\EOx, kf2=\EOQ, kf3=\EOR, kf4=\EOS, kf5=\EOt, + kf6=\EOu, kf7=\EOv, kf8=\EOl, kf9=\EOw, rc=\E8, + rev=\E[7m$<2>, ri=\EM$<5>, rmacs=^O, rmam=\E[?7l, + rmkx=\E[?1l\E>, rmso=\E[m$<2>, rmul=\E[m$<2>, + rs2=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h, sc=\E7, + sgr=\E[0%?%p1%p6%|%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;$<2>, + sgr0=\E[m\017$<2>, smacs=^N, smam=\E[?7h, smkx=\E[?1h\E=, + smso=\E[7m$<2>, smul=\E[4m$<2>, tbc=\E[3g, + */ + /* + am terminal has automatic margnins + msgr safe to move while in standout mode + xenl newline ignored after 80 cols (concept) + xon terminal uses xon/xoff handshake + cols number of columns in a line + it tabs initially every # spaces + lines number of lines on screen of page + vt virstual terminal number(CB/unix) + acsc graphics charset pairs, based on vt100 + bel bell + blink turn on blinking + bold turn on bold(extra bright) mode + clear clear screen and home cursor(P*) + cr carriage return (P)(P*) + csr change region to line #1 to line #2(P) + cub move #1 characters to the left (P) + cub1 move left one space + cud down #1 lines (P*) + cud1 down one line + cuf move to #1 characters to the right. + cuf1 non-destructive space (move right one space) + cup move to row #1 columns #2 + cuu up #1 lines (P*) + cuu1 up one line + ed clear to end of screen (P*) + el clear to end of line (P) + el1 Clear to begining of line + enacs enable alterate char set + home home cursor (if no cup) + ht tab to next 8-space hardware tab stop + hts set a tab in every row, current columns + ind scroll text up + ka1 upper left of keypad + ka3 upper right of keypad + kb2 center of keypad + kbs backspace key + kc1 lower left of keypad + kc3 lower right of keypad + kcub1 left-arrow key + kcud1 down-arrow key + kcuf1 right-arrow key + kcuu1 up-arrow key + kent enter/sekd key + kf0 F0 function key + kf1 F1 function key + kf10 F10 function key + kf2 F2 function key + kf3 F3 function key + kf4 F4 function key + kf5 F5 function key + kf6 F6 function key + kf7 F7 function key + kf8 F8 function key + kf9 F9 function key + rc restore cursor to position of last save_cursor + rev turn on reverse video mode + ri scroll text down (P) + rmacs end alternate character set + rmam turn off automatic margins + rmkx leave 'keybroad_transmit' mode + rmso exit standout mode + rmul exit underline mode + rs2 reset string + sc save current cursor position (P) + sgr define video attribute #1-#9(PG9) + sgr0 turn off all attributes + smacs start alternate character set (P) + smam turn on automatic margins + smkx enter 'keyborad_transmit' mode + smso begin standout mode + smul begin underline mode + tbc clear all tab stops(P) + */ + if(b==0){ + continue; + } + + if(b==0x1b){ + b=getChar(); + + //System.out.println("@1: "+ new Character((char)b)+"["+Integer.toHexString(b&0xff)+"]"); + + if(b=='M'){ // sr \EM sr scroll text down (P) + scroll_reverse(); + continue; + } + + if(b=='D'){ // sf + scroll_forward(); + continue; + } + + if(b=='7'){ + save_cursor(); + continue; + } + + if(b=='('){ + b=getChar(); + if(b=='B'){ + b=getChar(); + if(b==0x1b){ + b=getChar(); + if(b==')'){ + b=getChar(); + if(b=='0'){ // enacs + ena_acs(); + continue; + } + else{ + pushChar((byte)'0'); + } + } + else{ + pushChar((byte)')'); + } + } + else{ + pushChar((byte)0x1b); + } + } + else{ + pushChar((byte)'B'); + } + } + + if(b=='>'){ + b=getChar(); // 0x1b + b=getChar(); // '[' + b=getChar(); // '?' + b=getChar(); // '3' + b=getChar(); // 'l' + b=getChar(); // 0x1b + b=getChar(); // '[' + b=getChar(); // '?' + b=getChar(); // '4' + b=getChar(); // 'l' + b=getChar(); // 0x1b + b=getChar(); // '[' + b=getChar(); // '?' + b=getChar(); // '5' + b=getChar(); // 'l' + b=getChar(); // 0x1b + b=getChar(); // '[' + b=getChar(); // '?' + b=getChar(); // '7' + b=getChar(); // 'h' + b=getChar(); // 0x1b + b=getChar(); // '[' + b=getChar(); // '?' + b=getChar(); // '8' + b=getChar(); // 'h' + + reset_2string(); + continue; + } + + if(b!='['){ + System.out.print("@11: "+new Character((char)b)+"[" + +Integer.toHexString(b&0xff)+"]"); + pushChar(b); + continue; + } + + //System.out.print("@2: "+ new Character((char)b)+"["+Integer.toHexString(b&0xff)+"]"); + + intargi=0; + intarg[intargi]=0; + int digit=0; + + while(true){ + b=getChar(); + //System.out.print("#"+new Character((char)b)+"["+Integer.toHexString(b&0xff)+"]"); + if(b==';'){ + if(digit>0){ + intargi++; + intarg[intargi]=0; + digit=0; + } + continue; + } + + if('0'<=b&&b<='9'){ + intarg[intargi]=intarg[intargi]*10+(b-'0'); + digit++; + continue; + } + + pushChar(b); + break; + } + + b=getChar(); + + //System.out.print("@4: "+ new Character((char)b)+"["+Integer.toHexString(b&0xff)+"]"); + + if(b=='m'){ + /* + b=getChar(); + if(b=='$'){ + b=getChar(); // < + b=getChar(); // 2 + b=getChar(); // > + } + else{ + pushChar(b); + } + */ + + if(digit==0&&intargi==0){ + b=getChar(); + if(b==0x0f){ // sgr0 + exit_attribute_mode(); + continue; + } + else{ // rmso, rmul + exit_underline_mode(); + exit_standout_mode(); + pushChar(b); + continue; + } + } + + for(int i=0; i<=intargi; i++){ + Object fg=null; + Object bg=null; + Object tmp=null; + + switch(intarg[i]){ + case 0: // Reset all attributes + exit_standout_mode(); + continue; + case 1: // Bright // bold + enter_bold_mode(); + continue; + case 2: // Dim + break; + case 4: // Underline + enter_underline_mode(); + continue; + case 5: // Blink + case 8: // Hidden + break; + case 7: // reverse + enter_reverse_mode(); + continue; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + tmp=term.getColor(intarg[i]-30); + if(tmp!=null) + fg=tmp; + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + tmp=term.getColor(intarg[i]-40); + if(tmp!=null) + bg=tmp; + break; + default: + break; + } + if(fg!=null) + term.setForeGround(fg); + if(bg!=null) + term.setBackGround(bg); + } + //System.out.println("fg: "+fg+" bg: "+bg); + continue; + } + + if(b=='r'){ // csr + change_scroll_region(intarg[0], intarg[1]); + //System.out.println("r: "+region_y1+", "+region_y2+", intargi="+intargi); + continue; + } + + if(b=='H'){ // cup + /* + b=getChar(); + if(b!='$'){ // home + pushChar(b); + } + else{ + b=getChar(); // < + b=getChar(); // 5 + b=getChar(); // > + } + */ + + if(digit==0&&intargi==0){ + intarg[0]=intarg[1]=1; + } + + //System.out.println("H: "+region_y1+", "+region_y2+", intargi="+intargi); + cursor_address(intarg[0], intarg[1]); + continue; + } + + if(b=='B'){ // cud + parm_down_cursor(intarg[0]); + continue; + } + + if(b=='D'){ // cub + parm_left_cursor(intarg[0]); + continue; + } + + if(b=='C'){ // cuf + if(digit==0&&intargi==0){ + intarg[0]=1; + } + parm_right_cursor(intarg[0]); + continue; + } + + if(b=='K'){ // el + /* + b=getChar(); // + if(b=='$'){ + b=getChar(); // < + b=getChar(); // 3 + b=getChar(); // > + } + else{ + pushChar(b); + } + */ + + if(digit==0&&intargi==0){ // el + clr_eol(); + } + else{ // el1 + clr_bol(); + } + continue; + } + + if(b=='J'){ + //for(int i=0; i<intargi; i++){ System.out.print(intarg[i]+" ");} + //System.out.println(intarg[0]+"<- intargi="+intargi); + clr_eos(); + continue; + } + + if(b=='A'){ // cuu + if(digit==0&&intargi==0){ + intarg[0]=1; + } + parm_up_cursor(intarg[0]); + continue; + } + + if(b=='?'){ + b=getChar(); + if(b=='1'){ + b=getChar(); + if(b=='l'||b=='h'){ + b=getChar(); + if(b==0x1b){ + b=getChar(); + if(b=='>'|| // rmkx , leave 'keybroad_transmit' mode + b=='='){ // smkx , enter 'keyborad_transmit' mode + // TODO + continue; + } + } + } + else if(b=='h'){ + b=getChar(); + if(b==0x1b){ + b=getChar(); + if(b=='='){ // smkx enter 'keyborad_transmit' mode + continue; + } + } + } + } + else if(b=='7'){ + b=getChar(); + if(b=='h'){ // smam + // TODO + //System.out.println("turn on automatic magins"); + continue; + } + else if(b=='l'){ // rmam + // TODO + //System.out.println("turn off automatic magins"); + continue; + } + pushChar(b); + b='7'; + } + else{ + } + } + + if(b=='h'){ // kh \Eh home key + continue; + } + + System.out.println("unknown "+Integer.toHexString(b&0xff)+" " + +new Character((char)b)+", "+intarg[0]+", "+intarg[1]+", " + +intarg[2]+",intargi="+intargi); + continue; + } + + if(b==0x07){ // bel ^G + bell(); + continue; + } + + if(b==0x09){ // ht(^I) + tab(); + continue; + } + + if(b==0x0f){ // rmacs ^O // end alternate character set (P) + exit_alt_charset_mode(); + continue; + } + + if(b==0x0e){ // smacs ^N // start alternate character set (P) + enter_alt_charset_mode(); + continue; + } + + if(b==0x0d){ + carriage_return(); + continue; + } + + if(b==0x08){ + cursor_left(); + continue; + } + + if(b==0x0a){ // '\n' + //System.out.println("x="+x+",y="+y); + cursor_down(); + //check_region(); + continue; + } + + if(b!=0x0a){ // !'\n' + pushChar(b); + draw_text(); + continue; + } + } + } + catch(Exception e){ + } + } + + private static byte[] ENTER= {(byte)0x0d}; + private static byte[] UP= {(byte)0x1b, (byte)0x4f, (byte)0x41}; + private static byte[] DOWN= {(byte)0x1b, (byte)0x4f, (byte)0x42}; + private static byte[] RIGHT= {(byte)0x1b, (byte)/*0x5b*/0x4f, (byte)0x43}; + private static byte[] LEFT= {(byte)0x1b, (byte)/*0x5b*/0x4f, (byte)0x44}; + private static byte[] F1= {(byte)0x1b, (byte)0x4f, (byte)'P'}; + private static byte[] F2= {(byte)0x1b, (byte)0x4f, (byte)'Q'}; + private static byte[] F3= {(byte)0x1b, (byte)0x4f, (byte)'R'}; + private static byte[] F4= {(byte)0x1b, (byte)0x4f, (byte)'S'}; + private static byte[] F5= {(byte)0x1b, (byte)0x4f, (byte)'t'}; + private static byte[] F6= {(byte)0x1b, (byte)0x4f, (byte)'u'}; + private static byte[] F7= {(byte)0x1b, (byte)0x4f, (byte)'v'}; + private static byte[] F8= {(byte)0x1b, (byte)0x4f, (byte)'I'}; + private static byte[] F9= {(byte)0x1b, (byte)0x4f, (byte)'w'}; + private static byte[] F10= {(byte)0x1b, (byte)0x4f, (byte)'x'}; + private static byte[] tab= {(byte)0x09}; + + public byte[] getCodeENTER(){ + return ENTER; + } + + public byte[] getCodeUP(){ + return UP; + } + + public byte[] getCodeDOWN(){ + return DOWN; + } + + public byte[] getCodeRIGHT(){ + return RIGHT; + } + + public byte[] getCodeLEFT(){ + return LEFT; + } + + public byte[] getCodeF1(){ + return F1; + } + + public byte[] getCodeF2(){ + return F2; + } + + public byte[] getCodeF3(){ + return F3; + } + + public byte[] getCodeF4(){ + return F4; + } + + public byte[] getCodeF5(){ + return F5; + } + + public byte[] getCodeF6(){ + return F6; + } + + public byte[] getCodeF7(){ + return F7; + } + + public byte[] getCodeF8(){ + return F8; + } + + public byte[] getCodeF9(){ + return F9; + } + + public byte[] getCodeF10(){ + return F10; + } + + public byte[] getCodeTAB(){ + return tab; + } +} diff --git a/src/com/jcraft/jcterm/Term.java b/src/com/jcraft/jcterm/Term.java new file mode 100644 index 0000000..c574509 --- /dev/null +++ b/src/com/jcraft/jcterm/Term.java @@ -0,0 +1,80 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* JCTerm + * Copyright (C) 2002,2007 ymnk, JCraft,Inc. + * + * Written by: ymnk<ymnk@jcaft.com> + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package com.jcraft.jcterm; + +import java.io.InputStream; +import java.io.OutputStream; + +public interface Term{ + + void start(InputStream in, OutputStream out); + + int getRowCount(); + + int getColumnCount(); + + int getCharWidth(); + + int getCharHeight(); + + void setCursor(int x, int y); + + void clear(); + + void draw_cursor(); + + void redraw(int x, int y, int width, int height); + + //void redraw(); + void clear_area(int x1, int y1, int x2, int y2); + + void scroll_area(int x, int y, int w, int h, int dx, int dy); + + void drawBytes(byte[] buf, int s, int len, int x, int y); + + void drawString(String str, int x, int y); + + void beep(); + + void setDefaultForeGround(Object foreground); + + void setDefaultBackGround(Object background); + + void setForeGround(Object foreground); + + void setBackGround(Object background); + + void setBold(); + + void setUnderline(); + + void setReverse(); + + void resetAllAttributes(); + + int getTermWidth(); + + int getTermHeight(); + + Object getColor(int index); +} diff --git a/src/de/mud/terminal/SoftFont.java b/src/de/mud/terminal/SoftFont.java new file mode 100644 index 0000000..5f13f28 --- /dev/null +++ b/src/de/mud/terminal/SoftFont.java @@ -0,0 +1,1145 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ + +package de.mud.terminal; + +import android.graphics.Canvas; +import android.graphics.Paint; + +/** + * Any characters that are not available in standard java fonts may be + * drawn using the softfont utility. This utility class was derived from + * the cpi fonts used in linux console drivers.<P> + * <small>Font file generated by cpi2fnt</small> + * <P> + * <B>Maintainer:</B> Marcus Meissner + * + * @version $Id: SoftFont.java 499 2005-09-29 08:24:54Z leo $ + * @author Matthias L. Jugel, Marcus Meissner + */ +public class SoftFont { + final static private char SF_BITMAP = 0; + final static private char SF_FILLRECT = 1; + + //final static private char SF_CHAR = 0; + final static private char SF_WIDTH= 1; + final static private char SF_HEIGHT= 2; + final static private char SF_TYPE = 3; + final static private char SF_DATA = 4; + + java.util.Hashtable<Integer, Integer> font; + + /** softfont characterdata */ + private static char[][] fontdata = { + + {0x01,8,8,SF_BITMAP, /* 1 0x01 '^A' */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + },{ 0x02,8,8,SF_BITMAP,/* 2 0x02 '^B' */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + },{ 0x03,8,8,SF_BITMAP,/* 3 0x03 '^C' */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + },{ 0x04,8,8,SF_BITMAP,/* 4 0x04 '^D' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + },{ 0x05,8,8,SF_BITMAP,/* 5 0x05 '^E' */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + },{ 0x06,8,8,SF_BITMAP,/* 6 0x06 '^F' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + },{ 0x2666,8,8,SF_BITMAP,/* 9830 0x2666 BLACK DIAMOND */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + },{ 0x07,8,8,SF_BITMAP,/* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + },{ 0x08,8,8,SF_BITMAP,/* 8 0x08 '^H' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + },{ 0x09,8,8,SF_BITMAP,/* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + },{ 0x0a,8,8,SF_BITMAP,/* 10 0x0a '^J' */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + },{ 0x0b,8,8,SF_BITMAP,/* 11 0x0b '^K' */ + 0x0f, /* 00001111 */ + 0x07, /* 00000111 */ + 0x0f, /* 00001111 */ + 0x7d, /* 01111101 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + },{ 0x0c,8,8,SF_BITMAP,/* 12 0x0c '^L' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + },{ 0x0d,8,8,SF_BITMAP,/* 13 0x0d '^M' */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + },{ 0x0e,8,8,SF_BITMAP,/* 14 0x0e '^N' */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + },{ 0x0f,8,8,SF_BITMAP,/* 15 0x0f '^O' */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + },{ 0x10,8,8,SF_BITMAP,/* 16 0x10 '^P' */ + 0x80, /* 10000000 */ + 0xe0, /* 11100000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xe0, /* 11100000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + },{ 0x11,8,8,SF_BITMAP,/* 17 0x11 '^Q' */ + 0x02, /* 00000010 */ + 0x0e, /* 00001110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x0e, /* 00001110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + },{ 0x12,8,8,SF_BITMAP,/* 18 0x12 '^R' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + },{ 0x13,8,8,SF_BITMAP,/* 19 0x13 '^S' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + },{ 0x14,8,8,SF_BITMAP,/* 20 0x14 '^T' */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + },{ 0x15,8,8,SF_BITMAP,/* 21 0x15 '^U' */ + 0x3e, /* 00111110 */ + 0x61, /* 01100001 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x86, /* 10000110 */ + 0x7c, /* 01111100 */ + },{ 0x16,8,8,SF_BITMAP,/* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + },{ 0x17,8,8,SF_BITMAP,/* 23 0x17 '^W' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + },{ 0x18,8,8,SF_BITMAP,/* 24 0x18 '^X' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + },{ 0x19,8,8,SF_BITMAP,/* 25 0x19 '^Y' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + },{ 0x1a,8,8,SF_BITMAP,/* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + },{ 0x1b,8,8,SF_BITMAP,/* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + },{ 0x1c,8,8,SF_BITMAP,/* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + },{ 0x1d,8,8,SF_BITMAP,/* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x24, /* 00100100 */ + 0x66, /* 01100110 */ + 0xff, /* 11111111 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + },{ 0x1e,8,8,SF_BITMAP,/* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + },{ 0x1f,8,8,SF_BITMAP,/* 31 0x1f '^_' */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + },{ 0x7f,8,8,SF_BITMAP,/* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + },{ 0x2591,8,8,SF_BITMAP,/* LIGHT SHADE */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + },{ 0x2592,8,8,SF_BITMAP,/* MEDIUM SHADE */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + },{ 0x2593,8,8,SF_BITMAP,/* DARK SHADE */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + },{ 0x221a,8,8,SF_BITMAP,/* SQUARE ROOT */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + },{ 0x2320,8,8,SF_FILLRECT,/* UPPER INTERVAL*/ + 0x4031, + 0x3127, + 0x6122, + /* 00001110 */ + /* 00011011 */ + /* 00011011 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2321,8,8,SF_FILLRECT,/* BOTTOM HALF INTEGRAL */ + 0x3027, + 0x0522, + 0x1731, + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 11011000 */ + /* 11011000 */ + /* 01110000 */ + },{ 0x25a0,8,8,SF_FILLRECT,/* BLACK SQUARE */ + 0x2244, + /* 00000000 */ + /* 00000000 */ + /* 00111100 */ + /* 00111100 */ + /* 00111100 */ + /* 00111100 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2502,8,8,SF_FILLRECT,/*BOX DRAWINGS LIGHT VERTICAL*/ + 0x3028, + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2524,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT VERTICAL AND LEFT */ + 0x3028, + 0x0431, + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 11111000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2561,8,8,SF_FILLRECT,/*BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE*/ + 0x3028, + 0x0231, + 0x0431, + /* 00011000 */ + /* 00011000 */ + /* 11111000 */ + /* 00011000 */ + /* 11111000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2562,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE */ + 0x2028, + 0x5028, + 0x0421, + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 11110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x2556,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE */ + 0x0471, + 0x2523, + 0x5523, + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 11111110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x2555,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE */ + 0x3226, + 0x0231, + 0x0431, + /* 00000000 */ + /* 00000000 */ + /* 11111000 */ + /* 00011000 */ + /* 11111000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2563,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL AND LEFT*/ + 0x2022, + 0x0221, + 0x0421, + 0x2424, + 0x5028, + /* 00110110 */ + /* 00110110 */ + /* 11110110 */ + /* 00000110 */ + /* 11110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x2551,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL */ + 0x2028, + 0x5028, + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x2557,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE DOWN AND LEFT */ + 0x0271, + 0x5325, + 0x0441, + 0x2523, + /* 00000000 */ + /* 00000000 */ + /* 11111110 */ + /* 00000110 */ + /* 11110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x255d,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE UP AND LEFT */ + 0x2022, + 0x0241, + 0x5025, + 0x0451, + /* 00110110 */ + /* 00110110 */ + /* 11110110 */ + /* 00000110 */ + /* 11111110 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x255c,8,8,SF_FILLRECT,/* BOX DRAWINGS UP DOUBLE AND LEFT SINGLE */ + 0x2024, + 0x5024, + 0x0471, + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 11111110 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x255b,8,8,SF_FILLRECT,/* BOX DRAWINGS UP SINGLE AND LEFT DOUBLE */ + 0x3025, + 0x0231, + 0x0431, + /* 00011000 */ + /* 00011000 */ + /* 11111000 */ + /* 00011000 */ + /* 11111000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2510,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT DOWN AND LEFT */ + 0x0451, + 0x3523, + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 11111000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2514,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT UP AND RIGHT */ + 0x3025, + 0x5431, + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2534,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT UP AND HORIZONTAL */ + 0x3024, + 0x0481, + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 11111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x252c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ + 0x0481, + 0x3523, + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 11111111 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x251c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ + 0x3028, + 0x5431, + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011111 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2500,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT HORIZONTAL */ + 0x0481, + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 11111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2594,8,8,SF_FILLRECT,/* UPPER 1/8 (1st scanline) */ + 0x0081, + /* 11111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x25ac,8,8,SF_FILLRECT,/* LOWER 1/8 (7nd scanline) */ + 0x0781, + /* 11111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x253c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ + 0x3028, + 0x0481, + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 11111111 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x255e,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE */ + 0x3028, + 0x5231, + 0x5431, + /* 00011000 */ + /* 00011000 */ + /* 00011111 */ + /* 00011000 */ + /* 00011111 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x255f,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE */ + 0x2028, + 0x5028, + 0x7411, + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110111 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x255a,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE UP AND RIGHT */ + 0x2025, + 0x5023, + 0x7211, + 0x4441, + /* 00110110 */ + /* 00110110 */ + /* 00110111 */ + /* 00110000 */ + /* 00111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2554,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE DOWN AND RIGHT */ + 0x2261, + 0x2325, + 0x5424, + 0x7411, + /* 00000000 */ + /* 00000000 */ + /* 00111111 */ + /* 00110000 */ + /* 00110111 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x2569,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE UP AND HORIZONTAL */ + 0x2022, + 0x0241, + 0x5022, + 0x5231, + 0x0481, + /* 00110110 */ + /* 00110110 */ + /* 11110111 */ + /* 00000000 */ + /* 11111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2566,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL */ + 0x0281, + 0x0441, + 0x2523, + 0x5431, + 0x5523, + /* 00000000 */ + /* 00000000 */ + /* 11111111 */ + /* 00000000 */ + /* 11110111 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x2560,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL AND RIGHT */ + 0x2028, + 0x5022, + 0x5231, + 0x5431, + 0x5623, + /* 00110110 */ + /* 00110110 */ + /* 00110111 */ + /* 00110000 */ + /* 00110111 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x2550,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE HORIZONTAL */ + 0x0281, + 0x0481, + /* 00000000 */ + /* 00000000 */ + /* 11111111 */ + /* 00000000 */ + /* 11111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x256c,8,8,SF_FILLRECT,/* BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL */ + 0x2022, + 0x0241, + 0x5022, + 0x5231, + 0x0441, + 0x2523, + 0x5431, + 0x5523, + /* 00110110 */ + /* 00110110 */ + /* 11110111 */ + /* 00000000 */ + /* 11110111 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x2567,8,8,SF_FILLRECT,/* BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE */ + 0x3022, + 0x0281, + 0x0481, + /* 00011000 */ + /* 00011000 */ + /* 11111111 */ + /* 00000000 */ + /* 11111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2568,8,8,SF_FILLRECT,/* BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE */ + 0x2024, + 0x5024, + 0x0481, + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 11111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2564,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE */ + 0x0281, + 0x0481, + 0x3523, + /* 00000000 */ + /* 00000000 */ + /* 11111111 */ + /* 00000000 */ + /* 11111111 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2565,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE */ + 0x0481, + 0x2523, + 0x5523, + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 11111111 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x2559,8,8,SF_FILLRECT,/* BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE */ + 0x2024, + 0x5024, + 0x2461, + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2558,8,8,SF_FILLRECT,/* BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE */ + 0x3025, + 0x5231, + 0x5431, + /* 00011000 */ + /* 00011000 */ + /* 00011111 */ + /* 00011000 */ + /* 00011111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2552,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE */ + 0x3226, + 0x5231, + 0x5431, + /* 00000000 */ + /* 00000000 */ + /* 00011111 */ + /* 00011000 */ + /* 00011111 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2553,8,8,SF_FILLRECT,/* BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE */ + 0x2461, + 0x2523, + 0x5523, + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00111111 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x256b,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE */ + 0x2028, + 0x5028, + 0x0481, + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + /* 11111111 */ + /* 00110110 */ + /* 00110110 */ + /* 00110110 */ + },{ 0x256a,8,8,SF_FILLRECT,/* BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE */ + 0x3028, + 0x0281, + 0x0481, + /* 00011000 */ + /* 00011000 */ + /* 11111111 */ + /* 00011000 */ + /* 11111111 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2518,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT UP AND LEFT */ + 0x3025, + 0x0431, + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + /* 11111000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x250c,8,8,SF_FILLRECT,/* BOX DRAWINGS LIGHT DOWN AND RIGHT */ + 0x3451, + 0x3523, + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00011111 */ + /* 00011000 */ + /* 00011000 */ + /* 00011000 */ + },{ 0x2588,8,8,SF_FILLRECT,/* FULL BLOCK */ + 0x0088, + /* 11111111 */ + /* 11111111 */ + /* 11111111 */ + /* 11111111 */ + /* 11111111 */ + /* 11111111 */ + /* 11111111 */ + /* 11111111 */ + },{ 0x2584,8,8,SF_FILLRECT,/* LOWER HALF BLOCK */ + 0x0484, + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 11111111 */ + /* 11111111 */ + /* 11111111 */ + /* 11111111 */ + },{ 0x258c,8,8,SF_FILLRECT,/* LEFT HALF BLOCK */ + 0x0048, + /* 11110000 */ + /* 11110000 */ + /* 11110000 */ + /* 11110000 */ + /* 11110000 */ + /* 11110000 */ + /* 11110000 */ + /* 11110000 */ + },{ 0x2590,8,8,SF_FILLRECT,/* RIGHT HALF BLOCK */ + 0x4048, + /* 00001111 */ + /* 00001111 */ + /* 00001111 */ + /* 00001111 */ + /* 00001111 */ + /* 00001111 */ + /* 00001111 */ + /* 00001111 */ + },{ 0x2580,8,8,SF_FILLRECT,/* UPPER HALF BLOCK */ + 0x0084, + /* 11111111 */ + /* 11111111 */ + /* 11111111 */ + /* 11111111 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2261,8,8,SF_FILLRECT,/* EQUIVALENT SIGN */ + 0x2081, + 0x4081, + 0x6081, + /* 00000000 */ + /* 00000000 */ + /* 11111111 */ + /* 00000000 */ + /* 11111111 */ + /* 00000000 */ + /* 11111111 */ + /* 00000000 */ + },{ 0x221e,8,8,SF_BITMAP,/* INFINITY */ + 0x00, + 0x00, + 0x7e, + 0xdb, + 0xdb, + 0x7e, + 0x00, + 0x00, + /* 00000000 */ + /* 00000000 */ + /* 01111110 */ + /* 11011011 */ + /* 11011011 */ + /* 01111110 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x207f,8,8,SF_FILLRECT,/* small superscript n */ + 0x1041, + 0x1124, + 0x4124, + /* 01111000 */ + /* 01101100 */ + /* 01101100 */ + /* 01101100 */ + /* 01101100 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x00b2,8,8,SF_BITMAP,/* small superscript 2 */ + 0x70, /* 01110000 */ + 0x1c, /* 00011100 */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + },{ 0x2219,8,8,SF_FILLRECT,/* BULLET OPERATOR */ + 0x3322, + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + /* 00011000 */ + /* 00011000 */ + /* 00000000 */ + /* 00000000 */ + /* 00000000 */ + },{ 0x2191,8,8,SF_BITMAP,/* UP ARROW */ + 0x08, /* 00001000 */ + 0x1c, /* 00011100 */ + 0x3e, /* 00111110 */ + 0x7f, /* 01111111 */ + 0x1c, /* 00011100 */ + 0x1c, /* 00011100 */ + 0x1c, /* 00011100 */ + 0x1c, /* 00011100 */ + },{ 0x2193,8,8,SF_BITMAP,/* DOWN ARROW */ + 0x1c, /* 00011100 */ + 0x1c, /* 00011100 */ + 0x1c, /* 00011100 */ + 0x1c, /* 00011100 */ + 0x7f, /* 01111111 */ + 0x3e, /* 00111110 */ + 0x1c, /* 00011100 */ + 0x08, /* 00001000 */ + },{ 0x25ba,8,8,SF_BITMAP,/* RIGHT ARROW (TRIANGLE ONLY) */ + 0x00, /* 00000000 */ + 0x40, /* 01000000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x70, /* 01110000 */ + 0x60, /* 01100000 */ + 0x40, /* 01000000 */ + 0x00, /* 00000000 */ + },{ 0x25c4,8,8,SF_BITMAP,/* LEFT ARROW (TRIANGLE ONLY) */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x3e, /* 00111110 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + }}; + + public SoftFont() { + font = new java.util.Hashtable<Integer, Integer>(); + for (int i=0;i<fontdata.length;i++) + font.put(new Integer(fontdata[i][0]),new Integer(i)); + } + + public boolean inSoftFont(char c) { + boolean insoftfont; + + insoftfont = (null!=font.get(new Integer(c))); + if (!insoftfont && (int)c>=0x100) { + System.out.println("Character "+((int)c)+" not in softfont"); + } + return insoftfont; + } + + public void drawChar(Canvas g, Paint p, char c,int x,int y,int cw,int ch) { + double dw,dh; + Object Ientry; + int w,h,entry,i,fontwidth,fontheight; + + Ientry = font.get(new Integer(c)); + if (Ientry == null) + return; + entry = ((Integer)Ientry).intValue(); + fontwidth = fontdata[entry][SF_WIDTH]; + fontheight = fontdata[entry][SF_HEIGHT]; + + dw = cw*1.0/fontwidth; + dh = ch*1.0/fontheight; + + switch (fontdata[entry][SF_TYPE]) { + case SF_BITMAP: + for (h=0;h<fontheight;h++) { + for (w=0;w<fontwidth;w++) { + //FIXME: 8 bit max currently... + if (0!=(fontdata[entry][h+SF_DATA] & (1<<(7-w)))) { + g.drawRect( + x+(int)(w*dw), + y+(int)(h*dh), + ((int)((w+1)*dw))-(int)(w*dw), + ((int)((h+1)*dh))-(int)(h*dh), + p + ); + } + } + } + break; + case SF_FILLRECT: + i=SF_DATA; + while (i<fontdata[entry].length) { + int xw,xh; + + w=(fontdata[entry][i]&0xF000)>>12; + h=(fontdata[entry][i]&0x0F00)>>8; + xw = (fontdata[entry][i]&0x00F0)>>4; + xh = (fontdata[entry][i]&0x000F); + g.drawRect( + x+(int)(w*dw), + y+(int)(h*dh), + ((int)((w+xw)*dw))-(int)(w*dw), + ((int)((h+xh)*dh))-(int)(h*dh), + p + ); + i++; + } + break; + default: + break; + } + } +} diff --git a/src/de/mud/terminal/VDUBuffer.java b/src/de/mud/terminal/VDUBuffer.java new file mode 100644 index 0000000..7f203e6 --- /dev/null +++ b/src/de/mud/terminal/VDUBuffer.java @@ -0,0 +1,791 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ + +package de.mud.terminal; + +/** + * Implementation of a Video Display Unit (VDU) buffer. This class contains + * all methods to manipulate the buffer that stores characters and their + * attributes as well as the regions displayed. + * + * @author Matthias L. Jugel, Marcus Mei�ner + * @version $Id: VDUBuffer.java 503 2005-10-24 07:34:13Z marcus $ + */ +public class VDUBuffer { + + /** The current version id tag */ + public final static String ID = "$Id: VDUBuffer.java 503 2005-10-24 07:34:13Z marcus $"; + + /** Enable debug messages. */ + public final static int debug = 0; + + public int height, width; /* rows and columns */ + public boolean[] update; /* contains the lines that need update */ + public char[][] charArray; /* contains the characters */ + public int[][] charAttributes; /* contains character attrs */ + public int bufSize; + public int maxBufSize; /* buffer sizes */ + public int screenBase; /* the actual screen start */ + public int windowBase; /* where the start displaying */ + public int scrollMarker; /* marks the last line inserted */ + + private int topMargin; /* top scroll margin */ + private int bottomMargin; /* bottom scroll margin */ + + // cursor variables + protected boolean showcursor = true; + protected int cursorX, cursorY; + + /** Scroll up when inserting a line. */ + public final static boolean SCROLL_UP = false; + /** Scroll down when inserting a line. */ + public final static boolean SCROLL_DOWN = true; + + /** Make character normal. */ + public final static int NORMAL = 0x00; + /** Make character bold. */ + public final static int BOLD = 0x01; + /** Underline character. */ + public final static int UNDERLINE = 0x02; + /** Invert character. */ + public final static int INVERT = 0x04; + /** Lower intensity character. */ + public final static int LOW = 0x08; + /** Invisible character. */ + public final static int INVISIBLE = 0x10; + + /** how much to left shift the foreground color */ + public final static int COLOR_FG_SHIFT = 5; + /** how much to left shift the background color */ + public final static int COLOR_BG_SHIFT = 9; + /** color mask */ + public final static int COLOR = 0x1fe0; + /** foreground color mask */ + public final static int COLOR_FG = 0x1e0; + /** background color mask */ + public final static int COLOR_BG = 0x1e00; + + /** + * Create a new video display buffer with the passed width and height in + * characters. + * @param width the length of the character lines + * @param height the amount of lines on the screen + */ + public VDUBuffer(int width, int height) { + // set the display screen size + setScreenSize(width, height, false); + } + + /** + * Create a standard video display buffer with 80 columns and 24 lines. + */ + public VDUBuffer() { + this(80, 24); + } + + /** + * Put a character on the screen with normal font and outline. + * The character previously on that position will be overwritten. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @param ch the character to show on the screen + * @see #insertChar + * @see #deleteChar + * @see #redraw + */ + public void putChar(int c, int l, char ch) { + putChar(c, l, ch, NORMAL); + } + + /** + * Put a character on the screen with specific font and outline. + * The character previously on that position will be overwritten. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @param ch the character to show on the screen + * @param attributes the character attributes + * @see #BOLD + * @see #UNDERLINE + * @see #INVERT + * @see #INVISIBLE + * @see #NORMAL + * @see #LOW + * @see #insertChar + * @see #deleteChar + * @see #redraw + */ + + public void putChar(int c, int l, char ch, int attributes) { + c = checkBounds(c, 0, width - 1); + l = checkBounds(l, 0, height - 1); + charArray[screenBase + l][c] = ch; + charAttributes[screenBase + l][c] = attributes; + markLine(l, 1); + } + + /** + * Get the character at the specified position. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @see #putChar + */ + public char getChar(int c, int l) { + c = checkBounds(c, 0, width - 1); + l = checkBounds(l, 0, height - 1); + return charArray[screenBase + l][c]; + } + + /** + * Get the attributes for the specified position. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @see #putChar + */ + public int getAttributes(int c, int l) { + c = checkBounds(c, 0, width - 1); + l = checkBounds(l, 0, height - 1); + return charAttributes[screenBase + l][c]; + } + + /** + * Insert a character at a specific position on the screen. + * All character right to from this position will be moved one to the right. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @param ch the character to insert + * @param attributes the character attributes + * @see #BOLD + * @see #UNDERLINE + * @see #INVERT + * @see #INVISIBLE + * @see #NORMAL + * @see #LOW + * @see #putChar + * @see #deleteChar + * @see #redraw + */ + public void insertChar(int c, int l, char ch, int attributes) { + c = checkBounds(c, 0, width - 1); + l = checkBounds(l, 0, height - 1); + System.arraycopy(charArray[screenBase + l], c, + charArray[screenBase + l], c + 1, width - c - 1); + System.arraycopy(charAttributes[screenBase + l], c, + charAttributes[screenBase + l], c + 1, width - c - 1); + putChar(c, l, ch, attributes); + } + + /** + * Delete a character at a given position on the screen. + * All characters right to the position will be moved one to the left. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @see #putChar + * @see #insertChar + * @see #redraw + */ + public void deleteChar(int c, int l) { + c = checkBounds(c, 0, width - 1); + l = checkBounds(l, 0, height - 1); + if (c < width - 1) { + System.arraycopy(charArray[screenBase + l], c + 1, + charArray[screenBase + l], c, width - c - 1); + System.arraycopy(charAttributes[screenBase + l], c + 1, + charAttributes[screenBase + l], c, width - c - 1); + } + putChar(width - 1, l, (char) 0); + } + + /** + * Put a String at a specific position. Any characters previously on that + * position will be overwritten. You need to call redraw() for screen update. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @param s the string to be shown on the screen + * @see #BOLD + * @see #UNDERLINE + * @see #INVERT + * @see #INVISIBLE + * @see #NORMAL + * @see #LOW + * @see #putChar + * @see #insertLine + * @see #deleteLine + * @see #redraw + */ + public void putString(int c, int l, String s) { + putString(c, l, s, NORMAL); + } + + /** + * Put a String at a specific position giving all characters the same + * attributes. Any characters previously on that position will be + * overwritten. You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @param s the string to be shown on the screen + * @param attributes character attributes + * @see #BOLD + * @see #UNDERLINE + * @see #INVERT + * @see #INVISIBLE + * @see #NORMAL + * @see #LOW + * @see #putChar + * @see #insertLine + * @see #deleteLine + * @see #redraw + */ + public void putString(int c, int l, String s, int attributes) { + for (int i = 0; i < s.length() && c + i < width; i++) + putChar(c + i, l, s.charAt(i), attributes); + } + + /** + * Insert a blank line at a specific position. + * The current line and all previous lines are scrolled one line up. The + * top line is lost. You need to call redraw() to update the screen. + * @param l the y-coordinate to insert the line + * @see #deleteLine + * @see #redraw + */ + public void insertLine(int l) { + insertLine(l, 1, SCROLL_UP); + } + + /** + * Insert blank lines at a specific position. + * You need to call redraw() to update the screen + * @param l the y-coordinate to insert the line + * @param n amount of lines to be inserted + * @see #deleteLine + * @see #redraw + */ + public void insertLine(int l, int n) { + insertLine(l, n, SCROLL_UP); + } + + /** + * Insert a blank line at a specific position. Scroll text according to + * the argument. + * You need to call redraw() to update the screen + * @param l the y-coordinate to insert the line + * @param scrollDown scroll down + * @see #deleteLine + * @see #SCROLL_UP + * @see #SCROLL_DOWN + * @see #redraw + */ + public void insertLine(int l, boolean scrollDown) { + insertLine(l, 1, scrollDown); + } + + /** + * Insert blank lines at a specific position. + * The current line and all previous lines are scrolled one line up. The + * top line is lost. You need to call redraw() to update the screen. + * @param l the y-coordinate to insert the line + * @param n number of lines to be inserted + * @param scrollDown scroll down + * @see #deleteLine + * @see #SCROLL_UP + * @see #SCROLL_DOWN + * @see #redraw + */ + public synchronized void insertLine(int l, int n, boolean scrollDown) { + l = checkBounds(l, 0, height - 1); + + char cbuf[][] = null; + int abuf[][] = null; + int offset = 0; + int oldBase = screenBase; + + if (l > bottomMargin) /* We do not scroll below bottom margin (below the scrolling region). */ + return; + int top = (l < topMargin ? + 0 : (l > bottomMargin ? + (bottomMargin + 1 < height ? + bottomMargin + 1 : height - 1) : topMargin)); + int bottom = (l > bottomMargin ? + height - 1 : (l < topMargin ? + (topMargin > 0 ? + topMargin - 1 : 0) : bottomMargin)); + + // System.out.println("l is "+l+", top is "+top+", bottom is "+bottom+", bottomargin is "+bottomMargin+", topMargin is "+topMargin); + + if (scrollDown) { + if (n > (bottom - top)) n = (bottom - top); + cbuf = new char[bottom - l - (n - 1)][width]; + abuf = new int[bottom - l - (n - 1)][width]; + + System.arraycopy(charArray, oldBase + l, cbuf, 0, bottom - l - (n - 1)); + System.arraycopy(charAttributes, oldBase + l, + abuf, 0, bottom - l - (n - 1)); + System.arraycopy(cbuf, 0, charArray, oldBase + l + n, + bottom - l - (n - 1)); + System.arraycopy(abuf, 0, charAttributes, oldBase + l + n, + bottom - l - (n - 1)); + cbuf = charArray; + abuf = charAttributes; + } else { + try { + if (n > (bottom - top) + 1) n = (bottom - top) + 1; + if (bufSize < maxBufSize) { + if (bufSize + n > maxBufSize) { + offset = n - (maxBufSize - bufSize); + scrollMarker += offset; + bufSize = maxBufSize; + screenBase = maxBufSize - height - 1; + windowBase = screenBase; + } else { + scrollMarker += n; + screenBase += n; + windowBase += n; + bufSize += n; + } + + cbuf = new char[bufSize][width]; + abuf = new int[bufSize][width]; + } else { + offset = n; + cbuf = charArray; + abuf = charAttributes; + } + // copy anything from the top of the buffer (+offset) to the new top + // up to the screenBase. + if (oldBase > 0) { + System.arraycopy(charArray, offset, + cbuf, 0, + oldBase - offset); + System.arraycopy(charAttributes, offset, + abuf, 0, + oldBase - offset); + } + // copy anything from the top of the screen (screenBase) up to the + // topMargin to the new screen + if (top > 0) { + System.arraycopy(charArray, oldBase, + cbuf, screenBase, + top); + System.arraycopy(charAttributes, oldBase, + abuf, screenBase, + top); + } + // copy anything from the topMargin up to the amount of lines inserted + // to the gap left over between scrollback buffer and screenBase + if (oldBase > 0) { + System.arraycopy(charArray, oldBase + top, + cbuf, oldBase - offset, + n); + System.arraycopy(charAttributes, oldBase + top, + abuf, oldBase - offset, + n); + } + // copy anything from topMargin + n up to the line linserted to the + // topMargin + System.arraycopy(charArray, oldBase + top + n, + cbuf, screenBase + top, + l - top - (n - 1)); + System.arraycopy(charAttributes, oldBase + top + n, + abuf, screenBase + top, + l - top - (n - 1)); + // + // copy the all lines next to the inserted to the new buffer + if (l < height - 1) { + System.arraycopy(charArray, oldBase + l + 1, + cbuf, screenBase + l + 1, + (height - 1) - l); + System.arraycopy(charAttributes, oldBase + l + 1, + abuf, screenBase + l + 1, + (height - 1) - l); + } + } catch (ArrayIndexOutOfBoundsException e) { + // this should not happen anymore, but I will leave the code + // here in case something happens anyway. That code above is + // so complex I always have a hard time understanding what + // I did, even though there are comments + System.err.println("*** Error while scrolling up:"); + System.err.println("--- BEGIN STACK TRACE ---"); + e.printStackTrace(); + System.err.println("--- END STACK TRACE ---"); + System.err.println("bufSize=" + bufSize + ", maxBufSize=" + maxBufSize); + System.err.println("top=" + top + ", bottom=" + bottom); + System.err.println("n=" + n + ", l=" + l); + System.err.println("screenBase=" + screenBase + ", windowBase=" + windowBase); + System.err.println("oldBase=" + oldBase); + System.err.println("size.width=" + width + ", size.height=" + height); + System.err.println("abuf.length=" + abuf.length + ", cbuf.length=" + cbuf.length); + System.err.println("*** done dumping debug information"); + } + } + + // this is a little helper to mark the scrolling + scrollMarker -= n; + + + for (int i = 0; i < n; i++) { + cbuf[(screenBase + l) + (scrollDown ? i : -i)] = new char[width]; + abuf[(screenBase + l) + (scrollDown ? i : -i)] = new int[width]; + } + + charArray = cbuf; + charAttributes = abuf; + + if (scrollDown) + markLine(l, bottom - l + 1); + else + markLine(top, l - top + 1); + + display.updateScrollBar(); + } + + /** + * Delete a line at a specific position. Subsequent lines will be scrolled + * up to fill the space and a blank line is inserted at the end of the + * screen. + * @param l the y-coordinate to insert the line + * @see #deleteLine + */ + public void deleteLine(int l) { + l = checkBounds(l, 0, height - 1); + + int bottom = (l > bottomMargin ? height - 1: + (l < topMargin?topMargin:bottomMargin + 1)); + System.arraycopy(charArray, screenBase + l + 1, + charArray, screenBase + l, bottom - l - 1); + System.arraycopy(charAttributes, screenBase + l + 1, + charAttributes, screenBase + l, bottom - l - 1); + charArray[screenBase + bottom - 1] = new char[width]; + charAttributes[screenBase + bottom - 1] = new int[width]; + markLine(l, bottom - l); + } + + /** + * Delete a rectangular portion of the screen. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (row) + * @param w with of the area in characters + * @param h height of the area in characters + * @param curAttr attribute to fill + * @see #deleteChar + * @see #deleteLine + * @see #redraw + */ + public void deleteArea(int c, int l, int w, int h, int curAttr) { + c = checkBounds(c, 0, width - 1); + l = checkBounds(l, 0, height - 1); + + char cbuf[] = new char[w]; + int abuf[] = new int[w]; + + for (int i = 0; i < w; i++) abuf[i] = curAttr; + for (int i = 0; i < h && l + i < height; i++) { + System.arraycopy(cbuf, 0, charArray[screenBase + l + i], c, w); + System.arraycopy(abuf, 0, charAttributes[screenBase + l + i], c, w); + } + markLine(l, h); + } + + /** + * Delete a rectangular portion of the screen. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (row) + * @param w with of the area in characters + * @param h height of the area in characters + * @see #deleteChar + * @see #deleteLine + * @see #redraw + */ + public void deleteArea(int c, int l, int w, int h) { + c = checkBounds(c, 0, width - 1); + l = checkBounds(l, 0, height - 1); + + char cbuf[] = new char[w]; + int abuf[] = new int[w]; + + for (int i = 0; i < h && l + i < height; i++) { + System.arraycopy(cbuf, 0, charArray[screenBase + l + i], c, w); + System.arraycopy(abuf, 0, charAttributes[screenBase + l + i], c, w); + } + markLine(l, h); + } + + /** + * Sets whether the cursor is visible or not. + * @param doshow + */ + public void showCursor(boolean doshow) { + if (doshow != showcursor) + markLine(cursorY, 1); + showcursor = doshow; + } + + /** + * Puts the cursor at the specified position. + * @param c column + * @param l line + */ + public void setCursorPosition(int c, int l) { + cursorX = checkBounds(c, 0, width - 1); + cursorY = checkBounds(l, 0, height - 1); + markLine(cursorY, 1); + } + + /** + * Get the current column of the cursor position. + */ + public int getCursorColumn() { + return cursorX; + } + + /** + * Get the current line of the cursor position. + */ + public int getCursorRow() { + return cursorY; + } + + /** + * Set the current window base. This allows to view the scrollback buffer. + * @param line the line where the screen window starts + * @see #setBufferSize + * @see #getBufferSize + */ + public void setWindowBase(int line) { + if (line > screenBase) + line = screenBase; + else if (line < 0) line = 0; + windowBase = line; + update[0] = true; + redraw(); + } + + /** + * Get the current window base. + * @see #setWindowBase + */ + public int getWindowBase() { + return windowBase; + } + + /** + * Set the top scroll margin for the screen. If the current bottom margin + * is smaller it will become the top margin and the line will become the + * bottom margin. + * @param l line that is the margin + */ + public void setTopMargin(int l) { + if (l > bottomMargin) { + topMargin = bottomMargin; + bottomMargin = l; + } else + topMargin = l; + if (topMargin < 0) topMargin = 0; + if (bottomMargin > height - 1) bottomMargin = height - 1; + } + + /** + * Get the top scroll margin. + */ + public int getTopMargin() { + return topMargin; + } + + /** + * Set the bottom scroll margin for the screen. If the current top margin + * is bigger it will become the bottom margin and the line will become the + * top margin. + * @param l line that is the margin + */ + public void setBottomMargin(int l) { + if (l < topMargin) { + bottomMargin = topMargin; + topMargin = l; + } else + bottomMargin = l; + if (topMargin < 0) topMargin = 0; + if (bottomMargin > height - 1) bottomMargin = height - 1; + } + + /** + * Get the bottom scroll margin. + */ + public int getBottomMargin() { + return bottomMargin; + } + + /** + * Set scrollback buffer size. + * @param amount new size of the buffer + */ + public void setBufferSize(int amount) { + if (amount < height) amount = height; + if (amount < maxBufSize) { + char cbuf[][] = new char[amount][width]; + int abuf[][] = new int[amount][width]; + int copyStart = bufSize - amount < 0 ? 0 : bufSize - amount; + int copyCount = bufSize - amount < 0 ? bufSize : amount; + if (charArray != null) + System.arraycopy(charArray, copyStart, cbuf, 0, copyCount); + if (charAttributes != null) + System.arraycopy(charAttributes, copyStart, abuf, 0, copyCount); + charArray = cbuf; + charAttributes = abuf; + bufSize = copyCount; + screenBase = bufSize - height; + windowBase = screenBase; + } + maxBufSize = amount; + + update[0] = true; + redraw(); + } + + /** + * Retrieve current scrollback buffer size. + * @see #setBufferSize + */ + public int getBufferSize() { + return bufSize; + } + + /** + * Retrieve maximum buffer Size. + * @see #getBufferSize + */ + public int getMaxBufferSize() { + return maxBufSize; + } + + /** + * Change the size of the screen. This will include adjustment of the + * scrollback buffer. + * @param w of the screen + * @param h of the screen + */ + public void setScreenSize(int w, int h, boolean broadcast) { + char cbuf[][]; + int abuf[][]; + int bsize = bufSize; + + if (w < 1 || h < 1) return; + + if (debug > 0) + System.err.println("VDU: screen size [" + w + "," + h + "]"); + + if (h > maxBufSize) + maxBufSize = h; + + if (h > bufSize) { + bufSize = h; + screenBase = 0; + windowBase = 0; + } + + if (windowBase + h >= bufSize) + windowBase = bufSize - h; + + if (screenBase + h >= bufSize) + screenBase = bufSize - h; + + + cbuf = new char[bufSize][w]; + abuf = new int[bufSize][w]; + + if (charArray != null && charAttributes != null) { + for (int i = 0; i < bsize && i < bufSize; i++) { + System.arraycopy(charArray[i], 0, cbuf[i], 0, + w < width ? w : width); + System.arraycopy(charAttributes[i], 0, abuf[i], 0, + w < width ? w : width); + } + } + + charArray = cbuf; + charAttributes = abuf; + width = w; + height = h; + topMargin = 0; + bottomMargin = h - 1; + update = new boolean[h + 1]; + update[0] = true; + /* FIXME: ??? + if(resizeStrategy == RESIZE_FONT) + setBounds(getBounds()); + */ + } + + /** + * Get amount of rows on the screen. + */ + public int getRows() { + return height; + } + + /** + * Get amount of columns on the screen. + */ + public int getColumns() { + return width; + } + + /** + * Mark lines to be updated with redraw(). + * @param l starting line + * @param n amount of lines to be updated + * @see #redraw + */ + public void markLine(int l, int n) { + l = checkBounds(l, 0, height - 1); + for (int i = 0; (i < n) && (l + i < height); i++) + update[l + i + 1] = true; + } + + private int checkBounds(int value, int lower, int upper) { + if (value < lower) return lower; + if (value > upper) return upper; + return value; + } + + /** a generic display that should redraw on demand */ + protected VDUDisplay display; + + public void setDisplay(VDUDisplay display) { + this.display = display; + } + + /** + * Trigger a redraw on the display. + */ + protected void redraw() { + if (display != null) + display.redraw(); + } +} diff --git a/src/de/mud/terminal/VDUDisplay.java b/src/de/mud/terminal/VDUDisplay.java new file mode 100644 index 0000000..4323465 --- /dev/null +++ b/src/de/mud/terminal/VDUDisplay.java @@ -0,0 +1,37 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ + +package de.mud.terminal; + +/** + * Generic display + */ +public interface VDUDisplay { + public void redraw(); + public void updateScrollBar(); + + public void setVDUBuffer(VDUBuffer buffer); + public VDUBuffer getVDUBuffer(); +} diff --git a/src/de/mud/terminal/VDUInput.java b/src/de/mud/terminal/VDUInput.java new file mode 100644 index 0000000..79a8bb7 --- /dev/null +++ b/src/de/mud/terminal/VDUInput.java @@ -0,0 +1,89 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ +package de.mud.terminal; + +import java.util.Properties; + +/** + * An interface for a terminal that accepts input from keyboard and mouse. + * + * @author Matthias L. Jugel, Marcus Mei�ner + * @version $Id: VDUInput.java 499 2005-09-29 08:24:54Z leo $ + */ +public interface VDUInput { + + public final static int KEY_CONTROL = 0x01; + public final static int KEY_SHIFT = 0x02; + public final static int KEY_ALT = 0x04; + public final static int KEY_ACTION = 0x08; + + + /** + * Direct access to writing data ... + * @param b + */ + void write(byte b[]); + + /** + * Terminal is mouse-aware and requires (x,y) coordinates of + * on the terminal (character coordinates) and the button clicked. + * @param x + * @param y + * @param modifiers + */ + void mousePressed(int x, int y, int modifiers); + + /** + * Terminal is mouse-aware and requires the coordinates and button + * of the release. + * @param x + * @param y + * @param modifiers + */ + void mouseReleased(int x, int y, int modifiers); + + /** + * Override the standard key codes used by the terminal emulation. + * @param codes a properties object containing key code definitions + */ + void setKeyCodes(Properties codes); + + /** + * main keytyping event handler... + * @param keyCode the key code + * @param keyChar the character represented by the key + * @param modifiers shift/alt/control modifiers + */ + void keyPressed(int keyCode, char keyChar, int modifiers); + + /** + * Handle key Typed events for the terminal, this will get + * all normal key types, but no shift/alt/control/numlock. + * @param keyCode the key code + * @param keyChar the character represented by the key + * @param modifiers shift/alt/control modifiers + */ + void keyTyped(int keyCode, char keyChar, int modifiers); +} diff --git a/src/de/mud/terminal/vt320.java b/src/de/mud/terminal/vt320.java new file mode 100644 index 0000000..099c877 --- /dev/null +++ b/src/de/mud/terminal/vt320.java @@ -0,0 +1,2722 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ + +package de.mud.terminal; + +import java.util.Properties; + +/** + * Implementation of a VT terminal emulation plus ANSI compatible. + * <P> + * <B>Maintainer:</B> Marcus Mei\u00dfner + * + * @version $Id: vt320.java 507 2005-10-25 10:14:52Z marcus $ + * @author Matthias L. Jugel, Marcus Mei\u00dfner + */ +public abstract class vt320 extends VDUBuffer implements VDUInput { + /** The current version id tag.<P> + * $Id: vt320.java 507 2005-10-25 10:14:52Z marcus $ + */ + public final static String ID = "$Id: vt320.java 507 2005-10-25 10:14:52Z marcus $"; + + /** the debug level */ + private final static int debug = 0; + + /** + * Write an answer back to the remote host. This is needed to be able to + * send terminal answers requests like status and type information. + * @param b the array of bytes to be sent + */ + public abstract void write(byte[] b); + + /** + * Play the beep sound ... + */ + public void beep() { /* do nothing by default */ + } + + /** + * Put string at current cursor position. Moves cursor + * according to the String. Does NOT wrap. + * @param s the string + */ + public void putString(String s) { + int len = s.length(); + // System.err.println("'"+s+"'"); + + if (len > 0) { + markLine(R, 1); + for (int i = 0; i < len; i++) { + // System.err.print(s.charAt(i)+"("+(int)s.charAt(i)+")"); + putChar(s.charAt(i), false); + } + setCursorPosition(C, R); + redraw(); + } + } + + protected void sendTelnetCommand(byte cmd) { + } + + /** + * Sent the changed window size from the terminal to all listeners. + */ + protected void setWindowSize(int c, int r) { + /* To be overridden by Terminal.java */ + } + + public void setScreenSize(int c, int r, boolean broadcast) { + int oldrows = getRows(); + //int oldcols = getColumns(); + + if (debug>2) System.err.println("setscreensize ("+c+","+r+","+broadcast+")"); + + super.setScreenSize(c,r,false); + + /* Tricky, since the VDUBuffer works strangely. */ + if (r > oldrows) { + setCursorPosition(C, R + (r-oldrows)); + redraw(); + } + if (broadcast) { + setWindowSize(c, r); /* broadcast up */ + } + } + + + /** + * Create a new vt320 terminal and intialize it with useful settings. + */ + public vt320(int width, int height) { + super(width, height); + setVMS(false); + setIBMCharset(false); + setTerminalID("vt320"); + setBufferSize(100); + //setBorder(2, false); + + int nw = getColumns(); + if (nw < 132) nw = 132; //catch possible later 132/80 resizes + Tabs = new byte[nw]; + for (int i = 0; i < nw; i += 8) { + Tabs[i] = 1; + } + + /* top row of numpad */ + PF1 = "\u001bOP"; + PF2 = "\u001bOQ"; + PF3 = "\u001bOR"; + PF4 = "\u001bOS"; + + /* the 3x2 keyblock on PC keyboards */ + Insert = new String[4]; + Remove = new String[4]; + KeyHome = new String[4]; + KeyEnd = new String[4]; + NextScn = new String[4]; + PrevScn = new String[4]; + Escape = new String[4]; + BackSpace = new String[4]; + TabKey = new String[4]; + Insert[0] = Insert[1] = Insert[2] = Insert[3] = "\u001b[2~"; + Remove[0] = Remove[1] = Remove[2] = Remove[3] = "\u001b[3~"; + PrevScn[0] = PrevScn[1] = PrevScn[2] = PrevScn[3] = "\u001b[5~"; + NextScn[0] = NextScn[1] = NextScn[2] = NextScn[3] = "\u001b[6~"; + KeyHome[0] = KeyHome[1] = KeyHome[2] = KeyHome[3] = "\u001b[H"; + KeyEnd[0] = KeyEnd[1] = KeyEnd[2] = KeyEnd[3] = "\u001b[F"; + Escape[0] = Escape[1] = Escape[2] = Escape[3] = "\u001b"; + if (vms) { + BackSpace[1] = "" + (char) 10; // VMS shift deletes word back + BackSpace[2] = "\u0018"; // VMS control deletes line back + BackSpace[0] = BackSpace[3] = "\u007f"; // VMS other is delete + } else { + BackSpace[0] = BackSpace[1] = BackSpace[2] = BackSpace[3] = "\b"; + } + + /* some more VT100 keys */ + Find = "\u001b[1~"; + Select = "\u001b[4~"; + Help = "\u001b[28~"; + Do = "\u001b[29~"; + + FunctionKey = new String[21]; + FunctionKey[0] = ""; + FunctionKey[1] = PF1; + FunctionKey[2] = PF2; + FunctionKey[3] = PF3; + FunctionKey[4] = PF4; + /* following are defined differently for vt220 / vt132 ... */ + FunctionKey[5] = "\u001b[15~"; + FunctionKey[6] = "\u001b[17~"; + FunctionKey[7] = "\u001b[18~"; + FunctionKey[8] = "\u001b[19~"; + FunctionKey[9] = "\u001b[20~"; + FunctionKey[10] = "\u001b[21~"; + FunctionKey[11] = "\u001b[23~"; + FunctionKey[12] = "\u001b[24~"; + FunctionKey[13] = "\u001b[25~"; + FunctionKey[14] = "\u001b[26~"; + FunctionKey[15] = Help; + FunctionKey[16] = Do; + FunctionKey[17] = "\u001b[31~"; + FunctionKey[18] = "\u001b[32~"; + FunctionKey[19] = "\u001b[33~"; + FunctionKey[20] = "\u001b[34~"; + + FunctionKeyShift = new String[21]; + FunctionKeyAlt = new String[21]; + FunctionKeyCtrl = new String[21]; + + for (int i = 0; i < 20; i++) { + FunctionKeyShift[i] = ""; + FunctionKeyAlt[i] = ""; + FunctionKeyCtrl[i] = ""; + } + FunctionKeyShift[15] = Find; + FunctionKeyShift[16] = Select; + + + TabKey[0] = "\u0009"; + TabKey[1] = "\u001bOP\u0009"; + TabKey[2] = TabKey[3] = ""; + + KeyUp = new String[4]; + KeyUp[0] = "\u001b[A"; + KeyDown = new String[4]; + KeyDown[0] = "\u001b[B"; + KeyRight = new String[4]; + KeyRight[0] = "\u001b[C"; + KeyLeft = new String[4]; + KeyLeft[0] = "\u001b[D"; + Numpad = new String[10]; + Numpad[0] = "\u001bOp"; + Numpad[1] = "\u001bOq"; + Numpad[2] = "\u001bOr"; + Numpad[3] = "\u001bOs"; + Numpad[4] = "\u001bOt"; + Numpad[5] = "\u001bOu"; + Numpad[6] = "\u001bOv"; + Numpad[7] = "\u001bOw"; + Numpad[8] = "\u001bOx"; + Numpad[9] = "\u001bOy"; + KPMinus = PF4; + KPComma = "\u001bOl"; + KPPeriod = "\u001bOn"; + KPEnter = "\u001bOM"; + + NUMPlus = new String[4]; + NUMPlus[0] = "+"; + NUMDot = new String[4]; + NUMDot[0] = "."; + } + + /** + * Create a default vt320 terminal with 80 columns and 24 lines. + */ + public vt320() { + this(80, 24); + } + + /** + * Terminal is mouse-aware and requires (x,y) coordinates of + * on the terminal (character coordinates) and the button clicked. + * @param x + * @param y + * @param modifiers + */ + public void mousePressed(int x, int y, int modifiers) { + if (mouserpt == 0) + return; + + int mods = modifiers; + mousebut = 3; + if ((mods & 16) == 16) mousebut = 0; + if ((mods & 8) == 8) mousebut = 1; + if ((mods & 4) == 4) mousebut = 2; + + int mousecode; + if (mouserpt == 9) /* X10 Mouse */ + mousecode = 0x20 | mousebut; + else /* normal xterm mouse reporting */ + mousecode = mousebut | 0x20 | ((mods & 7) << 2); + + byte b[] = new byte[6]; + + b[0] = 27; + b[1] = (byte) '['; + b[2] = (byte) 'M'; + b[3] = (byte) mousecode; + b[4] = (byte) (0x20 + x + 1); + b[5] = (byte) (0x20 + y + 1); + + write(b); // FIXME: writeSpecial here + } + + /** + * Terminal is mouse-aware and requires the coordinates and button + * of the release. + * @param x + * @param y + * @param modifiers + */ + public void mouseReleased(int x, int y, int modifiers) { + if (mouserpt == 0) + return; + + /* problem is tht modifiers still have the released button set in them. + int mods = modifiers; + mousebut = 3; + if ((mods & 16)==16) mousebut=0; + if ((mods & 8)==8 ) mousebut=1; + if ((mods & 4)==4 ) mousebut=2; + */ + + int mousecode; + if (mouserpt == 9) + mousecode = 0x20 + mousebut; /* same as press? appears so. */ + else + mousecode = '#'; + + byte b[] = new byte[6]; + b[0] = 27; + b[1] = (byte) '['; + b[2] = (byte) 'M'; + b[3] = (byte) mousecode; + b[4] = (byte) (0x20 + x + 1); + b[5] = (byte) (0x20 + y + 1); + write(b); // FIXME: writeSpecial here + mousebut = 0; + } + + + /** we should do localecho (passed from other modules). false is default */ + private boolean localecho = false; + + /** + * Enable or disable the local echo property of the terminal. + * @param echo true if the terminal should echo locally + */ + public void setLocalEcho(boolean echo) { + localecho = echo; + } + + /** + * Enable the VMS mode of the terminal to handle some things differently + * for VMS hosts. + * @param vms true for vms mode, false for normal mode + */ + public void setVMS(boolean vms) { + this.vms = vms; + } + + /** + * Enable the usage of the IBM character set used by some BBS's. Special + * graphical character are available in this mode. + * @param ibm true to use the ibm character set + */ + public void setIBMCharset(boolean ibm) { + useibmcharset = ibm; + } + + /** + * Override the standard key codes used by the terminal emulation. + * @param codes a properties object containing key code definitions + */ + public void setKeyCodes(Properties codes) { + String res, prefixes[] = {"", "S", "C", "A"}; + int i; + + for (i = 0; i < 10; i++) { + res = codes.getProperty("NUMPAD" + i); + if (res != null) Numpad[i] = unEscape(res); + } + for (i = 1; i < 20; i++) { + res = codes.getProperty("F" + i); + if (res != null) FunctionKey[i] = unEscape(res); + res = codes.getProperty("SF" + i); + if (res != null) FunctionKeyShift[i] = unEscape(res); + res = codes.getProperty("CF" + i); + if (res != null) FunctionKeyCtrl[i] = unEscape(res); + res = codes.getProperty("AF" + i); + if (res != null) FunctionKeyAlt[i] = unEscape(res); + } + for (i = 0; i < 4; i++) { + res = codes.getProperty(prefixes[i] + "PGUP"); + if (res != null) PrevScn[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "PGDOWN"); + if (res != null) NextScn[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "END"); + if (res != null) KeyEnd[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "HOME"); + if (res != null) KeyHome[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "INSERT"); + if (res != null) Insert[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "REMOVE"); + if (res != null) Remove[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "UP"); + if (res != null) KeyUp[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "DOWN"); + if (res != null) KeyDown[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "LEFT"); + if (res != null) KeyLeft[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "RIGHT"); + if (res != null) KeyRight[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "ESCAPE"); + if (res != null) Escape[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "BACKSPACE"); + if (res != null) BackSpace[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "TAB"); + if (res != null) TabKey[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "NUMPLUS"); + if (res != null) NUMPlus[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "NUMDECIMAL"); + if (res != null) NUMDot[i] = unEscape(res); + } + } + + /** + * Set the terminal id used to identify this terminal. + * @param terminalID the id string + */ + public void setTerminalID(String terminalID) { + this.terminalID = terminalID; + + if (terminalID.equals("scoansi")) { + FunctionKey[1] = "\u001b[M"; FunctionKey[2] = "\u001b[N"; + FunctionKey[3] = "\u001b[O"; FunctionKey[4] = "\u001b[P"; + FunctionKey[5] = "\u001b[Q"; FunctionKey[6] = "\u001b[R"; + FunctionKey[7] = "\u001b[S"; FunctionKey[8] = "\u001b[T"; + FunctionKey[9] = "\u001b[U"; FunctionKey[10] = "\u001b[V"; + FunctionKey[11] = "\u001b[W"; FunctionKey[12] = "\u001b[X"; + FunctionKey[13] = "\u001b[Y"; FunctionKey[14] = "?"; + FunctionKey[15] = "\u001b[a"; FunctionKey[16] = "\u001b[b"; + FunctionKey[17] = "\u001b[c"; FunctionKey[18] = "\u001b[d"; + FunctionKey[19] = "\u001b[e"; FunctionKey[20] = "\u001b[f"; + PrevScn[0] = PrevScn[1] = PrevScn[2] = PrevScn[3] = "\u001b[I"; + NextScn[0] = NextScn[1] = NextScn[2] = NextScn[3] = "\u001b[G"; + // more theoretically. + } + } + + public void setAnswerBack(String ab) { + this.answerBack = unEscape(ab); + } + + /** + * Get the terminal id used to identify this terminal. + */ + public String getTerminalID() { + return terminalID; + } + + /** + * A small conveniance method thar converts the string to a byte array + * for sending. + * @param s the string to be sent + */ + private boolean write(String s, boolean doecho) { + if (debug > 2) System.out.println("write(|" + s + "|," + doecho); + if (s == null) // aka the empty string. + return true; + /* NOTE: getBytes() honours some locale, it *CONVERTS* the string. + * However, we output only 7bit stuff towards the target, and *some* + * 8 bit control codes. We must not mess up the latter, so we do hand + * by hand copy. + */ + + byte arr[] = new byte[s.length()]; + for (int i = 0; i < s.length(); i++) { + arr[i] = (byte) s.charAt(i); + } + write(arr); + + if (doecho) + putString(s); + return true; + } + + private boolean write(String s) { + return write(s, localecho); + } + + // =================================================================== + // the actual terminal emulation code comes here: + // =================================================================== + + private String terminalID = "vt320"; + private String answerBack = "Use Terminal.answerback to set ...\n"; + + // X - COLUMNS, Y - ROWS + int R,C; + int attributes = 0; + + int Sc,Sr,Sa,Stm,Sbm; + char Sgr,Sgl; + char Sgx[]; + + int insertmode = 0; + int statusmode = 0; + boolean vt52mode = false; + boolean keypadmode = false; /* false - numeric, true - application */ + boolean output8bit = false; + int normalcursor = 0; + boolean moveoutsidemargins = true; + boolean wraparound = true; + boolean sendcrlf = true; + boolean capslock = false; + boolean numlock = false; + int mouserpt = 0; + byte mousebut = 0; + + boolean useibmcharset = false; + + int lastwaslf = 0; + boolean usedcharsets = false; + + private final static char ESC = 27; + private final static char IND = 132; + private final static char NEL = 133; + private final static char RI = 141; + private final static char SS2 = 142; + private final static char SS3 = 143; + private final static char DCS = 144; + private final static char HTS = 136; + private final static char CSI = 155; + private final static char OSC = 157; + private final static int TSTATE_DATA = 0; + private final static int TSTATE_ESC = 1; /* ESC */ + private final static int TSTATE_CSI = 2; /* ESC [ */ + private final static int TSTATE_DCS = 3; /* ESC P */ + private final static int TSTATE_DCEQ = 4; /* ESC [? */ + private final static int TSTATE_ESCSQUARE = 5; /* ESC # */ + private final static int TSTATE_OSC = 6; /* ESC ] */ + private final static int TSTATE_SETG0 = 7; /* ESC (? */ + private final static int TSTATE_SETG1 = 8; /* ESC )? */ + private final static int TSTATE_SETG2 = 9; /* ESC *? */ + private final static int TSTATE_SETG3 = 10; /* ESC +? */ + private final static int TSTATE_CSI_DOLLAR = 11; /* ESC [ Pn $ */ + private final static int TSTATE_CSI_EX = 12; /* ESC [ ! */ + private final static int TSTATE_ESCSPACE = 13; /* ESC <space> */ + private final static int TSTATE_VT52X = 14; + private final static int TSTATE_VT52Y = 15; + private final static int TSTATE_CSI_TICKS = 16; + private final static int TSTATE_CSI_EQUAL = 17; /* ESC [ = */ + + /* Keys we support */ + public final static int KEY_PAUSE = 1; + public final static int KEY_F1 = 2; + public final static int KEY_F2 = 3; + public final static int KEY_F3 = 4; + public final static int KEY_F4 = 5; + public final static int KEY_F5 = 6; + public final static int KEY_F6 = 7; + public final static int KEY_F7 = 8; + public final static int KEY_F8 = 9; + public final static int KEY_F9 = 10; + public final static int KEY_F10 = 11; + public final static int KEY_F11 = 12; + public final static int KEY_F12 = 13; + public final static int KEY_UP = 14; + public final static int KEY_DOWN =15 ; + public final static int KEY_LEFT = 16; + public final static int KEY_RIGHT = 17; + public final static int KEY_PAGE_DOWN = 18; + public final static int KEY_PAGE_UP = 19; + public final static int KEY_INSERT = 20; + public final static int KEY_DELETE = 21; + public final static int KEY_BACK_SPACE = 22; + public final static int KEY_HOME = 23; + public final static int KEY_END = 24; + public final static int KEY_NUM_LOCK = 25; + public final static int KEY_CAPS_LOCK = 26; + public final static int KEY_SHIFT = 27; + public final static int KEY_CONTROL = 28; + public final static int KEY_ALT = 29; + public final static int KEY_ENTER = 30; + public final static int KEY_NUMPAD0 = 31; + public final static int KEY_NUMPAD1 = 32; + public final static int KEY_NUMPAD2 = 33; + public final static int KEY_NUMPAD3 = 34; + public final static int KEY_NUMPAD4 = 35; + public final static int KEY_NUMPAD5 = 36; + public final static int KEY_NUMPAD6 = 37; + public final static int KEY_NUMPAD7 = 38; + public final static int KEY_NUMPAD8 = 39; + public final static int KEY_NUMPAD9 = 40; + public final static int KEY_DECIMAL = 41; + public final static int KEY_ADD = 42; + public final static int KEY_ESCAPE = 43; + + /* The graphics charsets + * B - default ASCII + * A - ISO Latin 1 + * 0 - DEC SPECIAL + * < - User defined + * .... + */ + char gx[] = {// same initial set as in XTERM. + 'B', // g0 + '0', // g1 + 'B', // g2 + 'B', // g3 + }; + char gl = 0; // default GL to G0 + char gr = 2; // default GR to G2 + int onegl = -1; // single shift override for GL. + + // Map from scoansi linedrawing to DEC _and_ unicode (for the stuff which + // is not in linedrawing). Got from experimenting with scoadmin. + private final static String scoansi_acs = "Tm7k3x4u?kZl@mYjEnB\u2566DqCtAvM\u2550:\u2551N\u2557I\u2554;\u2557H\u255a0a<\u255d"; + // array to store DEC Special -> Unicode mapping + // Unicode DEC Unicode name (DEC name) + private static char DECSPECIAL[] = { + '\u0040', //5f blank + '\u2666', //60 black diamond + '\u2592', //61 grey square + '\u2409', //62 Horizontal tab (ht) pict. for control + '\u240c', //63 Form Feed (ff) pict. for control + '\u240d', //64 Carriage Return (cr) pict. for control + '\u240a', //65 Line Feed (lf) pict. for control + '\u00ba', //66 Masculine ordinal indicator + '\u00b1', //67 Plus or minus sign + '\u2424', //68 New Line (nl) pict. for control + '\u240b', //69 Vertical Tab (vt) pict. for control + '\u2518', //6a Forms light up and left + '\u2510', //6b Forms light down and left + '\u250c', //6c Forms light down and right + '\u2514', //6d Forms light up and right + '\u253c', //6e Forms light vertical and horizontal + '\u2594', //6f Upper 1/8 block (Scan 1) + '\u2580', //70 Upper 1/2 block (Scan 3) + '\u2500', //71 Forms light horizontal or ?em dash? (Scan 5) + '\u25ac', //72 \u25ac black rect. or \u2582 lower 1/4 (Scan 7) + '\u005f', //73 \u005f underscore or \u2581 lower 1/8 (Scan 9) + '\u251c', //74 Forms light vertical and right + '\u2524', //75 Forms light vertical and left + '\u2534', //76 Forms light up and horizontal + '\u252c', //77 Forms light down and horizontal + '\u2502', //78 vertical bar + '\u2264', //79 less than or equal + '\u2265', //7a greater than or equal + '\u00b6', //7b paragraph + '\u2260', //7c not equal + '\u00a3', //7d Pound Sign (british) + '\u00b7' //7e Middle Dot + }; + + /** Strings to send on function key pressing */ + private String Numpad[]; + private String FunctionKey[]; + private String FunctionKeyShift[]; + private String FunctionKeyCtrl[]; + private String FunctionKeyAlt[]; + private String TabKey[]; + private String KeyUp[],KeyDown[],KeyLeft[],KeyRight[]; + private String KPMinus, KPComma, KPPeriod, KPEnter; + private String PF1, PF2, PF3, PF4; + private String Help, Do, Find, Select; + + private String KeyHome[], KeyEnd[], Insert[], Remove[], PrevScn[], NextScn[]; + private String Escape[], BackSpace[], NUMDot[], NUMPlus[]; + + private String osc,dcs; /* to memorize OSC & DCS control sequence */ + + /** vt320 state variable (internal) */ + private int term_state = TSTATE_DATA; + /** in vms mode, set by Terminal.VMS property */ + private boolean vms = false; + /** Tabulators */ + private byte[] Tabs; + /** The list of integers as used by CSI */ + private int[] DCEvars = new int[30]; + private int DCEvar; + + /** + * Replace escape code characters (backslash + identifier) with their + * respective codes. + * @param tmp the string to be parsed + * @return a unescaped string + */ + static String unEscape(String tmp) { + int idx = 0, oldidx = 0; + String cmd; + // System.err.println("unescape("+tmp+")"); + cmd = ""; + while ((idx = tmp.indexOf('\\', oldidx)) >= 0 && + ++idx <= tmp.length()) { + cmd += tmp.substring(oldidx, idx - 1); + if (idx == tmp.length()) return cmd; + switch (tmp.charAt(idx)) { + case 'b': + cmd += "\b"; + break; + case 'e': + cmd += "\u001b"; + break; + case 'n': + cmd += "\n"; + break; + case 'r': + cmd += "\r"; + break; + case 't': + cmd += "\t"; + break; + case 'v': + cmd += "\u000b"; + break; + case 'a': + cmd += "\u0012"; + break; + default : + if ((tmp.charAt(idx) >= '0') && (tmp.charAt(idx) <= '9')) { + int i; + for (i = idx; i < tmp.length(); i++) + if ((tmp.charAt(i) < '0') || (tmp.charAt(i) > '9')) + break; + cmd += (char) Integer.parseInt(tmp.substring(idx, i)); + idx = i - 1; + } else + cmd += tmp.substring(idx, ++idx); + break; + } + oldidx = ++idx; + } + if (oldidx <= tmp.length()) cmd += tmp.substring(oldidx); + return cmd; + } + + /** + * A small conveniance method thar converts a 7bit string to the 8bit + * version depending on VT52/Output8Bit mode. + * + * @param s the string to be sent + */ + private boolean writeSpecial(String s) { + if (s == null) + return true; + if (((s.length() >= 3) && (s.charAt(0) == 27) && (s.charAt(1) == 'O'))) { + if (vt52mode) { + if ((s.charAt(2) >= 'P') && (s.charAt(2) <= 'S')) { + s = "\u001b" + s.substring(2); /* ESC x */ + } else { + s = "\u001b?" + s.substring(2); /* ESC ? x */ + } + } else { + if (output8bit) { + s = "\u008f" + s.substring(2); /* SS3 x */ + } /* else keep string as it is */ + } + } + if (((s.length() >= 3) && (s.charAt(0) == 27) && (s.charAt(1) == '['))) { + if (output8bit) { + s = "\u009b" + s.substring(2); /* CSI ... */ + } /* else keep */ + } + return write(s, false); + } + + /** + * main keytyping event handler... + */ + public void keyPressed(int keyCode, char keyChar, int modifiers) { + boolean control = (modifiers & VDUInput.KEY_CONTROL) != 0; + boolean shift = (modifiers & VDUInput.KEY_SHIFT) != 0; + boolean alt = (modifiers & VDUInput.KEY_ALT) != 0; + + if (debug > 1) System.out.println("keyPressed("+keyCode+", "+(int)keyChar+", "+modifiers+")"); + + int xind; + String fmap[]; + xind = 0; + fmap = FunctionKey; + if (shift) { + fmap = FunctionKeyShift; + xind = 1; + } + if (control) { + fmap = FunctionKeyCtrl; + xind = 2; + } + if (alt) { + fmap = FunctionKeyAlt; + xind = 3; + } + + switch (keyCode) { + case KEY_PAUSE: + if (shift || control) + sendTelnetCommand((byte) 243); // BREAK + break; + case KEY_F1: + writeSpecial(fmap[1]); + break; + case KEY_F2: + writeSpecial(fmap[2]); + break; + case KEY_F3: + writeSpecial(fmap[3]); + break; + case KEY_F4: + writeSpecial(fmap[4]); + break; + case KEY_F5: + writeSpecial(fmap[5]); + break; + case KEY_F6: + writeSpecial(fmap[6]); + break; + case KEY_F7: + writeSpecial(fmap[7]); + break; + case KEY_F8: + writeSpecial(fmap[8]); + break; + case KEY_F9: + writeSpecial(fmap[9]); + break; + case KEY_F10: + writeSpecial(fmap[10]); + break; + case KEY_F11: + writeSpecial(fmap[11]); + break; + case KEY_F12: + writeSpecial(fmap[12]); + break; + case KEY_UP: + writeSpecial(KeyUp[xind]); + break; + case KEY_DOWN: + writeSpecial(KeyDown[xind]); + break; + case KEY_LEFT: + writeSpecial(KeyLeft[xind]); + break; + case KEY_RIGHT: + writeSpecial(KeyRight[xind]); + break; + case KEY_PAGE_DOWN: + writeSpecial(NextScn[xind]); + break; + case KEY_PAGE_UP: + writeSpecial(PrevScn[xind]); + break; + case KEY_INSERT: + writeSpecial(Insert[xind]); + break; + case KEY_DELETE: + writeSpecial(Remove[xind]); + break; + case KEY_BACK_SPACE: + writeSpecial(BackSpace[xind]); + if (localecho) { + if (BackSpace[xind] == "\b") { + putString("\b \b"); // make the last char 'deleted' + } else { + putString(BackSpace[xind]); // echo it + } + } + break; + case KEY_HOME: + writeSpecial(KeyHome[xind]); + break; + case KEY_END: + writeSpecial(KeyEnd[xind]); + break; + case KEY_NUM_LOCK: + if (vms && control) { + writeSpecial(PF1); + } + if (!control) + numlock = !numlock; + break; + case KEY_CAPS_LOCK: + capslock = !capslock; + return; + case KEY_SHIFT: + case KEY_CONTROL: + case KEY_ALT: + return; + default: + break; + } + } +/* + public void keyReleased(KeyEvent evt) { + if (debug > 1) System.out.println("keyReleased("+evt+")"); + // ignore + } +*/ + /** + * Handle key Typed events for the terminal, this will get + * all normal key types, but no shift/alt/control/numlock. + */ + public void keyTyped(int keyCode, char keyChar, int modifiers) { + boolean control = (modifiers & VDUInput.KEY_CONTROL) != 0; + boolean shift = (modifiers & VDUInput.KEY_SHIFT) != 0; + boolean alt = (modifiers & VDUInput.KEY_ALT) != 0; + + if (debug > 1) System.out.println("keyTyped("+keyCode+", "+(int)keyChar+", "+modifiers+")"); + + if (keyChar == '\t') { + if (shift) { + write(TabKey[1], false); + } else { + if (control) { + write(TabKey[2], false); + } else { + if (alt) { + write(TabKey[3], false); + } else { + write(TabKey[0], false); + } + } + } + return; + } + if (alt) { + write("" + ((char) (keyChar | 0x80))); + return; + } + + if (((keyCode == KEY_ENTER) || (keyChar == 10)) + && !control) { + write("\r", false); + if (localecho) putString("\r\n"); // bad hack + return; + } + + if ((keyCode == 10) && !control) { + System.out.println("Sending \\r"); + write("\r", false); + return; + } + + // FIXME: on german PC keyboards you have to use Alt-Ctrl-q to get an @, + // so we can't just use it here... will probably break some other VMS + // codes. -Marcus + // if(((!vms && keyChar == '2') || keyChar == '@' || keyChar == ' ') + // && control) + if (((!vms && keyChar == '2') || keyChar == ' ') && control) + write("" + (char) 0); + + if (vms) { + if (keyChar == 127 && !control) { + if (shift) + writeSpecial(Insert[0]); // VMS shift delete = insert + else + writeSpecial(Remove[0]); // VMS delete = remove + return; + } else if (control) + switch (keyChar) { + case '0': + writeSpecial(Numpad[0]); + return; + case '1': + writeSpecial(Numpad[1]); + return; + case '2': + writeSpecial(Numpad[2]); + return; + case '3': + writeSpecial(Numpad[3]); + return; + case '4': + writeSpecial(Numpad[4]); + return; + case '5': + writeSpecial(Numpad[5]); + return; + case '6': + writeSpecial(Numpad[6]); + return; + case '7': + writeSpecial(Numpad[7]); + return; + case '8': + writeSpecial(Numpad[8]); + return; + case '9': + writeSpecial(Numpad[9]); + return; + case '.': + writeSpecial(KPPeriod); + return; + case '-': + case 31: + writeSpecial(KPMinus); + return; + case '+': + writeSpecial(KPComma); + return; + case 10: + writeSpecial(KPEnter); + return; + case '/': + writeSpecial(PF2); + return; + case '*': + writeSpecial(PF3); + return; + /* NUMLOCK handled in keyPressed */ + default: + break; + } + /* Now what does this do and how did it get here. -Marcus + if (shift && keyChar < 32) { + write(PF1+(char)(keyChar + 64)); + return; + } + */ + } + + // FIXME: not used? + //String fmap[]; + int xind; + xind = 0; + //fmap = FunctionKey; + if (shift) { + //fmap = FunctionKeyShift; + xind = 1; + } + if (control) { + //fmap = FunctionKeyCtrl; + xind = 2; + } + if (alt) { + //fmap = FunctionKeyAlt; + xind = 3; + } + + if (keyCode == KEY_ESCAPE) { + writeSpecial(Escape[xind]); + return; + } + + if ((modifiers & VDUInput.KEY_ACTION) != 0) + switch (keyCode) { + case KEY_NUMPAD0: + writeSpecial(Numpad[0]); + return; + case KEY_NUMPAD1: + writeSpecial(Numpad[1]); + return; + case KEY_NUMPAD2: + writeSpecial(Numpad[2]); + return; + case KEY_NUMPAD3: + writeSpecial(Numpad[3]); + return; + case KEY_NUMPAD4: + writeSpecial(Numpad[4]); + return; + case KEY_NUMPAD5: + writeSpecial(Numpad[5]); + return; + case KEY_NUMPAD6: + writeSpecial(Numpad[6]); + return; + case KEY_NUMPAD7: + writeSpecial(Numpad[7]); + return; + case KEY_NUMPAD8: + writeSpecial(Numpad[8]); + return; + case KEY_NUMPAD9: + writeSpecial(Numpad[9]); + return; + case KEY_DECIMAL: + writeSpecial(NUMDot[xind]); + return; + case KEY_ADD: + writeSpecial(NUMPlus[xind]); + return; + } + + if (!((keyChar == 8) || (keyChar == 127) || (keyChar == '\r') || (keyChar == '\n'))) { + write("" + keyChar); + return; + } + } + + private void handle_dcs(String dcs) { + System.out.println("DCS: " + dcs); + } + + private void handle_osc(String osc) { + System.out.println("OSC: " + osc); + } + + private final static char unimap[] = { + //# + //# Name: cp437_DOSLatinUS to Unicode table + //# Unicode version: 1.1 + //# Table version: 1.1 + //# Table format: Format A + //# Date: 03/31/95 + //# Authors: Michel Suignard <michelsu@microsoft.com> + //# Lori Hoerth <lorih@microsoft.com> + //# General notes: none + //# + //# Format: Three tab-separated columns + //# Column #1 is the cp1255_WinHebrew code (in hex) + //# Column #2 is the Unicode (in hex as 0xXXXX) + //# Column #3 is the Unicode name (follows a comment sign, '#') + //# + //# The entries are in cp437_DOSLatinUS order + //# + + 0x0000, // #NULL + 0x0001, // #START OF HEADING + 0x0002, // #START OF TEXT + 0x0003, // #END OF TEXT + 0x0004, // #END OF TRANSMISSION + 0x0005, // #ENQUIRY + 0x0006, // #ACKNOWLEDGE + 0x0007, // #BELL + 0x0008, // #BACKSPACE + 0x0009, // #HORIZONTAL TABULATION + 0x000a, // #LINE FEED + 0x000b, // #VERTICAL TABULATION + 0x000c, // #FORM FEED + 0x000d, // #CARRIAGE RETURN + 0x000e, // #SHIFT OUT + 0x000f, // #SHIFT IN + 0x0010, // #DATA LINK ESCAPE + 0x0011, // #DEVICE CONTROL ONE + 0x0012, // #DEVICE CONTROL TWO + 0x0013, // #DEVICE CONTROL THREE + 0x0014, // #DEVICE CONTROL FOUR + 0x0015, // #NEGATIVE ACKNOWLEDGE + 0x0016, // #SYNCHRONOUS IDLE + 0x0017, // #END OF TRANSMISSION BLOCK + 0x0018, // #CANCEL + 0x0019, // #END OF MEDIUM + 0x001a, // #SUBSTITUTE + 0x001b, // #ESCAPE + 0x001c, // #FILE SEPARATOR + 0x001d, // #GROUP SEPARATOR + 0x001e, // #RECORD SEPARATOR + 0x001f, // #UNIT SEPARATOR + 0x0020, // #SPACE + 0x0021, // #EXCLAMATION MARK + 0x0022, // #QUOTATION MARK + 0x0023, // #NUMBER SIGN + 0x0024, // #DOLLAR SIGN + 0x0025, // #PERCENT SIGN + 0x0026, // #AMPERSAND + 0x0027, // #APOSTROPHE + 0x0028, // #LEFT PARENTHESIS + 0x0029, // #RIGHT PARENTHESIS + 0x002a, // #ASTERISK + 0x002b, // #PLUS SIGN + 0x002c, // #COMMA + 0x002d, // #HYPHEN-MINUS + 0x002e, // #FULL STOP + 0x002f, // #SOLIDUS + 0x0030, // #DIGIT ZERO + 0x0031, // #DIGIT ONE + 0x0032, // #DIGIT TWO + 0x0033, // #DIGIT THREE + 0x0034, // #DIGIT FOUR + 0x0035, // #DIGIT FIVE + 0x0036, // #DIGIT SIX + 0x0037, // #DIGIT SEVEN + 0x0038, // #DIGIT EIGHT + 0x0039, // #DIGIT NINE + 0x003a, // #COLON + 0x003b, // #SEMICOLON + 0x003c, // #LESS-THAN SIGN + 0x003d, // #EQUALS SIGN + 0x003e, // #GREATER-THAN SIGN + 0x003f, // #QUESTION MARK + 0x0040, // #COMMERCIAL AT + 0x0041, // #LATIN CAPITAL LETTER A + 0x0042, // #LATIN CAPITAL LETTER B + 0x0043, // #LATIN CAPITAL LETTER C + 0x0044, // #LATIN CAPITAL LETTER D + 0x0045, // #LATIN CAPITAL LETTER E + 0x0046, // #LATIN CAPITAL LETTER F + 0x0047, // #LATIN CAPITAL LETTER G + 0x0048, // #LATIN CAPITAL LETTER H + 0x0049, // #LATIN CAPITAL LETTER I + 0x004a, // #LATIN CAPITAL LETTER J + 0x004b, // #LATIN CAPITAL LETTER K + 0x004c, // #LATIN CAPITAL LETTER L + 0x004d, // #LATIN CAPITAL LETTER M + 0x004e, // #LATIN CAPITAL LETTER N + 0x004f, // #LATIN CAPITAL LETTER O + 0x0050, // #LATIN CAPITAL LETTER P + 0x0051, // #LATIN CAPITAL LETTER Q + 0x0052, // #LATIN CAPITAL LETTER R + 0x0053, // #LATIN CAPITAL LETTER S + 0x0054, // #LATIN CAPITAL LETTER T + 0x0055, // #LATIN CAPITAL LETTER U + 0x0056, // #LATIN CAPITAL LETTER V + 0x0057, // #LATIN CAPITAL LETTER W + 0x0058, // #LATIN CAPITAL LETTER X + 0x0059, // #LATIN CAPITAL LETTER Y + 0x005a, // #LATIN CAPITAL LETTER Z + 0x005b, // #LEFT SQUARE BRACKET + 0x005c, // #REVERSE SOLIDUS + 0x005d, // #RIGHT SQUARE BRACKET + 0x005e, // #CIRCUMFLEX ACCENT + 0x005f, // #LOW LINE + 0x0060, // #GRAVE ACCENT + 0x0061, // #LATIN SMALL LETTER A + 0x0062, // #LATIN SMALL LETTER B + 0x0063, // #LATIN SMALL LETTER C + 0x0064, // #LATIN SMALL LETTER D + 0x0065, // #LATIN SMALL LETTER E + 0x0066, // #LATIN SMALL LETTER F + 0x0067, // #LATIN SMALL LETTER G + 0x0068, // #LATIN SMALL LETTER H + 0x0069, // #LATIN SMALL LETTER I + 0x006a, // #LATIN SMALL LETTER J + 0x006b, // #LATIN SMALL LETTER K + 0x006c, // #LATIN SMALL LETTER L + 0x006d, // #LATIN SMALL LETTER M + 0x006e, // #LATIN SMALL LETTER N + 0x006f, // #LATIN SMALL LETTER O + 0x0070, // #LATIN SMALL LETTER P + 0x0071, // #LATIN SMALL LETTER Q + 0x0072, // #LATIN SMALL LETTER R + 0x0073, // #LATIN SMALL LETTER S + 0x0074, // #LATIN SMALL LETTER T + 0x0075, // #LATIN SMALL LETTER U + 0x0076, // #LATIN SMALL LETTER V + 0x0077, // #LATIN SMALL LETTER W + 0x0078, // #LATIN SMALL LETTER X + 0x0079, // #LATIN SMALL LETTER Y + 0x007a, // #LATIN SMALL LETTER Z + 0x007b, // #LEFT CURLY BRACKET + 0x007c, // #VERTICAL LINE + 0x007d, // #RIGHT CURLY BRACKET + 0x007e, // #TILDE + 0x007f, // #DELETE + 0x00c7, // #LATIN CAPITAL LETTER C WITH CEDILLA + 0x00fc, // #LATIN SMALL LETTER U WITH DIAERESIS + 0x00e9, // #LATIN SMALL LETTER E WITH ACUTE + 0x00e2, // #LATIN SMALL LETTER A WITH CIRCUMFLEX + 0x00e4, // #LATIN SMALL LETTER A WITH DIAERESIS + 0x00e0, // #LATIN SMALL LETTER A WITH GRAVE + 0x00e5, // #LATIN SMALL LETTER A WITH RING ABOVE + 0x00e7, // #LATIN SMALL LETTER C WITH CEDILLA + 0x00ea, // #LATIN SMALL LETTER E WITH CIRCUMFLEX + 0x00eb, // #LATIN SMALL LETTER E WITH DIAERESIS + 0x00e8, // #LATIN SMALL LETTER E WITH GRAVE + 0x00ef, // #LATIN SMALL LETTER I WITH DIAERESIS + 0x00ee, // #LATIN SMALL LETTER I WITH CIRCUMFLEX + 0x00ec, // #LATIN SMALL LETTER I WITH GRAVE + 0x00c4, // #LATIN CAPITAL LETTER A WITH DIAERESIS + 0x00c5, // #LATIN CAPITAL LETTER A WITH RING ABOVE + 0x00c9, // #LATIN CAPITAL LETTER E WITH ACUTE + 0x00e6, // #LATIN SMALL LIGATURE AE + 0x00c6, // #LATIN CAPITAL LIGATURE AE + 0x00f4, // #LATIN SMALL LETTER O WITH CIRCUMFLEX + 0x00f6, // #LATIN SMALL LETTER O WITH DIAERESIS + 0x00f2, // #LATIN SMALL LETTER O WITH GRAVE + 0x00fb, // #LATIN SMALL LETTER U WITH CIRCUMFLEX + 0x00f9, // #LATIN SMALL LETTER U WITH GRAVE + 0x00ff, // #LATIN SMALL LETTER Y WITH DIAERESIS + 0x00d6, // #LATIN CAPITAL LETTER O WITH DIAERESIS + 0x00dc, // #LATIN CAPITAL LETTER U WITH DIAERESIS + 0x00a2, // #CENT SIGN + 0x00a3, // #POUND SIGN + 0x00a5, // #YEN SIGN + 0x20a7, // #PESETA SIGN + 0x0192, // #LATIN SMALL LETTER F WITH HOOK + 0x00e1, // #LATIN SMALL LETTER A WITH ACUTE + 0x00ed, // #LATIN SMALL LETTER I WITH ACUTE + 0x00f3, // #LATIN SMALL LETTER O WITH ACUTE + 0x00fa, // #LATIN SMALL LETTER U WITH ACUTE + 0x00f1, // #LATIN SMALL LETTER N WITH TILDE + 0x00d1, // #LATIN CAPITAL LETTER N WITH TILDE + 0x00aa, // #FEMININE ORDINAL INDICATOR + 0x00ba, // #MASCULINE ORDINAL INDICATOR + 0x00bf, // #INVERTED QUESTION MARK + 0x2310, // #REVERSED NOT SIGN + 0x00ac, // #NOT SIGN + 0x00bd, // #VULGAR FRACTION ONE HALF + 0x00bc, // #VULGAR FRACTION ONE QUARTER + 0x00a1, // #INVERTED EXCLAMATION MARK + 0x00ab, // #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + 0x00bb, // #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + 0x2591, // #LIGHT SHADE + 0x2592, // #MEDIUM SHADE + 0x2593, // #DARK SHADE + 0x2502, // #BOX DRAWINGS LIGHT VERTICAL + 0x2524, // #BOX DRAWINGS LIGHT VERTICAL AND LEFT + 0x2561, // #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE + 0x2562, // #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE + 0x2556, // #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE + 0x2555, // #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE + 0x2563, // #BOX DRAWINGS DOUBLE VERTICAL AND LEFT + 0x2551, // #BOX DRAWINGS DOUBLE VERTICAL + 0x2557, // #BOX DRAWINGS DOUBLE DOWN AND LEFT + 0x255d, // #BOX DRAWINGS DOUBLE UP AND LEFT + 0x255c, // #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE + 0x255b, // #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE + 0x2510, // #BOX DRAWINGS LIGHT DOWN AND LEFT + 0x2514, // #BOX DRAWINGS LIGHT UP AND RIGHT + 0x2534, // #BOX DRAWINGS LIGHT UP AND HORIZONTAL + 0x252c, // #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + 0x251c, // #BOX DRAWINGS LIGHT VERTICAL AND RIGHT + 0x2500, // #BOX DRAWINGS LIGHT HORIZONTAL + 0x253c, // #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + 0x255e, // #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE + 0x255f, // #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE + 0x255a, // #BOX DRAWINGS DOUBLE UP AND RIGHT + 0x2554, // #BOX DRAWINGS DOUBLE DOWN AND RIGHT + 0x2569, // #BOX DRAWINGS DOUBLE UP AND HORIZONTAL + 0x2566, // #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL + 0x2560, // #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT + 0x2550, // #BOX DRAWINGS DOUBLE HORIZONTAL + 0x256c, // #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL + 0x2567, // #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE + 0x2568, // #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE + 0x2564, // #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE + 0x2565, // #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE + 0x2559, // #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE + 0x2558, // #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE + 0x2552, // #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE + 0x2553, // #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE + 0x256b, // #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE + 0x256a, // #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE + 0x2518, // #BOX DRAWINGS LIGHT UP AND LEFT + 0x250c, // #BOX DRAWINGS LIGHT DOWN AND RIGHT + 0x2588, // #FULL BLOCK + 0x2584, // #LOWER HALF BLOCK + 0x258c, // #LEFT HALF BLOCK + 0x2590, // #RIGHT HALF BLOCK + 0x2580, // #UPPER HALF BLOCK + 0x03b1, // #GREEK SMALL LETTER ALPHA + 0x00df, // #LATIN SMALL LETTER SHARP S + 0x0393, // #GREEK CAPITAL LETTER GAMMA + 0x03c0, // #GREEK SMALL LETTER PI + 0x03a3, // #GREEK CAPITAL LETTER SIGMA + 0x03c3, // #GREEK SMALL LETTER SIGMA + 0x00b5, // #MICRO SIGN + 0x03c4, // #GREEK SMALL LETTER TAU + 0x03a6, // #GREEK CAPITAL LETTER PHI + 0x0398, // #GREEK CAPITAL LETTER THETA + 0x03a9, // #GREEK CAPITAL LETTER OMEGA + 0x03b4, // #GREEK SMALL LETTER DELTA + 0x221e, // #INFINITY + 0x03c6, // #GREEK SMALL LETTER PHI + 0x03b5, // #GREEK SMALL LETTER EPSILON + 0x2229, // #INTERSECTION + 0x2261, // #IDENTICAL TO + 0x00b1, // #PLUS-MINUS SIGN + 0x2265, // #GREATER-THAN OR EQUAL TO + 0x2264, // #LESS-THAN OR EQUAL TO + 0x2320, // #TOP HALF INTEGRAL + 0x2321, // #BOTTOM HALF INTEGRAL + 0x00f7, // #DIVISION SIGN + 0x2248, // #ALMOST EQUAL TO + 0x00b0, // #DEGREE SIGN + 0x2219, // #BULLET OPERATOR + 0x00b7, // #MIDDLE DOT + 0x221a, // #SQUARE ROOT + 0x207f, // #SUPERSCRIPT LATIN SMALL LETTER N + 0x00b2, // #SUPERSCRIPT TWO + 0x25a0, // #BLACK SQUARE + 0x00a0, // #NO-BREAK SPACE + }; + + public char map_cp850_unicode(char x) { + if (x >= 0x100) + return x; + return unimap[x]; + } + + private void _SetCursor(int row, int col) { + int maxr = getRows(); + int tm = getTopMargin(); + + R = (row < 0)?0:row; + C = (col < 0)?0:col; + + if (!moveoutsidemargins) { + R += tm; + maxr = getBottomMargin(); + } + if (R > maxr) R = maxr; + } + + private void putChar(char c, boolean doshowcursor) { + int rows = getRows(); //statusline + int columns = getColumns(); + int tm = getTopMargin(); + int bm = getBottomMargin(); + // byte msg[]; + boolean mapped = false; + + if (debug > 4) System.out.println("putChar(" + c + " [" + ((int) c) + "]) at R=" + R + " , C=" + C + ", columns=" + columns + ", rows=" + rows); + markLine(R, 1); + if (c > 255) { + if (debug > 0) + System.out.println("char > 255:" + (int) c); + //return; + } + + + switch (term_state) { + case TSTATE_DATA: + /* FIXME: we shouldn't use chars with bit 8 set if ibmcharset. + * probably... but some BBS do anyway... + */ + if (!useibmcharset) { + boolean doneflag = true; + switch (c) { + case OSC: + osc = ""; + term_state = TSTATE_OSC; + break; + case RI: + if (R > tm) + R--; + else + insertLine(R, 1, SCROLL_DOWN); + if (debug > 1) + System.out.println("RI"); + break; + case IND: + if (debug > 2) + System.out.println("IND at " + R + ", tm is " + tm + ", bm is " + bm); + if (R == bm || R == rows - 1) + insertLine(R, 1, SCROLL_UP); + else + R++; + if (debug > 1) + System.out.println("IND (at " + R + " )"); + break; + case NEL: + if (R == bm || R == rows - 1) + insertLine(R, 1, SCROLL_UP); + else + R++; + C = 0; + if (debug > 1) + System.out.println("NEL (at " + R + " )"); + break; + case HTS: + Tabs[C] = 1; + if (debug > 1) + System.out.println("HTS"); + break; + case DCS: + dcs = ""; + term_state = TSTATE_DCS; + break; + default: + doneflag = false; + break; + } + if (doneflag) break; + } + switch (c) { + case SS3: + onegl = 3; + break; + case SS2: + onegl = 2; + break; + case CSI: // should be in the 8bit section, but some BBS use this + DCEvar = 0; + DCEvars[0] = 0; + DCEvars[1] = 0; + DCEvars[2] = 0; + DCEvars[3] = 0; + term_state = TSTATE_CSI; + break; + case ESC: + term_state = TSTATE_ESC; + lastwaslf = 0; + break; + case 5: /* ENQ */ + write(answerBack, false); + break; + case 12: + /* FormFeed, Home for the BBS world */ + deleteArea(0, 0, columns, rows, attributes); + C = R = 0; + break; + case '\b': /* 8 */ + C--; + if (C < 0) + C = 0; + lastwaslf = 0; + break; + case '\t': + do { + // Don't overwrite or insert! TABS are not destructive, but movement! + C++; + } while (C < columns && (Tabs[C] == 0)); + lastwaslf = 0; + break; + case '\r': + C = 0; + break; + case '\n': + if (debug > 3) + System.out.println("R= " + R + ", bm " + bm + ", tm=" + tm + ", rows=" + rows); + if (!vms) { + if (lastwaslf != 0 && lastwaslf != c) // Ray: I do not understand this logic. + break; + lastwaslf = c; + /*C = 0;*/ + } + if (R == bm || R >= rows - 1) + insertLine(R, 1, SCROLL_UP); + else + R++; + break; + case 7: + beep(); + break; + case '\016': /* SMACS , as */ + /* ^N, Shift out - Put G1 into GL */ + gl = 1; + usedcharsets = true; + break; + case '\017': /* RMACS , ae */ + /* ^O, Shift in - Put G0 into GL */ + gl = 0; + usedcharsets = true; + break; + default: + { + int thisgl = gl; + + if (onegl >= 0) { + thisgl = onegl; + onegl = -1; + } + lastwaslf = 0; + if (c < 32) { + if (c != 0) + if (debug > 0) + System.out.println("TSTATE_DATA char: " + ((int) c)); + /*break; some BBS really want those characters, like hearst etc. */ + if (c == 0) /* print 0 ... you bet */ + break; + } + if (C >= columns) { + if (wraparound) { + if (R < rows - 1) + R++; + else + insertLine(R, 1, SCROLL_UP); + C = 0; + } else { + // cursor stays on last character. + C = columns - 1; + } + } + + // Mapping if DEC Special is chosen charset + if (usedcharsets) { + if (c >= '\u0020' && c <= '\u007f') { + switch (gx[thisgl]) { + case '0': + // Remap SCOANSI line drawing to VT100 line drawing chars + // for our SCO using customers. + if (terminalID.equals("scoansi") || terminalID.equals("ansi")) { + for (int i = 0; i < scoansi_acs.length(); i += 2) { + if (c == scoansi_acs.charAt(i)) { + c = scoansi_acs.charAt(i + 1); + break; + } + } + } + if (c >= '\u005f' && c <= '\u007e') { + c = DECSPECIAL[(short) c - 0x5f]; + mapped = true; + } + break; + case '<': // 'user preferred' is currently 'ISO Latin-1 suppl + c = (char) (((int) c & 0x7f) | 0x80); + mapped = true; + break; + case 'A': + case 'B': // Latin-1 , ASCII -> fall through + mapped = true; + break; + default: + System.out.println("Unsupported GL mapping: " + gx[thisgl]); + break; + } + } + if (!mapped && (c >= '\u0080' && c <= '\u00ff')) { + switch (gx[gr]) { + case '0': + if (c >= '\u00df' && c <= '\u00fe') { + c = DECSPECIAL[c - '\u00df']; + mapped = true; + } + break; + case '<': + case 'A': + case 'B': + mapped = true; + break; + default: + System.out.println("Unsupported GR mapping: " + gx[gr]); + break; + } + } + } + if (!mapped && useibmcharset) + c = map_cp850_unicode(c); + + /*if(true || (statusmode == 0)) { */ + if (insertmode == 1) { + insertChar(C, R, c, attributes); + } else { + putChar(C, R, c, attributes); + } + /* + } else { + if (insertmode==1) { + insertChar(C, rows, c, attributes); + } else { + putChar(C, rows, c, attributes); + } + } + */ + C++; + break; + } + } /* switch(c) */ + break; + case TSTATE_OSC: + if ((c < 0x20) && (c != ESC)) {// NP - No printing character + handle_osc(osc); + term_state = TSTATE_DATA; + break; + } + //but check for vt102 ESC \ + if (c == '\\' && osc.charAt(osc.length() - 1) == ESC) { + handle_osc(osc); + term_state = TSTATE_DATA; + break; + } + osc = osc + c; + break; + case TSTATE_ESCSPACE: + term_state = TSTATE_DATA; + switch (c) { + case 'F': /* S7C1T, Disable output of 8-bit controls, use 7-bit */ + output8bit = false; + break; + case 'G': /* S8C1T, Enable output of 8-bit control codes*/ + output8bit = true; + break; + default: + System.out.println("ESC <space> " + c + " unhandled."); + } + break; + case TSTATE_ESC: + term_state = TSTATE_DATA; + switch (c) { + case ' ': + term_state = TSTATE_ESCSPACE; + break; + case '#': + term_state = TSTATE_ESCSQUARE; + break; + case 'c': + /* Hard terminal reset */ + /* reset character sets */ + gx[0] = 'B'; + gx[1] = '0'; + gx[2] = 'B'; + gx[3] = 'B'; + gl = 0; // default GL to G0 + gr = 1; // default GR to G1 + /* reset tabs */ + int nw = getColumns(); + if (nw < 132) nw = 132; + Tabs = new byte[nw]; + for (int i = 0; i < nw; i += 8) { + Tabs[i] = 1; + } + /*FIXME:*/ + break; + case '[': + DCEvar = 0; + DCEvars[0] = 0; + DCEvars[1] = 0; + DCEvars[2] = 0; + DCEvars[3] = 0; + term_state = TSTATE_CSI; + break; + case ']': + osc = ""; + term_state = TSTATE_OSC; + break; + case 'P': + dcs = ""; + term_state = TSTATE_DCS; + break; + case 'A': /* CUU */ + R--; + if (R < 0) R = 0; + break; + case 'B': /* CUD */ + R++; + if (R > rows - 1) R = rows - 1; + break; + case 'C': + C++; + if (C >= columns) C = columns - 1; + break; + case 'I': // RI + insertLine(R, 1, SCROLL_DOWN); + break; + case 'E': /* NEL */ + if (R == bm || R == rows - 1) + insertLine(R, 1, SCROLL_UP); + else + R++; + C = 0; + if (debug > 1) + System.out.println("ESC E (at " + R + ")"); + break; + case 'D': /* IND */ + if (R == bm || R == rows - 1) + insertLine(R, 1, SCROLL_UP); + else + R++; + if (debug > 1) + System.out.println("ESC D (at " + R + " )"); + break; + case 'J': /* erase to end of screen */ + if (R < rows - 1) + deleteArea(0, R + 1, columns, rows - R - 1, attributes); + if (C < columns - 1) + deleteArea(C, R, columns - C, 1, attributes); + break; + case 'K': + if (C < columns - 1) + deleteArea(C, R, columns - C, 1, attributes); + break; + case 'M': // RI + System.out.println("ESC M : R is "+R+", tm is "+tm+", bm is "+bm); + if (R > bm) // outside scrolling region + break; + if (R > tm) { // just go up 1 line. + R--; + } else { // scroll down + insertLine(R, 1, SCROLL_DOWN); + } + /* else do nothing ; */ + if (debug > 2) + System.out.println("ESC M "); + break; + case 'H': + if (debug > 1) + System.out.println("ESC H at " + C); + /* right border probably ...*/ + if (C >= columns) + C = columns - 1; + Tabs[C] = 1; + break; + case 'N': // SS2 + onegl = 2; + break; + case 'O': // SS3 + onegl = 3; + break; + case '=': + /*application keypad*/ + if (debug > 0) + System.out.println("ESC ="); + keypadmode = true; + break; + case '<': /* vt52 mode off */ + vt52mode = false; + break; + case '>': /*normal keypad*/ + if (debug > 0) + System.out.println("ESC >"); + keypadmode = false; + break; + case '7': /*save cursor, attributes, margins */ + Sc = C; + Sr = R; + Sgl = gl; + Sgr = gr; + Sa = attributes; + Sgx = new char[4]; + for (int i = 0; i < 4; i++) Sgx[i] = gx[i]; + Stm = getTopMargin(); + Sbm = getBottomMargin(); + if (debug > 1) + System.out.println("ESC 7"); + break; + case '8': /*restore cursor, attributes, margins */ + C = Sc; + R = Sr; + gl = Sgl; + gr = Sgr; + for (int i = 0; i < 4; i++) gx[i] = Sgx[i]; + setTopMargin(Stm); + setBottomMargin(Sbm); + attributes = Sa; + if (debug > 1) + System.out.println("ESC 8"); + break; + case '(': /* Designate G0 Character set (ISO 2022) */ + term_state = TSTATE_SETG0; + usedcharsets = true; + break; + case ')': /* Designate G1 character set (ISO 2022) */ + term_state = TSTATE_SETG1; + usedcharsets = true; + break; + case '*': /* Designate G2 Character set (ISO 2022) */ + term_state = TSTATE_SETG2; + usedcharsets = true; + break; + case '+': /* Designate G3 Character set (ISO 2022) */ + term_state = TSTATE_SETG3; + usedcharsets = true; + break; + case '~': /* Locking Shift 1, right */ + gr = 1; + usedcharsets = true; + break; + case 'n': /* Locking Shift 2 */ + gl = 2; + usedcharsets = true; + break; + case '}': /* Locking Shift 2, right */ + gr = 2; + usedcharsets = true; + break; + case 'o': /* Locking Shift 3 */ + gl = 3; + usedcharsets = true; + break; + case '|': /* Locking Shift 3, right */ + gr = 3; + usedcharsets = true; + break; + case 'Y': /* vt52 cursor address mode , next chars are x,y */ + term_state = TSTATE_VT52Y; + break; + default: + System.out.println("ESC unknown letter: " + c + " (" + ((int) c) + ")"); + break; + } + break; + case TSTATE_VT52X: + C = c - 37; + term_state = TSTATE_VT52Y; + break; + case TSTATE_VT52Y: + R = c - 37; + term_state = TSTATE_DATA; + break; + case TSTATE_SETG0: + if (c != '0' && c != 'A' && c != 'B' && c != '<') + System.out.println("ESC ( " + c + ": G0 char set? (" + ((int) c) + ")"); + else { + if (debug > 2) System.out.println("ESC ( : G0 char set (" + c + " " + ((int) c) + ")"); + gx[0] = c; + } + term_state = TSTATE_DATA; + break; + case TSTATE_SETG1: + if (c != '0' && c != 'A' && c != 'B' && c != '<') { + System.out.println("ESC ) " + c + " (" + ((int) c) + ") :G1 char set?"); + } else { + if (debug > 2) System.out.println("ESC ) :G1 char set (" + c + " " + ((int) c) + ")"); + gx[1] = c; + } + term_state = TSTATE_DATA; + break; + case TSTATE_SETG2: + if (c != '0' && c != 'A' && c != 'B' && c != '<') + System.out.println("ESC*:G2 char set? (" + ((int) c) + ")"); + else { + if (debug > 2) System.out.println("ESC*:G2 char set (" + c + " " + ((int) c) + ")"); + gx[2] = c; + } + term_state = TSTATE_DATA; + break; + case TSTATE_SETG3: + if (c != '0' && c != 'A' && c != 'B' && c != '<') + System.out.println("ESC+:G3 char set? (" + ((int) c) + ")"); + else { + if (debug > 2) System.out.println("ESC+:G3 char set (" + c + " " + ((int) c) + ")"); + gx[3] = c; + } + term_state = TSTATE_DATA; + break; + case TSTATE_ESCSQUARE: + switch (c) { + case '8': + for (int i = 0; i < columns; i++) + for (int j = 0; j < rows; j++) + putChar(i, j, 'E', 0); + break; + default: + System.out.println("ESC # " + c + " not supported."); + break; + } + term_state = TSTATE_DATA; + break; + case TSTATE_DCS: + if (c == '\\' && dcs.charAt(dcs.length() - 1) == ESC) { + handle_dcs(dcs); + term_state = TSTATE_DATA; + break; + } + dcs = dcs + c; + break; + + case TSTATE_DCEQ: + term_state = TSTATE_DATA; + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + ((int) c) - 48; + term_state = TSTATE_DCEQ; + break; + case ';': + DCEvar++; + DCEvars[DCEvar] = 0; + term_state = TSTATE_DCEQ; + break; + case 's': // XTERM_SAVE missing! + if (true || debug > 1) + System.out.println("ESC [ ? " + DCEvars[0] + " s unimplemented!"); + break; + case 'r': // XTERM_RESTORE + if (true || debug > 1) + System.out.println("ESC [ ? " + DCEvars[0] + " r"); + /* DEC Mode reset */ + for (int i = 0; i <= DCEvar; i++) { + switch (DCEvars[i]) { + case 3: /* 80 columns*/ + setScreenSize(80, getRows(), true); + break; + case 4: /* scrolling mode, smooth */ + break; + case 5: /* light background */ + break; + case 6: /* DECOM (Origin Mode) move inside margins. */ + moveoutsidemargins = true; + break; + case 7: /* DECAWM: Autowrap Mode */ + wraparound = false; + break; + case 12:/* local echo off */ + break; + case 9: /* X10 mouse */ + case 1000: /* xterm style mouse report on */ + case 1001: + case 1002: + case 1003: + mouserpt = DCEvars[i]; + break; + default: + System.out.println("ESC [ ? " + DCEvars[0] + " r, unimplemented!"); + } + } + break; + case 'h': // DECSET + if (debug > 0) + System.out.println("ESC [ ? " + DCEvars[0] + " h"); + /* DEC Mode set */ + for (int i = 0; i <= DCEvar; i++) { + switch (DCEvars[i]) { + case 1: /* Application cursor keys */ + KeyUp[0] = "\u001bOA"; + KeyDown[0] = "\u001bOB"; + KeyRight[0] = "\u001bOC"; + KeyLeft[0] = "\u001bOD"; + break; + case 2: /* DECANM */ + vt52mode = false; + break; + case 3: /* 132 columns*/ + setScreenSize(132, getRows(), true); + break; + case 6: /* DECOM: move inside margins. */ + moveoutsidemargins = false; + break; + case 7: /* DECAWM: Autowrap Mode */ + wraparound = true; + break; + case 25: /* turn cursor on */ + showCursor(true); + break; + case 9: /* X10 mouse */ + case 1000: /* xterm style mouse report on */ + case 1001: + case 1002: + case 1003: + mouserpt = DCEvars[i]; + break; + + /* unimplemented stuff, fall through */ + /* 4 - scrolling mode, smooth */ + /* 5 - light background */ + /* 12 - local echo off */ + /* 18 - DECPFF - Printer Form Feed Mode -> On */ + /* 19 - DECPEX - Printer Extent Mode -> Screen */ + default: + System.out.println("ESC [ ? " + DCEvars[0] + " h, unsupported."); + break; + } + } + break; + case 'i': // DEC Printer Control, autoprint, echo screenchars to printer + // This is different to CSI i! + // Also: "Autoprint prints a final display line only when the + // cursor is moved off the line by an autowrap or LF, FF, or + // VT (otherwise do not print the line)." + switch (DCEvars[0]) { + case 1: + if (debug > 1) + System.out.println("CSI ? 1 i : Print line containing cursor"); + break; + case 4: + if (debug > 1) + System.out.println("CSI ? 4 i : Start passthrough printing"); + break; + case 5: + if (debug > 1) + System.out.println("CSI ? 4 i : Stop passthrough printing"); + break; + } + break; + case 'l': //DECRST + /* DEC Mode reset */ + if (debug > 0) + System.out.println("ESC [ ? " + DCEvars[0] + " l"); + for (int i = 0; i <= DCEvar; i++) { + switch (DCEvars[i]) { + case 1: /* Application cursor keys */ + KeyUp[0] = "\u001b[A"; + KeyDown[0] = "\u001b[B"; + KeyRight[0] = "\u001b[C"; + KeyLeft[0] = "\u001b[D"; + break; + case 2: /* DECANM */ + vt52mode = true; + break; + case 3: /* 80 columns*/ + setScreenSize(80, getRows(), true); + break; + case 6: /* DECOM: move outside margins. */ + moveoutsidemargins = true; + break; + case 7: /* DECAWM: Autowrap Mode OFF */ + wraparound = false; + break; + case 25: /* turn cursor off */ + showCursor(false); + break; + /* Unimplemented stuff: */ + /* 4 - scrolling mode, jump */ + /* 5 - dark background */ + /* 7 - DECAWM - no wrap around mode */ + /* 12 - local echo on */ + /* 18 - DECPFF - Printer Form Feed Mode -> Off*/ + /* 19 - DECPEX - Printer Extent Mode -> Scrolling Region */ + case 9: /* X10 mouse */ + case 1000: /* xterm style mouse report OFF */ + case 1001: + case 1002: + case 1003: + mouserpt = 0; + break; + default: + System.out.println("ESC [ ? " + DCEvars[0] + " l, unsupported."); + break; + } + } + break; + case 'n': + if (debug > 0) + System.out.println("ESC [ ? " + DCEvars[0] + " n"); + switch (DCEvars[0]) { + case 15: + /* printer? no printer. */ + write(((char) ESC) + "[?13n", false); + System.out.println("ESC[5n"); + break; + default: + System.out.println("ESC [ ? " + DCEvars[0] + " n, unsupported."); + break; + } + break; + default: + System.out.println("ESC [ ? " + DCEvars[0] + " " + c + ", unsupported."); + break; + } + break; + case TSTATE_CSI_EX: + term_state = TSTATE_DATA; + switch (c) { + case ESC: + term_state = TSTATE_ESC; + break; + default: + System.out.println("Unknown character ESC[! character is " + (int) c); + break; + } + break; + case TSTATE_CSI_TICKS: + term_state = TSTATE_DATA; + switch (c) { + case 'p': + System.out.println("Conformance level: " + DCEvars[0] + " (unsupported)," + DCEvars[1]); + if (DCEvars[0] == 61) { + output8bit = false; + break; + } + if (DCEvars[1] == 1) { + output8bit = false; + } else { + output8bit = true; /* 0 or 2 */ + } + break; + default: + System.out.println("Unknown ESC [... \"" + c); + break; + } + break; + case TSTATE_CSI_EQUAL: + term_state = TSTATE_DATA; + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + ((int) c) - 48; + term_state = TSTATE_CSI_EQUAL; + break; + case ';': + DCEvar++; + DCEvars[DCEvar] = 0; + term_state = TSTATE_CSI_EQUAL; + break; + + case 'F': /* SCO ANSI foreground */ + { + int newcolor; + + System.out.println("ESC [ = "+DCEvars[0]+" F"); + + attributes &= ~COLOR_FG; + newcolor = ((DCEvars[0] & 1) << 2) | + (DCEvars[0] & 2) | + ((DCEvars[0] & 4) >> 2) ; + attributes |= (newcolor+1) << COLOR_FG_SHIFT; + + break; + } + case 'G': /* SCO ANSI background */ + { + int newcolor; + + System.out.println("ESC [ = "+DCEvars[0]+" G"); + + attributes &= ~COLOR_BG; + newcolor = ((DCEvars[0] & 1) << 2) | + (DCEvars[0] & 2) | + ((DCEvars[0] & 4) >> 2) ; + attributes |= (newcolor+1) << COLOR_BG_SHIFT; + break; + } + + default: + System.out.print("Unknown ESC [ = "); + for (int i=0;i<=DCEvar;i++) + System.out.print(DCEvars[i]+","); + System.out.println("" + c); + break; + } + break; + case TSTATE_CSI_DOLLAR: + term_state = TSTATE_DATA; + switch (c) { + case '}': + System.out.println("Active Status Display now " + DCEvars[0]); + statusmode = DCEvars[0]; + break; + /* bad documentation? + case '-': + System.out.println("Set Status Display now "+DCEvars[0]); + break; + */ + case '~': + System.out.println("Status Line mode now " + DCEvars[0]); + break; + default: + System.out.println("UNKNOWN Status Display code " + c + ", with Pn=" + DCEvars[0]); + break; + } + break; + case TSTATE_CSI: + term_state = TSTATE_DATA; + switch (c) { + case '"': + term_state = TSTATE_CSI_TICKS; + break; + case '$': + term_state = TSTATE_CSI_DOLLAR; + break; + case '=': + term_state = TSTATE_CSI_EQUAL; + break; + case '!': + term_state = TSTATE_CSI_EX; + break; + case '?': + DCEvar = 0; + DCEvars[0] = 0; + term_state = TSTATE_DCEQ; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + ((int) c) - 48; + term_state = TSTATE_CSI; + break; + case ';': + DCEvar++; + DCEvars[DCEvar] = 0; + term_state = TSTATE_CSI; + break; + case 'c':/* send primary device attributes */ + /* send (ESC[?61c) */ + + String subcode = ""; + if (terminalID.equals("vt320")) subcode = "63;"; + if (terminalID.equals("vt220")) subcode = "62;"; + if (terminalID.equals("vt100")) subcode = "61;"; + write(((char) ESC) + "[?" + subcode + "1;2c", false); + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " c"); + break; + case 'q': + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " q"); + break; + case 'g': + /* used for tabsets */ + switch (DCEvars[0]) { + case 3:/* clear them */ + Tabs = new byte[getColumns()]; + break; + case 0: + Tabs[C] = 0; + break; + } + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " g"); + break; + case 'h': + switch (DCEvars[0]) { + case 4: + insertmode = 1; + break; + case 20: + System.out.println("Setting CRLF to TRUE"); + sendcrlf = true; + break; + default: + System.out.println("unsupported: ESC [ " + DCEvars[0] + " h"); + break; + } + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " h"); + break; + case 'i': // Printer Controller mode. + // "Transparent printing sends all output, except the CSI 4 i + // termination string, to the printer and not the screen, + // uses an 8-bit channel if no parity so NUL and DEL will be + // seen by the printer and by the termination recognizer code, + // and all translation and character set selections are + // bypassed." + switch (DCEvars[0]) { + case 0: + if (debug > 1) + System.out.println("CSI 0 i: Print Screen, not implemented."); + break; + case 4: + if (debug > 1) + System.out.println("CSI 4 i: Enable Transparent Printing, not implemented."); + break; + case 5: + if (debug > 1) + System.out.println("CSI 4/5 i: Disable Transparent Printing, not implemented."); + break; + default: + System.out.println("ESC [ " + DCEvars[0] + " i, unimplemented!"); + } + break; + case 'l': + switch (DCEvars[0]) { + case 4: + insertmode = 0; + break; + case 20: + System.out.println("Setting CRLF to FALSE"); + sendcrlf = false; + break; + default: + System.out.println("ESC [ " + DCEvars[0] + " l, unimplemented!"); + break; + } + break; + case 'A': // CUU + { + int limit; + /* FIXME: xterm only cares about 0 and topmargin */ + if (R > bm) + limit = bm + 1; + else if (R >= tm) { + limit = tm; + } else + limit = 0; + if (DCEvars[0] == 0) + R--; + else + R -= DCEvars[0]; + if (R < limit) + R = limit; + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " A"); + break; + } + case 'B': // CUD + /* cursor down n (1) times */ + { + int limit; + if (R < tm) + limit = tm - 1; + else if (R <= bm) { + limit = bm; + } else + limit = rows - 1; + if (DCEvars[0] == 0) + R++; + else + R += DCEvars[0]; + if (R > limit) + R = limit; + else { + if (debug > 2) System.out.println("Not limited."); + } + if (debug > 2) System.out.println("to: " + R); + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " B (at C=" + C + ")"); + break; + } + case 'C': + if (DCEvars[0] == 0) + C++; + else + C += DCEvars[0]; + if (C > columns - 1) + C = columns - 1; + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " C"); + break; + case 'd': // CVA + R = DCEvars[0]; + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " d"); + break; + case 'D': + if (DCEvars[0] == 0) + C--; + else + C -= DCEvars[0]; + if (C < 0) C = 0; + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " D"); + break; + case 'r': // DECSTBM + if (DCEvar > 0) // Ray: Any argument is optional + { + R = DCEvars[1] - 1; + if (R < 0) + R = rows - 1; + else if (R >= rows) { + R = rows - 1; + } + } else + R = rows - 1; + setBottomMargin(R); + if (R >= DCEvars[0]) { + R = DCEvars[0] - 1; + if (R < 0) + R = 0; + } + setTopMargin(R); + _SetCursor(0, 0); + if (debug > 1) + System.out.println("ESC [" + DCEvars[0] + " ; " + DCEvars[1] + " r"); + break; + case 'G': /* CUP / cursor absolute column */ + C = DCEvars[0]; + if (debug > 1) System.out.println("ESC [ " + DCEvars[0] + " G"); + break; + case 'H': /* CUP / cursor position */ + /* gets 2 arguments */ + _SetCursor(DCEvars[0] - 1, DCEvars[1] - 1); + if (debug > 2) { + System.out.println("ESC [ " + DCEvars[0] + ";" + DCEvars[1] + " H, moveoutsidemargins " + moveoutsidemargins); + System.out.println(" -> R now " + R + ", C now " + C); + } + break; + case 'f': /* move cursor 2 */ + /* gets 2 arguments */ + R = DCEvars[0] - 1; + C = DCEvars[1] - 1; + if (C < 0) C = 0; + if (R < 0) R = 0; + if (debug > 2) + System.out.println("ESC [ " + DCEvars[0] + ";" + DCEvars[1] + " f"); + break; + case 'S': /* ind aka 'scroll forward' */ + if (DCEvars[0] == 0) + insertLine(rows - 1, SCROLL_UP); + else + insertLine(rows - 1, DCEvars[0], SCROLL_UP); + break; + case 'L': + /* insert n lines */ + if (DCEvars[0] == 0) + insertLine(R, SCROLL_DOWN); + else + insertLine(R, DCEvars[0], SCROLL_DOWN); + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + "" + (c) + " (at R " + R + ")"); + break; + case 'T': /* 'ri' aka scroll backward */ + if (DCEvars[0] == 0) + insertLine(0, SCROLL_DOWN); + else + insertLine(0, DCEvars[0], SCROLL_DOWN); + break; + case 'M': + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + "" + (c) + " at R=" + R); + if (DCEvars[0] == 0) + deleteLine(R); + else + for (int i = 0; i < DCEvars[0]; i++) + deleteLine(R); + break; + case 'K': + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " K"); + /* clear in line */ + switch (DCEvars[0]) { + case 6: /* 97801 uses ESC[6K for delete to end of line */ + case 0:/*clear to right*/ + if (C < columns - 1) + deleteArea(C, R, columns - C, 1, attributes); + break; + case 1:/*clear to the left, including this */ + if (C > 0) + deleteArea(0, R, C + 1, 1, attributes); + break; + case 2:/*clear whole line */ + deleteArea(0, R, columns, 1, attributes); + break; + } + break; + case 'J': + /* clear below current line */ + switch (DCEvars[0]) { + case 0: + if (R < rows - 1) + deleteArea(0, R + 1, columns, rows - R - 1, attributes); + if (C < columns - 1) + deleteArea(C, R, columns - C, 1, attributes); + break; + case 1: + if (R > 0) + deleteArea(0, 0, columns, R, attributes); + if (C > 0) + deleteArea(0, R, C + 1, 1, attributes);// include up to and including current + break; + case 2: + deleteArea(0, 0, columns, rows, attributes); + break; + } + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " J"); + break; + case '@': + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " @"); + for (int i = 0; i < DCEvars[0]; i++) + insertChar(C, R, ' ', attributes); + break; + case 'X': + { + int toerase = DCEvars[0]; + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " X, C=" + C + ",R=" + R); + if (toerase == 0) + toerase = 1; + if (toerase + C > columns) + toerase = columns - C; + deleteArea(C, R, toerase, 1, attributes); + // does not change cursor position + break; + } + case 'P': + if (debug > 1) + System.out.println("ESC [ " + DCEvars[0] + " P, C=" + C + ",R=" + R); + if (DCEvars[0] == 0) DCEvars[0] = 1; + for (int i = 0; i < DCEvars[0]; i++) + deleteChar(C, R); + break; + case 'n': + switch (DCEvars[0]) { + case 5: /* malfunction? No malfunction. */ + writeSpecial(((char) ESC) + "[0n"); + if (debug > 1) + System.out.println("ESC[5n"); + break; + case 6: + // DO NOT offset R and C by 1! (checked against /usr/X11R6/bin/resize + // FIXME check again. + // FIXME: but vttest thinks different??? + writeSpecial(((char) ESC) + "[" + R + ";" + C + "R"); + if (debug > 1) + System.out.println("ESC[6n"); + break; + default: + if (debug > 0) + System.out.println("ESC [ " + DCEvars[0] + " n??"); + break; + } + break; + case 's': /* DECSC - save cursor */ + Sc = C; + Sr = R; + Sa = attributes; + if (debug > 3) + System.out.println("ESC[s"); + break; + case 'u': /* DECRC - restore cursor */ + C = Sc; + R = Sr; + attributes = Sa; + if (debug > 3) + System.out.println("ESC[u"); + break; + case 'm': /* attributes as color, bold , blink,*/ + if (debug > 3) + System.out.print("ESC [ "); + if (DCEvar == 0 && DCEvars[0] == 0) + attributes = 0; + for (int i = 0; i <= DCEvar; i++) { + switch (DCEvars[i]) { + case 0: + if (DCEvar > 0) { + if (terminalID.equals("scoansi")) { + attributes &= COLOR; /* Keeps color. Strange but true. */ + } else { + attributes = 0; + } + } + break; + case 1: + attributes |= BOLD; + attributes &= ~LOW; + break; + case 2: + /* SCO color hack mode */ + if (terminalID.equals("scoansi") && ((DCEvar - i) >= 2)) { + int ncolor; + attributes &= ~(COLOR | BOLD); + + ncolor = DCEvars[i + 1]; + if ((ncolor & 8) == 8) + attributes |= BOLD; + ncolor = ((ncolor & 1) << 2) | (ncolor & 2) | ((ncolor & 4) >> 2); + attributes |= ((ncolor) + 1) << COLOR_FG_SHIFT; + ncolor = DCEvars[i + 2]; + ncolor = ((ncolor & 1) << 2) | (ncolor & 2) | ((ncolor & 4) >> 2); + attributes |= ((ncolor) + 1) << COLOR_BG_SHIFT; + i += 2; + } else { + attributes |= LOW; + } + break; + case 4: + attributes |= UNDERLINE; + break; + case 7: + attributes |= INVERT; + break; + case 8: + attributes |= INVISIBLE; + break; + case 5: /* blink on */ + break; + /* 10 - ANSI X3.64-1979, select primary font, don't display control + * chars, don't set bit 8 on output */ + case 10: + gl = 0; + usedcharsets = true; + break; + /* 11 - ANSI X3.64-1979, select second alt. font, display control + * chars, set bit 8 on output */ + case 11: /* SMACS , as */ + case 12: + gl = 1; + usedcharsets = true; + break; + case 21: /* normal intensity */ + attributes &= ~(LOW | BOLD); + break; + case 25: /* blinking off */ + break; + case 27: + attributes &= ~INVERT; + break; + case 28: + attributes &= ~INVISIBLE; + break; + case 24: + attributes &= ~UNDERLINE; + break; + case 22: + attributes &= ~BOLD; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + attributes &= ~COLOR_FG; + attributes |= ((DCEvars[i] - 30) + 1) << COLOR_FG_SHIFT; + break; + case 39: + attributes &= ~COLOR_FG; + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + attributes &= ~COLOR_BG; + attributes |= ((DCEvars[i] - 40) + 1) << COLOR_BG_SHIFT; + break; + case 49: + attributes &= ~COLOR_BG; + break; + + default: + System.out.println("ESC [ " + DCEvars[i] + " m unknown..."); + break; + } + if (debug > 3) + System.out.print("" + DCEvars[i] + ";"); + } + if (debug > 3) + System.out.print(" (attributes = " + attributes + ")m \n"); + break; + default: + System.out.println("ESC [ unknown letter:" + c + " (" + ((int) c) + ")"); + break; + } + break; + default: + term_state = TSTATE_DATA; + break; + } + if (C > columns) C = columns; + if (R > rows) R = rows; + if (C < 0) C = 0; + if (R < 0) R = 0; + if (doshowcursor) + setCursorPosition(C, R); + markLine(R, 1); + } + + /* hard reset the terminal */ + public void reset() { + gx[0] = 'B'; + gx[1] = '0'; + gx[2] = 'B'; + gx[3] = 'B'; + gl = 0; // default GL to G0 + gr = 1; // default GR to G1 + /* reset tabs */ + int nw = getColumns(); + if (nw < 132) nw = 132; + Tabs = new byte[nw]; + for (int i = 0; i < nw; i += 8) { + Tabs[i] = 1; + } + /*FIXME:*/ + term_state = TSTATE_DATA; + } +} diff --git a/src/org/theb/provider/HostDb.java b/src/org/theb/provider/HostDb.java index 3bed790..e38396c 100644 --- a/src/org/theb/provider/HostDb.java +++ b/src/org/theb/provider/HostDb.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ package org.theb.provider; import android.net.ContentURI; diff --git a/src/org/theb/ssh/ConnectionThread.java b/src/org/theb/ssh/ConnectionThread.java new file mode 100644 index 0000000..a201e62 --- /dev/null +++ b/src/org/theb/ssh/ConnectionThread.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ +package org.theb.ssh; + +import java.io.InputStream; +import java.io.OutputStream; + +public abstract class ConnectionThread extends Thread { + public ConnectionThread(FeedbackUI ui, String hostname, String username, int port) {} + public abstract void finish(); + public abstract InputStream getReader(); + public abstract OutputStream getWriter(); + public abstract void setPassword(String password); +} diff --git a/src/org/theb/ssh/FeedbackUI.java b/src/org/theb/ssh/FeedbackUI.java new file mode 100644 index 0000000..ba4ae03 --- /dev/null +++ b/src/org/theb/ssh/FeedbackUI.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ +package org.theb.ssh; + +public interface FeedbackUI { + public void connectionLost(Throwable reason); + public void setWaiting(boolean isWaiting, String title, String message); + public void askPassword(); +} diff --git a/src/org/theb/ssh/HostDbProvider.java b/src/org/theb/ssh/HostDbProvider.java index 20ec224..f0eac73 100644 --- a/src/org/theb/ssh/HostDbProvider.java +++ b/src/org/theb/ssh/HostDbProvider.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ package org.theb.ssh; import java.util.HashMap; diff --git a/src/org/theb/ssh/HostEditor.java b/src/org/theb/ssh/HostEditor.java index d642fd4..967cf5b 100644 --- a/src/org/theb/ssh/HostEditor.java +++ b/src/org/theb/ssh/HostEditor.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ package org.theb.ssh; import org.theb.provider.HostDb; diff --git a/src/org/theb/ssh/HostsList.java b/src/org/theb/ssh/HostsList.java index 4d9e398..7114497 100644 --- a/src/org/theb/ssh/HostsList.java +++ b/src/org/theb/ssh/HostsList.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ package org.theb.ssh; import org.theb.provider.HostDb; diff --git a/src/org/theb/ssh/InteractiveHostKeyVerifier.java b/src/org/theb/ssh/InteractiveHostKeyVerifier.java index a461724..7c21f80 100644 --- a/src/org/theb/ssh/InteractiveHostKeyVerifier.java +++ b/src/org/theb/ssh/InteractiveHostKeyVerifier.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ package org.theb.ssh; import com.trilead.ssh2.ServerHostKeyVerifier; diff --git a/src/org/theb/ssh/JCTerminalView.java b/src/org/theb/ssh/JCTerminalView.java new file mode 100644 index 0000000..47d12aa --- /dev/null +++ b/src/org/theb/ssh/JCTerminalView.java @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ +package org.theb.ssh; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PixelXorXfermode; +import android.graphics.Typeface; +import android.graphics.Paint.FontMetricsInt; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; + +import com.jcraft.jcterm.Emulator; +import com.jcraft.jcterm.EmulatorVT100; +import com.jcraft.jcterm.Term; + +public class JCTerminalView extends View implements Term, Terminal { + private final Paint mPaint; + private Bitmap mBitmap; + private Canvas mCanvas; + + private final Paint mCursorPaint; + + private Emulator emulator = null; + + private boolean mBold = false; + private boolean mUnderline = false; + private boolean mReverse = false; + + private int mDefaultForeground = Color.WHITE; + private int mDefaultBackground = Color.BLACK; + private int mForeground = Color.WHITE; + private int mBackground = Color.BLACK; + + private boolean mAntialias = true; + + private int mTermWidth = 80; + private int mTermHeight = 24; + + private int mCharHeight; + private int mCharWidth; + private int mDescent; + + + // Cursor location + private int x = 0; + private int y = 0; + + private final Object[] mColors = {Color.BLACK, Color.RED, Color.GREEN, Color.YELLOW, + Color.BLUE, Color.MAGENTA, Color.CYAN, Color.WHITE}; + + public JCTerminalView(Context c) { + super(c); + mPaint = new Paint(); + mPaint.setAntiAlias(mAntialias); + mPaint.setColor(mDefaultForeground); + + mCursorPaint = new Paint(); + mCursorPaint.setAntiAlias(mAntialias); + mCursorPaint.setColor(mDefaultForeground); + mCursorPaint.setXfermode(new PixelXorXfermode(mDefaultBackground)); + + setFont(Typeface.MONOSPACE); + } + + @Override + protected void onDraw(Canvas canvas) { + if (mBitmap != null) { + canvas.drawBitmap(mBitmap, 0, 0, null); + + if (mCharHeight > 0 && y > mCharHeight) { + // Invert pixels for cursor position. + canvas.drawRect(x, y - mCharHeight, x + mCharWidth, y, mCursorPaint); + } + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + Log.d("SSH/TerminalView", "onSizeChanged called"); + Bitmap newBitmap = Bitmap.createBitmap(w, h, false); + Canvas newCanvas = new Canvas(); + + newCanvas.setDevice(newBitmap); + + if (mBitmap != null) + newCanvas.drawBitmap(mBitmap, 0, 0, mPaint); + + mBitmap = newBitmap; + mCanvas = newCanvas; + + setSize(w, h); + } + + private void setSize(int w, int h) { + int column = w / getCharWidth(); + int row = h / getCharHeight(); + + mTermWidth = column; + mTermHeight = row; + + if (emulator != null) + emulator.reset(); + + clear_area(0, 0, w, h); + + // TODO: finish this method + } + + private void setFont(Typeface typeface) { + mPaint.setTypeface(typeface); + mPaint.setTextSize(8); + FontMetricsInt fm = mPaint.getFontMetricsInt(); + mDescent = fm.descent; + + float[] widths = new float[1]; + mPaint.getTextWidths("X", widths); + mCharWidth = (int)widths[0]; + + // Is this right? + mCharHeight = Math.abs(fm.top) + Math.abs(fm.descent); + Log.d("SSH", "character height is " + mCharHeight); + // mCharHeight += mLineSpace * 2; + // mDescent += mLineSpace; + } + + public void beep() { + // TODO Auto-generated method stub + + } + + public void clear() { + mPaint.setColor(getBackgroundColor()); + mCanvas.drawRect(0, 0, mCanvas.getBitmapWidth(), + mCanvas.getBitmapHeight(), mPaint); + mPaint.setColor(getForegroundColor()); + } + + private int getBackgroundColor() { + if (mReverse) + return mForeground; + return mBackground; + } + + private int getForegroundColor() { + if (mReverse) + return mBackground; + return mForeground; + } + + public void clear_area(int x1, int y1, int x2, int y2) { + mPaint.setColor(getBackgroundColor()); + if (mCanvas != null) + mCanvas.drawRect(x1, y1, x2, y2, mPaint); + mPaint.setColor(getForegroundColor()); + } + + public void drawBytes(byte[] buf, int s, int len, int x, int y) { + String chars = null; + try { + chars = new String(buf, "ASCII"); + drawString(chars.substring(s, s+len), x, y); + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + Log.e("SSH", "Can't convert bytes to ASCII"); + } + } + + public void drawString(String str, int x, int y) { + mPaint.setFakeBoldText(mBold); + mPaint.setUnderlineText(mUnderline); + mCanvas.drawText(str, x, y - mDescent, mPaint); + } + + public void draw_cursor() { + postInvalidate(); + } + + public int getCharHeight() { + return mCharHeight; + } + + public int getCharWidth() { + return mCharWidth; + } + + public Object getColor(int index) { + if (mColors == null || index < 0 || mColors.length <= index) + return null; + return mColors[index]; + } + + public int getColumnCount() { + return mTermWidth; + } + + public int getRowCount() { + return mTermHeight; + } + + public int getTermHeight() { + return mTermHeight * mCharHeight; + } + + public int getTermWidth() { + return mTermWidth * mCharWidth; + } + + public void redraw(int x, int y, int width, int height) { + //invalidate(x, y, x+width, y+height); + postInvalidate(); + } + + public void resetAllAttributes() { + mBold = false; + mUnderline = false; + mReverse = false; + + mBackground = mDefaultBackground; + mForeground = mDefaultForeground; + + if (mPaint != null) + mPaint.setColor(mForeground); + } + + public void scroll_area(int x, int y, int w, int h, int dx, int dy) { + // TODO: make scrolling more efficient (memory-wise) + mCanvas.drawBitmap(Bitmap.createBitmap(mBitmap, x, y, w, h), x+dx, y+dy, null); + } + + private int toColor(Object o) { + if (o instanceof Integer) { + return ((Integer)o).intValue(); + } + + if (o instanceof String) { + return Color.parseColor((String)o); + } + + return Color.WHITE; + } + + public void setBackGround(Object background) { + mBackground = toColor(background); + } + + public void setBold() { + mBold = true; + } + + public void setCursor(int x, int y) { + // Make sure we don't go outside the bounds of the window. + this.x = Math.max( + Math.min(x, getWidth() - mCharWidth), + 0); + this.y = Math.max( + Math.min(y, getHeight()), + mCharHeight); + } + + public void setDefaultBackGround(Object background) { + mDefaultBackground = toColor(background); + } + + public void setDefaultForeGround(Object foreground) { + mDefaultForeground = toColor(foreground); + } + + public void setForeGround(Object foreground) { + mForeground = toColor(foreground); + } + + public void setReverse() { + mReverse = true; + if (mPaint != null) + mPaint.setColor(getForegroundColor()); + } + + public void setUnderline() { + mUnderline = true; + } + + public void start(InputStream in, OutputStream out) { + emulator = new EmulatorVT100(this, in); + emulator.reset(); + emulator.start(); + + clear(); + } + + public byte[] getKeyCode(int keyCode, int meta) { + if (keyCode == KeyEvent.KEYCODE_NEWLINE) + return emulator.getCodeENTER(); + else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) + return emulator.getCodeLEFT(); + else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) + return emulator.getCodeUP(); + else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) + return emulator.getCodeDOWN(); + else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) + return emulator.getCodeRIGHT(); + else + return null; + } +} diff --git a/src/org/theb/ssh/JTATerminalView.java b/src/org/theb/ssh/JTATerminalView.java new file mode 100644 index 0000000..2238f02 --- /dev/null +++ b/src/org/theb/ssh/JTATerminalView.java @@ -0,0 +1,323 @@ +package org.theb.ssh; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import de.mud.terminal.SoftFont; +import de.mud.terminal.VDUBuffer; +import de.mud.terminal.VDUDisplay; +import de.mud.terminal.vt320; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.graphics.Paint.FontMetricsInt; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; + +public class JTATerminalView extends View implements VDUDisplay, Terminal, Runnable { + private Paint paint; + private Canvas canvas; + private Bitmap bitmap; + + protected vt320 emulation; + private VDUBuffer buffer; + + private InputStream in; + private OutputStream out; + + private String encoding = "ASCII"; + private SoftFont sf = new SoftFont(); + + private Thread reader = null; + + private int charWidth; + private int charHeight; + private int charDescent; + + private int termWidth; + private int termHeight; + + private int color[] = { + Color.BLACK, + Color.RED, + Color.GREEN, + Color.YELLOW, + Color.BLUE, + Color.MAGENTA, + Color.CYAN, + Color.WHITE, + }; + + private final static int COLOR_FG_STD = 7; + private final static int COLOR_BG_STD = 0; + + public JTATerminalView(Context context) { + super(context); + + paint = new Paint(); + paint.setAntiAlias(true); + setFont(Typeface.MONOSPACE, 8); + + emulation = new vt320() { + public void write(byte[] b) { + try { + JTATerminalView.this.write(b); + } catch (IOException e) { + Log.e("SSH", "couldn't write" + b.toString()); + reader = null; + } + } + + public void sendTelnetCommand(byte cmd) { + // TODO: implement telnet command sending + } + + public void setWindowSize(int c, int r) { + // TODO: implement window sizing + } + }; + + setVDUBuffer(emulation); + emulation.setDisplay(this); + } + + @Override + protected void onDraw(Canvas canvas) { + if (bitmap != null) { + canvas.drawBitmap(bitmap, 0, 0, null); + /* + if (charHeight > 0 && y > charHeight) { + // Invert pixels for cursor position. + Bitmap cursor = Bitmap.createBitmap(mBitmap, x, y - mCharHeight, mCharWidth, mCharHeight); + for (int cy = 0; cy < mCharHeight; cy++) + for (int cx = 0; cx < mCharWidth; cx++) + cursor.setPixel(cx, cy, (~cursor.getPixel(cx, cy) & 0xFFFFFFFF) | 0xFF000000); + canvas.drawBitmap(cursor, x, y - mCharHeight, null); + cursor = null; + } + */ + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + Log.d("SSH/TerminalView", "onSizeChanged called"); + Bitmap newBitmap = Bitmap.createBitmap(w, h, false); + Canvas newCanvas = new Canvas(); + + newCanvas.setDevice(newBitmap); + + if (bitmap != null) + newCanvas.drawBitmap(bitmap, 0, 0, paint); + + bitmap = newBitmap; + canvas = newCanvas; + + setSize(w, h); + } + + private void setSize(int w, int h) { + termWidth = w / charWidth; + termHeight = h / charHeight; + + buffer.setScreenSize(termWidth, buffer.height = termHeight, true); + } + + private void setFont(Typeface typeface, int size) { + paint.setTypeface(typeface); + paint.setTextSize(size); + + FontMetricsInt fm = paint.getFontMetricsInt(); + + charDescent = fm.descent; + + float[] widths = new float[1]; + paint.getTextWidths("X", widths); + charWidth = (int)widths[0]; + + charHeight = Math.abs(fm.top) + Math.abs(fm.descent); + } + + public void write(byte[] b) throws IOException { + Log.e("SSH/JTATerm/write", "Trying to write" + b.toString()); + out.write(b); + } + + public int getColumnCount() { + return termWidth; + } + + public InputStream getInput() { + return in; + } + + public byte[] getKeyCode(int keyCode, int meta) { + switch (keyCode) { + case KeyEvent.KEYCODE_NEWLINE: + emulation.keyTyped(vt320.KEY_ENTER, ' ', meta); + break; + case KeyEvent.KEYCODE_DPAD_LEFT: + emulation.keyPressed(vt320.KEY_LEFT, ' ', meta); + break; + case KeyEvent.KEYCODE_DPAD_UP: + emulation.keyPressed(vt320.KEY_UP, ' ', meta); + break; + case KeyEvent.KEYCODE_DPAD_DOWN: + emulation.keyPressed(vt320.KEY_DOWN, ' ', meta) ; + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + emulation.keyPressed(vt320.KEY_RIGHT, ' ', meta); + break; + } + return null; + } + + public OutputStream getOutput() { + return out; + } + + public int getRowCount() { + return termHeight; + } + + private int darken(int color) { + return Color.argb(0xFF, + (int)(Color.red(color) * 0.8), + (int)(Color.green(color) * 0.8), + (int)(Color.blue(color) * 0.8) + ); + } + + /* + private int brighten(int color) { + return Color.argb(0xFF, + (int)(Color.red(color) * 1.2), + (int)(Color.green(color) * 1.2), + (int)(Color.blue(color) * 1.2) + ); + } + */ + + public void redraw() { + // Make sure the buffer is in the center of the screen. + int xoffset = (getWidth() - buffer.width * charWidth) / 2; + int yoffset = (getHeight() - buffer.height * charHeight) / 2; + + // Draw the mouse-selection + //int selectStartLine = selectBegin.y - buffer.windowBase; + //int selectEndLine = selectEnd.y - buffer.windowBase; + + int fg, bg; + + for (int l = 0; l < buffer.height; l++) { + if (!buffer.update[0] && !buffer.update[l + 1]) continue; + + for (int c = 0; c < buffer.width; c++) { + int addr = 0; + int currAttr = buffer.charAttributes[buffer.windowBase + l][c]; + + fg = darken(color[COLOR_FG_STD]); + bg = darken(color[COLOR_BG_STD]); + + if ((currAttr & VDUBuffer.COLOR_FG) != 0) + fg = darken(color[((currAttr & VDUBuffer.COLOR_FG) >> VDUBuffer.COLOR_FG_SHIFT) - 1]); + if ((currAttr & VDUBuffer.COLOR_BG) != 0) + bg = darken(darken(color[((currAttr & VDUBuffer.COLOR_BG) >> VDUBuffer.COLOR_BG_SHIFT) - 1])); + paint.setFakeBoldText((currAttr & VDUBuffer.BOLD) != 0); + + if ((currAttr & VDUBuffer.LOW) != 0) + fg = darken(fg); + + if ((currAttr & VDUBuffer.INVERT) != 0) { + int swapc = bg; + bg = fg; + fg = swapc; + } + + // If this character is in the special font, print it and continue to the next character. + if (sf.inSoftFont(buffer.charArray[buffer.windowBase + l][c])) { + paint.setColor(bg); + canvas.drawRect(c * charWidth + xoffset, l * charHeight + yoffset, + c * (charWidth + 1) + xoffset, (l+1) * charHeight + yoffset, paint); + paint.setColor(fg); + paint.setUnderlineText((currAttr & VDUBuffer.UNDERLINE) != 0); + if ((currAttr & VDUBuffer.INVISIBLE) == 0) + sf.drawChar(canvas, paint, buffer.charArray[buffer.windowBase + l][c], xoffset + c * charWidth, l * charHeight + yoffset, charWidth, charHeight); + continue; + } + + // Determine the amount of continuous characters with the same settings and print them all at once. + while ((c + addr < buffer.width) && + ((buffer.charArray[buffer.windowBase + l][c + addr] < ' ') || + (buffer.charAttributes[buffer.windowBase + l][c + addr] == currAttr)) && + !sf.inSoftFont(buffer.charArray[buffer.windowBase + l][c + addr])) { + if (buffer.charArray[buffer.windowBase + l][c + addr] < ' ') { + buffer.charArray[buffer.windowBase + l][c + addr] = ' '; + buffer.charAttributes[buffer.windowBase + l][c + addr] = 0; + continue; + } + addr++; + } + + paint.setColor(bg); + canvas.drawRect(c * charWidth + xoffset, l * charHeight + yoffset, + addr * (charWidth + 1) + xoffset, (l+1) * charHeight + yoffset, paint); + paint.setColor(fg); + + paint.setUnderlineText((currAttr & VDUBuffer.UNDERLINE) != 0); + if ((currAttr & VDUBuffer.INVISIBLE) == 0) + canvas.drawText(buffer.charArray[buffer.windowBase + l], + c, addr, + c * charWidth + xoffset, + (l + 1) * charHeight - charDescent + yoffset, + paint); + + c += addr - 1; + } + } + + buffer.update[0] = false; + + postInvalidate(); + } + + public void updateScrollBar() { + // TODO Auto-generated method stub + } + + public void start(InputStream in, OutputStream out) { + this.in = in; + this.out = out; + + reader = new Thread(this); + reader.start(); + } + + public VDUBuffer getVDUBuffer() { + return buffer; + } + + public void setVDUBuffer(VDUBuffer buffer) { + this.buffer = buffer; + } + + public void run() { + byte[] b = new byte[256]; + int n = 0; + while (n >= 0) + try { + n = in.read(b); + if (n > 0) emulation.putString(new String(b, 0, n, encoding)); + redraw(); + } catch (IOException e) { + reader = null; + break; + } + } +} diff --git a/src/org/theb/ssh/PasswordDialog.java b/src/org/theb/ssh/PasswordDialog.java index 87839b7..faf636a 100644 --- a/src/org/theb/ssh/PasswordDialog.java +++ b/src/org/theb/ssh/PasswordDialog.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ package org.theb.ssh; import android.app.Activity; diff --git a/src/org/theb/ssh/PreferencesDialog.java b/src/org/theb/ssh/PreferencesDialog.java new file mode 100644 index 0000000..06bc229 --- /dev/null +++ b/src/org/theb/ssh/PreferencesDialog.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ +package org.theb.ssh; + +import android.app.Dialog; +import android.content.Context; + +public class PreferencesDialog extends Dialog { + + public PreferencesDialog(Context context) { + super(context); + // TODO Auto-generated constructor stub + } + + public PreferencesDialog(Context context, int theme) { + super(context, theme); + // TODO Auto-generated constructor stub + } + + public PreferencesDialog(Context context, boolean cancelable, + OnCancelListener cancelListener) { + super(context, cancelable, cancelListener); + // TODO Auto-generated constructor stub + } + +} diff --git a/src/org/theb/ssh/SecureShell.java b/src/org/theb/ssh/SecureShell.java index 31625f9..8d4dda6 100644 --- a/src/org/theb/ssh/SecureShell.java +++ b/src/org/theb/ssh/SecureShell.java @@ -1,12 +1,30 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ package org.theb.ssh; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; -import java.util.concurrent.Semaphore; import org.theb.provider.HostDb; +import com.trilead.ssh2.ConnectionMonitor; + import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; @@ -17,22 +35,16 @@ import android.os.Handler; import android.text.method.KeyCharacterMap; import android.util.Log; import android.view.KeyEvent; +import android.view.View; import android.view.Window; -import android.widget.TextView; - -import com.trilead.ssh2.ChannelCondition; -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.ConnectionMonitor; -import com.trilead.ssh2.Session; -public class SecureShell extends Activity { - private TextView mOutput; +public class SecureShell extends Activity implements FeedbackUI, ConnectionMonitor { private ConnectionThread mConn; - private String mBuffer; - private KeyCharacterMap mKMap; + // Activities we support. static final int PASSWORD_REQUEST = 0; + // Database projection indices. private static final int HOSTNAME_INDEX = 1; private static final int USERNAME_INDEX = 2; private static final int PORT_INDEX = 3; @@ -46,6 +58,15 @@ public class SecureShell extends Activity { private Cursor mCursor; + // Map to convert from keyboard presses to actual characters. + private KeyCharacterMap mKMap; + + // Terminal window + private Terminal mTerminal; + + // We change the meta state when the user presses the center button. + private boolean mMetaState = false; + // Store the username, hostname, and port from the database. private String mHostname; private String mUsername; @@ -56,241 +77,21 @@ public class SecureShell extends Activity { private boolean mIsWaiting; private String mWaitingTitle; private String mWaitingMessage; - - // Connection lost reason. - private String mDisconnectReason; - - // This is for the password dialog. - Semaphore sPass; - String mPassword = null; - - Connection conn; - Session sess; - InputStream stdin; - InputStream stderr; - OutputStream stdout; - int x; - int y; - + final Handler mHandler = new Handler(); - final Runnable mUpdateView = new Runnable() { - public void run() { - updateViewInUI(); - } - }; - - class ConnectionThread extends Thread { - String hostname; - String username; - int port; - - char[][] lines; - int posy = 0; - int posx = 0; - - public ConnectionThread(String hostname, String username, int port) { - this.hostname = hostname; - this.username = username; - this.port = port; - } - - public void run() { - conn = new Connection(hostname, port); - - conn.addConnectionMonitor(mConnectionMonitor); - - setWaiting(true, "Connection", - "Connecting to " + hostname + "..."); - - Log.d("SSH", "Starting connection attempt..."); - - try { - conn.connect(new InteractiveHostKeyVerifier()); - - setWaiting(true, "Authenticating", - "Trying to authenticate..."); - - Log.d("SSH", "Starting authentication..."); - -// boolean enableKeyboardInteractive = true; -// boolean enableDSA = true; -// boolean enableRSA = true; - - while (true) { - /* - if ((enableDSA || enableRSA ) && - mConn.isAuthMethodAvailable(username, "publickey"); - */ - - if (conn.isAuthMethodAvailable(username, "password")) { - Log.d("SSH", "Trying password authentication..."); - setWaiting(true, "Authenticating", - "Trying to authenticate using password..."); - - // Set a semaphore that is unset by the returning dialog. - sPass = new Semaphore(0); - askPassword(); - - // Wait for the user to answer. - sPass.acquire(); - sPass = null; - if (mPassword == null) - continue; - - boolean res = conn.authenticateWithPassword(username, mPassword); - if (res == true) - break; - - continue; - } - - throw new IOException("No supported authentication methods available."); - } - - Log.d("SSH", "Opening session..."); - setWaiting(true, "Session", "Requesting shell..."); - - sess = conn.openSession(); - - y = (int)(mOutput.getHeight() / mOutput.getLineHeight()); - // TODO: figure out how to get the width of monospace font characters. - x = y * 3; - Log.d("SSH", "Requesting PTY of size " + x + "x" + y); - - sess.requestPTY("dumb", x, y, 0, 0, null); - - Log.d("SSH", "Requesting shell..."); - sess.startShell(); - - stdout = sess.getStdin(); - stderr = sess.getStderr(); - stdin = sess.getStdout(); - - setWaiting(false, null, null); - } catch (IOException e) { - Log.e("SSH", e.getMessage()); - setWaiting(false, null, null); - return; - } catch (InterruptedException e) { - // This thread is coming to an end. Let us exit. - Log.e("SSH", "Connection thread interrupted."); - return; - } - - byte[] buff = new byte[8192]; - lines = new char[y][]; - - try { - while (true) { - if ((stdin.available() == 0) && (stderr.available() == 0)) { - int conditions = sess.waitForCondition( - ChannelCondition.STDOUT_DATA - | ChannelCondition.STDERR_DATA - | ChannelCondition.EOF, 2000); - if ((conditions & ChannelCondition.TIMEOUT) != 0) - continue; - if ((conditions & ChannelCondition.EOF) != 0) - if ((conditions & - (ChannelCondition.STDERR_DATA - | ChannelCondition.STDOUT_DATA)) == 0) - break; - } - - if (stdin.available() > 0) { - int len = stdin.read(buff); - addText(buff, len); - } - if (stderr.available() > 0) { - int len = stderr.read(buff); - addText(buff, len); - } - } - } catch (Exception e) { - Log.e("SSH", "Got exception reading: " + e.getMessage()); - } - } - - public void addText(byte[] data, int len) { - for (int i = 0; i < len; i++) { - char c = (char) (data[i] & 0xff); - - if (c == 8) { // Backspace, VERASE - if (posx < 0) - continue; - posx--; - continue; - } - if (c == '\r') { - posx = 0; - continue; - } - - if (c == '\n') { - posy++; - if (posy >= y) { - for (int k = 1; k < y; k++) - lines[k - 1] = lines[k]; - - posy--; - lines[y - 1] = new char[x]; - - for (int k = 0; k < x; k++) - lines[y - 1][k] = ' '; - } - continue; - } - - if (c < 32) { - continue; - } - - if (posx >= x) { - posx = 0; - posy++; - if (posy >= y) { - posy--; - - for (int k = 1; k < y; k++) - lines[k - 1] = lines[k]; - lines[y - 1] = new char[x]; - for (int k = 0; k < x; k++) - lines[y - 1][k] = ' '; - } - } - - if (lines[posy] == null) { - lines[posy] = new char[x]; - for (int k = 0; k < x; k++) - lines[posy][k] = ' '; - } + // Tell the user why we disconnected. + private String mDisconnectReason; - lines[posy][posx] = c; - posx++; - } - - StringBuffer sb = new StringBuffer(x * y); - - for (int i = 0; i < lines.length; i++) { - if (i != 0) - sb.append('\n'); - - if (lines[i] != null) - sb.append(lines[i]); - } - - mBuffer = sb.toString(); - mHandler.post(mUpdateView); - } - } - @Override public void onCreate(Bundle savedValues) { super.onCreate(savedValues); requestWindowFeature(Window.FEATURE_PROGRESS); - setContentView(R.layout.secure_shell); - mOutput = (TextView) findViewById(R.id.output); + mTerminal = new JTATerminalView(this); + + // TODO: implement scroll bar on right. + setContentView((View)mTerminal); Log.d("SSH", "using URI " + getIntent().getData().toString()); @@ -307,7 +108,7 @@ public class SecureShell extends Activity { this.setTitle(title); - mConn = new ConnectionThread(mHostname, mUsername, mPort); + mConn = new TrileadConnectionThread(this, mTerminal, mHostname, mUsername, mPort); mKMap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD); @@ -340,10 +141,9 @@ public class SecureShell extends Activity { } }; - public String askPassword() { + public void askPassword() { Intent intent = new Intent(this, PasswordDialog.class); this.startSubActivity(intent, PASSWORD_REQUEST); - return null; } @Override @@ -351,55 +151,71 @@ public class SecureShell extends Activity { String data, Bundle extras) { if (requestCode == PASSWORD_REQUEST) { - - // If the request was cancelled, then we didn't get anything. - if (resultCode == RESULT_CANCELED) - mPassword = ""; - else - mPassword = data; - - sPass.release(); + mConn.setPassword(data); } } @Override public void onDestroy() { super.onDestroy(); - - if (sess != null) { - sess.close(); - sess = null; - } - - if (conn != null) { - conn.close(); - conn = null; - } + + mConn.finish(); + mConn = null; finish(); } @Override public boolean onKeyDown(int keyCode, KeyEvent msg) { - if (stdout != null) { - int c = mKMap.get(keyCode, msg.getMetaState()); + final OutputStream out = mConn.getWriter(); + if (out != null) { try { - stdout.write(c); + if (mKMap.isPrintingKey(keyCode) + || keyCode == KeyEvent.KEYCODE_SPACE) { + int c = mKMap.get(keyCode, msg.getMetaState()); + if (mMetaState) { + // Support CTRL-A through CTRL-Z + if (c >= 0x61 && c <= 0x79) + c -= 0x60; + else if (c >= 0x40 && c <= 0x59) + c -= 0x39; + mMetaState = false; + } + out.write(c); + } else if (keyCode == KeyEvent.KEYCODE_DEL) { + out.write(0x08); // CTRL-H + } else if (keyCode == KeyEvent.KEYCODE_NEWLINE + || keyCode == KeyEvent.KEYCODE_DPAD_LEFT + || keyCode == KeyEvent.KEYCODE_DPAD_UP + || keyCode == KeyEvent.KEYCODE_DPAD_DOWN + || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { + byte[] output = mTerminal.getKeyCode(keyCode, msg.getMetaState()); + if (output != null) + out.write(output); + } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { + if (mMetaState) { + out.write(0x1B); // ESCAPE + mMetaState = false; + } else { + mMetaState = true; + } + } else { + // This is not something we handle. + return super.onKeyDown(keyCode, msg); + } + return true; } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Log.e("SSH", "Can't write to stdout: "+ e.getMessage()); } } - - return super.onKeyDown(keyCode, msg); - } - - public void updateViewInUI() { - mOutput.setText(mBuffer); + return super.onKeyDown(keyCode, msg); } final Runnable mDisconnectAlert = new Runnable() { public void run() { + if (SecureShell.this.isFinishing()) + return; + AlertDialog d = AlertDialog.show(SecureShell.this, "Connection Lost", mDisconnectReason, "Ok", false); d.show(); @@ -407,11 +223,9 @@ public class SecureShell extends Activity { } }; - final ConnectionMonitor mConnectionMonitor = new ConnectionMonitor() { - public void connectionLost(Throwable reason) { - Log.d("SSH", "Connection ended."); - mDisconnectReason = reason.getMessage(); - mHandler.post(mDisconnectAlert); - } - }; + public void connectionLost(Throwable reason) { + Log.d("SSH", "Connection ended."); + mDisconnectReason = reason.getMessage(); + mHandler.post(mDisconnectAlert); + } } diff --git a/src/org/theb/ssh/ShellView.java b/src/org/theb/ssh/ShellView.java deleted file mode 100644 index 15d783b..0000000 --- a/src/org/theb/ssh/ShellView.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.theb.ssh; - -import java.io.IOException; - -import com.trilead.ssh2.Connection; -import com.trilead.ssh2.Session; - -import android.content.Context; -import android.graphics.Canvas; -import android.util.Log; -import android.widget.EditText; -import android.widget.TextView; - -public class ShellView extends EditText { - - private Connection mConn; - private Session mSess; - - private String mHostname; - private String mUsername; - private String mPassword; - - public ShellView(Context context) { - super(context); - // TODO Auto-generated constructor stub - - mPassword = "OEfmP07-"; - } - - @Override - public void onDraw(Canvas canvas) { - super.onDraw(canvas); - - append("Connecting... "); - mConn = new Connection(mHostname); - - try { - mConn.connect(new InteractiveHostKeyVerifier()); - append("OK\n"); - } catch (IOException e) { - append("Failed.\n"); - Log.e("SSH", e.getMessage()); - append("\nWhoops: " + e.getCause().getMessage() + "\n"); - } - - boolean enableKeyboardInteractive = true; - boolean enableDSA = true; - boolean enableRSA = true; - - try { - while (true) { - /* - if ((enableDSA || enableRSA ) && - mConn.isAuthMethodAvailable(username, "publickey"); - */ - - if (mConn.isAuthMethodAvailable(mUsername, "password")) { - boolean res = mConn.authenticateWithPassword(mUsername, mPassword); - if (res == true) - break; - - append("Login failed.\n"); - continue; - } - - throw new IOException("No supported authentication methods available."); - } - - mSess = mConn.openSession(); - append("Logged in as " + mUsername + ".\n"); - } catch (IOException e) { - Log.e("SSH", e.getMessage()); - } - append("Exiting\n"); - } - -} diff --git a/src/org/theb/ssh/Terminal.java b/src/org/theb/ssh/Terminal.java new file mode 100644 index 0000000..1212aa2 --- /dev/null +++ b/src/org/theb/ssh/Terminal.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ +package org.theb.ssh; + +import java.io.InputStream; +import java.io.OutputStream; + +public interface Terminal { + public int getWidth(); + public int getHeight(); + public int getRowCount(); + public int getColumnCount(); + public void start(InputStream in, OutputStream out); + public byte[] getKeyCode(int keyCode, int meta); +} diff --git a/src/org/theb/ssh/TrileadConnectionThread.java b/src/org/theb/ssh/TrileadConnectionThread.java new file mode 100644 index 0000000..63180b0 --- /dev/null +++ b/src/org/theb/ssh/TrileadConnectionThread.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2007 Kenny Root (kenny at the-b.org) + * + * This file is part of Connectbot. + * + * Connectbot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Connectbot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Connectbot. If not, see <http://www.gnu.org/licenses/>. + */ +package org.theb.ssh; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.Semaphore; + +import com.trilead.ssh2.Connection; +import com.trilead.ssh2.ConnectionMonitor; +import com.trilead.ssh2.Session; + +public class TrileadConnectionThread extends ConnectionThread { + private String hostname; + private String username; + private String password; + private int port; + + private Connection connection; + private Session session; + + private InputStream stdOut; + private OutputStream stdIn; + + private Semaphore sPass; + + protected FeedbackUI ui; + protected Terminal term; + + public TrileadConnectionThread(FeedbackUI ui, Terminal term, String hostname, String username, int port) { + super(ui, hostname, username, port); + this.ui = ui; + this.term = term; + this.hostname = hostname; + this.username = username; + this.port = port; + } + + @Override + public void finish() { + if (session != null) { + session.close(); + session = null; + } + + if (connection != null) { + connection.close(); + connection = null; + } + } + + @Override + public InputStream getReader() { + return stdOut; + } + + @Override + public OutputStream getWriter() { + return stdIn; + } + + @Override + public void run() { + connection = new Connection(hostname, port); + + connection.addConnectionMonitor((ConnectionMonitor) ui); + + ui.setWaiting(true, "Connection", "Connecting to " + hostname + "..."); + + try { + connection.connect(new InteractiveHostKeyVerifier()); + + ui.setWaiting(true, "Authenticating", "Trying to authenticate..."); + + // boolean enableKeyboardInteractive = true; + // boolean enableDSA = true; + // boolean enableRSA = true; + + while (true) { + /* + * if ((enableDSA || enableRSA ) && + * mConn.isAuthMethodAvailable(username, "publickey"); + */ + + if (connection.isAuthMethodAvailable(username, "password")) { + ui.setWaiting(true, "Authenticating", + "Trying to authenticate using password..."); + + // Set a semaphore that is unset by the returning dialog. + sPass = new Semaphore(0); + ui.askPassword(); + + // Wait for the user to answer. + sPass.acquire(); + sPass = null; + if (password == null) + continue; + + boolean res = connection.authenticateWithPassword(username, + password); + password = null; + if (res == true) + break; + + continue; + } + + throw new IOException( + "No supported authentication methods available."); + } + + ui.setWaiting(true, "Session", "Requesting shell..."); + + session = connection.openSession(); + + session.requestPTY("vt100", + term.getColumnCount(), term.getRowCount(), + term.getWidth(), term.getHeight(), + null); + + session.startShell(); + + stdIn = session.getStdin(); + // stderr = session.getStderr(); + stdOut = session.getStdout(); + + ui.setWaiting(false, null, null); + } catch (IOException e) { + ui.setWaiting(false, null, null); + return; + } catch (InterruptedException e) { + // This thread is coming to an end. Let us exit. + return; + } + + term.start(stdOut, stdIn); + } + + @Override + public void setPassword(String password) { + if (password == null) + this.password = ""; + else + this.password = password; + sPass.release(); + } +} |