commit 8b822a61d7721fce16e145b57dc67ca6ae8b752d Author: Carsten Haitzler Date: Tue Jun 12 10:10:01 2012 +0000 finally a terminal emulator for efl! sure - uses a lot of text objects. need evas textgrid obj and more, but this marks a first "usable" state for terminology. more needs doing like underline and strikethrough support, blink support, visual bell, config panel etc. SVN revision: 72007 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..854dee60 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Carsten Haitzler (Rasterman) diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..78c14199 --- /dev/null +++ b/COPYING @@ -0,0 +1,357 @@ +Copyright notice for terminology: + +Copyright (C) 2012-2012 Carsten Haitzler and various contributors (see AUTHORS) + +This library 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; version 2 of the License. + +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +Below is a copy of the GNU General Public License that is distributed +along with this library. If you do not have a copy below, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..f67815ae --- /dev/null +++ b/INSTALL @@ -0,0 +1,11 @@ +COMPILING and INSTALLING: + +If you got a official release tar archive do: + ./configure + +Then to compile: + make + +To install (run this as root, or the user who handles installs): + make install + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..fafb3a74 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,29 @@ +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src data + +MAINTAINERCLEANFILES = \ +Makefile.in \ +$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz \ +$(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.bz2 \ +aclocal.m4 \ +config.guess \ +terminology_config.h.in \ +config.sub \ +configure \ +compile \ +depcomp \ +install-sh \ +ltconfig \ +ltmain.sh \ +missing \ +mkinstalldirs \ +stamp-h.in \ +stamp-h \ +m4/libtool.m4 \ +m4/lt~obsolete.m4 \ +m4/ltoptions.m4 \ +m4/ltsugar.m4 \ +m4/ltversion.m4 + +EXTRA_DIST = README AUTHORS COPYING diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/README b/README new file mode 100644 index 00000000..d2271096 --- /dev/null +++ b/README @@ -0,0 +1,23 @@ +Terminology +----------- + +An EFL terminal emtulator + +Requiremnents: +-------------- + + * elementary 1.1 (1.0.99 SVN latest) + * evas 1.3 (1.2.99 SVN latest) + * ecore + * edje + +Please see http://www.enlightenment.org for information on these. + +Compiling: +---------- + +Once you have met requirements, compiling and installing is simple: + + ./configure + make + make install diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 00000000..31ccda04 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +rm -rf autom4te.cache +rm -f aclocal.m4 ltmain.sh + +touch README + +echo "Running aclocal..." ; aclocal -I m4 $ACLOCAL_FLAGS || exit 1 +echo "Running autoheader..." ; autoheader || exit 1 +echo "Running autoconf..." ; autoconf || exit 1 +echo "Running libtoolize..." ; (libtoolize --copy --automake || glibtoolize --automake) || exit 1 +echo "Running automake..." ; automake --add-missing --copy --gnu || exit 1 + +if [ -z "$NOCONFIGURE" ]; then + ./configure "$@" +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..05d92dd8 --- /dev/null +++ b/configure.ac @@ -0,0 +1,63 @@ +dnl Process this file with autoconf to produce a configure script. + +# get rid of that stupid cache mechanism +rm -f config.cache + +AC_INIT([terminology], [0.1.0], [enlightenment-devel@lists.sourceforge.net]) +AC_PREREQ([2.52]) +AC_CONFIG_SRCDIR([configure.ac]) +AC_CONFIG_MACRO_DIR([m4]) + +AC_CONFIG_HEADERS([terminology_config.h]) + +AM_INIT_AUTOMAKE([1.6 dist-bzip2]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AM_PROG_CC_STDC +AM_PROG_CC_C_O + +requirements="\ + elementary >= 1.0.99 \ + eina >= 1.2.99 \ + eet >= 1.6.99 \ + evas >= 1.2.99 \ + ecore >= 1.2.99 \ + edje >= 1.2.99" + +PKG_CHECK_MODULES([TERMINOLOGY], [${requirements}]) + +EFL_WITH_BIN([edje], [edje-cc], [edje_cc]) + +AC_CONFIG_FILES([ +Makefile +src/Makefile +src/bin/Makefile +data/Makefile +data/desktop/Makefile +data/icons/Makefile +data/images/Makefile +data/fonts/Makefile +data/themes/Makefile +data/themes/images/Makefile +]) + +AC_OUTPUT + +##################################################################### +## Info + +echo +echo +echo +echo "------------------------------------------------------------------------" +echo "$PACKAGE $VERSION" +echo "------------------------------------------------------------------------" +echo +echo "Compilation................: make (or gmake)" +echo " CPPFLAGS.................: $CPPFLAGS" +echo " CFLAGS...................: $CFLAGS" +echo " LDFLAGS..................: $LDFLAGS" +echo +echo "Installation...............: make install (as root if needed, with 'su' or 'sudo')" +echo " prefix...................: $prefix" +echo diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 00000000..735ca178 --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,3 @@ +MAINTAINERCLEANFILES = Makefile.in +SUBDIRS = desktop icons images fonts themes + diff --git a/data/desktop/Makefile.am b/data/desktop/Makefile.am new file mode 100644 index 00000000..e9d5fe67 --- /dev/null +++ b/data/desktop/Makefile.am @@ -0,0 +1,6 @@ +MAINTAINERCLEANFILES = Makefile.in +filesdir = $(datadir)/applications +files_DATA = \ +terminology.desktop + +EXTRA_DIST = $(files_DATA) diff --git a/data/desktop/terminology.desktop b/data/desktop/terminology.desktop new file mode 100644 index 00000000..0e7236b6 --- /dev/null +++ b/data/desktop/terminology.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Application +Name=Terminology +Generic=Terminal +Comment=Terminal emulator +Exec=terminology +Icon=terminology.png +Categories=Utility;TerminalEmulator; +Terminal=false +StartupWMClass=terminology diff --git a/data/fonts/Makefile.am b/data/fonts/Makefile.am new file mode 100644 index 00000000..b122eded --- /dev/null +++ b/data/fonts/Makefile.am @@ -0,0 +1,6 @@ +MAINTAINERCLEANFILES = Makefile.in +filesdir = $(pkgdatadir)/fonts +files_DATA = \ +nex6x10.pcf + +EXTRA_DIST = $(files_DATA) diff --git a/data/fonts/nex6x10.pcf b/data/fonts/nex6x10.pcf new file mode 100644 index 00000000..f41a5c64 Binary files /dev/null and b/data/fonts/nex6x10.pcf differ diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am new file mode 100644 index 00000000..e7129068 --- /dev/null +++ b/data/icons/Makefile.am @@ -0,0 +1,6 @@ +MAINTAINERCLEANFILES = Makefile.in +filesdir = $(datadir)/icons +files_DATA = \ +terminology.png + +EXTRA_DIST = $(files_DATA) diff --git a/data/icons/terminology.png b/data/icons/terminology.png new file mode 100644 index 00000000..fca757ac Binary files /dev/null and b/data/icons/terminology.png differ diff --git a/data/images/Makefile.am b/data/images/Makefile.am new file mode 100644 index 00000000..411e19c8 --- /dev/null +++ b/data/images/Makefile.am @@ -0,0 +1,5 @@ +MAINTAINERCLEANFILES = Makefile.in +filesdir = $(pkgdatadir)/images +files_DATA = + +EXTRA_DIST = $(files_DATA) diff --git a/data/themes/Makefile.am b/data/themes/Makefile.am new file mode 100644 index 00000000..6125a548 --- /dev/null +++ b/data/themes/Makefile.am @@ -0,0 +1,23 @@ +MAINTAINERCLEANFILES = Makefile.in + +SUBDIRS = images + +EDJE_CC = @edje_cc@ +EDJE_FLAGS_VERBOSE_ = +EDJE_FLAGS_VERBOSE_0 = +EDJE_FLAGS_VERBOSE_1 = -v +EDJE_FLAGS = $(EDJE_FLAGS_$(V)) -id $(top_srcdir)/data/themes/images -fd $(top_srcdir)/data/fonts + +filesdir = $(pkgdatadir)/themes +files_DATA = \ +default.edj + +EXTRA_DIST = default.edc + +default.edj: Makefile default.edc + $(EDJE_CC) $(EDJE_FLAGS) \ + $(top_srcdir)/data/themes/default.edc \ + $(top_builddir)/data/themes/default.edj + +clean-local: + rm -f *.edj diff --git a/data/themes/default.edc b/data/themes/default.edc new file mode 100644 index 00000000..cc6fc012 --- /dev/null +++ b/data/themes/default.edc @@ -0,0 +1,91 @@ +collections { + group { name: "terminology/background"; + images { + image: "bg_bevel.png" COMP; + image: "bg_shine.png" COMP; + } + parts { + part { name: "base"; type: RECT; + mouse_events: 1; + description { state: "default" 0.0; + color: 48 48 48 255; + } + } + part { name: "terminology.content"; type: SWALLOW; + description { state: "default" 0.0; + rel1.offset: 1 2; + rel2.offset: -2 -2; + } + } + part { name: "bevel"; + mouse_events: 0; + description { state: "default" 0.0; + rel1.offset: 0 1; + rel2.offset: -1 -1; + image.normal: "bg_bevel.png"; + image.border: 2 2 2 2; + image.middle: 0; + fill.smooth: 0; + } + } + part { name: "shine"; + mouse_events: 0; + description { state: "default" 0.0; + rel1.offset: 0 1; + rel2.offset: -1 -1; + image.normal: "bg_shine.png"; + align: 0.5 0.0; + aspect: (255/120) (255/120); + aspect_preference: HORIZONTAL; + } + } + } + } + group { name: "terminology/cursor"; + parts { + part { name: "base"; type: RECT; + mouse_events: 1; + description { state: "default" 0.0; + color: 255 255 255 30; + } + description { state: "focused" 0.0; + color: 255 255 255 160; + } + } + programs { + program { name: "focus_in"; + signal: "focus,in"; + source: "terminology"; + action: STATE_SET "focused" 0.0; + target: "base"; + after: "focus2"; + } + program { name: "focus2"; + in: 0.2 0.0; + action: STATE_SET "default" 0.0; + target: "base"; + after: "focus3"; + } + program { name: "focus3"; + in: 0.2 0.0; + action: STATE_SET "focused" 0.0; + target: "base"; + after: "focus2"; + } + program { name: "focus_out"; + signal: "focus,out"; + source: "terminology"; + action: ACTION_STOP; + target: "focus_in"; + target: "focus2"; + target: "focus3"; + after: "focus_out2"; + } + program { name: "focus_out2"; + action: STATE_SET "default" 0.0; + target: "base"; + } + } + } + } +} diff --git a/data/themes/images/Makefile.am b/data/themes/images/Makefile.am new file mode 100644 index 00000000..f4f40d0a --- /dev/null +++ b/data/themes/images/Makefile.am @@ -0,0 +1,4 @@ +MAINTAINERCLEANFILES = Makefile.in +EXTRA_DIST = \ +bg_bevel.png \ +bg_shine.png diff --git a/data/themes/images/bg_bevel.png b/data/themes/images/bg_bevel.png new file mode 100644 index 00000000..87e4d62d Binary files /dev/null and b/data/themes/images/bg_bevel.png differ diff --git a/data/themes/images/bg_shine.png b/data/themes/images/bg_shine.png new file mode 100644 index 00000000..47e5280f Binary files /dev/null and b/data/themes/images/bg_shine.png differ diff --git a/m4/efl_binary.m4 b/m4/efl_binary.m4 new file mode 100644 index 00000000..c7746881 --- /dev/null +++ b/m4/efl_binary.m4 @@ -0,0 +1,71 @@ +dnl Copyright (C) 2010 Vincent Torri +dnl That code is public domain and can be freely used or copied. + +dnl Macro that check if a binary is built or not + +dnl Usage: EFL_ENABLE_BIN(binary) +dnl Call AC_SUBST(BINARY_PRG) (BINARY is the uppercase of binary, - being transformed into _) +dnl Define have_binary (- is transformed into _) +dnl Define conditional BUILD_BINARY (BINARY is the uppercase of binary, - being transformed into _) + +AC_DEFUN([EFL_ENABLE_BIN], +[ + +m4_pushdef([UP], m4_translit([[$1]], [-a-z], [_A-Z]))dnl +m4_pushdef([DOWN], m4_translit([[$1]], [-A-Z], [_a-z]))dnl + +have_[]m4_defn([DOWN])="yes" + +dnl configure option + +AC_ARG_ENABLE([$1], + [AC_HELP_STRING([--disable-$1], [disable building of ]DOWN)], + [ + if test "x${enableval}" = "xyes" ; then + have_[]m4_defn([DOWN])="yes" + else + have_[]m4_defn([DOWN])="no" + fi + ]) + +AC_MSG_CHECKING([whether to build ]DOWN[ binary]) +AC_MSG_RESULT([$have_[]m4_defn([DOWN])]) + +if test "x$have_[]m4_defn([DOWN])" = "xyes"; then + UP[]_PRG=DOWN[${EXEEXT}] +fi + +AC_SUBST(UP[]_PRG) + +AM_CONDITIONAL(BUILD_[]UP, test "x$have_[]m4_defn([DOWN])" = "xyes") + +AS_IF([test "x$have_[]m4_defn([DOWN])" = "xyes"], [$2], [$3]) + +]) + + +dnl Macro that check if a binary is built or not + +dnl Usage: EFL_WITH_BIN(package, binary, default_value) +dnl Call AC_SUBST(_binary) (_binary is the lowercase of binary, - being transformed into _ by default, or the value set by the user) + +AC_DEFUN([EFL_WITH_BIN], +[ + +m4_pushdef([DOWN], m4_translit([[$2]], [-A-Z], [_a-z]))dnl + +dnl configure option + +AC_ARG_WITH([$2], + [AC_HELP_STRING([--with-$2=PATH], [specify a specific path to ]DOWN[ @<:@default=$3@:>@])], + [_efl_with_binary=${withval}], + [_efl_with_binary=$(pkg-config --variable=prefix $1)/bin/$3]) + +DOWN=${_efl_with_binary} +AC_MSG_NOTICE(DOWN[ set to ${_efl_with_binary}]) + +with_binary_[]m4_defn([DOWN])=${_efl_with_binary} + +AC_SUBST(DOWN) + +]) diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..ccfbf1af --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,2 @@ +MAINTAINERCLEANFILES = Makefile.in +SUBDIRS = bin diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am new file mode 100644 index 00000000..02f8496e --- /dev/null +++ b/src/bin/Makefile.am @@ -0,0 +1,19 @@ +MAINTAINERCLEANFILES = Makefile.in + +bin_PROGRAMS = terminology + +terminology_CPPFLAGS = -I. \ +-DPACKAGE_BIN_DIR=\"$(bindir)\" -DPACKAGE_LIB_DIR=\"$(libdir)\" \ +-DPACKAGE_DATA_DIR=\"$(pkgdatadir)\" @TERMINOLOGY_CFLAGS@ + +terminology_LDADD = @TERMINOLOGY_LIBS@ + +terminology_SOURCES = \ +col.c col.h \ +config.c config.h \ +keyin.c keyin.h \ +main.c \ +termio.c termio.h \ +termpty.c termpty.h \ +utf8.c utf8.h \ +win.c win.h diff --git a/src/bin/col.c b/src/bin/col.c new file mode 100644 index 00000000..32680821 --- /dev/null +++ b/src/bin/col.c @@ -0,0 +1,332 @@ +#include "col.h" + +const Color colors[2][11] = +{ + { // normal + { 0xaa, 0xaa, 0xaa, 0xff }, // COL_DEF + { 0x00, 0x00, 0x00, 0xff }, // COL_BLACK + { 0xcc, 0x33, 0x33, 0xff }, // COL_RED + { 0x33, 0xcc, 0x33, 0xff }, // COL_GREEN + { 0xcc, 0x88, 0x33, 0xff }, // COL_YELLOW + { 0x33, 0x33, 0xcc, 0xff }, // COL_BLUE + { 0xcc, 0x33, 0xcc, 0xff }, // COL_MAGENTA + { 0x33, 0xcc, 0xcc, 0xff }, // COL_CYAN + { 0xcc, 0xcc, 0xcc, 0xff }, // COL_WHITE + { 0x22, 0x22, 0x22, 0xff }, // COL_INVERSE + { 0xaa, 0xaa, 0xaa, 0xff }, // COL_INVERSEBG + }, + { // bright/bold + { 0xee, 0xee, 0xee, 0xff }, // COL_DEF + { 0x66, 0x66, 0x66, 0xff }, // COL_BLACK + { 0xff, 0x66, 0x66, 0xff }, // COL_RED + { 0x66, 0xff, 0x66, 0xff }, // COL_GREEN + { 0xff, 0xff, 0x66, 0xff }, // COL_YELLOW + { 0x66, 0x66, 0xff, 0xff }, // COL_BLUE + { 0xff, 0x66, 0xff, 0xff }, // COL_MAGENTA + { 0x66, 0xff, 0xff, 0xff }, // COL_CYAN + { 0xff, 0xff, 0xff, 0xff }, // COL_WHITE + { 0x11, 0x11, 0x11, 0xff }, // COL_INVERSE + { 0xee, 0xee, 0xee, 0xff }, // COL_INVERSEBG + } +}; + +const Color colors256[256] = +{ + // basic 16 repeated + { 0x00, 0x00, 0x00, 0xff }, // COL_BLACK + { 0xcc, 0x33, 0x33, 0xff }, // COL_RED + { 0x33, 0xcc, 0x33, 0xff }, // COL_GREEN + { 0xcc, 0x88, 0x33, 0xff }, // COL_YELLOW + { 0x33, 0x33, 0xcc, 0xff }, // COL_BLUE + { 0xcc, 0x33, 0xcc, 0xff }, // COL_MAGENTA + { 0x33, 0xcc, 0xcc, 0xff }, // COL_CYAN + { 0xcc, 0xcc, 0xcc, 0xff }, // COL_WHITE + + { 0x66, 0x66, 0x66, 0xff }, // COL_BLACK + { 0xff, 0x66, 0x66, 0xff }, // COL_RED + { 0x66, 0xff, 0x66, 0xff }, // COL_GREEN + { 0xff, 0xff, 0x66, 0xff }, // COL_YELLOW + { 0x66, 0x66, 0xff, 0xff }, // COL_BLUE + { 0xff, 0x66, 0xff, 0xff }, // COL_MAGENTA + { 0x66, 0xff, 0xff, 0xff }, // COL_CYAN + { 0xff, 0xff, 0xff, 0xff }, // COL_WHITE + + // pure 6x6x6 colorcube + { 0x00, 0x00, 0x00, 0xff }, + { 0x00, 0x00, 0x5f, 0xff }, + { 0x00, 0x00, 0x87, 0xff }, + { 0x00, 0x00, 0xaf, 0xff }, + { 0x00, 0x00, 0xd7, 0xff }, + { 0x00, 0x00, 0xff, 0xff }, + + { 0x00, 0x5f, 0x00, 0xff }, + { 0x00, 0x5f, 0x5f, 0xff }, + { 0x00, 0x5f, 0x87, 0xff }, + { 0x00, 0x5f, 0xaf, 0xff }, + { 0x00, 0x5f, 0xd7, 0xff }, + { 0x00, 0x5f, 0xff, 0xff }, + + { 0x00, 0x87, 0x00, 0xff }, + { 0x00, 0x87, 0x5f, 0xff }, + { 0x00, 0x87, 0x87, 0xff }, + { 0x00, 0x87, 0xaf, 0xff }, + { 0x00, 0x87, 0xd7, 0xff }, + { 0x00, 0x87, 0xff, 0xff }, + + { 0x00, 0xaf, 0x00, 0xff }, + { 0x00, 0xaf, 0x5f, 0xff }, + { 0x00, 0xaf, 0x87, 0xff }, + { 0x00, 0xaf, 0xaf, 0xff }, + { 0x00, 0xaf, 0xd7, 0xff }, + { 0x00, 0xaf, 0xff, 0xff }, + + { 0x00, 0xd7, 0x00, 0xff }, + { 0x00, 0xd7, 0x5f, 0xff }, + { 0x00, 0xd7, 0x87, 0xff }, + { 0x00, 0xd7, 0xaf, 0xff }, + { 0x00, 0xd7, 0xd7, 0xff }, + { 0x00, 0xd7, 0xff, 0xff }, + + { 0x00, 0xff, 0x00, 0xff }, + { 0x00, 0xff, 0x5f, 0xff }, + { 0x00, 0xff, 0x87, 0xff }, + { 0x00, 0xff, 0xaf, 0xff }, + { 0x00, 0xff, 0xd7, 0xff }, + { 0x00, 0xff, 0xff, 0xff }, + + { 0x5f, 0x00, 0x00, 0xff }, + { 0x5f, 0x00, 0x5f, 0xff }, + { 0x5f, 0x00, 0x87, 0xff }, + { 0x5f, 0x00, 0xaf, 0xff }, + { 0x5f, 0x00, 0xd7, 0xff }, + { 0x5f, 0x00, 0xff, 0xff }, + + { 0x5f, 0x5f, 0x00, 0xff }, + { 0x5f, 0x5f, 0x5f, 0xff }, + { 0x5f, 0x5f, 0x87, 0xff }, + { 0x5f, 0x5f, 0xaf, 0xff }, + { 0x5f, 0x5f, 0xd7, 0xff }, + { 0x5f, 0x5f, 0xff, 0xff }, + + { 0x5f, 0x87, 0x00, 0xff }, + { 0x5f, 0x87, 0x5f, 0xff }, + { 0x5f, 0x87, 0x87, 0xff }, + { 0x5f, 0x87, 0xaf, 0xff }, + { 0x5f, 0x87, 0xd7, 0xff }, + { 0x5f, 0x87, 0xff, 0xff }, + + { 0x5f, 0xaf, 0x00, 0xff }, + { 0x5f, 0xaf, 0x5f, 0xff }, + { 0x5f, 0xaf, 0x87, 0xff }, + { 0x5f, 0xaf, 0xaf, 0xff }, + { 0x5f, 0xaf, 0xd7, 0xff }, + { 0x5f, 0xaf, 0xff, 0xff }, + + { 0x5f, 0xd7, 0x00, 0xff }, + { 0x5f, 0xd7, 0x5f, 0xff }, + { 0x5f, 0xd7, 0x87, 0xff }, + { 0x5f, 0xd7, 0xaf, 0xff }, + { 0x5f, 0xd7, 0xd7, 0xff }, + { 0x5f, 0xd7, 0xff, 0xff }, + + { 0x5f, 0xff, 0x00, 0xff }, + { 0x5f, 0xff, 0x5f, 0xff }, + { 0x5f, 0xff, 0x87, 0xff }, + { 0x5f, 0xff, 0xaf, 0xff }, + { 0x5f, 0xff, 0xd7, 0xff }, + { 0x5f, 0xff, 0xff, 0xff }, + + { 0x87, 0x00, 0x00, 0xff }, + { 0x87, 0x00, 0x5f, 0xff }, + { 0x87, 0x00, 0x87, 0xff }, + { 0x87, 0x00, 0xaf, 0xff }, + { 0x87, 0x00, 0xd7, 0xff }, + { 0x87, 0x00, 0xff, 0xff }, + + { 0x87, 0x5f, 0x00, 0xff }, + { 0x87, 0x5f, 0x5f, 0xff }, + { 0x87, 0x5f, 0x87, 0xff }, + { 0x87, 0x5f, 0xaf, 0xff }, + { 0x87, 0x5f, 0xd7, 0xff }, + { 0x87, 0x5f, 0xff, 0xff }, + + { 0x87, 0x87, 0x00, 0xff }, + { 0x87, 0x87, 0x5f, 0xff }, + { 0x87, 0x87, 0x87, 0xff }, + { 0x87, 0x87, 0xaf, 0xff }, + { 0x87, 0x87, 0xd7, 0xff }, + { 0x87, 0x87, 0xff, 0xff }, + + { 0x87, 0xaf, 0x00, 0xff }, + { 0x87, 0xaf, 0x5f, 0xff }, + { 0x87, 0xaf, 0x87, 0xff }, + { 0x87, 0xaf, 0xaf, 0xff }, + { 0x87, 0xaf, 0xd7, 0xff }, + { 0x87, 0xaf, 0xff, 0xff }, + + { 0x87, 0xd7, 0x00, 0xff }, + { 0x87, 0xd7, 0x5f, 0xff }, + { 0x87, 0xd7, 0x87, 0xff }, + { 0x87, 0xd7, 0xaf, 0xff }, + { 0x87, 0xd7, 0xd7, 0xff }, + { 0x87, 0xd7, 0xff, 0xff }, + + { 0x87, 0xff, 0x00, 0xff }, + { 0x87, 0xff, 0x5f, 0xff }, + { 0x87, 0xff, 0x87, 0xff }, + { 0x87, 0xff, 0xaf, 0xff }, + { 0x87, 0xff, 0xd7, 0xff }, + { 0x87, 0xff, 0xff, 0xff }, + + { 0xaf, 0x00, 0x00, 0xff }, + { 0xaf, 0x00, 0x5f, 0xff }, + { 0xaf, 0x00, 0x87, 0xff }, + { 0xaf, 0x00, 0xaf, 0xff }, + { 0xaf, 0x00, 0xd7, 0xff }, + { 0xaf, 0x00, 0xff, 0xff }, + + { 0xaf, 0x5f, 0x00, 0xff }, + { 0xaf, 0x5f, 0x5f, 0xff }, + { 0xaf, 0x5f, 0x87, 0xff }, + { 0xaf, 0x5f, 0xaf, 0xff }, + { 0xaf, 0x5f, 0xd7, 0xff }, + { 0xaf, 0x5f, 0xff, 0xff }, + + { 0xaf, 0x87, 0x00, 0xff }, + { 0xaf, 0x87, 0x5f, 0xff }, + { 0xaf, 0x87, 0x87, 0xff }, + { 0xaf, 0x87, 0xaf, 0xff }, + { 0xaf, 0x87, 0xd7, 0xff }, + { 0xaf, 0x87, 0xff, 0xff }, + + { 0xaf, 0xaf, 0x00, 0xff }, + { 0xaf, 0xaf, 0x5f, 0xff }, + { 0xaf, 0xaf, 0x87, 0xff }, + { 0xaf, 0xaf, 0xaf, 0xff }, + { 0xaf, 0xaf, 0xd7, 0xff }, + { 0xaf, 0xaf, 0xff, 0xff }, + + { 0xaf, 0xd7, 0x00, 0xff }, + { 0xaf, 0xd7, 0x5f, 0xff }, + { 0xaf, 0xd7, 0x87, 0xff }, + { 0xaf, 0xd7, 0xaf, 0xff }, + { 0xaf, 0xd7, 0xd7, 0xff }, + { 0xaf, 0xd7, 0xff, 0xff }, + + { 0xaf, 0xff, 0x00, 0xff }, + { 0xaf, 0xff, 0x5f, 0xff }, + { 0xaf, 0xff, 0x87, 0xff }, + { 0xaf, 0xff, 0xaf, 0xff }, + { 0xaf, 0xff, 0xd7, 0xff }, + { 0xaf, 0xff, 0xff, 0xff }, + + { 0xd7, 0x00, 0x00, 0xff }, + { 0xd7, 0x00, 0x5f, 0xff }, + { 0xd7, 0x00, 0x87, 0xff }, + { 0xd7, 0x00, 0xaf, 0xff }, + { 0xd7, 0x00, 0xd7, 0xff }, + { 0xd7, 0x00, 0xff, 0xff }, + + { 0xd7, 0x5f, 0x00, 0xff }, + { 0xd7, 0x5f, 0x5f, 0xff }, + { 0xd7, 0x5f, 0x87, 0xff }, + { 0xd7, 0x5f, 0xaf, 0xff }, + { 0xd7, 0x5f, 0xd7, 0xff }, + { 0xd7, 0x5f, 0xff, 0xff }, + + { 0xd7, 0x87, 0x00, 0xff }, + { 0xd7, 0x87, 0x5f, 0xff }, + { 0xd7, 0x87, 0x87, 0xff }, + { 0xd7, 0x87, 0xaf, 0xff }, + { 0xd7, 0x87, 0xd7, 0xff }, + { 0xd7, 0x87, 0xff, 0xff }, + + { 0xd7, 0xaf, 0x00, 0xff }, + { 0xd7, 0xaf, 0x5f, 0xff }, + { 0xd7, 0xaf, 0x87, 0xff }, + { 0xd7, 0xaf, 0xaf, 0xff }, + { 0xd7, 0xaf, 0xd7, 0xff }, + { 0xd7, 0xaf, 0xff, 0xff }, + + { 0xd7, 0xd7, 0x00, 0xff }, + { 0xd7, 0xd7, 0x5f, 0xff }, + { 0xd7, 0xd7, 0x87, 0xff }, + { 0xd7, 0xd7, 0xaf, 0xff }, + { 0xd7, 0xd7, 0xd7, 0xff }, + { 0xd7, 0xd7, 0xff, 0xff }, + + { 0xd7, 0xff, 0x00, 0xff }, + { 0xd7, 0xff, 0x5f, 0xff }, + { 0xd7, 0xff, 0x87, 0xff }, + { 0xd7, 0xff, 0xaf, 0xff }, + { 0xd7, 0xff, 0xd7, 0xff }, + { 0xd7, 0xff, 0xff, 0xff }, + + { 0xff, 0x00, 0x00, 0xff }, + { 0xff, 0x00, 0x5f, 0xff }, + { 0xff, 0x00, 0x87, 0xff }, + { 0xff, 0x00, 0xaf, 0xff }, + { 0xff, 0x00, 0xd7, 0xff }, + { 0xff, 0x00, 0xff, 0xff }, + + { 0xff, 0x5f, 0x00, 0xff }, + { 0xff, 0x5f, 0x5f, 0xff }, + { 0xff, 0x5f, 0x87, 0xff }, + { 0xff, 0x5f, 0xaf, 0xff }, + { 0xff, 0x5f, 0xd7, 0xff }, + { 0xff, 0x5f, 0xff, 0xff }, + + { 0xff, 0x87, 0x00, 0xff }, + { 0xff, 0x87, 0x5f, 0xff }, + { 0xff, 0x87, 0x87, 0xff }, + { 0xff, 0x87, 0xaf, 0xff }, + { 0xff, 0x87, 0xd7, 0xff }, + { 0xff, 0x87, 0xff, 0xff }, + + { 0xff, 0xaf, 0x00, 0xff }, + { 0xff, 0xaf, 0x5f, 0xff }, + { 0xff, 0xaf, 0x87, 0xff }, + { 0xff, 0xaf, 0xaf, 0xff }, + { 0xff, 0xaf, 0xd7, 0xff }, + { 0xff, 0xaf, 0xff, 0xff }, + + { 0xff, 0xd7, 0x00, 0xff }, + { 0xff, 0xd7, 0x5f, 0xff }, + { 0xff, 0xd7, 0x87, 0xff }, + { 0xff, 0xd7, 0xaf, 0xff }, + { 0xff, 0xd7, 0xd7, 0xff }, + { 0xff, 0xd7, 0xff, 0xff }, + + { 0xff, 0xff, 0x00, 0xff }, + { 0xff, 0xff, 0x5f, 0xff }, + { 0xff, 0xff, 0x87, 0xff }, + { 0xff, 0xff, 0xaf, 0xff }, + { 0xff, 0xff, 0xd7, 0xff }, + { 0xff, 0xff, 0xff, 0xff }, + + // greyscale ramp (24 not including black and white, so 26 if included) + { 0x08, 0x08, 0x08, 0xff }, + { 0x12, 0x12, 0x12, 0xff }, + { 0x1c, 0x1c, 0x1c, 0xff }, + { 0x26, 0x26, 0x26, 0xff }, + { 0x30, 0x30, 0x30, 0xff }, + { 0x3a, 0x3a, 0x3a, 0xff }, + { 0x44, 0x44, 0x44, 0xff }, + { 0x4e, 0x4e, 0x4e, 0xff }, + { 0x58, 0x58, 0x58, 0xff }, + { 0x62, 0x62, 0x62, 0xff }, + { 0x6c, 0x6c, 0x6c, 0xff }, + { 0x76, 0x76, 0x76, 0xff }, + { 0x80, 0x80, 0x80, 0xff }, + { 0x8a, 0x8a, 0x8a, 0xff }, + { 0x94, 0x94, 0x94, 0xff }, + { 0x9e, 0x9e, 0x9e, 0xff }, + { 0xa8, 0xa8, 0xa8, 0xff }, + { 0xb2, 0xb2, 0xb2, 0xff }, + { 0xbc, 0xbc, 0xbc, 0xff }, + { 0xc6, 0xc6, 0xc6, 0xff }, + { 0xd0, 0xd0, 0xd0, 0xff }, + { 0xda, 0xda, 0xda, 0xff }, + { 0xe4, 0xe4, 0xe4, 0xff }, + { 0xee, 0xee, 0xee, 0xff }, +}; diff --git a/src/bin/col.h b/src/bin/col.h new file mode 100644 index 00000000..828e9570 --- /dev/null +++ b/src/bin/col.h @@ -0,0 +1,9 @@ +typedef struct _Color Color; + +struct _Color +{ + unsigned char r, g, b, a; +}; + +extern const Color colors[2][11]; +extern const Color colors256[256]; diff --git a/src/bin/config.c b/src/bin/config.c new file mode 100644 index 00000000..0db18ab8 --- /dev/null +++ b/src/bin/config.c @@ -0,0 +1,24 @@ +#include +#include "config.h" + +Config *config = NULL; + +void +config_init(void) +{ + config = calloc(1, sizeof(Config)); +// config->font.bitmap = 0; +// config->font.name = eina_stringshare_add("Monospace"); + config->font.bitmap = 1; + config->font.name = eina_stringshare_add("nex6x10.pcf"); + config->font.size = 10; + config->scrollback = 4096; + config->theme = "default.edj"; + config->jump_on_change = 1; + config->wordsep = "'\"()[]{}=*!#$^\\:;,?` "; +} + +void +config_shutdown(void) +{ +} diff --git a/src/bin/config.h b/src/bin/config.h new file mode 100644 index 00000000..a52e65b0 --- /dev/null +++ b/src/bin/config.h @@ -0,0 +1,20 @@ +typedef struct _Config Config; + +struct _Config +{ + struct { + const char *name; + int size; + unsigned char bitmap; + } font; + int scrollback; + const char *theme; + unsigned char jump_on_change; + const char *wordsep; +}; + +extern Config *config; + +void config_init(void); +void config_shutdown(void); + diff --git a/src/bin/keyin.c b/src/bin/keyin.c new file mode 100644 index 00000000..034ff603 --- /dev/null +++ b/src/bin/keyin.c @@ -0,0 +1,222 @@ +#include +#include "termpty.h" +#include "keyin.h" + +typedef struct _Keyout Keyout; + +struct _Keyout +{ + const char *in; + const char *out; + int outlen; +}; + +#define KEY(in, out) {in, out, sizeof(out) - 1} + +static const Keyout crlf_keyout[] = +{ + KEY("Return", "\r\n"), + + KEY(NULL, "END") +}; + +static const Keyout nocrlf_keyout[] = +{ + KEY("Return", "\r"), + + KEY(NULL, "END") +}; + +static const Keyout appcur_keyout[] = +{ + KEY("Left", "\033OD"), + KEY("Right", "\033OC"), + KEY("Up", "\033OA"), + KEY("Down", "\033OB"), + + KEY(NULL, "END") +}; + +static const Keyout keyout[] = +{ + KEY("BackSpace", "\177"), +// KEY("BackSpace", "\b"), + KEY("Left", "\033[D"), + KEY("Right", "\033[C"), + KEY("Up", "\033[A"), + KEY("Down", "\033[B"), +// KEY("Tab", "\t"), +// KEY("ISO_Left_Tab", "\t"), + KEY("Home", "\033[7~"), + KEY("End", "\033[8~"), + KEY("Prior", "\033[5~"), + KEY("Next", "\033[6~"), + KEY("Insert", "\033[2~"), + KEY("Delete", "\033[3~"), + KEY("Menu", "\033[29~"), + KEY("Find", "\033[1~"), + KEY("Help", "\033[28~"), + KEY("Execute", "\033[3~"), + KEY("Select", "\033[4~"), + KEY("F1", "\033[11~"), + KEY("F2", "\033[12~"), + KEY("F3", "\033[13~"), + KEY("F4", "\033[14~"), + KEY("F5", "\033[15~"), + KEY("F6", "\033[17~"), + KEY("F7", "\033[18~"), + KEY("F8", "\033[19~"), + KEY("F9", "\033[20~"), + KEY("F10", "\033[21~"), + KEY("F11", "\033[23~"), + KEY("F12", "\033[24~"), + KEY("F13", "\033[25~"), + KEY("F14", "\033[26~"), + KEY("F15", "\033[28~"), + KEY("F16", "\033[29~"), + KEY("F17", "\033[31~"), + KEY("F18", "\033[32~"), + KEY("F19", "\033[33~"), + KEY("F20", "\033[34~"), + KEY("F21", "\033[35~"), + KEY("F22", "\033[36~"), + KEY("F23", "\033[37~"), + KEY("F24", "\033[38~"), + KEY("F25", "\033[39~"), + KEY("F26", "\033[40~"), + KEY("F27", "\033[41~"), + KEY("F28", "\033[42~"), + KEY("F29", "\033[43~"), + KEY("F30", "\033[44~"), + KEY("F31", "\033[45~"), + KEY("F32", "\033[46~"), + KEY("F33", "\033[47~"), + KEY("F34", "\033[48~"), + KEY("F35", "\033[49~"), + KEY("KP_F1", "\033OP"), + KEY("KP_F2", "\033OQ"), + KEY("KP_F3", "\033OR"), + KEY("KP_F4", "\033OS"), + KEY("KP_Begin", "\033Ou"), + KEY("KP_Multiply", "\033Oj"), + KEY("KP_Add", "\033Ok"), + KEY("KP_Separator", "\033Ol"), + KEY("KP_Subtract", "\033Om"), + KEY("KP_Decimal", "\033On"), + KEY("KP_Divide", "\033Oo"), + KEY("KP_0", "\033Op"), + KEY("KP_0", "\033Oq"), + KEY("KP_0", "\033Or"), + KEY("KP_0", "\033Os"), + KEY("KP_0", "\033Ot"), + KEY("KP_0", "\033Ou"), + KEY("KP_0", "\033Ov"), + KEY("KP_0", "\033Ow"), + KEY("KP_0", "\033Ox"), + KEY("KP_0", "\033Oy"), + + KEY(NULL, "END") +}; + +static const Keyout kp_keyout[] = +{ + KEY("KP_Left", "\033[D"), + KEY("KP_Right", "\033[C"), + KEY("KP_Up", "\033[A"), + KEY("KP_Down", "\033[B"), + KEY("KP_Home", "\033[7~"), + KEY("KP_End", "\033[8~"), + KEY("KP_Prior", "\033[5~"), + KEY("KP_Next", "\033[6~"), + KEY("KP_Insert", "\033[2~"), + KEY("KP_Delete", "\033[3~"), + KEY("KP_Enter", "\r"), + + KEY(NULL, "END") +}; + +static const Keyout kps_keyout[] = +{ + KEY("KP_Left", "\033Ot"), + KEY("KP_Right", "\033Ov"), + KEY("KP_Up", "\033Ox"), + KEY("KP_Down", "\033Or"), + KEY("KP_Home", "\033Ow"), + KEY("KP_End", "\033Oq"), + KEY("KP_Prior", "\033Oy"), + KEY("KP_Next", "\033Os"), + KEY("KP_Insert", "\033Op"), + KEY("KP_Delete", "\033On"), + KEY("KP_Enter", "\033OM"), + + KEY(NULL, "END") +}; + +static Eina_Bool +_key_try(Termpty *ty, const Keyout *map, Evas_Event_Key_Down *ev) +{ + int i; + + if (!ev->keyname) return EINA_FALSE; + for (i = 0; map[i].in; i++) + { + if (!strcmp(ev->keyname, map[i].in)) + { + termpty_write(ty, map[i].out, map[i].outlen); + return EINA_TRUE; + } + } + return EINA_FALSE; +} + +void +keyin_handle(Termpty *ty, Evas_Event_Key_Down *ev) +{ + if (ty->state.crlf) + { + if (_key_try(ty, crlf_keyout, ev)) return; + } + else + { + if (_key_try(ty, nocrlf_keyout, ev)) return; + } + if (ty->state.appcursor) + { + if (_key_try(ty, appcur_keyout, ev)) return; + } + if (_key_try(ty, keyout, ev)) return; + if ( + ((ty->state.alt_kp) && + (evas_key_modifier_is_set(ev->modifiers, "Shift"))) || + ((!ty->state.alt_kp) && + (!evas_key_modifier_is_set(ev->modifiers, "Shift")))) + { + if (_key_try(ty, kp_keyout, ev)) return; + } + else + { + if (_key_try(ty, kps_keyout, ev)) return; + } + if (evas_key_modifier_is_set(ev->modifiers, "Control")) + { + if (!strcmp(ev->keyname, "Minus")) + { + termpty_write(ty, "\037", 1); // generate US (unit separator) + return; + } + else if (!strcmp(ev->keyname, "space")) + { + termpty_write(ty, "\0", 1); // generate 0 byte for ctrl+space + return; + } + } + if (ev->string) + { + if ((ev->string[0]) && (!ev->string[1])) + { + if (evas_key_modifier_is_set(ev->modifiers, "Alt")) + termpty_write(ty, "\033", 1); + } + termpty_write(ty, ev->string, strlen(ev->string)); + } +} diff --git a/src/bin/keyin.h b/src/bin/keyin.h new file mode 100644 index 00000000..10174ca5 --- /dev/null +++ b/src/bin/keyin.h @@ -0,0 +1,2 @@ +void keyin_handle(Termpty *ty, Evas_Event_Key_Down *ev); + diff --git a/src/bin/main.c b/src/bin/main.c new file mode 100644 index 00000000..d6498f01 --- /dev/null +++ b/src/bin/main.c @@ -0,0 +1,95 @@ +#include +#include "win.h" +#include "termio.h" +#include "config.h" + +const char *cmd = NULL; +static Evas_Object *win, *bg, *term; + +static void +_cb_focus_in(void *data, Evas_Object *obj, void *event) +{ + edje_object_signal_emit(bg, "focus,in", "terminology"); + elm_object_focus_set(data, EINA_TRUE); +} + +static void +_cb_focus_out(void *data, Evas_Object *obj, void *event) +{ + edje_object_signal_emit(bg, "focus,out", "terminology"); + elm_object_focus_set(data, EINA_FALSE); +} + +static void +_cb_size_hint(void *data, Evas *e, Evas_Object *obj, void *event) +{ + Evas_Coord mw, mh, rw, rh, w = 0, h = 0; + + evas_object_size_hint_min_get(obj, &mw, &mh); + evas_object_size_hint_request_get(obj, &rw, &rh); + + edje_object_size_min_calc(bg, &w, &h); + evas_object_size_hint_min_set(bg, w, h); + elm_win_size_base_set(win, w - mw, h - mh); + elm_win_size_step_set(win, mw, mh); + if (!evas_object_data_get(obj, "sizedone")) + { + evas_object_resize(win, w - mw + rw, h - mh + rh); + evas_object_data_set(obj, "sizedone", obj); + } +} + +EAPI_MAIN int +elm_main(int argc, char **argv) +{ + int i; + Evas_Object *o; + char buf[4096]; + + config_init(); + elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); + elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR); + elm_app_compile_data_dir_set(PACKAGE_DATA_DIR); + elm_app_info_set(elm_main, "terminology", "themes/default.edj"); + + for (i = 1; i < argc; i++) + { + if ((!strcmp(argv[i], "-e")) && (i < (argc - 1))) + { + i++; + cmd = argv[i]; + } + } + + win = tg_win_add(); + + bg = o = edje_object_add(evas_object_evas_get(win)); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL); + snprintf(buf, sizeof(buf), "%s/themes/%s", + elm_app_data_dir_get(), config->theme); + edje_object_file_set(o, buf, "terminology/background"); + elm_win_resize_object_add(win, o); + evas_object_show(o); + + term = o = termio_add(win, cmd, 80, 24); + termio_win_set(o, win); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_fill_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_event_callback_add(o, EVAS_CALLBACK_CHANGED_SIZE_HINTS, + _cb_size_hint, win); + edje_object_part_swallow(bg, "terminology.content", o); + evas_object_show(o); + + evas_object_smart_callback_add(win, "focus,in", _cb_focus_in, term); + evas_object_smart_callback_add(win, "focus,out", _cb_focus_out, term); + _cb_size_hint(win, evas_object_evas_get(win), term, NULL); + + evas_object_show(win); + + elm_run(); + elm_shutdown(); + config_shutdown(); + return 0; +} +ELM_MAIN() diff --git a/src/bin/termio.c b/src/bin/termio.c new file mode 100644 index 00000000..300aba85 --- /dev/null +++ b/src/bin/termio.c @@ -0,0 +1,1084 @@ +#include +#include "termio.h" +#include "termpty.h" +#include "utf8.h" +#include "col.h" +#include "keyin.h" +#include "config.h" + +typedef struct _Termio Termio; +typedef struct _Termch Termch; + +struct _Termio +{ + Evas_Object_Smart_Clipped_Data __clipped_data; + struct { + int size; + const char *name; + int chw, chh; + } font; + struct { + int w, h; + Termch *array; + } grid; + struct { + Evas_Object *obj, *selo1, *selo2, *selo3; + int x, y; + struct { + int x, y; + } sel1, sel2; + Eina_Bool sel : 1; + Eina_Bool makesel : 1; + } cur; + int scroll; + Evas_Object *event; + Termpty *pty; + Ecore_Job *job; + Ecore_Timer *delayed_size_timer; + Evas_Object *win; + Eina_Bool jump_on_change : 1; +}; + +struct _Termch +{ + Evas_Object *bg; + Evas_Object *tx; +}; + +static Evas_Smart *_smart = NULL; +static Evas_Smart_Class _termio_sc = EVAS_SMART_CLASS_INIT_NULL; + +static void _smart_calculate(Evas_Object *obj); + +static void +_smart_apply(Evas_Object *obj) +{ + Termio *sd = evas_object_smart_data_get(obj); + Evas_Coord ox, oy, ow, oh; + char txt[8]; + if (!sd) return; + evas_object_geometry_get(obj, &ox, &oy, &ow, &oh); + + if (sd->grid.array) + { + int i, j, x, y, w; + + i = 0; + for (y = 0; y < sd->grid.h; y++) + { + Termcell *cells; + + w = 0; + cells = termpty_cellrow_get(sd->pty, y - sd->scroll, &w); + j = 0; + for (x = 0; x < sd->grid.w; x++) + { + Evas_Object *bg = sd->grid.array[i].bg; + Evas_Object *tx = sd->grid.array[i].tx; + + if ((!cells) || (x >= w)) + { + evas_object_hide(bg); + evas_object_hide(tx); + } + else + { + Color c1, c2; + + if (cells[j].att.invisible) + { + evas_object_hide(tx); + evas_object_hide(bg); + } + else + { + int cbd, cbdbg, cfg, cbg; + + // colors + cbd = cells[j].att.bold; + cbdbg = 0; + cfg = cells[j].att.fg; + cbg = cells[j].att.bg; + + if (cells[j].att.inverse) + { + cfg = COL_INVERSE; + cbg = COL_INVERSEBG; + cbdbg = cbd; + c1 = colors[cbd][cfg]; + c2 = colors[cbdbg][cbg]; + if (cbg == COL_DEF) evas_object_hide(bg); + else evas_object_show(bg); + } + else + { + if (cells[j].att.fg256) + c1 = colors256[cfg]; + else + c1 = colors[cbd][cfg]; + if (cells[j].att.bg256) + { + c2 = colors256[cbg]; + evas_object_show(bg); + } + else + { + c2 = colors[cbdbg][cbg]; + if (cbg == COL_DEF) evas_object_hide(bg); + else evas_object_show(bg); + } + } + if (cells[j].att.faint) + { + c1.r /= 2; + c1.g /= 2; + c1.b /= 2; + c1.a /= 2; + + c2.r /= 2; + c2.g /= 2; + c2.b /= 2; + c2.a /= 2; + } +// if (cells[j].att.unerline) {} +// if (cells[j].att.italic) {} // never going 2 support +// if (cells[j].att.strike) {} +// if (cells[j].att.blink) {} +// if (cells[j].att.blink2) {} + evas_object_color_set(tx, c1.r, c1.g, c1.b, c1.a); + evas_object_color_set(bg, c2.r, c2.g, c2.b, c2.a); + + // text - convert glyph back to utf8 str seq + if (cells[j].glyph > 0) + { + int g = cells[j].glyph; + + glyph_to_utf8(g, txt); + // special case for whitespace :) + if (cells[j].glyph == ' ') + { + evas_object_hide(tx); + } + else + { + evas_object_text_text_set(tx, txt); + evas_object_show(tx); + } + } + else + { + evas_object_hide(tx); + if (cbg == COL_DEF) evas_object_hide(bg); + else evas_object_show(bg); + } + } + } + j++; + i++; + } + } + } + if ((sd->scroll != 0) || (sd->pty->state.hidecursor)) + evas_object_hide(sd->cur.obj); + else + evas_object_show(sd->cur.obj); + sd->cur.x = sd->pty->state.cx; + sd->cur.y = sd->pty->state.cy; + evas_object_move(sd->cur.obj, + ox + (sd->cur.x * sd->font.chw), + oy + (sd->cur.y * sd->font.chh)); + if (sd->cur.sel) + { + int x1, y1, x2, y2; + + x1 = sd->cur.sel1.x; + y1 = sd->cur.sel1.y; + x2 = sd->cur.sel2.x; + y2 = sd->cur.sel2.y; + if ((y1 > y2) || ((y1 == y2) && (x2 < x1))) + { + int t; + + t = x1; x1 = x2; x2 = t; + t = y1; y1 = y2; y2 = t; + } + + if (y2 > y1) + { + evas_object_move(sd->cur.selo1, + ox + (x1 * sd->font.chw), + oy + ((y1 + sd->scroll) * sd->font.chh)); + evas_object_resize(sd->cur.selo1, + (sd->grid.w - x1) * sd->font.chw, + sd->font.chh); + evas_object_show(sd->cur.selo1); + + evas_object_move(sd->cur.selo3, + ox, oy + ((y2 + sd->scroll) * sd->font.chh)); + evas_object_resize(sd->cur.selo3, + (x2 + 1) * sd->font.chw, + sd->font.chh); + evas_object_show(sd->cur.selo3); + } + else + { + evas_object_move(sd->cur.selo1, + ox + (x1 * sd->font.chw), + oy + ((y1 + sd->scroll) * sd->font.chh)); + evas_object_resize(sd->cur.selo1, + (x2 - x1 + 1) * sd->font.chw, + sd->font.chh); + evas_object_show(sd->cur.selo1); + evas_object_hide(sd->cur.selo3); + } + if (y2 > (y1 + 1)) + { + evas_object_move(sd->cur.selo2, + ox, oy + ((y1 + 1 + sd->scroll) * sd->font.chh)); + evas_object_resize(sd->cur.selo2, + sd->grid.w * sd->font.chw, + (y2 - y1 - 1) * sd->font.chh); + evas_object_show(sd->cur.selo2); + } + else + evas_object_hide(sd->cur.selo2); + } + else + { + evas_object_hide(sd->cur.selo1); + evas_object_hide(sd->cur.selo2); + evas_object_hide(sd->cur.selo3); + } +} + +static void +_smart_size(Evas_Object *obj, int w, int h) +{ + Termio *sd = evas_object_smart_data_get(obj); + if (!sd) return; + + if (w < 1) w = 1; + if (h < 1) h = 1; + if ((w == sd->grid.w) && (h == sd->grid.h)) return; + + evas_event_freeze(evas_object_evas_get(obj)); + if (sd->grid.array) + { + int i, size = sd->grid.w * sd->grid.h; + + for (i = 0; i < size; i++) + { + if (sd->grid.array[i].bg) evas_object_del(sd->grid.array[i].bg); + if (sd->grid.array[i].tx) evas_object_del(sd->grid.array[i].tx); + } + free(sd->grid.array); + sd->grid.array = NULL; + } + sd->grid.w = w; + sd->grid.h = h; + sd->grid.array = calloc(1, sizeof(Termch) * sd->grid.w * sd->grid.h); + if (sd->grid.array) + { + int i, x, y; + + i = 0; + for (y = 0; y < sd->grid.h; y++) + { + for (x = 0; x < sd->grid.w; x++) + { + Evas_Object *bg, *tx; + + bg = evas_object_rectangle_add(evas_object_evas_get(obj)); + tx = evas_object_text_add(evas_object_evas_get(obj)); + evas_object_pass_events_set(bg, EINA_TRUE); + evas_object_pass_events_set(tx, EINA_TRUE); + evas_object_propagate_events_set(bg, EINA_FALSE); + evas_object_propagate_events_set(tx, EINA_FALSE); + sd->grid.array[i].bg = bg; + sd->grid.array[i].tx = tx; + evas_object_smart_member_add(bg, obj); + evas_object_smart_member_add(tx, obj); + evas_object_resize(bg, sd->font.chw, sd->font.chh); + evas_object_text_font_set(tx, sd->font.name, sd->font.size); + evas_object_color_set(tx, 0, 0, 0, 0); + evas_object_color_set(bg, 0, 0, 0, 0); + i++; + } + } + } + evas_object_raise(sd->cur.selo1); + evas_object_raise(sd->cur.selo2); + evas_object_raise(sd->cur.selo3); + evas_object_raise(sd->cur.obj); + evas_object_resize(sd->cur.obj, sd->font.chw, sd->font.chh); + evas_object_size_hint_min_set(obj, sd->font.chw, sd->font.chh); + evas_object_size_hint_request_set(obj, + sd->font.chw * sd->grid.w, + sd->font.chh * sd->grid.h); + evas_object_raise(sd->event); + termpty_resize(sd->pty, w, h); + _smart_calculate(obj); + _smart_apply(obj); + evas_event_thaw(evas_object_evas_get(obj)); +} + +static Eina_Bool +_smart_cb_delayed_size(void *data) +{ + Evas_Object *obj = data; + Termio *sd = evas_object_smart_data_get(obj); + Evas_Coord ow = 0, oh = 0; + int w, h; + + if (!sd) return EINA_FALSE; + sd->delayed_size_timer = NULL; + + evas_object_geometry_get(obj, NULL, NULL, &ow, &oh); + + w = ow / sd->font.chw; + h = oh / sd->font.chh; + _smart_size(obj, w, h); + return EINA_FALSE; +} + +static void +_smart_cb_change(void *data) +{ + Evas_Object *obj = data; + Termio *sd; + sd = evas_object_smart_data_get(obj); + if (!sd) return; + sd->job = NULL; + _smart_apply(obj); +} + +static void +_take_selection(Evas_Object *obj) +{ + Termio *sd = evas_object_smart_data_get(obj); + int x1, y1, x2, y2; + char *s; + + if (!sd) return; + x1 = sd->cur.sel1.x; + y1 = sd->cur.sel1.y; + x2 = sd->cur.sel2.x; + y2 = sd->cur.sel2.y; + if ((y1 > y2) || ((y1 == y2) && (x2 < x1))) + { + int t; + + t = x1; x1 = x2; x2 = t; + t = y1; y1 = y2; y2 = t; + } + s = termio_selection_get(obj, x1, y1, x2, y2); + if (s) + { + if (sd->win) + elm_cnp_selection_set(sd->win, ELM_SEL_TYPE_PRIMARY, + ELM_SEL_FORMAT_TEXT, s, strlen(s)); + free(s); + } +} + +static void +_clear_selection(Evas_Object *obj) +{ + Termio *sd = evas_object_smart_data_get(obj); + if (!sd) return; + if (!sd->win) return; + elm_object_cnp_selection_clear(sd->win, ELM_SEL_TYPE_PRIMARY); +} + +static Eina_Bool +_getsel_cb(void *data, Evas_Object *obj, Elm_Selection_Data *ev) +{ + Termio *sd = evas_object_smart_data_get(data); + if (!sd) return EINA_FALSE; + + if (ev->format == ELM_SEL_FORMAT_TEXT) + { + if (ev->len > 0) + termpty_write(sd->pty, ev->data, ev->len - 1); + } + return EINA_TRUE; +} + +static void +_paste_selection(Evas_Object *obj) +{ + Termio *sd = evas_object_smart_data_get(obj); + if (!sd) return; + if (!sd->win) return; + elm_cnp_selection_get(sd->win, ELM_SEL_TYPE_PRIMARY, ELM_SEL_FORMAT_TEXT, + _getsel_cb, obj); +} + +void +_smart_cb_key_down(void *data, Evas *e, Evas_Object *obj, void *event) +{ + Evas_Event_Key_Down *ev = event; + Termio *sd; + + sd = evas_object_smart_data_get(data); + if (!sd) return; + if (evas_key_modifier_is_set(ev->modifiers, "Shift")) + { + if (ev->keyname) + { + int by = sd->grid.h - 2; + + if (by < 1) by = 1; + if (!strcmp(ev->keyname, "Prior")) + { + sd->scroll += by; + if (sd->scroll > sd->pty->backscroll_num) + sd->scroll = sd->pty->backscroll_num; + if (sd->job) ecore_job_del(sd->job); + sd->job = ecore_job_add(_smart_cb_change, obj); + return; + } + else if (!strcmp(ev->keyname, "Next")) + { + sd->scroll -= by; + if (sd->scroll < 0) sd->scroll = 0; + if (sd->job) ecore_job_del(sd->job); + sd->job = ecore_job_add(_smart_cb_change, obj); + return; + } + else if (!strcmp(ev->keyname, "Insert")) + { + _paste_selection(data); + return; + } + } + } + keyin_handle(sd->pty, ev); +} + +void +_smart_cb_focus_in(void *data, Evas *e, Evas_Object *obj, void *event) +{ + Termio *sd; + + sd = evas_object_smart_data_get(data); + if (!sd) return; + edje_object_signal_emit(sd->cur.obj, "focus,in", "terminology"); + if (!sd->win) return; + elm_win_keyboard_mode_set(sd->win, ELM_WIN_KEYBOARD_TERMINAL); +} + +void +_smart_cb_focus_out(void *data, Evas *e, Evas_Object *obj, void *event) +{ + Termio *sd; + + sd = evas_object_smart_data_get(data); + if (!sd) return; + edje_object_signal_emit(sd->cur.obj, "focus,out", "terminology"); + if (!sd->win) return; + elm_win_keyboard_mode_set(sd->win, ELM_WIN_KEYBOARD_OFF); +} + +static void +_smart_xy_to_cursor(Evas_Object *obj, Evas_Coord x, Evas_Coord y, int *cx, int *cy) +{ + Termio *sd; + Evas_Coord ox, oy; + + sd = evas_object_smart_data_get(obj); + if (!sd) + { + *cx = 0; + *cy = 0; + return; + } + evas_object_geometry_get(obj, &ox, &oy, NULL, NULL); + *cx = (x - ox) / sd->font.chw; + *cy = (y - oy) / sd->font.chh; + if (*cx < 0) *cx = 0; + else if (*cx >= sd->grid.w) *cx = sd->grid.w - 1; + if (*cy < 0) *cy = 0; + else if (*cy >= sd->grid.h) *cy = sd->grid.h - 1; +} + +static void +_sel_line(Evas_Object *obj, int cx, int cy) +{ + Termio *sd = evas_object_smart_data_get(obj); + if (!sd) return; + + sd->cur.sel = 1; + sd->cur.makesel = 0; + sd->cur.sel1.x = 0; + sd->cur.sel1.y = cy; + sd->cur.sel2.x = sd->grid.w - 1; + sd->cur.sel2.y = cy; +} + +static Eina_Bool +_glyph_is_wordsep(int g) +{ + int i; + + if (g == 0) return EINA_TRUE; + if (!config->wordsep) return EINA_FALSE; + for (i = 0;;) + { + int g2 = 0; + + if (!config->wordsep[i]) break; + i = evas_string_char_next_get(config->wordsep, i, &g2); + if (i < 0) break; + if (g == g2) return EINA_TRUE; + } + return EINA_FALSE; +} + +static void +_sel_word(Evas_Object *obj, int cx, int cy) +{ + Termio *sd = evas_object_smart_data_get(obj); + Termcell *cells; + int x, w = 0; + if (!sd) return; + + cells = termpty_cellrow_get(sd->pty, cy - sd->scroll, &w); + if (!cells) return; + sd->cur.sel = 1; + sd->cur.makesel = 0; + sd->cur.sel1.x = cx; + sd->cur.sel1.y = cy; + for (x = sd->cur.sel1.x; x >= 0; x--) + { + if (x >= w) break; + if (_glyph_is_wordsep(cells[x].glyph)) break; + sd->cur.sel1.x = x; + } + sd->cur.sel2.x = cx; + sd->cur.sel2.y = cy; + for (x = sd->cur.sel2.x; x < sd->grid.w; x++) + { + if (x >= w) break; + if (_glyph_is_wordsep(cells[x].glyph)) break; + sd->cur.sel2.x = x; + } +} + +static void +_smart_cb_mouse_down(void *data, Evas *e, Evas_Object *obj, void *event) +{ + Evas_Event_Mouse_Down *ev = event; + Termio *sd; + int cx, cy; + + sd = evas_object_smart_data_get(data); + if (!sd) return; + _smart_xy_to_cursor(data, ev->canvas.x, ev->canvas.y, &cx, &cy); + if (ev->button == 1) + { + if (ev->flags & EVAS_BUTTON_TRIPLE_CLICK) + { + _sel_line(data, cx, cy - sd->scroll); + if (sd->cur.sel) _take_selection(data); + } + else if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK) + { + _sel_word(data, cx, cy - sd->scroll); + if (sd->cur.sel) _take_selection(data); + } + else + { + if (sd->cur.sel) + { + sd->cur.sel = 0; + _clear_selection(data); + } + sd->cur.makesel = 1; + sd->cur.sel1.x = cx; + sd->cur.sel1.y = cy - sd->scroll; + sd->cur.sel2.x = cx; + sd->cur.sel2.y = cy - sd->scroll; + } + if (sd->job) ecore_job_del(sd->job); + sd->job = ecore_job_add(_smart_cb_change, data); + } + else if (ev->button == 2) + _paste_selection(data); + else if (ev->button == 3) + { + // XXX: popup config panel + } +} + +static void +_smart_cb_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event) +{ + Evas_Event_Mouse_Up *ev = event; + Termio *sd; + int cx, cy; + + sd = evas_object_smart_data_get(data); + if (!sd) return; + _smart_xy_to_cursor(data, ev->canvas.x, ev->canvas.y, &cx, &cy); + if (sd->cur.makesel) + { + sd->cur.makesel = 0; + if (sd->cur.sel) + { + sd->cur.sel2.x = cx; + sd->cur.sel2.y = cy - sd->scroll; + if (sd->job) ecore_job_del(sd->job); + sd->job = ecore_job_add(_smart_cb_change, data); + _take_selection(data); + } + } +} + +static void +_smart_cb_mouse_move(void *data, Evas *e, Evas_Object *obj, void *event) +{ + Evas_Event_Mouse_Move *ev = event; + Termio *sd; + int cx, cy; + + sd = evas_object_smart_data_get(data); + if (!sd) return; + _smart_xy_to_cursor(data, ev->cur.canvas.x, ev->cur.canvas.y, &cx, &cy); + if (sd->cur.makesel) + { + if (!sd->cur.sel) + { + if ((cx != sd->cur.sel1.x) || + ((cy - sd->scroll) != sd->cur.sel1.y)) + sd->cur.sel = 1; + } + sd->cur.sel2.x = cx; + sd->cur.sel2.y = cy - sd->scroll; + if (sd->job) ecore_job_del(sd->job); + sd->job = ecore_job_add(_smart_cb_change, data); + } +} + +static void +_smart_cb_mouse_wheel(void *data, Evas *e, Evas_Object *obj, void *event) +{ + Evas_Event_Mouse_Wheel *ev = event; + Termio *sd; + + sd = evas_object_smart_data_get(data); + if (!sd) return; + if (sd->pty->altbuf) return; + sd->scroll -= (ev->z * 4); + if (sd->scroll > sd->pty->backscroll_num) + sd->scroll = sd->pty->backscroll_num; + else if (sd->scroll < 0) sd->scroll = 0; + if (sd->job) ecore_job_del(sd->job); + sd->job = ecore_job_add(_smart_cb_change, data); +} + +static void +_smart_add(Evas_Object *obj) +{ + Termio *sd; + Evas_Object_Smart_Clipped_Data *cd; + _termio_sc.add(obj); + cd = evas_object_smart_data_get(obj); + if (!cd) return; + sd = calloc(1, sizeof(Termio)); + if (!sd) return; + sd->__clipped_data = *cd; + free(cd); + evas_object_smart_data_set(obj, sd); + + sd->jump_on_change = config->jump_on_change; + + { + Evas_Object *o; + Evas_Coord w = 2, h = 2; + char buf[4096]; + + if (config->font.bitmap) + { + snprintf(buf, sizeof(buf), "%s/fonts/%s", + elm_app_data_dir_get(), config->font.name); + sd->font.name = eina_stringshare_add(buf); + } + else + sd->font.name = eina_stringshare_add(config->font.name); + sd->font.size = config->font.size; + o = evas_object_text_add(evas_object_evas_get(obj)); + evas_object_text_font_set(o, sd->font.name, sd->font.size); + evas_object_text_text_set(o, "X"); + evas_object_geometry_get(o, NULL, NULL, &w, &h); + evas_object_del(o); + if (w < 1) w = 1; + if (h < 1) h = 1; + sd->font.chw = w; + sd->font.chh = h; + + o = evas_object_rectangle_add(evas_object_evas_get(obj)); + evas_object_pass_events_set(o, EINA_TRUE); + evas_object_propagate_events_set(o, EINA_FALSE); + evas_object_smart_member_add(o, obj); + sd->cur.selo1 = o; + evas_object_color_set(o, 64, 64, 64, 64); + o = evas_object_rectangle_add(evas_object_evas_get(obj)); + evas_object_pass_events_set(o, EINA_TRUE); + evas_object_propagate_events_set(o, EINA_FALSE); + evas_object_smart_member_add(o, obj); + sd->cur.selo2 = o; + evas_object_color_set(o, 64, 64, 64, 64); + o = evas_object_rectangle_add(evas_object_evas_get(obj)); + evas_object_pass_events_set(o, EINA_TRUE); + evas_object_propagate_events_set(o, EINA_FALSE); + evas_object_smart_member_add(o, obj); + sd->cur.selo3 = o; + evas_object_color_set(o, 64, 64, 64, 64); + + o = edje_object_add(evas_object_evas_get(obj)); + evas_object_pass_events_set(o, EINA_TRUE); + evas_object_propagate_events_set(o, EINA_FALSE); + evas_object_smart_member_add(o, obj); + sd->cur.obj = o; + snprintf(buf, sizeof(buf), "%s/themes/%s", + elm_app_data_dir_get(), config->theme); + edje_object_file_set(o, buf, "terminology/cursor"); + evas_object_resize(o, sd->font.chw, sd->font.chh); + evas_object_show(o); + + o = evas_object_rectangle_add(evas_object_evas_get(obj)); + evas_object_smart_member_add(o, obj); + sd->event = o; + evas_object_color_set(o, 0, 0, 0, 0); + evas_object_show(o); + + evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, + _smart_cb_mouse_down, obj); + evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP, + _smart_cb_mouse_up, obj); + evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE, + _smart_cb_mouse_move, obj); + evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_WHEEL, + _smart_cb_mouse_wheel, obj); + } + + evas_object_event_callback_add(obj, EVAS_CALLBACK_KEY_DOWN, + _smart_cb_key_down, obj); + evas_object_event_callback_add(obj, EVAS_CALLBACK_FOCUS_IN, + _smart_cb_focus_in, obj); + evas_object_event_callback_add(obj, EVAS_CALLBACK_FOCUS_OUT, + _smart_cb_focus_out, obj); +} + +static void +_smart_del(Evas_Object *obj) +{ + Termio *sd = evas_object_smart_data_get(obj); + if (!sd) return; + if (sd->cur.obj) evas_object_del(sd->cur.obj); + if (sd->event) evas_object_del(sd->event); + if (sd->cur.selo1) evas_object_del(sd->cur.selo1); + if (sd->cur.selo2) evas_object_del(sd->cur.selo2); + if (sd->cur.selo3) evas_object_del(sd->cur.selo3); + if (sd->job) ecore_job_del(sd->job); + if (sd->delayed_size_timer) ecore_timer_del(sd->delayed_size_timer); + if (sd->grid.array) free(sd->grid.array); + if (sd->font.name) eina_stringshare_del(sd->font.name); + if (sd->pty) termpty_free(sd->pty); + sd->cur.obj = NULL; + sd->event = NULL; + sd->cur.selo1 = NULL; + sd->cur.selo2 = NULL; + sd->cur.selo3 = NULL; + sd->job = NULL; + sd->delayed_size_timer = NULL; + sd->grid.array = NULL; + sd->font.name = NULL; + sd->pty = NULL; + _termio_sc.del(obj); + evas_object_smart_data_set(obj, NULL); +} + +static void +_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h) +{ + Termio *sd = evas_object_smart_data_get(obj); + Evas_Coord ow, oh; + if (!sd) return; + evas_object_geometry_get(obj, NULL, NULL, &ow, &oh); + if ((ow == w) && (oh == h)) return; + evas_object_smart_changed(obj); + if (sd->delayed_size_timer) ecore_timer_del(sd->delayed_size_timer); + sd->delayed_size_timer = ecore_timer_add(0.02, _smart_cb_delayed_size, obj); + evas_object_resize(sd->event, ow, oh); +} + +static void +_smart_calculate(Evas_Object *obj) +{ + Termio *sd = evas_object_smart_data_get(obj); + Evas_Coord ox, oy, ow, oh; + + if (!sd) return; + + evas_object_geometry_get(obj, &ox, &oy, &ow, &oh); + if (sd->grid.array) + { + int i, x, y; + + i = 0; + for (y = 0; y < sd->grid.h; y++) + { + for (x = 0; x < sd->grid.w; x++) + { + evas_object_move(sd->grid.array[i].bg, + ox + (x * sd->font.chw), + oy + (y * sd->font.chh)); + evas_object_move(sd->grid.array[i].tx, + ox + (x * sd->font.chw), + oy + (y * sd->font.chh)); + i++; + } + } + } + evas_object_move(sd->cur.obj, + ox + (sd->cur.x * sd->font.chw), + oy + (sd->cur.y * sd->font.chh)); + evas_object_move(sd->event, ox, oy); + evas_object_resize(sd->event, ow, oh); +} + +static void +_smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y) +{ + Termio *sd = evas_object_smart_data_get(obj); + if (!sd) return; + evas_object_smart_changed(obj); +} + +static void +_smart_init(void) +{ + static Evas_Smart_Class sc; + + evas_object_smart_clipped_smart_set(&_termio_sc); + sc = _termio_sc; + sc.name = "termio"; + sc.version = EVAS_SMART_CLASS_VERSION; + sc.add = _smart_add; + sc.del = _smart_del; + sc.resize = _smart_resize; + sc.move = _smart_move; + sc.calculate = _smart_calculate; + _smart = evas_smart_class_new(&sc); +} + +static void +_smart_pty_change(void *data) +{ + Evas_Object *obj = data; + Termio *sd; + sd = evas_object_smart_data_get(obj); + if (!sd) return; + + if (sd->jump_on_change) // if scroll to bottom on updates + { + // if term changed = croll back to bottom + sd->scroll = 0; + } + if (sd->job) ecore_job_del(sd->job); + sd->job = ecore_job_add(_smart_cb_change, obj); +} + +static void +_smart_pty_scroll(void *data) +{ + Evas_Object *obj = data; + Termio *sd; + int changed = 0; + sd = evas_object_smart_data_get(obj); + if (!sd) return; + + if ((!sd->jump_on_change) && // if NOT scroll to bottom on updates + (sd->scroll > 0)) + { + // adjust scroll position for added scrollback + sd->scroll++; + if (sd->scroll > sd->pty->backscroll_num) + sd->scroll = sd->pty->backscroll_num; + changed = 1; + } + if (sd->cur.sel) + { + sd->cur.sel1.y--; + sd->cur.sel2.y--; + changed = 1; + } + if (changed) + { + if (sd->job) ecore_job_del(sd->job); + sd->job = ecore_job_add(_smart_cb_change, obj); + } +} + +static void +_smart_pty_title(void *data) +{ + Evas_Object *obj = data; + Termio *sd; + sd = evas_object_smart_data_get(obj); + if (!sd) return; + if (!sd->win) return; + elm_win_title_set(sd->win, sd->pty->prop.title); +} + +static void +_smart_pty_icon(void *data) +{ + Evas_Object *obj = data; + Termio *sd; + sd = evas_object_smart_data_get(obj); + if (!sd) return; + if (!sd->win) return; + elm_win_icon_name_set(sd->win, sd->pty->prop.icon); +} + +static void +_smart_pty_cancel_sel(void *data) +{ + Evas_Object *obj = data; + Termio *sd; + sd = evas_object_smart_data_get(obj); + if (!sd) return; + if (sd->cur.sel) + { + sd->cur.sel = 0; + _clear_selection(data); + sd->cur.makesel = 0; + if (sd->job) ecore_job_del(sd->job); + sd->job = ecore_job_add(_smart_cb_change, data); + } +} + +Evas_Object * +termio_add(Evas_Object *parent, const char *cmd, int w, int h) +{ + Evas *e; + Evas_Object *obj; + Termio *sd; + + EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); + e = evas_object_evas_get(parent); + if (!e) return NULL; + + if (!_smart) _smart_init(); + obj = evas_object_smart_add(e, _smart); + sd = evas_object_smart_data_get(obj); + if (!sd) return obj; + sd->pty = termpty_new(cmd, w, h, config->scrollback); + sd->pty->cb.change.func = _smart_pty_change; + sd->pty->cb.change.data = obj; + sd->pty->cb.scroll.func = _smart_pty_scroll; + sd->pty->cb.scroll.data = obj; + sd->pty->cb.set_title.func = _smart_pty_title; + sd->pty->cb.set_title.data = obj; + sd->pty->cb.set_icon.func = _smart_pty_icon; + sd->pty->cb.set_icon.data = obj; + sd->pty->cb.cancel_sel.func = _smart_pty_cancel_sel; + sd->pty->cb.cancel_sel.data = obj; + _smart_size(obj, w, h); + return obj; +} + +void +termio_win_set(Evas_Object *obj, Evas_Object *win) +{ + Termio *sd; + sd = evas_object_smart_data_get(obj); + if (!sd) return; + sd->win = win; +} + +char * +termio_selection_get(Evas_Object *obj, int c1x, int c1y, int c2x, int c2y) +{ + Termio *sd; + sd = evas_object_smart_data_get(obj); + Eina_Strbuf *sb; + char *s, txt[8]; + int x, y; + + if (!sd) return NULL; + sb = eina_strbuf_new(); + for (y = c1y; y <= c2y; y++) + { + Termcell *cells; + int w, last0, v, x1, x2; + + w = 0; + last0 = -1; + cells = termpty_cellrow_get(sd->pty, y - sd->scroll, &w); + if (w > sd->grid.w) w = sd->grid.w; + x1 = c1x; + x2 = c2x; + if (c1y != c2y) + { + if (y == c1y) x2 = w - 1; + else if (y == c2y) x1 = 0; + else + { + x1 = 0; + x2 = w - 1; + } + } + for (x = x1; x <= x2; x++) + { + if (x >= w) break; + if (cells[x].glyph == 0) + { + if (last0 < 0) last0 = x; + } + else if (cells[x].att.newline) + { + last0 = -1; + eina_strbuf_append(sb, "\n"); + break; + } + else if (cells[x].att.tab) + { + eina_strbuf_append(sb, "\t"); + x = ((x + 8) / 8) * 8; + x--; + } + else + { + if (last0 >= 0) + { + v = x - last0 - 1; + last0 = -1; + while (v >= 0) + { + eina_strbuf_append(sb, " "); + v--; + } + if (x == (w - 1)) + { + if (!cells[x].att.autowrapped) + eina_strbuf_append(sb, "\n"); + } + } + glyph_to_utf8(cells[x].glyph, txt); + eina_strbuf_append(sb, txt); + } + } + if (last0 >= 0) + { + eina_strbuf_append(sb, "\n"); + } + } + + s = eina_strbuf_string_steal(sb); + eina_strbuf_free(sb); + return s; +} diff --git a/src/bin/termio.h b/src/bin/termio.h new file mode 100644 index 00000000..333b36c3 --- /dev/null +++ b/src/bin/termio.h @@ -0,0 +1,3 @@ +Evas_Object *termio_add(Evas_Object *parent, const char *cmd, int w, int h); +void termio_win_set(Evas_Object *obj, Evas_Object *win); +char *termio_selection_get(Evas_Object *obj, int c1x, int c1y, int c2x, int c2y); diff --git a/src/bin/termpty.c b/src/bin/termpty.c new file mode 100644 index 00000000..8bf007cc --- /dev/null +++ b/src/bin/termpty.c @@ -0,0 +1,1574 @@ +#define _XOPEN_SOURCE 600 +#include +#include "termpty.h" +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// +//#define DBG(f, args...) printf(f, ##args) +#define DBG(f, args...) do {} while (0) +/////////////////////////////////////////////////////////////////////////// +//#define INF(f, args...) printf("==INF: "f, ##args) +#define INF(f, args...) do {} while (0) +/////////////////////////////////////////////////////////////////////////// +#define ERR(f, args...) printf("######ERR: "f, ##args) +//#define ERR(f, args...) do {} while (0) + +static void +_text_clear(Termpty *ty, Termcell *cells, int count, int val, Eina_Bool inherit_att) +{ + int i; + Termatt clear; + + memset(&clear, 0, sizeof(clear)); + if (inherit_att) + { + for (i = 0; i < count; i++) + { + cells[i].glyph = val; + cells[i].att = ty->state.att; + } + } + else + { + for (i = 0; i < count; i++) + { + cells[i].glyph = val; + cells[i].att = clear; + } + } +} + +static void +_text_copy(Termpty *ty, Termcell *cells, Termcell *dest, int count) +{ + memcpy(dest, cells, sizeof(*(cells)) * count); +} + +static void +_text_save_top(Termpty *ty) +{ + Termsave *ts; + + ts = malloc(sizeof(Termsave) + ((ty->w - 1) * sizeof(Termcell))); + ts->w = ty->w; + _text_copy(ty, ty->screen, ts->cell, ty->w); + if (!ty->back) ty->back = calloc(1, sizeof(Termsave *) * ty->backmax); + if (ty->back[ty->backpos]) free(ty->back[ty->backpos]); + ty->back[ty->backpos] = ts; + ty->backpos++; + if (ty->backpos >= ty->backmax) ty->backpos = 0; + ty->backscroll_num++; + if (ty->backscroll_num >= ty->backmax) ty->backscroll_num = ty->backmax - 1; +} + +static void +_text_scroll(Termpty *ty) +{ + Termcell *cells = NULL, *cells2; + int y, y1 = 0, y2 = ty->h - 1; + + if (ty->state.scroll_y2 != 0) + { + y1 = ty->state.scroll_y1; + y2 = ty->state.scroll_y2 - 1; + } + else + { + if (!ty->altbuf) + { + _text_save_top(ty); + if (ty->cb.scroll.func) ty->cb.scroll.func(ty->cb.scroll.data); + } + else + if (ty->cb.cancel_sel.func) + ty->cb.cancel_sel.func(ty->cb.cancel_sel.data); + } + DBG("... scroll!!!!! [%i->%i]\n", y1, y2); + cells2 = &(ty->screen[y2 * ty->w]); + for (y = y1; y < y2; y++) + { + cells = &(ty->screen[y * ty->w]); + cells2 = &(ty->screen[(y + 1) * ty->w]); + _text_copy(ty, cells2, cells, ty->w); + } + _text_clear(ty, cells2, ty->w, ' ', EINA_TRUE); +} + +static void +_text_scroll_rev(Termpty *ty) +{ + Termcell *cells, *cells2 = NULL; + int y, y1 = 0, y2 = ty->h - 1; + + if (ty->state.scroll_y2 != 0) + { + y1 = ty->state.scroll_y1; + y2 = ty->state.scroll_y2 - 1; + } + DBG("... scroll rev!!!!! [%i->%i]\n", y1, y2); + cells = &(ty->screen[y2 * ty->w]); + for (y = y2; y > y1; y--) + { + cells = &(ty->screen[(y - 1) * ty->w]); + cells2 = &(ty->screen[y * ty->w]); + _text_copy(ty, cells, cells2, ty->w); + } + _text_clear(ty, cells, ty->w, ' ', EINA_TRUE); +} + +static void +_text_scroll_test(Termpty *ty) +{ + int e = ty->h; + + if (ty->state.scroll_y2 != 0) e = ty->state.scroll_y2; + if (ty->state.cy >= e) + { + _text_scroll(ty); + ty->state.cy = e - 1; + } +} + +static void +_text_scroll_rev_test(Termpty *ty) +{ + int b = 0; + + if (ty->state.scroll_y2 != 0) b = ty->state.scroll_y1; + if (ty->state.cy < b) + { + _text_scroll_rev(ty); + ty->state.cy = b; + } +} + +static void +_text_append(Termpty *ty, const int *glyphs, int len) +{ + Termcell *cells; + int i, j; + + cells = &(ty->screen[ty->state.cy * ty->w]); + for (i = 0; i < len; i++) + { + if (ty->state.wrapnext) + { + cells[ty->state.cx].att.autowrapped = 1; + ty->state.wrapnext = 0; + ty->state.cx = 0; + ty->state.cy++; + _text_scroll_test(ty); + cells = &(ty->screen[ty->state.cy * ty->w]); + } + if (ty->state.insert) + { + for (j = ty->w - 1; j > ty->state.cx; j--) + cells[j] = cells[j - 1]; + } + cells[ty->state.cx].glyph = glyphs[i]; + cells[ty->state.cx].att = ty->state.att; + if (ty->state.wrap) + { + ty->state.wrapnext = 0; + if (ty->state.cx >= (ty->w - 1)) ty->state.wrapnext = 1; + else ty->state.cx++; + } + else + { + ty->state.wrapnext = 0; + ty->state.cx++; + if (ty->state.cx >= ty->w) + ty->state.cx = ty->w - 1; + } + } +} + +static void +_term_write(Termpty *ty, const char *txt, int size) +{ + if (write(ty->fd, txt, size) < 0) perror("write"); +} +#define _term_txt_write(ty, txt) _term_write(ty, txt, sizeof(txt) - 1) + +#define CLR_END 0 +#define CLR_BEGIN 1 +#define CLR_ALL 2 + +static void +_clear_line(Termpty *ty, int mode, int limit) +{ + Termcell *cells; + int n = 0; + + cells = &(ty->screen[ty->state.cy * ty->w]); + switch (mode) + { + case CLR_END: + n = ty->w - ty->state.cx; + cells = &(cells[ty->state.cx]); + break; + case CLR_BEGIN: + n = ty->state.cx + 1; + break; + case CLR_ALL: + n = ty->w; + break; + default: + return; + } + if (n > limit) n = limit; + _text_clear(ty, cells, n, 0, EINA_TRUE); +} + +static void +_clear_screen(Termpty *ty, int mode) +{ + Termcell *cells; + + cells = ty->screen; + switch (mode) + { + case CLR_END: + _clear_line(ty, mode, ty->w); + if (ty->state.cy < (ty->h - 1)) + { + cells = &(ty->screen[(ty->state.cy + 1) * ty->w]); + _text_clear(ty, cells, ty->w * (ty->h - ty->state.cy - 1), 0, EINA_TRUE); + } + break; + case CLR_BEGIN: + if (ty->state.cy > 0) + _text_clear(ty, cells, ty->w * ty->state.cy, 0, EINA_TRUE); + _clear_line(ty, mode, ty->w); + break; + case CLR_ALL: + _text_clear(ty, cells, ty->w * ty->h, 0, EINA_TRUE); + break; + default: + break; + } + if (ty->cb.cancel_sel.func) + ty->cb.cancel_sel.func(ty->cb.cancel_sel.data); +} + +static void +_clear_all(Termpty *ty) +{ + if (!ty->screen) return; + memset(ty->screen, 0, sizeof(*(ty->screen)) * ty->w * ty->h); +} + +static void +_reset_att(Termatt *att) +{ + att->fg = COL_DEF; + att->bg = COL_DEF; + att->bold = 0; + att->faint = 0; + att->italic = 0; + att->underline = 0; + att->blink = 0; + att->blink2 = 0; + att->inverse = 0; + att->invisible = 0; + att->strike = 0; + att->fg256 = 0; + att->bg256 = 0; + att->autowrapped = 0; + att->newline = 0; + att->tab = 0; +} + +static void +_reset_state(Termpty *ty) +{ + ty->state.cx = 0; + ty->state.cy = 0; + ty->state.scroll_y1 = 0; + ty->state.scroll_y2 = 0; + ty->state.had_cr_x = 0; + ty->state.had_cr_y = 0; + _reset_att(&(ty->state.att)); + ty->state.charset = 0; + ty->state.charsetch = 'B'; + ty->state.chset[0] = 'B'; + ty->state.chset[1] = 'B'; + ty->state.chset[2] = 'B'; + ty->state.chset[3] = 'B'; + ty->state.multibyte = 0; + ty->state.alt_kp = 0; + ty->state.insert = 0; + ty->state.appcursor = 0; + ty->state.wrap = 1; + ty->state.wrapnext = 0; + ty->state.hidecursor = 0; + ty->state.crlf = 0; + ty->state.had_cr = 0; +} + +static void +_cursor_copy(Termstate *state, Termstate *dest) +{ + dest->cx = state->cx; + dest->cy = state->cy; +} + +static int +_csi_arg_get(char **ptr) +{ + char *b = *ptr; + int octal = 0; + int sum = 0; + + while ((*b) && (!isdigit(*b))) b++; + if (!*b) + { + *ptr = NULL; + return 0; + } + if (*b == '0') octal = 1; + while (isdigit(*b)) + { + if (octal) sum *= 8; + else sum *= 10; + sum += *b - '0'; + b++; + } + *ptr = b; + return sum; +} + +static int +_handle_esc_csi(Termpty *ty, const int *c, int *ce) +{ + int *cc, arg, first = 1, i; + char buf[4096], *b; + + cc = (int *)c; + b = buf; + while ((cc < ce) && (*cc >= '0') && (*cc <= '?')) + { + *b = *cc; + b++; + cc++; + } + // if cc == ce then we got to the end of the string with no end marker + // so return -2 to indicate to go back to the escape beginning when + // there is more bufer available + if (cc == ce) return -2; + *b = 0; + b = buf; + DBG(" CSI: '%c' args '%s'\n", *cc, buf); + switch (*cc) + { + case 'm': // color set + while (b) + { + arg = _csi_arg_get(&b); + if ((first) && (!b)) + _reset_att(&(ty->state.att)); + else if (b) + { + first = 0; + switch (arg) + { + case 0: // reset to normal + _reset_att(&(ty->state.att)); + break; + case 1: // bold/bright + ty->state.att.bold = 1; + break; + case 2: // faint + ty->state.att.faint = 1; + break; + case 3: // italic + ty->state.att.italic = 1; + break; + case 4: // underline + ty->state.att.underline = 1; + break; + case 5: // blink + ty->state.att.blink = 1; + break; + case 6: // blink rapid + ty->state.att.blink2 = 1; + break; + case 7: // reverse + ty->state.att.inverse = 1; + break; + case 8: // invisible + ty->state.att.invisible = 1; + break; + case 9: // strikethrough + ty->state.att.strike = 1; + break; + case 21: // no bold/bright + ty->state.att.bold = 0; + break; + case 22: // no faint + ty->state.att.faint = 0; + break; + case 23: // no italic + ty->state.att.italic = 0; + break; + case 24: // no underline + ty->state.att.underline = 0; + break; + case 25: // no blink + ty->state.att.blink = 0; + ty->state.att.blink2 = 0; + break; + case 27: // no reverse + ty->state.att.inverse = 0; + break; + case 28: // no invisible + ty->state.att.invisible = 0; + break; + case 29: // no strikethrough + ty->state.att.strike = 0; + break; + case 30: // fg + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + ty->state.att.fg256 = 0; + ty->state.att.fg = (arg - 30) + COL_BLACK; + break; + case 38: // xterm 256 fg color ??? + // now check if next arg is 5 + arg = _csi_arg_get(&b); + if (arg != 5) ERR("Failed xterm 256 color fg esc 5\n"); + else + { + // then get next arg - should be color index 0-255 + arg = _csi_arg_get(&b); + if (!b) ERR("Failed xterm 256 color fg esc val\n"); + else + { + ty->state.att.fg256 = 1; + ty->state.att.fg = arg; + } + } + break; + case 39: // default fg color + ty->state.att.fg256 = 0; + ty->state.att.fg = COL_DEF; + break; + case 40: // bg + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + ty->state.att.bg256 = 0; + ty->state.att.bg = (arg - 40) + COL_BLACK; + break; + case 48: // xterm 256 bg color ??? + // now check if next arg is 5 + arg = _csi_arg_get(&b); + if (arg != 5) ERR("Failed xterm 256 color bg esc 5\n"); + else + { + // then get next arg - should be color index 0-255 + arg = _csi_arg_get(&b); + if (!b) ERR("Failed xterm 256 color bg esc val\n"); + else + { + ty->state.att.bg256 = 1; + ty->state.att.bg = arg; + } + } + break; + case 49: // default bg color + ty->state.att.bg256 = 0; + ty->state.att.bg = COL_DEF; + break; + default: // not handled??? + ERR(" color cmd [%i] not handled\n", arg); + break; + } + } + } + break; + case '@': // insert N blank chars + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + { + int pi = ty->state.insert; + int blank[1] = { ' ' }; + + ty->state.wrapnext = 0; + ty->state.insert = 1; + for (i = 0; i < arg; i++) + _text_append(ty, blank, 1); + ty->state.insert = pi; + } + break; + case 'A': // cursor up N + case 'e': // cursor up N + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + ty->state.wrapnext = 0; + for (i = 0; i < arg; i++) + { + ty->state.cy--; + _text_scroll_rev_test(ty); + } + break; + case 'B': // cursor down N + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + ty->state.wrapnext = 0; + for (i = 0; i < arg; i++) + { + ty->state.cy++; + _text_scroll_test(ty); + } + break; + case 'D': // cursor left N + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + ty->state.wrapnext = 0; + for (i = 0; i < arg; i++) + { + ty->state.cx--; + if (ty->state.cx < 0) ty->state.cx = 0; + } + break; + case 'C': // cursor right N + case 'a': // cursor right N + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + ty->state.wrapnext = 0; + for (i = 0; i < arg; i++) + { + ty->state.cx++; + if (ty->state.cx >= ty->w) ty->state.cx = ty->w - 1; + } + break; + case 'H': // cursor pos set + case 'f': // cursor pos set + ty->state.wrapnext = 0; + if (!*b) + { + ty->state.cx = 0; + ty->state.cy = 0; + } + else + { + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + arg--; + if (arg < 0) arg = 0; + else if (arg >= ty->h) arg = ty->h - 1; + if (b) ty->state.cy = arg; + if (b) + { + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + arg--; + } + else arg = 0; + if (arg < 0) arg = 0; + else if (arg >= ty->w) arg = ty->w - 1; + if (b) ty->state.cx = arg; + } + break; + case 'G': // to column N + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + ty->state.wrapnext = 0; + ty->state.cx = arg - 1; + if (ty->state.cx < 0) ty->state.cx = 0; + else if (ty->state.cx >= ty->w) ty->state.cx = ty->w - 1; + break; + case 'd': // to row N + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + ty->state.wrapnext = 0; + ty->state.cy = arg - 1; + if (ty->state.cy < 0) ty->state.cy = 0; + else if (ty->state.cy >= ty->h) ty->state.cy = ty->h - 1; + break; + case 'E': // down relative N rows, and to col 0 + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + ty->state.wrapnext = 0; + ty->state.cy += arg; + if (ty->state.cy < 0) ty->state.cy = 0; + else if (ty->state.cy >= ty->h) ty->state.cy = ty->h - 1; + ty->state.cx = 0; + break; + case 'F': // up relative N rows, and to col 0 + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + ty->state.wrapnext = 0; + ty->state.cy -= arg; + if (ty->state.cy < 0) ty->state.cy = 0; + else if (ty->state.cy >= ty->h) ty->state.cy = ty->h - 1; + ty->state.cx = 0; + break; + case 'X': // erase N chars + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + _clear_line(ty, CLR_END, arg); + break; + case 'S': // scroll up N lines + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + for (i = 0; i < arg; i++) _text_scroll(ty); + break; + case 'T': // scroll down N lines + arg = _csi_arg_get(&b); + if (arg < 1) arg = 1; + for (i = 0; i < arg; i++) _text_scroll_rev(ty); + break; + case 'M': // delete N lines - cy + case 'L': // insert N lines - cy + arg = _csi_arg_get(&b); + { + int sy1, sy2; + + sy1 = ty->state.scroll_y1; + sy2 = ty->state.scroll_y2; + if (ty->state.scroll_y2 == 0) + { + ty->state.scroll_y1 = ty->state.cy; + ty->state.scroll_y2 = ty->h; + } + else + { + ty->state.scroll_y1 = ty->state.cy; + if (ty->state.scroll_y2 <= ty->state.scroll_y1) + ty->state.scroll_y2 = ty->state.scroll_y1 + 1; + } + if (arg < 1) arg = 1; + for (i = 0; i < arg; i++) + { + if (*cc == 'M') _text_scroll(ty); + else _text_scroll_rev(ty); + } + ty->state.scroll_y1 = sy1; + ty->state.scroll_y2 = sy2; + } + break; + case 'P': // erase and scrollback N chars + arg = _csi_arg_get(&b); + { + Termcell *cells; + int x, lim; + + if (arg < 1) arg = 1; + cells = &(ty->screen[ty->state.cy * ty->w]); + lim = ty->w - arg; + for (x = ty->state.cx; x < (ty->w); x++) + { + if (x < lim) + cells[x] = cells[x + arg]; + else + memset(&(cells[x]), 0, sizeof(*cells)); + } + } + break; + case 'c': // query device id + { + char bf[32]; +// 0 Base VT100, no options +// 1 Preprocessor option (STP) +// 2 Advanced video option (AVO) +// 3 AVO and STP +// 4 Graphics processor option (GO) +// 5 GO and STP +// 6 GO and AVO +// 7 GO, STP, and AVO + snprintf(bf, sizeof(bf), "\033[?1;%ic", 0); + _term_write(ty, bf, strlen(bf)); + } + break; + case 'J': // "2j" erases the screen, 1j erase from screen start to curs, 0j erase cursor to end of screen + arg = _csi_arg_get(&b); + if (b) + { + if ((arg >= CLR_END) && (arg <= CLR_ALL)) + _clear_screen(ty, arg); + else + ERR("invalid clr scr %i\n", arg); + } + else _clear_screen(ty, CLR_END); + break; + case 'K': // 0K erase to end of line, 1K erase from screen start to cursor, 2K erase all of line + arg = _csi_arg_get(&b); + if (b) + { + if ((arg >= CLR_END) && (arg <= CLR_ALL)) + _clear_line(ty, arg, ty->w); + else + ERR("invalid clr lin %i\n", arg); + } + else _clear_line(ty, CLR_END, ty->w); + break; + case 'h': // list - set screen mode or line wrap ("7h" == turn on line wrap, "7l" disables line wrap , ...) + case 'l': + { + int mode = 0, priv = 0; + int handled = 0; + + if (*cc == 'h') mode = 1; + if (*b == '?') + { + priv = 1; + b++; + } + if (priv) + { + while (b) + { + arg = _csi_arg_get(&b); + if (b) + { + int i, size; + + switch (arg) + { + case 1: + handled = 1; + ty->state.appcursor = mode; + break; + case 5: + handled = 1; + break; + case 7: + handled = 1; + DBG("DDD: set wrap mode to %i\n", mode); + ty->state.wrap = mode; + break; + case 20: + ty->state.crlf = mode; + break; + case 12: + handled = 1; +// DBG("XXX: set blinking cursor to (stop?) %i\n", mode); + break; + case 25: + handled = 1; + ty->state.hidecursor = !mode; + break; + case 1000: + handled = 1; + INF("XXX: set x11 mouse reporting to %i\n", mode); + break; + case 1049: + case 47: + case 1047: + handled = 1; + DBG("DDD: switch buf\n"); + if (ty->altbuf) + { + // if we are looking at alt buf now, + // clear main buf before we swap it back + // into the sreen2 save (so save is + // clear) + _clear_all(ty); +// _cursor_copy(&(ty->swap), &(ty->state)); + ty->state = ty->swap; + } + else + { +// _cursor_copy(&(ty->state), &(ty->swap)); + ty->swap = ty->state; + } + size = ty->w * ty->h; + // swap screen content now + for (i = 0; i < size; i++) + { + Termcell t; + + t = ty->screen[i]; + ty->screen[i] = ty->screen2[i]; + ty->screen2[i] = t; + } + ty->altbuf = !ty->altbuf; + if (ty->cb.cancel_sel.func) + ty->cb.cancel_sel.func(ty->cb.cancel_sel.data); + break; + case 1048: + if (mode) + _cursor_copy(&(ty->state), &(ty->save)); + else + _cursor_copy(&(ty->save), &(ty->state)); + break; + default: + ERR("unhandled screen mode arg %i\n", arg); + break; + } + } + } + } + else + { + while (b) + { + arg = _csi_arg_get(&b); + if (b) + { + if (arg == 1) + { + handled = 1; + ty->state.appcursor = mode; + } + else if (arg == 4) + { + handled = 1; + DBG("DDD: set insert mode to %i\n", mode); + ty->state.insert = mode; + } +// else if (arg == 24) +// { +// ERR("unhandled #24 arg %i\n", arg); +// // ??? +// } + else + ERR("unhandled screen non-priv mode arg %i, mode %i, ch '%c'\n", arg, mode, *cc); + } + } + } + if (!handled) ERR("unhandled '%c' : '%s'\n", *cc, buf); + } + break; + case 'r': + arg = _csi_arg_get(&b); + if (!b) + { + INF("no region args reset region\n"); + ty->state.scroll_y1 = 0; + ty->state.scroll_y2 = 0; + } + else + { + int arg2; + + arg2 = _csi_arg_get(&b); + if (!b) + { + INF("failed to give 2 region args reset region\n"); + ty->state.scroll_y1 = 0; + ty->state.scroll_y2 = 0; + } + else + { + if (arg >= arg2) + { + ERR("scroll region beginning >= end [%i %i]\n", arg, arg2); + ty->state.scroll_y1 = 0; + ty->state.scroll_y2 = 0; + } + else + { + INF("2 region args: %i %i\n", arg, arg2); + if (arg >= ty->h) arg = ty->h - 1; + if (arg2 > ty->h) arg2 = ty->h; + arg2++; + ty->state.scroll_y1 = arg - 1; + ty->state.scroll_y2 = arg2 - 1; + } + } + } + break; + case 's': // store cursor pos + _cursor_copy(&(ty->state), &(ty->save)); + break; + case 'u': // restore cursor pos + _cursor_copy(&(ty->save), &(ty->state)); + break; +/* + case 'R': // report cursor + break; + case 'n': // "6n" queires cursor pos, 0n, 3n, 5n too + break; + case 's': + break; + case 't': + break; + case 'p': // define key assignments based on keycode + break; + case 'q': // set/clear led's + break; + case 'x': // request terminal parameters + break; + case 'r': // set top and bottom margins + break; + case 'y': // invoke confidence test + break; + case 'g': // clear tabulation + break; + */ + default: + ERR("unhandled CSI '%c' (0x%02x), buf='%s'\n", *cc, *cc, buf); + break; + } + cc++; + return cc - c; +} + +static int +_handle_esc_xterm(Termpty *ty, const int *c, int *ce) +{ + int *cc; + char buf[4096], *b; + + cc = (int *)c; + b = buf; + while ((cc < ce) && (*cc >= ' ') && (*cc < 0x7f)) + { + *b = *cc; + b++; + cc++; + } + *b = 0; + if ((*cc < ' ') || (*cc >= 0x7f)) cc++; + else return -2; + switch (buf[0]) + { + case '0': + // XXX: title + name - callback + b = &(buf[2]); + if (ty->prop.title) eina_stringshare_del(ty->prop.title); + if (ty->prop.icon) eina_stringshare_del(ty->prop.icon); + ty->prop.title = eina_stringshare_add(b); + ty->prop.icon = eina_stringshare_add(b); + if (ty->cb.set_title.func) ty->cb.set_title.func(ty->cb.set_title.data); + if (ty->cb.set_icon.func) ty->cb.set_title.func(ty->cb.set_icon.data); + break; + case '1': + // XXX: icon name - callback + b = &(buf[2]); + if (ty->prop.icon) eina_stringshare_del(ty->prop.icon); + ty->prop.icon = eina_stringshare_add(b); + if (ty->cb.set_icon.func) ty->cb.set_title.func(ty->cb.set_icon.data); + break; + case '2': + // XXX: title - callback + b = &(buf[2]); + if (ty->prop.title) eina_stringshare_del(ty->prop.title); + ty->prop.title = eina_stringshare_add(b); + if (ty->cb.set_title.func) ty->cb.set_title.func(ty->cb.set_title.data); + break; + case '4': + // XXX: set palette entry. not supported. + b = &(buf[2]); + break; + default: + // many others + ERR("unhandled xterm esc '%c' -> '%s'\n", buf[0], buf); + break; + } + return cc - c; +} + +static int +_handle_esc(Termpty *ty, const int *c, int *ce) +{ + if ((ce - c) < 2) return 0; + DBG("ESC: '%c'\n", c[1]); + switch (c[1]) + { + case '[': + return 2 + _handle_esc_csi(ty, c + 2, ce); + case ']': + return 2 + _handle_esc_xterm(ty, c + 2, ce); + case '=': // set alternate keypad mode + ty->state.alt_kp = 1; + return 2; + case '>': // set numeric keypad mode + ty->state.alt_kp = 0; + return 2; + case 'M': // move to prev line + ty->state.wrapnext = 0; + ty->state.cy--; + _text_scroll_rev_test(ty); + return 2; + case 'D': // move to next line + ty->state.wrapnext = 0; + ty->state.cy++; + _text_scroll_test(ty); + return 2; + case 'E': // add \n\r + ty->state.wrapnext = 0; + ty->state.cx = 0; + ty->state.cy++; + _text_scroll_test(ty); + return 2; + case 'Z': // same a 'ESC [ Pn c' + _term_txt_write(ty, "\033[?1;2C"); + return 2; + case 'c': // reset terminal to initial state + DBG("reset to init mode and clear\n"); + _reset_state(ty); + _clear_screen(ty, CLR_ALL); + if (ty->cb.cancel_sel.func) + ty->cb.cancel_sel.func(ty->cb.cancel_sel.data); + return 2; + case '(': // charset 0 + ty->state.chset[0] = c[2]; + ty->state.multibyte = 0; + return 3; + case ')': // charset 1 + ty->state.chset[1] = c[2]; + ty->state.multibyte = 0; + return 3; + case '*': // charset 2 + ty->state.chset[2] = c[2]; + ty->state.multibyte = 0; + return 3; + case '+': // charset 3 + ty->state.chset[3] = c[2]; + ty->state.multibyte = 0; + return 3; + case '$': // charset -2 + ty->state.chset[2] = c[2]; + ty->state.multibyte = 1; + return 3; + case '#': // #8 == test mode -> fill screen with "E"; + if (c[2] == '8') + { + int i, size; + Termcell *cells; + + DBG("reset to init mode and clear then fill with E\n"); + _reset_state(ty); + ty->save = ty->state; + ty->swap = ty->state; + _clear_screen(ty, CLR_ALL); + if (ty->cb.cancel_sel.func) + ty->cb.cancel_sel.func(ty->cb.cancel_sel.data); + cells = ty->screen; + size = ty->w * ty->h; + if (cells) + { + for (i = 0; i < size; i++) cells[i].glyph = 'E'; + } + } + return 3; + case '@': // just consume this plus next char + return 3; + case '7': // save cursor pos + _cursor_copy(&(ty->state), &(ty->save)); + return 2; + case '8': // restore cursor pos + _cursor_copy(&(ty->save), &(ty->state)); + return 2; +/* + case 'G': // query gfx mode + return 3; + case 'H': // set tab at current column + return 2; + case 'n': // single shift 2 + return 2; + case 'o': // single shift 3 + return 2; + */ + default: + ERR("eek - esc unhandled '%c' (0x%02x)\n", c[1], c[1]); + break; + } + return 1; +} + +static int +_handle_seq(Termpty *ty, const int *c, int *ce) +{ + int *cc, len = 0; + + if (c[0] < 0x20) + { + switch (c[0]) + { +/* + case 0x00: // NUL + return 1; + case 0x01: // SOH (start of heading) + return 1; + case 0x02: // STX (start of text) + return 1; + case 0x03: // ETX (end of text) + return 1; + case 0x04: // EOT (end of transmission) + return 1; + */ + case 0x05: // ENQ (enquiry) + _term_txt_write(ty, "ABC\r\n"); + ty->state.had_cr = 0; + return 1; +/* + case 0x06: // ACK (acknowledge) + return 1; + */ + case 0x07: // BEL '\a' (bell) + INF("BEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEP\n"); + ty->state.had_cr = 0; + return 1; + case 0x08: // BS '\b' (backspace) + DBG("->BS\n"); + ty->state.wrapnext = 0; + ty->state.cx--; + if (ty->state.cx < 0) ty->state.cx = 0; + ty->state.had_cr = 0; + return 1; + case 0x09: // HT '\t' (horizontal tab) + DBG("->HT\n"); + ty->screen[ty->state.cx + (ty->state.cy * ty->w)].att.tab = 1; + ty->state.wrapnext = 0; + ty->state.cx += 8; + ty->state.cx = (ty->state.cx / 8) * 8; + if (ty->state.cx >= ty->w) + ty->state.cx = ty->w - 1; + ty->state.had_cr = 0; + return 1; + case 0x0a: // LF '\n' (new line) + case 0x0b: // VT '\v' (vertical tab) + case 0x0c: // FF '\f' (form feed) + DBG("->LF\n"); + if (ty->state.had_cr) + ty->screen[ty->state.had_cr_x + (ty->state.had_cr_y * ty->w)].att.newline = 1; + ty->state.wrapnext = 0; + if (ty->state.crlf) ty->state.cx = 0; + ty->state.cy++; + _text_scroll_test(ty); + ty->state.had_cr = 0; + return 1; + case 0x0d: // CR '\r' (carriage ret) + DBG("->CR\n"); + if (ty->state.cx != 0) + { + ty->state.had_cr_x = ty->state.cx; + ty->state.had_cr_y = ty->state.cy; + } + ty->state.wrapnext = 0; + ty->state.cx = 0; + ty->state.had_cr = 1; + return 1; +/* + case 0x0e: // SO (shift out) // Maps G1 character set into GL. + return 1; + case 0x0f: // SI (shift in) // Maps G0 character set into GL. + return 1; + case 0x10: // DLE (data link escape) + return 1; + case 0x11: // DC1 (device control 1) + return 1; + case 0x12: // DC2 (device control 2) + return 1; + case 0x13: // DC3 (device control 3) + return 1; + case 0x14: // DC4 (device control 4) + return 1; + case 0x15: // NAK (negative ack.) + return 1; + case 0x16: // SYN (synchronous idle) + return 1; + case 0x17: // ETB (end of trans. blk) + return 1; + case 0x18: // CAN (cancel) + return 1; + case 0x19: // EM (end of medium) + return 1; + case 0x1a: // SUB (substitute) + return 1; + */ + case 0x1b: // ESC (escape) + ty->state.had_cr = 0; + return _handle_esc(ty, c, ce); +/* + case 0x1c: // FS (file separator) + return 1; + case 0x1d: // GS (group separator) + return 1; + case 0x1e: // RS (record separator) + return 1; + case 0x1f: // US (unit separator) + return 1; + */ + default: + ERR("unhandled char 0x%02x\n", c[0]); + ty->state.had_cr = 0; + return 1; + } + } + else if (c[0] == 0xf7) // DEL + { + ERR("unhandled char 0x%02x [DEL]\n", c[0]); + ty->state.had_cr = 0; + return 1; + } + + cc = (int *)c; + DBG("txt: ["); + while ((cc < ce) && (*cc >= 0x20) && (*cc != 0xf7)) + { + DBG("%c", *cc); + cc++; + len++; + } + DBG("]\n"); + _text_append(ty, c, len); + ty->state.had_cr = 0; + return len; +} + +static void +_handle_buf(Termpty *ty, const int *glyphs, int len) +{ + int *c, *ce, n, *b, bytes; + + c = (int *)glyphs; + ce = &(c[len]); + + if (ty->buf) + { + bytes = (ty->buflen + len + 1) * sizeof(int); + b = realloc(ty->buf, bytes); + if (!b) + { + ERR("memerr\n"); + } + INF("realloc add %i + %i\n", (int)(ty->buflen * sizeof(int)), (int)(len * sizeof(int))); + bytes = len * sizeof(int); + memcpy(&(b[ty->buflen]), glyphs, bytes); + ty->buf = b; + ty->buflen += len; + ty->buf[ty->buflen] = 0; + c = ty->buf; + ce = c + ty->buflen; + while (c < ce) + { + n = _handle_seq(ty, c, ce); + if (n == 0) + { + int *tmp = ty->buf; + ty->buf = NULL; + ty->buflen = 0; + bytes = ((char *)ce - (char *)c) + sizeof(int); + INF("malloc til %i\n", (int)(bytes - sizeof(int))); + ty->buf = malloc(bytes); + if (!ty->buf) + { + ERR("memerr\n"); + } + bytes = (char *)ce - (char *)c; + memcpy(ty->buf, c, bytes); + ty->buflen = bytes / sizeof(int); + ty->buf[ty->buflen] = 0; + free(tmp); + break; + } + c += n; + } + if (c == ce) + { + if (ty->buf) + { + free(ty->buf); + ty->buf = NULL; + } + ty->buflen = 0; + } + } + else + { + while (c < ce) + { + n = _handle_seq(ty, c, ce); + if (n == 0) + { + bytes = ((char *)ce - (char *)c) + sizeof(int); + ty->buf = malloc(bytes); + INF("malloc %i\n", (int)(bytes - sizeof(int))); + if (!ty->buf) + { + ERR("memerr\n"); + } + bytes = (char *)ce - (char *)c; + memcpy(ty->buf, c, bytes); + ty->buflen = bytes / sizeof(int); + ty->buf[ty->buflen] = 0; + break; + } + c += n; + } + } +} + +static void +_pty_size(Termpty *ty) +{ + struct winsize sz; + + sz.ws_col = ty->w; + sz.ws_row = ty->h; + sz.ws_xpixel = 0; + sz.ws_ypixel = 0; + if (ioctl(ty->fd, TIOCSWINSZ, &sz) < 0) perror("Size set ioctl failed\n"); +} + +static Eina_Bool +_cb_exe_exit(void *data, int type, void *event) +{ + Ecore_Exe_Event_Del *ev = event; + Termpty *ty = data; + + if (ev->pid != ty->pid) return ECORE_CALLBACK_PASS_ON; + // XXX: report via cb + exit(ev->exit_code); + return ECORE_CALLBACK_PASS_ON; +} + +static Eina_Bool +_cb_fd_read(void *data, Ecore_Fd_Handler *fd_handler) +{ + Termpty *ty = data; + char buf[4097]; + int glyph[4097]; + int len, i, j, reads; + + // read up to 64 * 4096 bytes + for (reads = 0; reads < 64; reads++) + { + len = read(ty->fd, buf, sizeof(buf) - 1); + if (len <= 0) break; + buf[len] = 0; + // convert UTF8 to glyph integers + j = 0; + for (i = 0; i < len;) + { + int g = 0; + + if (buf[i]) + { + i = evas_string_char_next_get(buf, i, &g); + if (i < 0) break; +// DBG("(%i) %02x '%c'\n", j, g, g); + } + else + { + ERR("ZERO GLYPH!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + g = 0; + i++; + } + glyph[j] = g; + j++; + } + glyph[j] = 0; +// DBG("---------------- handle buf %i\n", j); + _handle_buf(ty, glyph, j); + } + if (ty->cb.change.func) ty->cb.change.func(ty->cb.change.data); + return EINA_TRUE; +} + +static void +_limit_coord(Termpty *ty, Termstate *state) +{ + state->wrapnext = 0; + if (state->cx >= ty->w) state->cx = ty->w - 1; + if (state->cy >= ty->h) state->cy = ty->h - 1; + if (state->had_cr_x >= ty->w) state->had_cr_x = ty->w - 1; + if (state->had_cr_y >= ty->h) state->had_cr_y = ty->h - 1; +} + +Termpty * +termpty_new(const char *cmd, int w, int h, int backscroll) +{ + Termpty *ty; + const char *pty; + + ty = calloc(1, sizeof(Termpty)); + if (!ty) return NULL; + ty->w = w; + ty->h = h; + ty->backmax = backscroll; + + _reset_state(ty); + ty->save = ty->state; + ty->swap = ty->state; + + ty->screen = calloc(1, sizeof(Termcell) * ty->w * ty->h); + if (!ty->screen) goto err; + ty->screen2 = calloc(1, sizeof(Termcell) * ty->w * ty->h); + if (!ty->screen2) goto err; + + ty->fd = posix_openpt(O_RDWR | O_NOCTTY); + if (ty->fd < 0) goto err; + if (grantpt(ty->fd) != 0) goto err; + if (unlockpt(ty->fd) != 0) goto err; + pty = ptsname(ty->fd); + ty->slavefd = open(pty, O_RDWR | O_NOCTTY); + if (ty->slavefd < 0) goto err; + fcntl(ty->fd, F_SETFL, O_NDELAY); + + ty->hand_exe_exit = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, + _cb_exe_exit, ty); + ty->pid = fork(); + if (!ty->pid) + { + char **args, *shell; + struct passwd *pw; + uid_t uid; + int i; + + for (i = 0; i < 100; i++) + { + if (i != ty->slavefd) close(i); + } + ty->fd = ty->slavefd; + setsid(); + + dup2(ty->fd, 0); + dup2(ty->fd, 1); + dup2(ty->fd, 2); + + if (ioctl(ty->fd, TIOCSCTTY, NULL) < 0) exit(1); + + uid = getuid(); + pw = getpwuid(uid); + if (!pw) shell = "/bin/sh"; + shell = pw->pw_shell; + if (!shell) shell = "/bin/sh"; + if (!cmd) cmd = shell; + args = malloc(2 * sizeof(char *)); + args[0] = (char *)cmd; + args[1] = NULL; + // pretend to be xterm + putenv("TERM=xterm"); + execvp(args[0], args); + exit(1); + } + ty->hand_fd = ecore_main_fd_handler_add(ty->fd, ECORE_FD_READ, + _cb_fd_read, ty, + NULL, NULL); + close(ty->slavefd); + _pty_size(ty); + return ty; +err: + if (ty->screen) free(ty->screen); + if (ty->screen2) free(ty->screen2); + if (ty->fd >= 0) close(ty->fd); + if (ty->slavefd >= 0) close(ty->slavefd); + free(ty); + return NULL; +} + +void +termpty_free(Termpty *ty) +{ + if (ty->hand_exe_exit) ecore_event_handler_del(ty->hand_exe_exit); + if (ty->hand_fd) ecore_main_fd_handler_del(ty->hand_fd); + if (ty->prop.title) eina_stringshare_del(ty->prop.title); + if (ty->prop.icon) eina_stringshare_del(ty->prop.icon); + if (ty->fd >= 0) close(ty->fd); + if (ty->slavefd >= 0) close(ty->slavefd); + if (ty->back) + { + int i; + + for (i = 0; i < ty->backmax; i++) + { + if (ty->back[i]) free(ty->back[i]); + } + free(ty->back); + } + if (ty->screen) free(ty->screen); + if (ty->screen2) free(ty->screen2); + if (ty->buf) free(ty->buf); + memset(ty, 0, sizeof(Termpty)); + free(ty); +} + +Termcell * +termpty_cellrow_get(Termpty *ty, int y, int *wret) +{ + Termsave *ts; + + if (y >= 0) + { + if (y >= ty->h) return NULL; + *wret = ty->w; + return &(ty->screen[y * ty->w]); + } + if (y < -ty->backmax) return NULL; + ts = ty->back[(ty->backmax + ty->backpos + y) % ty->backmax]; + if (!ts) return NULL; + *wret = ts->w; + return ts->cell; +} + +void +termpty_write(Termpty *ty, const char *input, int len) +{ + if (write(ty->fd, input, len) < 0) perror("write"); +} + +void +termpty_resize(Termpty *ty, int w, int h) +{ + Termcell *olds, *olds2; + int y, ww, hh, oldw, oldh; + + if ((ty->w == w) && (ty->h == h)) return; + + olds = ty->screen; + olds2 = ty->screen2; + oldw = ty->w; + oldh = ty->h; + + ty->w = w; + ty->h = h; + ty->state.had_cr = 0; + _limit_coord(ty, &(ty->state)); + _limit_coord(ty, &(ty->swap)); + _limit_coord(ty, &(ty->save)); + + ty->screen = calloc(1, sizeof(Termcell) * ty->w * ty->h); + if (!ty->screen) + { + ty->screen2 = NULL; + ERR("memerr"); + } + ty->screen2 = calloc(1, sizeof(Termcell) * ty->w * ty->h); + if (!ty->screen2) + { + ERR("memerr"); + } + + ww = ty->w; + hh = ty->h; + if (ww > oldw) ww = oldw; + if (hh > oldh) hh = oldh; + + for (y = 0; y < hh; y++) + { + Termcell *c1, *c2; + + c1 = &(olds[y * oldw]); + c2 = &(ty->screen[y * ty->w]); + _text_copy(ty, c1, c2, ww); + + c1 = &(olds2[y * oldw]); + c2 = &(ty->screen2[y * ty->w]); + _text_copy(ty, c1, c2, ww); + } + + free(olds); + free(olds2); + + _pty_size(ty); +} diff --git a/src/bin/termpty.h b/src/bin/termpty.h new file mode 100644 index 00000000..7065da69 --- /dev/null +++ b/src/bin/termpty.h @@ -0,0 +1,105 @@ +typedef struct _Termpty Termpty; +typedef struct _Termcell Termcell; +typedef struct _Termatt Termatt; +typedef struct _Termstate Termstate; +typedef struct _Termsave Termsave; + +#define COL_DEF 0 +#define COL_BLACK 1 +#define COL_RED 2 +#define COL_GREEN 3 +#define COL_YELLOW 4 +#define COL_BLUE 5 +#define COL_MAGENTA 6 +#define COL_CYAN 7 +#define COL_WHITE 8 + +#define COL_INVERSE 9 +#define COL_INVERSEBG 10 + +struct _Termatt +{ + unsigned char fg, bg; + unsigned short bold : 1; + unsigned short faint : 1; + unsigned short italic : 1; + unsigned short underline : 1; + unsigned short blink : 1; + unsigned short blink2 : 1; + unsigned short inverse : 1; + unsigned short invisible : 1; + unsigned short strike : 1; + unsigned short fg256 : 1; + unsigned short bg256 : 1; + // below used for working out text from selections + unsigned short autowrapped : 1; + unsigned short newline : 1; + unsigned short tab : 1; + // 2 bits left +}; + +struct _Termstate +{ + int cx, cy; + Termatt att; + unsigned char charset; + unsigned char charsetch; + unsigned char chset[4]; + int scroll_y1, scroll_y2; + int had_cr_x, had_cr_y; + unsigned int multibyte : 1; + unsigned int alt_kp : 1; + unsigned int insert : 1; + unsigned int appcursor : 1; + unsigned int wrap : 1; + unsigned int wrapnext : 1; + unsigned int hidecursor : 1; + unsigned int crlf : 1; + unsigned int had_cr : 1; +}; + +struct _Termpty +{ + Ecore_Event_Handler *hand_exe_exit; + Ecore_Fd_Handler *hand_fd; + struct { + struct { + void (*func) (void *data); + void *data; + } change, scroll, set_title, set_icon, cancel_sel; + } cb; + struct { + const char *title; + const char *icon; + } prop; + int w, h; + int fd, slavefd; + pid_t pid; + Termcell *screen, *screen2; + Termsave **back; + int backmax, backpos; + int backscroll_num; + int *buf; + int buflen; + Termstate state, save, swap; + unsigned int altbuf : 1; +}; + +struct _Termcell +{ + int glyph; + Termatt att; +}; + +struct _Termsave +{ + int w; + Termcell cell[1]; +}; + +Termpty *termpty_new(const char *cmd, int w, int h, int backscroll); +void termpty_free(Termpty *ty); +Termcell *termpty_cellrow_get(Termpty *ty, int y, int *wret); +void termpty_write(Termpty *ty, const char *input, int len); +void termpty_resize(Termpty *ty, int w, int h); + diff --git a/src/bin/utf8.c b/src/bin/utf8.c new file mode 100644 index 00000000..1de8669e --- /dev/null +++ b/src/bin/utf8.c @@ -0,0 +1,56 @@ +#include "utf8.h" + +void +glyph_to_utf8(int g, char *txt) +{ + if (g < (1 << (7))) + { // 0xxxxxxx + txt[0] = g & 0x7f; + txt[1] = 0; + } + else if (g < (1 << (5 + 6))) + { // 110xxxxx 10xxxxxx + txt[0] = 0xc0 | ((g >> 6) & 0x1f); + txt[1] = 0x80 | ((g ) & 0x3f); + txt[2] = 0; + } + else if (g < (1 << (4 + 6 + 6))) + { // 1110xxxx 10xxxxxx 10xxxxxx + txt[0] = 0xe0 | ((g >> 12) & 0x0f); + txt[1] = 0x80 | ((g >> 6 ) & 0x3f); + txt[2] = 0x80 | ((g ) & 0x3f); + txt[3] = 0; + } + else if (g < (1 << (3 + 6 + 6 + 6))) + { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + txt[0] = 0xf0 | ((g >> 18) & 0x07); + txt[1] = 0x80 | ((g >> 12) & 0x3f); + txt[2] = 0x80 | ((g >> 6 ) & 0x3f); + txt[3] = 0x80 | ((g ) & 0x3f); + txt[4] = 0; + } + else if (g < (1 << (2 + 6 + 6 + 6 + 6))) + { // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + txt[0] = 0xf8 | ((g >> 24) & 0x03); + txt[1] = 0x80 | ((g >> 18) & 0x3f); + txt[2] = 0x80 | ((g >> 12) & 0x3f); + txt[3] = 0x80 | ((g >> 6 ) & 0x3f); + txt[4] = 0x80 | ((g ) & 0x3f); + txt[5] = 0; + } + else if (g < (1 << (1 + 6 + 6 + 6 + 6 + 6))) + { // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + txt[0] = 0xfc | ((g >> 30) & 0x01); + txt[1] = 0x80 | ((g >> 24) & 0x3f); + txt[2] = 0x80 | ((g >> 18) & 0x3f); + txt[3] = 0x80 | ((g >> 12) & 0x3f); + txt[4] = 0x80 | ((g >> 6 ) & 0x3f); + txt[5] = 0x80 | ((g ) & 0x3f); + txt[6] = 0; + } + else + { // error - cant encode this in utf8 + txt[0] = 0; + } +} + diff --git a/src/bin/utf8.h b/src/bin/utf8.h new file mode 100644 index 00000000..0ac231e4 --- /dev/null +++ b/src/bin/utf8.h @@ -0,0 +1,2 @@ +void glyph_to_utf8(int g, char *txt); + diff --git a/src/bin/win.c b/src/bin/win.c new file mode 100644 index 00000000..20334de1 --- /dev/null +++ b/src/bin/win.c @@ -0,0 +1,24 @@ +#include +#include "win.h" +#include "config.h" + +Evas_Object * +tg_win_add(void) +{ + Evas_Object *win, *o; + char buf[4096]; + + win = elm_win_add(NULL, "main", ELM_WIN_BASIC); + elm_win_autodel_set(win, EINA_TRUE); + + elm_win_title_set(win, "Terminology"); + elm_win_icon_name_set(win, "Terminology"); + + o = evas_object_image_add(evas_object_evas_get(win)); + snprintf(buf, sizeof(buf), "%s/icons/terminology.png", + elm_app_data_dir_get()); + evas_object_image_file_set(o, buf, NULL); + elm_win_icon_object_set(win, o); + + return win; +} diff --git a/src/bin/win.h b/src/bin/win.h new file mode 100644 index 00000000..0009ed6d --- /dev/null +++ b/src/bin/win.h @@ -0,0 +1 @@ +Evas_Object *tg_win_add(void);