Overview

Originally created and developed for a long time by Chris Jones, the goal of this project is to produce a useful tool for arranging terminals. It is inspired by programs such as gnome-multi-term, quadkonsole, etc. in that the main focus is arranging terminals in grids (tabs is the most common default method, which Terminator also supports).

Much of the behaviour of Terminator is based on GNOME Terminal, and we are adding more features from that as time goes by, but we also want to extend out in different directions with useful features for sysadmins and other users. If you have any suggestions, please file wishlist bugs! (see below for the address)

Features:

  • Arrange terminals in a grid
  • Tabs
  • Drag and drop re-ordering of terminals
  • Lots of keyboard shortcuts
  • Save multiple layouts and profiles via GUI preferences editor
  • Simultaneous typing to arbitrary groups of terminals
  • and lots more...

Screenshots:

This is Chris' typical workspace for Terminals:
but you can do anything, from the sublime...
... to the ridiculous

Documentation

Documentation for all of Terminator's keybindings and config options in man pages is included in the source/packages. See:
man terminator
man terminator_config
It is my intent to gradually create a manual explaining all the features, nooks and crannies of Terminator. When I start to get somewhere I'll add a link here.

Bugs/problems/help

Please report all bugs on Launchpad Bugs.

Please, please, please, use Launchpad Bugs for Wishlist, Enhancements, Feature Requests as well. Don't worry that there's no way to set it as Wishlist yourself, file it as a bug, and I'll set it to Wishlist.

Some tips for good quality, useful bug reports:
  1. Search first. It may have been raised already, or even fixed/added in a newer release than the one you are using.
  2. Clear steps with expected and actual results help us to reproduce.
  3. State your version of Terminator, your distribution, and desktop flavour.
  4. Reproduce the problem with the "-d" debug option and attach the output to the ticket. This can help narrow what went wrong and why.
  5. If possible (not always) attach the config file which is usually  at:
    ${HOME}/.config/terminator/config
  6. Going the extra mile: Get the trunk of Terminator and test if it's still an issue, as I fix bugs without first raising them in Launchpad sometimes. You can run Terminator direct from a checked out folder without needing to build packages or install them. Simply:
    bzr branch lp:terminator
    cd terminator
    ./terminator
    You may want to backup your config file before doing this though.
If you are unsure if you have an issue or cannot figure out how to do something, please ask a question on Launchpad Answers. Your question may even be answered already in our Launchpad Answers FAQs. If not, ask your question there and maybe it will join the FAQ!

Please don't use Answers for requesting features or reporting bugs.

Other possibles sources of help are the two mail lists, gnome-terminator@lists.launchpad.net (development) and terminator-users@lists.launchpad.net (users), or there is usually a few long-time users hanging around in the IRC (see below) who may be able to help.

If you'd like to discuss Terminator, please feel free to drop by our IRC channel - #terminator on irc.freenode.net. I personally can't spend much time there but the folks who do are always friendly and helpful.

Installation

Linux Distributions

Ubuntu

Click here: Install Terminator

This may give you an older version, so to install the very latest Terminator:
sudo add-apt-repository ppa:gnome-terminator
sudo apt-get update
sudo apt-get install terminator
Thanks to Nicolas Valcarcel for his fantastic work in pushing Terminator into Debian and Ubuntu

If you're brave and prepared to accept the possibility of breakage, you could try the nightlies. Just change the first line above to:
sudo add-apt-repository ppa:gnome-terminator/nightly
I try not to merge things that will cause problems, but obviously there's a risk with running this. On the plus side you get the latest fixes and additions. On the negative side if you find an issue you are legally obliged (joke!) to raise a bug.

Debian

If you are running Debian sid then you should find the latest version, although it may be slightly behind the current release.

Fedora

To install Terminator in Fedora, run (as root):
yum install terminator

Foresight

To install Terminator in Foresight Linux, you can either use the Add/Remove program (PackageKit), or the following command:
sudo conary update terminator

OpenSuSE

To install Terminator in OpenSuSE, run (as root):
zypper install terminator

Mandriva

If you wish to install Terminator on Mandriva Linux, make sure that the package management system is configured to download packages from Mandriva's contrib/release and contrib/backports repositories; then run (as root):
urpmi terminator

RHEL

Packages for RHEL5 and 6 are in RPMForge. Instructions for adding this repository are available here. After you've done that, you can just run (as root):
yum install terminator

Slackware

There is a SlackBuild for Terminator available here. For more information on using SlackBuilds, see the HowTo.

FreeBSD

To install Terminator in FreeBSD, run one of the following lines (as root):
cd /usr/ports/x11/terminator && make install cleanportinstall terminator
pkg_add -r terminator
See this page for more information.

NetBSD

To install Terminator in NetBSD, run (as root):
pkgin install py-terminator
 See this page for more information.

Mac OS X

To install Terminator on Mac OS X you will need to be using the Fink project, and have it configured to allow unstable software. With those requirements satisfied, in a terminal run:
fink install terminator

Source

The latest version can be downloaded from Launchpad

Older versions can be found here.

Development

The development branch is published on Launchpad Code.

The main Launchpad page for Terminator is here

Thanks

Terminator is a team effort. Chris (and now me too) would like to thank the following:
  • Egmont Koblinger (who is one of the contributors to the VTE library) for the initial GTK3 port of Terminator, further testing, fixes, advice and knowledge. And you should all offer to buy him a beer for him giving me (Steve) a hard time over still using GTK2 in Terminator - you will all feel the difference!
  • Nicolas Valcarcel for his sterling work packaging Terminator for Debian and Ubuntu and pushing it into those distributions.
  • Emmanuel Bretelle for his excellent work on Tab support and many other features.
  • Chris Oattes, Thomas Meire, Kees Cook, Huang He for their contributions to the code.
  • Thomas Hurst for code, FreeBSD port and general coding mavenry.
  • The excellent army of translators on Launchpad who have brought Terminator to 52 languages.
  • Jorge and the other bloggers who help promote Terminator
  • You.
  • Everyone else Chris or I have forgotten.
And finally a personal thanks from me (Steve) to Chris for creating Terminator to start with, and entrusting me with it.
  1. Woohoo!

    So I finally did it, managing to actually sit down and sort the release out on a date I set for myself.

    So if you're looking for whizz-bang new features you're going to be very disappointed. This is mostly about correcting quirks, oddities, regressions and bugs. There are however some important fixes here, so I strongly encourage anyone holding back to update to, and start using this series. I personally think it is now more stable and better overall than the old gtk2 series.

    If you want to have a look at the detail go here and expand the Changelog.

    Onwards...

    So what's next? Well hopefully one final release before I decide we're ready to call it a 2.0. In a month I hope to push 1.92. This is the last opportunity for gtk2 -> gtk3 regressions to be fixed. Any critical, release blocker fixes can also be added to that release. This will also be the last release that any minor enhancements can be added before we hit 2.0. So if you are aware of any regressions (it working in gtk2, but not in gtk3) tag them with "regression". Any big ugly things can be tagged as "release-blocker". These should be things that cause major problems. I know we have a lot of bugs (still!) and I'd love to have them all fixed for 2.0, but it is unlikely to happen unless you are happy waiting till 2020 for 2.0 ;-) So if I end up with too much getting tagged as release-blocker I'll punt anything I consider less serious down the road till a later release.

    A quick "Thank you!"

    I should say a quick thank you to everyone who has contributed to this and previous releases. Whether that's a patch for a feature or bug, adding translations, or especially to those who take the time to engage in the back-and-forth that is sometimes so essential to getting to the root of an issue.
    10

    View comments

  2. Roadmaps. They're so much easier to write than to walk.

    So I did a little organising of the Milestones in Launchpad so there's something to aim for. It's all subject to change of course if conditions dictate. You can take a look at the very long term plan.

    1.91 Fix the regressions

    What I'm interested in here is anything that used to work in the old 1.0/gtk2 version that stopped working in the 1.90/gtk3 port. Please take a look in the bugs and see if the issue is already raised, or if it isn't, please raise a new issue. Either way, be sure to tag the bug with the "regression" tag if it isn't already.

    1.92 Fix the release blockers

    So this is for things that are broken that are fundamental to being able to use Terminator. Sorry, but pet peeves that one person complains about don't really count. We have far too many bug reports (many totally out of date, or abandoned by the originator) to fix everything, and also make a final 2.0 release any time soon. So for now, keep this to the big stuff. Again find it, or raise it, and make sure it's tagged with "release-blocker". Please don't get upset if I disagree, and think it can wait till a later release. I'm just trying to get the releases moving along a bit.

    2.0 Final release

    Assuming that things are quiet for a while after 1.92 is released, with only the odd new regression or release-blocker, then I'll cut the 2.0 release.

    It goes without saying, that you should be checking that whatever the bug is, it still occurs with the head of the gtk3 series. For those of you on Ubuntu there are nightly builds available in a PPA. The disadvantage with the PPA is that if it breaks, you may be temporarily unable to run Terminator. I use it myself though, and I try very hard not to allow brokenness to sneak out.

    For those without Ubuntu, or who are just cautious, you can keep whatever stable version you use, and just have a local checkout of gtk3 that you run from there. You may want to backup your config file, or be sure to use the -g flag for a temporary config file.

    In the future..

    Future "big" changes include the migration to Python 3, and eventually (once it stabilises) the migration to GTK 4. I'm a little excited for the GTK4 migration because at the moment there is a multi-paned widget pencilled in which would get rid of a lot of ugly code for handling nested paned widgets.
    3

    View comments

  3. "Because they're like buses!"


    Which is the answer to the question, "Why have you made two releases of Terminator back-to-back?" You wait ages for one, then two show up at once.

    More seriously, I have been delinquent, preoccupied, busy, uninterested, lazy, all of the above. To the point where I've annoyed some people. So it was time to pull my finger out and figure out some more stuff I didn't know. I think I now know how to push a release through Launchpad thanks to the instructions from Bryce. I figured out how to setup a nightly package build (PPA) of the gtk3 branch. I also figured out how to use readthedocs.org to host the manual (gtk2 & gtk3). For anyone wondering why, it was because the package maintainers of the distros did not seem to like me bundling javascript files needed for the page theme, so they stripped the manual out, making it worse than useless.

    For those eager for a long list of magical changes, then you're about to be disappointed. Not a great deal has changed since the 0.98 release with the obvious exception of the port to gtk3. Mostly it's a collection of bugfixes and minor enhancements

    So why two releases? For those of you who understand the techie bits a little, you will be aware that Terminator was originally built using the gtk2 library and the associated 0.28 libvte widget. That's a fossil, and it's beyond the time we should have dropped it in favour of the up-to-date gtk3 and 0.38+ libvte. There were however some fixes in the gtk2 focused trunk that hadn't been put into a release yet. So that is why we have a 1.0 release. It still has bugs, but I don't think I'll be looking at continuing it myself. If there's someone out there who still doesn't have gtk3 and wants to take on the overhead of looking after the gtk2 branch, contact me, and perhaps we can sort something out.

    Then there's the second release which is 1.90. This is pretty solid, and should be usable as your daily driver with 99% of the gtk2 functionality. Of course the new libvte widget is much improved so you'll notice nice improvements in places. One thing we did lose (the missing 1%) in the port to gtk3 was the background image. This is not the Terminator projects fault. The libvte project chose to excise the capability to set an image as the background from the widget. I have figured out a way to add this back in, but that can wait till 2.1. We do however still have true transparency.

    Note that with the 1.90 changelog it includes all changes since 0.97 where appropriate, along with fixes for issues arising from the porting, making it look more dramatic than it actually is.
    2

    View comments

  4. "Join the mobile infantry and save the galaxy!"

    So let's talk about that least favourite of things in Open Source. Bugs! Users get annoyed by them, and developers get annoyed by the never ending conveyor belt of issues that prevent them working on interesting new stuff.

    So some of you may have spotted that I finally initiated the big bug purge I've been threatening in previous blog posts. Here's the sequence.
    1. All bugs (not wishlist items, just bugs) raised before the 0.98 release have been marked Incomplete with a big boiler plate comment telling people to revisit and confirm on the new 0.98 release. There are a couple of new tags people can use to confirm presence in 0.98, and the gtk3 port if they feel generous.
    2. In a few weeks I'll take a crack at a Triage day. Anyone is welcome to try and help reproduce and confirm those still marked Incomplete. This will be a last ditch effort with no more than 5 minutes seeing if the issue is still there. Let's be honest, if the original raiser or anyone else affected can't be bothered to take the time to respond after a month, then why should someone else spend time trying to figure out how to reproduce it? If it still applies? If it was in fact a bug? And so on. If it is not obviously reproducible it will be left in Incomplete.
    3. After the Triage day has hopefully gotten through all bugs (not wishlist items, just bugs) then I'm going to activate the automatic bug expiry. Once a bugs last update passes 60 days, the bug goes to Expired.

    Bug handling

    I have had one person (possibly others) who are hesitant to use the status' because they've been "told off" by the developers of other projects, and people/projects are often different in how they want to handle bugs. So, with that in mind, let me present my idea of how a bug should be handled. First a pretty picture:
    So, the darker blue states are the ones available in Launchpad that can be manually set. The two marked with a red outline require bug supervisor role to set, which means a member of the Terminator team. The pale blue states are ones that I personally feel should be there, but are missing. I'll explain my intention with those in the appropriate sections below. The grey state is set automatically only, and cannot be set by anyone.

    Initial/New

    When you the user create a bug it goes into New. If another user clicks the "This bug affects you" link, this gets moved to Confirmed.

    Investigation

    If I (or indeed someone else) go to a New or Confirmed bug, and are unable to reproduce it then it will be marked Incomplete, and someone (preferably the original raiser, but it can be someone else affected) needs to revisit and provide the requested additional info. Ideally when that is added there would be a New Info (or similar) state that the user would set the bug to, and then the dashed line would be taken.

    Because we don't have this state, we "skip" straight through and abuse the Confirmed state. Set the bug (back) to Confirmed, and assign the official tag new-info. Once the ticket is reviewed the tag will be removed, and a new state assigned, possibly even Incomplete again.

    Note that I am aware of the two Incomplete options for with and without response, but the way it works is unclear, and I can't switch between the two myself, and it is not clear when Launchpad switches it. So, I'll be ignoring them and treating Incomplete as a single state.

    Acceptance

    At this point the bug should provide enough information to be reproducible. Only a supervisor can set an issue to Triaged. This state says, "Yes, the information provided either permits me to reproduce myself, or see what went wrong from provided logs, config, etc." Typically they go here when I don't have the time to start working on an immediate fix.

    Alternatively I (or anyone) could start working on a bug. Ideally the issue should be set to In Progress, and assigned to the person picking it up. That way, two people don't work on the same issue.

    Sometimes, for trivial or interesting bugs, they might get looked at and fixed so fast that they skip all acceptance categories, and go straight to one of the resolved states.

    Resolved

    Fix Committed is for when a fix is pushed to the main Launchpad bazaar repository and typically I do this. If you create a contribution via a branch, and commit to your branch, do not set to this yourself. Instead associate the bug with the branch, and request a merge. When I do the merge I will also set the bug to Fix Committed.

    An Invalid bug is usually because the user didn't understand something, or it is in fact a support request.

    Only a bug supervisor can set an issue to Won't Fix. It is the supervisors way of ending the discussion when it is felt that a bug does not fit the projects plans, but somene can't let it go.

    Opinion is typically when the user and I have a different expectation about behaviour or a new feature, or I think that something being proposed would actually be a negative for Terminator. Unlike Won't Fix, this can still be discussed within the ticket.

    Not Responsible is our second missing virtual state. For me this is when, for example, an issue actually resides in libvte, or GTK. Again, there is a new official tag not-responsible, and the bug will actually end up set to Invalid.

    The final virtual state is No Action, which is for various reasons. Sometimes other work has resolved an issue already, or the user was using an old version, and the fix is already in trunk or released. Again there is a new official tag no-action. These will then be put in one of the following: Invalid, Fix Committed, or Fix Released, depending on circumstance.

    Our last Resolved state is the automatically set Expired one.

    Available

    The last state is Fix Released, indicating that there has been a release containing a fix to the issue.

    Of course this flow and states are not set in stone. A bug can be brought out of Expired if necessary. Or back from In Progress to Confirmed or Triaged if the assignee decides to stop working on the bug for some reason.

    Bonus points for anyone who can name the title reference without using Google. Points are redeemable at all good tentacle warmer shops on Alpha Centauri. Valid for 1 year from issue, so I hope your Stardrive capable ship is ready to go.
    4

    View comments

  5. Welcome to the Revolution

    So Terminator has been stuck on GTK2 for a long time now. This has the knock-on effect of limiting the libvte to 0.28, which is old, has bugs, and doesn't get any updates. Don't get me wrong, it still works (mostly) and gets the job done (well mine, at least) but it is all getting a bit long in the tooth. On top of that modern distros are marching on; libvte 0.28 is starting to get dropped; eventually GTK2 will get dropped; default Python will be 3. For those of you stuck using crusty distros (hello business users!) do not fear. The intent is to keep GTK2 and GTK3 marching forward in lock-step for the time being. Eventually GTK2 will stop having features added - just occasional bug fixes, and GTK3 will become the primary focus for all new features and development. Hopefully by then the distros will all be caught up, and it won't require a blog post to tell you how to run it.

    Note that these instructions will be somewhat specific to Ubuntu. If you use another distro and get this working, please add any notes in the comments to assist your fellows.

    I would not recommend installing GTK3 as the only Terminator right now. It has rough edges and bugs, and it has not had any significant stabilisation period. My recommendation is that you have 0.98 (GTK2) installed as the default day-to-day version. This can be either using a distro specific package if available already, or use the commands:
    $ mkdir -p ~/Development/terminator
    $ cd ~/Development/terminator
    $ bzr branch http://bazaar.launchpad.net/~gnome-terminator/terminator/trunk/ gtk2-terminator
    $ cd gtk2-terminator
    $ python setup.py install
    This has the additional benefit of installing the fine manual, which can then be accessed from both GTK2 and GTK3 by pressing F1. At the moment it is not included in the GTK3 series because it will need a full review and updates, and till we're close to a release that just seems like overhead.

    OK, so you have 0.98 installed and working, let's get your GTK3 Terminator running:
    1. You need to ensure the following additional packages are installed on Ubuntu (tested in a clean 14.04):
      gir1.2-keybinder-0.0
      gir1.2-keybinder-3.0
      libkeybinder-3.0-0
      build-essential
      intltool
      libgtk-3-dev
      gobject-introspection
      libgirepository1.0-dev
      valac
      xmllint

      [edit 21/11/2015: missed one dependancy]
      gir1.2-gconf-2.0
      or better (if it's available in your distro) you just need one package:
      (lib)vte(2.91) version >= 0.38
      If you have this single libvte package you can skip the next step.

    2. If your distro does not have the newer libvte available, then get hold of the vte archive and uncompress it:
      $ mkdir -p ~/Development/vte
      $ cd ~/Development/vte

      $ wget http://ftp.gnome.org/pub/GNOME/sources/vte/0.38/vte-0.38.0.tar.xz
      $ tar xJvf vte-0.38.0.tar.xz
      $ cd vte-0.38.0

      $ ./configure --prefix=/usr; make; sudo make install
      You can of course use/try different versions, but this is the one I got working in 14.04 LTS. Go too old and the API changes. Too new and you'll have to compile a bazillion dependencies. Whether you hit problems or success with different versions, that info is useful, so please share it.
       
    3. Create a local copy of the gtk3 tree and you should be able to run direct from there, which works because of all the files installed by 0.98:
      $ mkdir -p ~/Development/terminator
      $ cd ~/Development/terminator
      $ bzr branch http://bazaar.launchpad.net/~gnome-terminator/terminator/gtk3/ gtk3-myuniquename
      $ cd gtk3-myuniquename
      $ ./terminator -g temp_config -u

      The -g is to avoid trashing your normal config, and the -u avoids using DBus to use an existing running Terminator instance to open a window.
    For myuniquename pick a name that hints what the branch is for, i.e. gtk3-fixes, or gtk3-coffeemakingplugin. It's not essential, but it'll be clearer/easier to keep gtk2 and gtk3 separate if people start sending in fixes.

    At this point you should have a new Terminator window, with the only visual difference I spotted so far being the grey popup menus, and slightly different rendering of widgets.

    One final tip: Creating new tabs might fail in some distros (Ubuntu) unless you add the /etc/profile.d/vte.sh to your ~/.bashrc file. It probably just needs a sensible fallback if the vte.sh hasn't been sourced, as this doesn't fail for splits.

    All that remains is for you to test it out, and see if you can break it. Then you get to fix it ;-)

    A couple of the more noticable things you will see due to the updated libvte:
    1. Much better performance (cat'ing a large file is roughly an order of magnitude quicker)
    2. Text reflow
    3. Italic escape codes now work
    4. + other stuff (see https://github.com/GNOME/vte/blob/master/NEWS for gory details)

    If you look in the manual under the "Getting involved", "GTK3 Port" you can find a list of things I've already spotted that are to be fixed/investigated. Feel free to look at those, or see if you can find other issues which can be reported through the normal Launchpad bugs. Just be sure to make it clear that it is specific to GTK3.
    21

    View comments

  6. Terminator has a new release!

    Hard to believe, but there is finally an update. This is not the official announcement, you can see that elsewhere. That gives you the 10,000 foot view, but the Changelog is quite large for those of you that like to pore over such things. This post is more a "state of the union" and update of the current situation.

    Grand Vizier of Releases

    Probably the most news-worthy update is that Bryce Harrington has joined us to act as release manager. Considering how often even I see his name, he should not need any introduction. Suffice to say he used to work at Canonical and has much better knowledge of Launchpad, debian packaging and executing releases than me. Hopefully this means we will get into a more regular release schedule because truth-be-told, it was one of things holding the release back. At least more regular than every two years :-)

    Newfangled tek-nol-o-gee

    The second big piece of news that was already mentioned elsewhere (but may have slipped your notice) is that we have a GTK3 port. There is still outstanding issues with it, with a snapshot of the known ones is in the shiny new manual. Help fixing these, and generally kicking the tyres and checking the oil will be much appreciated. Even though Terminator is still using the old GTK2 in the trunk, I'm not quite ready to completely drop the GTK2 version in favour of GTK3. Once the GTK3 is in good shape we'll look at making it the default, although there are some impediments to that. Chief among these is that a number of currently supported distros (typically LTS) do not have a new enough VTE package. Switching too early risks losing those people. It's one thing to ask people to unpack an archive and run the contents. It's a bit trickier to ask them to start compiling libraries and dependencies and installing those. Note to self: Write a blog entry detailing getting up and running with GTK3 Terminator.

    Me no know no other lingo

    So this is a call out to all the translators. Quite a lot of churn happened recently, so a big chunk of the interface is going to default back to English for most people. For those of you who would like to help, but don't have programming skills, this is an easy way (assuming you speak two or more languages) to contribute. I'm sure your non-English speaking co-linguals(?) will be very grateful. The only thing you need in order to contribute to the translations is a Launchpad account and a web browser. However, it will probably help to have the latest release of Terminator open too, so you can see the context for the strings.

    Speaking of translations, there is also the manual. This will take some commitment to translate though. Let me know if this is something you would consider taking on, and I'll sort out a mini guide for you describing the tooling and commands etc.

    Also a question to people who use other languages, especially non-Latin like Arabic, Cyrillic, Greek, Chinese etc. How do you deal with shortcuts? I couldn't find definitive guidance describing whether or not to enable translation/localisation for shortcuts. At the moment I'm not even 100% how to do it, but that's detail. The main question for now is, is it needed? Does this cause people problems with needing contorted claw-hands to trigger them? Or are some of them simply impossible to do?

    What next?

    Going back to my self assigned list of things to do:
    1. Get all merge requests and patches merged or rejected where possible. This in some cases may depend on the age and condition of the contributed code.
      Generally keeping on top of merge requests. Patches I'm still failing a bit on. At this point let's just get this interim release out there.
    2. Get the last few bugs fixed that are targeted for the 1.0 milestone.
      Well as you can see, this is not the 1.0 release, but 0.98, so in a way I've not failed here :-) There's just one outstanding 1.0 targeted bug currently, although it is one of "those" types. I probably need to do a review of the bugs and decide what is and isn't a 1.0 blocker.
    3. Have a bug triage/hack day to try and reproduce the bugs, prioritise, and target them to point releases. Anything not reproducible would be set to Incomplete and request better/further info.
      Not done yet. I think I'm going to do this pre 1.0 now though, and try to get the bug list down to a more manageable size. I'll figure out a day in the not too distant future and make an announcement here on this blog.
    4. Set the automatic expiry on for the bugs. I know this may be a bit controversial, but there some bugs which never go anywhere because they aren't reproducible and the original raiser never returns to the ticket.
      Not done yet. Once 3 is done.
    5. Decide whether the blueprints are actually useful for anything and if not, change them to a bug/wishlist or kill them.
      Unless someone comes up with a compelling argument for using blueprints, they will not be used.
    6. Some kind of web page, guide or tour would be nice. I notice some of the questions we get in Launchpad ask about features we already have, but are perhaps not obvious to people doing rapid test-drives.
      The new manual is in the new 0.98 release, so the manual came first, before 1.0.

    Help! I need some bodies

    Currently I'm consistently seeing over five hundred reads per blog post within two weeks of posting, so I know that even in this pre-release dormant period there is some interest in Terminator. I expect that will jump a bit after the release. Surprisingly Blogger (Google) doesn't supply me with all of your home addresses. This makes it hard for me to hunt you down and force you to contribute by jabbing you with pointy sticks. So I'll stick to guilt-tripping you all instead. We can get to a fully translated, well documented, less buggy 1.0 release with even more awesome... with your help. So, get translating. Proof read the manual and point out how bad my writing is. Test the software. Fix the software. Write a plug-in. Anything you can. I'll try to be more active in general and provide help when needed.
    4

    View comments

  7. Should this Terminator rename/rebrand?

    This is something I've been thinking about for some time, due to the Java based software.jessies.org terminal application that is also called Terminator.  The other Terminator is still active and seems to have started first, so it is not reasonable to ask them to drop the Terminator name.

    I regularly see conflicts/mistakes online (i.e. Wikipedia, Slant) because of this, and it causes confusion amongst users in forums too.

    We are approaching a release and although it won't be 1.0, the 1.0 will follow fairly soon after that. And then of course there is the 2.0 release that is the ongoing GTK3 port. If the project is to endure a renaming exercise it will be best to decide it now before we go making a big noise about new releases. At least then we could pre-announce the future name change as part of that.

    Here are the options.

    Nobody cares - keep it as it is.

    I've never received anything asking this project to change the name, although I can't say if Chris (this projects creator) ever did. Perhaps it is easier to just stay as we are and educate/correct people when they confuse the two. It doesn't bother me so much, especially as most of the confusion seems to be the other way which I suspect is due to this Terminator being more popular.

    A lack of desire on my side to go through renaming everything is definitely a plus for this option.

    Just rename, but keep the current Terminator™ vibe?

    From the existing branding:
    • Terminator - The robot future of terminals
    Some possible ideas:
    • T-1000 - "The mimetic polyalloy of terminals."
      • Pro: The strapline para-phrases Arnie, and gives a nice mental image of smoothly flowing to fit your needs.
      • Con: The actual name is kinda bland, and I just know that hyphen will become a pain.
      • The T-1000 in the movie never had a HUD that I can recall (unlike the T-800's red one) so there isn't a visual identity to key off, unless we make everything Chrome (which is a bugger to draw for the artistically challenged like me).
    • T1K
      • Pros/Cons as for T-1000 above, but a different way of writing it that avoids the hyphen.
    • Terminatrix - "The sexy terminal that has lots of tricks (geddit?) up its sleeve."
      • Pro: Name is more distinct, cool sounding, and not used by other software, at least in the terminal emulator space anyway.
      • Con: In these PC times the strapline probably offends someone, somewhere, and risks alienating some people. Especially using "sexy" and "trix"/"tricks" (American slang for prostitution) close together. Perhaps someone else has a better strapline. In my defence I spent about 30 seconds of brain power on that.
      • Pro: Having said that, T-X literally did have a lot of tricks up her (it's?) sleeve with that Skynet interpretation of a swiss-army knife for an arm.
      • Con: The T-X had a blue HUD that is more of-the-times, but would mean colourising our current iconography, meaning additional work.
      • Pro: Whenever people search for our app they get a moody stare-down from Kristanna Loken, instead of Arnie.
      • Con: There is a pre-T3 knock-off soft-core porn film from Japan (IMDB) called Terminatrix that pops up on a Google search... O-kaaaaaaayyyyyy...
    And yes, I realise they both eventually got their butts kicked by the T-800, but that puny T-800 endoskeleton was wrapped in an Arnie so I'm not going to hold that against them :-D
    Or perhaps you have another suggestion tied into the Terminator™ Universe? "Conner" as in John/Sarah? "ConSoul" as in the fight for John Conner's soul? Those are probably a bit too groan-worthy and diverge too much from the existing robotic theme anyway.

    A full rebrand?

    Ditch any Terminator™ associations, and come up with a completely new name/brand. I'm not keen on this, and it would have to be a very good, catchy name, with something to key off of for identity. This is quite hard to come up with for something as common and utilitarian as a terminal emulator. "Terminals of Endearment" anyone? ;-)

    If there's any interest I will pop up a poll with a jury (me) selected shortlist of suggestions to see which way the wind blows. You can comment here, or get my attention at my G+ page (use the about me pop-out on the right) or on my mostly dormant (so far) Twitter account @TheOddBodd if you want to make suggestions.

    For me I'd prefer to remain Terminator due to the effort involved doing all the renaming. If I was to choose, I'd pick "Terminatrix" assuming we can sanitise it enough that I don't end up with a horde of SJW's hoisting me by my own petard.

    [Edit@19:19] I should also add that a Pro for sticking as we are is that we have a pretty good/high position when googling, all things considered.
    33

    View comments

  8. Some random non-specific musings

    I'm not a developer, and never have been. I am instead the embodiment of an open-source tinkerer who got carried away, bit off more than he could chew, and then ended up with an unpaid mini-job to go with the one that pays the rent.

    So here I am, a non-developer, writing code in Python, which is admittedly one of the simpler languages, and I hit problems. Around this point the insecurities kick in; "I have no idea what I'm doing", "I'm a fraud", "At some point the grown-ups are going to come and take over" (I've passed the big 4-0 BTW, so the grown-ups are more metaphorical ones).

    I think my approach to coding anything is to just keep stubbornly bashing my head against whatever wall is in my way until shear persistence pays off. So I generally finish up with a bruised and bloody forehead, and with a very broad range of expletives thoroughly well exercised. I'm not elegant, clever, or knowledgeable about this stuff - I just dig my heels in and refuse to believe (despite significant evidence to the contrary sometimes) that I'm not capable of figuring it out.

    Bringing this back to Terminator, I've been contributing to it for quite some time now, and have been an inadequate maintainer for a couple of years, and in that time I've come to a conclusion...

    GTK was created to torture us non-developers and expose all our flaws and weaknesses.

    I can't think of any section of the non-GTK code in Terminator that bothers me. I've contributed some fairly hairy parts (performance optimisations, splitter balancing, grouping and broadcasting) and fixed bugs in most areas, but nearly every time I get properly stuck it's because of GTK. This is of course the observation of a non-developer, so maybe it's invalid and real developers waltz through coding GTK like a hot-knife through butter. Perhaps some real developers could comment?

    Here's a few instances where I just shake my head in mild despair.

    Is it a bug, or am I doing something stupid?

    In GTK2 when editing a text entry in a list view, sometimes the edited signal callback is passed a cell and a path that point to different rows. (trunk-1620) Am I doing something wrong? Is it a bug? I lean toward bug, because GTK3 is always consistent.

    In GTK2 I want the Profile and Layout lists to pre-select and scroll to the currently used entry. The treeviews scroll_to_cell method completely refuses to work. I have an equivalent micro-test that works correctly, but the main app completely refuses to budge with no indication why. I'm pretty sure I'm doing something stupid here, but it seems I haven't pounded my head against the aforementioned wall long enough yet to have divined an answer.

    In the GTK2 to GTK3 port, not only did it change from a directly accessible attribute, but the widget get_allocation method changed the return values. In GTK2 you get the x, y, w, h of the widget; in GTK3 x, y are zeroed but w, h are valid. So I now need to override the get_allocation method to fill in the x, y for GTK3, or do more substantial changes elsewhere that will cause more divergence between the two Terminator versions. (gtk3-1532 & gtk3-1533) Am I doing something wrong? Bug? Deliberate change?

    In the GTK2 to GTK3 port, the drag-and-drop was particularly troublesome. In particular the TargetList methods to add default targets for text and URI's resulted in complete corruption of the returned objects. The gibberish caused a segfault when trying to drop something in a terminal. So instead I've had to hard-code the values to work around it. (gtk3-1555, terminal.py, 366-389) Bug? I'm not 100% sure. Maybe I've misunderstood the proper usage?

    WTF? Who thought that was a good idea?

    These WTF moments tend to be encountered when doing the GTK2 to GTK3 changes.

    Something as simple as getting the size of the splitter handle now takes three lines instead of one because I'm forced to use a GObject (gtk3-1530) Bug? Deliberate change?

    GNOME GTK3 dev: "Hold on, lads! I've got an idea... Let's remove the ability to grab an image of a widget directly and instead people can replace some relatively simple to understand lines of code with a mess of obtuse cairo calls." (gtk3-1555, util.py, 158-169) Am I being dumb? Is this a sensible approach?

    And one final one, also encountered in fixing the drag-and-drop. What the heck is Gdk.atom_intern, and why do I have to turn the string 'vte' into one? (gtk3-1555, terminal.py, 949) Bug? Deliberate change?

    But like I said...

    I'm not a real developer. Maybe all these "moments" are just a result of not understanding the API well enough, or the reasons behind decisions taken.

    Note that when I talk about a bug, that could be anywhere. Terminator code, the GIR descriptions, the PyGObject interpretation of the GIR descriptions, PyGTK, the GTK widget itself. It is totally unclear, and a sizeable part of the frustration.
    2

    View comments

  9. Click-bait alert!

    Quite often I read the classic argument that Python is slow, or it's sucking down resources, or that it's a "bad" language for whatever reason. I'm going to ignore whether or not Python is good, bad or ugly. It's what I know, and why I was able to contribute to the Terminator project, so as far as I'm concerned that's a pointless discussion for me to have.

    But I have seen the speed and memory arguments levelled at Terminator on a couple of sites. And I did wonder... and then I got curious... and then I got busy!

    Now, I am no no way stating that the following is any kind of rigorous and scientific approach, or even that they prove anything for certain. It was just a case of scratching an itch like most of my diversions.

    The tests

    Performance

    Profiles were configured with command bash -c exit, and the commands run a couple of times to get the caches loaded up.

    GNOME-Terminal:

        time for i in {1..30} ; do gnome-terminal --profile=Quickexit; done

        real    0m10.606s


    Terminator:

        time for i in {1..30} ; do terminator -g deletemeconfig -p Quickexit; done

        GTK2: real    0m11.928s A smidgen slower.
        GTK3: real    0m10.885s Yeah, basically identical!


    Cold start, using sync && echo 3 > /proc/sys/vm/drop_caches before each run, then launching a single timed instance.

    Gnome-Terminal:

        time gnome-terminal --profile=Quickexit

        real    0m7.628s (approx median, there was a strange variance for GT, between 5 and 9 secs)


    Terminator:

        time terminator -g deletemeconfig -p Quickexit

        GTK2: real    0m11.390s (median of 3x)
        GTK3: real    0m11.264s (median of 3x)


    OK, so this is one place you would notice an appreciable difference in speed. How often do you start these things with completely empty caches/buffers?

    In GTK2 VTE there is a known issue which slows the cat’ing of large files quite a bit. The VTE in GTK3 Terminator is the same widget GNOME-Terminal uses, so this will get better, as and when we move fully to the in-progress GTK3 port. I should point out that this performance deficit is not due to the Python interpreter, or the Terminator Python code, but is solely down to the compiled C code VTE widget. So apart from attacking the project for not getting GTK3 Terminator done by now, this isn't the fault of Python.

    Memory use - The dumb way

    This creates a completely new and separate process for each of the 100 iterations.

    GNOME-Terminal:

        for i in {1..100} ; do gnome-terminal --disable-factory & done

        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free           # Before startup
                      total       used       free     shared    buffers     cached
         Mem:       3102404    1388776    1713628       4052        164      45340
         -/+ buffers/cache:    1343272    1759132
         Swap:      3121996     788704    2333292
         root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free          # After startup
                      total       used       free     shared    buffers     cached
         Mem:       3102404    2439524     662880      57196       1240      99212
         -/+ buffers/cache:    2339072     763332
         Swap:      3121996     751440    2370556
         root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free          # After kill
                      total       used       free     shared    buffers     cached
         Mem:       3102404    1466536    1635868       4796        160      45912
         -/+ buffers/cache:    1420464    1681940
         Swap:      3121996     751020    2370976

         Used (used mem -buffers/cache + swap)
             Before start: 2131976
             After start : 3090512 = 958536 kbytes, 936 Mbytes / 9.36 MBytes/instance
             After kill  : 2171484 =  39508 kbytes,  38 Mbytes not recovered


    Terminator GTK2:

        for i in {1..100} ; do terminator -g deletemeconfig -u & done

        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free
                     total       used       free     shared    buffers     cached
        Mem:       3102404    1313456    1788948       4284        152      43844
        -/+ buffers/cache:    1269460    1832944
        Swap:      3121996     736844    2385152
        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free
                     total       used       free     shared    buffers     cached
        Mem:       3102404    2866552     235852      19484       1084      65408
        -/+ buffers/cache:    2800060     302344
        Swap:      3121996     736340    2385656
        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free
                     total       used       free     shared    buffers     cached
        Mem:       3102404    1317724    1784680       4284        152      43464
        -/+ buffers/cache:    1274108    1828296
        Swap:      3121996     736304    2385692

        Used (used mem -buffers/cache + swap)
            before start: 2006304
            after start : 3536400 = 1530096 kbytes, 1494 Mbytes / 14.94 MBytes/instance
            after kill  : 2010412 =    4108 kbytes,    4 Mbytes not recovered


    Terminator GTK3:

        for i in {1..100} ; do terminator -g deletemeconfig -u & done

        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free
                 total       used       free     shared    buffers     cached
        Mem:       3102404    1467204    1635200       4816        120      46132
        -/+ buffers/cache:    1420952    1681452
        Swap:      3121996     751000    2370996
        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free
                     total       used       free     shared    buffers     cached
        Mem:       3102404    2848372     254032       7216        960      52652
        -/+ buffers/cache:    2794760     307644
        Swap:      3121996     750016    2371980
        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free
                     total       used       free     shared    buffers     cached
        Mem:       3102404    1483388    1619016       4820        148      46084
        -/+ buffers/cache:    1437156    1665248
        Swap:      3121996     749828    2372168

        Used (used mem -buffers/cache + swap)
            before start: 2171952
            after start : 3544776 = 1372824 kbytes, 1340 Mbytes / 13.41 MBytes/instance
            after kill  : 2186984 =   15032 kbytes,   15 Mbytes not recovered


    OK, so yes, there is more overhead. We did just start 100 Python interpreters! As titled, this is dumb, and even if you use this dumb method, are you really going to have a hundred of them?...

    Memory use - The sensible way

    This time we take advantage of the Factory in GNOME-Terminal and the DBus in Terminator. This means a single process, but 100 windows.

    GNOME-Terminal:

        gnome-terminal &
        for i in {1..100} ; do gnome-terminal & done

        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free          # Before 100
                     total       used       free     shared    buffers     cached
        Mem:       3102404    1490996    1611408       5344        172      47580
        -/+ buffers/cache:    1443244    1659160
        Swap:      3121996     749776    2372220
        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free          # After 100
                     total       used       free     shared    buffers     cached
        Mem:       3102404    1878228    1224176       5344        172      47388
        -/+ buffers/cache:    1830668    1271736
        Swap:      3121996     733396    2388600
        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free          # After kill
                     total       used       free     shared    buffers     cached
        Mem:       3102404    1491888    1610516       4840        272      46088
        -/+ buffers/cache:    1445528    1656876
        Swap:      3121996     733240    2388756

        Used (used mem -buffers/cache + swap)
            Before start: 2193020
            After start : 2564064 = 371044 kbytes, 362 Mbytes / 3.59 MBytes/instance
            After kill  : 2178768 = −14252 kbytes, -13.92 Mbytes recovered (first process)


    Terminator GTK2:

        terminator -g deletemeconfig &
        for i in {1..100} ; do terminator -g deletemeconfig -u & done

        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free
                     total       used       free     shared    buffers     cached
        Mem:       3102404    1324492    1777912       4388        152      49688
        -/+ buffers/cache:    1274652    1827752
        Swap:      3121996     744528    2377468
        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free
                     total       used       free     shared    buffers     cached
        Mem:       3102404    1652112    1450292       4756        860      49968
        -/+ buffers/cache:    1601284    1501120
        Swap:      3121996     744224    2377772
        root@pinpoint:~# sync && echo 3 > /proc/sys/vm/drop_caches && free
                     total       used       free     shared    buffers     cached
        Mem:       3102404    1305376    1797028       4236        124      42836
        -/+ buffers/cache:    1262416    1839988
        Swap:      3121996     744116    2377880

        Used (used mem -buffers/cache + swap)
            before start: 2019180
            after start : 2345508 = 326328 kbytes, 319 Mbytes / 3.16 MBytes/instance
            after kill  : 2006532 = −12648 kbytes,  -12.35 Mbytes recovered (first process)


    Terminator GTK3:

        Not possible at the moment because the DBus interface still needs fixing.

    So that one surprised me a bit. The fact that when using the single process we are more memory efficient. Python + 100 terminals is using <90% of the GNOME-Terminal + 100 terminals.

    Some may think that this is something to do with the different version of the VTE widget, but hang on a second. In the dumb method GTK2 Terminator used more memory than GTK3. Once the DBus is fixed for GTK3 could there potentially be more savings?

    Or perhaps the GTK2 VTE is just much smaller...

        618K /usr/lib/libvte.so.9.2800.2      (GTK2 VTE used by GTK2 Terminator)
        630K /usr/lib/libvte2_90.so.9.3400.9  (GTK3 VTE used by GNOME-Terminal)
        564K /usr/lib/libvte-2.91.so.0.3800.1 (GTK3 VTE used by GTK3 Terminator)


    Certainly for the two sets of results, showing a 400K difference per window, but only a 12K difference in the size of the VTE libs, that can't be the explanation. The only other explanation I can think of is that GNOME-Terminal is setting up some extra memory for something.

    In summary

    It’s a bit slower on startup, it takes a bit more memory, but that’s when you use the dumb method. In normal use, where you’re likely to be using the existing process to open a new window, it is for all practical purposes as fast as the compiled GNOME-Terminal. It may even (according to the last memory section) be a little lighter memory wise when used in a sane manner, and more obliging about giving it back!

    I didn’t compare to things like xterm, because frankly we’re not aimed at the same people. Personally I’d rather have the more extensive features saving me lots of time over the course of the day when using it, than save a handful of seconds every few days when I restart it, or worrying about an extra 5 or 10 MBytes.

    I'm open to suggestions if someone can point to issues/mistakes that would make substantive (i.e. >5%) differences to these figures, but as to the click-bait declaration in the title, I just don't see it.
    4

    View comments

  10. ...Something wicked this way comes!


    First however, a confession. I suck! No really, I do. I suck less than no-one as maintainer, but that's still a lot of suck. Even now, I'm still sucky because we're still not there yet, but we are closer. A lot closer.

    The Changelog is actually getting a bit ridiculous with so much stuff done, and is running at 3 big feature items, 35 enhancements, and 24 fixes over the course of 193 commits. I don't want to detail all these items here, but I will mention two that are, in my (occasionally) honest opinion, a "big deal". For details on all the other changes check out the Changelog.

    First up, Terminator has a manual! And it's glorious. I went through every-damn-thing I could find and documented it. Sure, there are probably some things I missed, or improvements that can be made, but it's still a "big deal". Why? As the current maintainer, and a contributor and user for even longer, I was learning stuff. GitHub has a nice little collection of Terminator plugins. Who knew! Not me. You can now find a list of those I found in the manual.
    While writing it I also noticed quite a few gaps in areas, which are now added to my personal list.

    As a little bonus autodoc was run over the code to generate documentation for the "API". I'm quoting API because as far as I know no-one is embedding Terminator into other applications. It's just useful to have all the parts documented like this.

    The documentation is produced with Sphinx and the ReadTheDocs theme. Right now I have a couple of niggles with this arrangement, so if anyone reading this is knowledgable, I'm stumped on the following:
    1. I can make the setup.py generate the html from ReST when building the debian package (or direct install) on my system, but if I put that in, then the PPA builds will break (no RTD theme in Ubuntu repositories, and I don't think pip will work in PPA build farm) so for the moment I have both ReST and html in the VCS.
    2. Getting rid of the unnecessary root node in the sidebar tree for the API doc
    The second big thing is that we have a GTK3 port. This is thanks to initial work by one of the VTE developers (Egmont Koblinger) who probably got tired of complaining about our old VTE. There are still problems with it (mentioned in the manual) but eventually it will fix a number of problems on it's own, like better performance and dynamic text wrapping on window resize. Assistance is always welcome to complete this work.

    (No screenshot, because it looks just like GTK2, except the popup menus are a darker grey.)

    The GTK3 port does put us in a bit of a quandary, and I'd like to clarify my thinking on this. Yes, right now Terminator uses an old version of the VTE widget, unsupported by the developers. But the GTK3 port needs libvte 0.36+. The latest Ubuntu LTS (14.04) only has 0.34. There was a rumour about an SRU update going in, but that never materialised. So I have four choices:
    1. Drop the GTK2 Terminator, and only work on GTK3: This would mean that anyone on the interim release could maybe use it as I work and test against 0.38, where 15.04 (Vivid) and 15.10 (Wily) only have 0.36. If like me you are an LTS 14.04 (Trusty) user, you will need to either wait till LTS 16.04 (X???) or you have to start compiling stuff. For most folks this probably isn't a practical answer, although there are some instructions here in comment #15 for the brave.
    2. I provide updated packages for libvte in a PPA: Not really something I'm comfortable or skilled in, and it does nothing for all the other distros using older versions of libvte, i.e. it looks like the latest CentOS 7 (so probably RHEL 7 too) ship with 0.34.
    3. I develop both in parallel, and port any contributions: Keeping GTK2 as default till my wet-finger-waved-in-the-air tells me it's time to make GTK3 the primary.
    I've been doing 3, and it has actually worked quite well so far. Most things apply to both series, and it's usually pretty easy to make a change in GTK2, then immediately do the same in GTK3. It also serves as a way of learning about the GObject Introspection used now.

    Sidebar: Sometimes GTK3 makes me want to repeatedly pound my head into a wall. Some of the stuff introduced, changed and removed caused me real problems to figure out what the hell was going on!

    What's next?

    There's a few bugs I'm not prepared to do a release without first fixing. There are a few bugs with patches attached. I'm going try to merge those that fix critical behaviour i.e. deleting/corrupting something, or segfaulting, although they're usually something in the underling C libraries, and not Terminator. Some icons still need a bit of polishing.

    Anything else will get pushed to a future point release for now, just so the release happens this decade!

    What can you do?

    I wouldn't mind a round of feedback on the manual. I'm still spotting bit's to fix, but a fresh set of eyes is always good. You will need to install the nightly PPA version (or do a bzr checkout of the trunk) to get the manual on your system.

    I did a huge clean-up of the preferences window, and also made everything translatable. Unfortunately this caused a lot of string changes, and Launchpad does not handle gettext fuzzy matches. So a lot of strings need to be updated. There are now 380 strings, and due to my work, a previous 100% coverage language like German now has roughly 60% coverage. So please, if you speak a language other than British or American English, get onto Launchpad and get translating. The vast majority of the strings are what will go out with the next release, so there shouldn't be any more churn messing up your work.


    This link will let you view the translation files before I went on my rampage of destruction. This might help speed things up.

    Props and shoutouts to the following, who were translating while I was making a number of round trips fixing things. I hope I didn't delete things:
    • ES: Hector A. Mantellini and "_s4kura"
    • PT: Paulo Henriques
    • JA: "ub"

    If you contribute to a news site, and would like me to ping you when I pull the trigger on the release, give me an e-mail address, and I'll bcc you at the time.

    All the usual OS project things apply too.

    Well that was a fairly long post. Let's see if I can make the next one in a slightly more timely fashion.
    4

    View comments

Blog Archive
Contributors
Contributors
Loading
Dynamic Views theme. Powered by Blogger. Report Abuse.