2012/12/04

Project Updates - Tickit window scrolling, Circle scroll indicators

Some progress on a few of my projects:

Tickit::Window now supports scroll and scrollrect even if the window partly covered by other floating windows. With the expose-after-scroll behaviour turned on (soon to be default in a later version once I know it works nicely), it always returns true, and tries to scroll as many regions of the screen as it can, queueing expose operations for the areas that needed it.

A good example of this working can be seen in the newly-updated Tickit::Widget::Scroller, which now supports a scroll position indicator, a small floating window in the top or bottom right corner.

Another example can be seen as the latest feature in Circle::FE::Term. Scrolling a channel window gives an indicator in the bottom right corner, which also includes a count of new lines of content added since scrolling, if there are any.

2012/11/24

The Past, The Present and The Future - LPW2012

For those who are interested, here are the slides of the talk I gave about Futures at LPW2012 this year:

The Past, The Present and The Future

2012/10/06

Pangoterm - config and scrollback

I've been working more on pangoterm, and it now has two important features that bring it much closer to being a real usable terminal.

The first is that it now supports a lot more configuration by command-line, and also the same things by a configuration file. Support for a config file isn't something I personally have directly needed so far, mostly because the compiled-in defaults are already what I'd like. But it's something other people have been asking for, and this helps support other users too, so it's nice to have.

I also found quite a neat way to implement it too, requiring just one line of macro to declare the existence of a configuration setting and give its default value and help description. This then creates a normal variable that can be read in the usual way:

CONF_INT(scrollback_size, 0, 1000, "Scrollback size", "LINES");
...
pt->scroll_size = CONF_scrollback_size;

The other main feature that's now in place is scrollback buffer. Now, whenever text disappears off the top of the screen it is saved, and can be recalled again by scrolling the mouse wheel or using the Shift-PageUp and Shift-PageDown keys. A small custom-drawn indicator shows the scroll position without needing to consume any screen space in the normal state of not being scrolled.

I also found and fixed an almost embarrassingly-bad bug around the redrawing code. When erasing cells, it used to make one cairo call for every cell to be erased, leading to many calls whenever an entire line had to be cleared. Now it composes multiple blank cells with the same pen, in the same way as it composes multiple cells of text; and is faster when scrolling partial lines, and so on.

While I've already been using it quite a lot lately to run the vim I use to edit its own source code, for some tasks I've still been using xterm. But not any more - with the addition of scrollback to pangoterm, I find I'm now using it for everything. It's become my default terminal both for home and work use.

2012/08/21

libvterm / pangoterm performance improvements

I recently made a couple of small but drastic improvements to libvterm and pangoterm's performance, when scrolling long output. Using my not-so-scientific test of taking a ~500KB text file and running:
$ time cat file
This technically measures the amount of time it takes cat to write the data into the TTY, but since the buffer is a fixed 4KiB in size, it also measures the time that pangoterm takes to read all but the final 4KiB of the file, which is under 1% of the file. I managed to obtain the following timings. Before I started:
real    0m4.206s
user    0m0.000s
sys     0m0.036s
By optimising libvterm's moverect buffer operation with memmove() (revision -r511):
real    0m2.035s
user    0m0.000s
sys     0m0.048s
With pangoterm deferred updates, that delay re-rendering of the screen until all the PTY data has been read, or every 20 msec (revision -r482):
real    0m0.358s
user    0m0.004s
sys     0m0.016s
It's now well over 10 times faster than it used to be. Ohh.. and it beats xterm. By quite a bit.
real    0m2.294s
user    0m0.000s
sys     0m0.036s

2012/07/10

Dear Perl users on Windows...

I am close to giving up trying to support my Perl modules on Windows. Personally, I have nothing against it, despite all the odd quirky "not-quite-UNIX"es about it. I'm sure the same could be said in reverse, of all the "not-quite-Windows"es about the UNIXes, coming from a Windows developer.

However, I am continually annoyed at the almost complete lack of any smoke-test results ever coming from Cygwin or MSWin32 machines. I can upload a new module to CPAN and within literally hours have a dozen or so smoke-test reports from Linux and FreeBSD machines. Within a few days, a flood more results from these, and also Darwin, OpenBSD, NetBSD, Solaris. But quite often I can sit and wait weeks, if not months before I see the first result from Cygwin or MSWin32.

It's not that I'll ever actually stop looking after code - if someone were to point out a problem or send me a patch, I'm sure I could include it. It's just very hard for me as a developer without a Windows box to know if any of my code actually works on Windows, or if not, to be able to fix it. While I don't have any BSD, Solaris, or OS X boxes, I do at least have the confidence of the smoke-test reports to claim my code works there.

I must therefore conclude one of two things:

  • Nobody is using Perl, or at least my Perl modules, on Windows; or
  • Nobody using Perl on Windows cares about testing and code quality

I would really dislike having to declare this, because I feel sure there ought to be Windows users around who care. So perhaps someone somewhere can look into actually installing the test reporter module. It isn't hard, really...

2012/06/28

Tickit, floating windows and menus

The latest release of Tickit, version 0.18, adds support for floating windows. These are windows that rather than divide space of their parent window, instead float above it by obscuring content below, creating a Z-ordering. Drawing operations on windows are aware of clipping regions caused by floating windows above them, and are accounted for automatically without individual widgets needing to be aware of it.

A special kind of floating window called a popup window can be created. These are always created as children of the root window, and take first priority at input events. These can be used to create things like popup menus, such as this early prototype of Tickit::Widget::Menu.
This is still somewhat of a work-in-progress, and by the time it's released it should support proper Unicode linedrawing rather than the ASCII variant. I'm also planning to create a way to make pop-up windows of arbitrary widgets, to create things like modal dialog boxes.

2012/05/23

Don't be too lazy

Today I fixed a bug in my C library rewrite of Tickit.

The manifestation of the bug was that Tickit::Console didn't work properly via the C/XS version of Tickit, but worked fine on the Perl version. It wasn't receiving keyboard input at all. Yet I know basic keyboard input works fine on all the simple demos with the C library version, so something more subtle was up.

On close inspection it turned out that libtickit was being too lazy with constructing its libtermkey instance for handling keyboard input. The previous code arrangement was that constructing the instance was deferred as late as possible, until one of the input-handling methods was actually called. This meant that setting the filehandle could be done as a normal accessor and not as a constructor argument. (C functions lack the neatness of Perl's named argument style, so my usual style is minimal constructors and lots of mutation accessors).

This worked fine for simple cases, because after setup the demo programs all wait on a call to tickit_term_input_wait(), and the first thing that does is create the TermKey instance, which sets up the terminal for non-canonical input mode and disables local echo. This ensures input arrives a key at a time.

However, since the Tickit::Console example runs via Tickit::Async, the way it runs is to wait in a poll() loop waiting on readability on STDIN. When STDIN becomes readable does it call tickit_term_input_readable(), and only then does it create the TermKey instance that actually sets up the terminal.

The upshot here is that until that entire first line of input is received, the terminal isn't actually set up to the correct mode in the first place.

Fixing this bug was a simple matter of making the underlying tickit_term_set_input_handle() mutator eagerly allocate the TermKey instance immediately, so that the terminal is already set up in the correct mode once the Tickit::Term constructor returns. That way it works correctly in both synchronous and asynchronous code.

In summary - when creating lazy functionality in a program, make sure that your laziness doesn't cause you to neglect to set something up that someone else was relying on. Be lazy, but don't be too lazy.