- changed version number report again, now to emulate xterm closer.
- added the OSC sequence 702 to detect the urxvt version number.
- small configure updates.
+ - overhauled color management: smaller codesize, alpha support.
+ - do not include X11/Intrinsic.h anymore, directly use
+ Xlib/Xutil/Xresource directly.
7.4 Sat Jan 28 15:26:27 CET 2006
- screen background wasn't always erased properly when scrolling,
In addition to the default foreground and background colours,
B<@@RXVT_NAME@@> can display up to 16 colours (8 ANSI colours plus
high-intensity bold/blink versions of the same). Here is a list of the
-colours with their B<rgb.txt> names.
+colours with their names.
=begin table
a number 0-15, as a convenient shorthand to reference the colour name of
color0-color15.
+If Xft support has been compiled in and as long as Xft/Xrender don't get
+their act together, rxvt-unicode will support C<#ARGB>, C<#AARRGGBB>
+and C<#AAAARRRRGGGGBBBB> colour specifications, in addition to the ones
+provided by X, where the additional A component specifies alpha (opacity)
+values (0 is completely transparent and the maximum is opaque). You
+probably need to specify B<"-depth 32">, too, as X is far from just
+supporting ARGB visuals out of the box.
+
Note that B<-rv> (B<"reverseVideo: True">) simulates reverse video by
always swapping the foreground/background colours. This is in contrast to
I<xterm>(1) where the colours are only swapped if they have not otherwise
#if TINTING
if (ISSET_PIXCOLOR (Color_tint))
{
- unsigned short rm, gm, bm;
+ rxvt_rgba c;
int shade = rs[Rs_shade] ? atoi (rs[Rs_shade]) : 100;
- pix_colors_focused[Color_tint].get (this, rm, gm, bm);
+ pix_colors_focused[Color_tint].get (this, c);
- ShadeXImage (this, image, shade, rm, gm, bm);
+ ShadeXImage (this, image, shade, c.r, c.g, c.b);
}
#endif
{
if (str[0] == '?' && !str[1])
{
- unsigned short r, g, b;
- pix_colors_focused[color].get (this, r, g, b);
- tt_printf ("\033]%d;rgb:%04x/%04x/%04x%c", report, r, g, b, resp);
+ rxvt_rgba c;
+ pix_colors_focused[color].get (this, c);
+
+#if XFT
+ if (c.a != rxvt_rgba::MAX_CC)
+ tt_printf ("\033]%d;#%04x%04x%04x%04x%c", report, c.a, c.r, c.g, c.b, resp);
+ else
+#endif
+ tt_printf ("\033]%d;rgb:%04x/%04x/%04x%c", report, c.r, c.g, c.b, resp);
}
else
set_window_color (color, str);
if ((buf = strchr (name, ';')) != NULL)
*buf++ = '\0';
- if (name[0] == '?' && !name[1])
- {
- unsigned short r, g, b;
- pix_colors_focused[color].get (this, r, g, b);
- tt_printf ("\033]%d;%d;rgb:%04x/%04x/%04x%c", op, color, r, g, b, resp);
- }
- else
- set_window_color (color, name);
+ process_color_seq (op, color, name, resp);
}
break;
case XTerm_Color00:
* xcol[2] == bot shadow */
xcol[1] = pix_colors[Color_scroll];
- xcol[0].set (this, 65535, 65535, 65535);
+ xcol[0].set (this, rxvt_rgba (rxvt_rgba::MAX_CC, rxvt_rgba::MAX_CC, rxvt_rgba::MAX_CC));
- unsigned short pr1, pg1, pb1, pr0, pg0, pb0;
+ rxvt_rgba c0, c1;
- xcol[0].get (this, pr0, pg0, pb0);
- xcol[1].get (this, pr1, pg1, pb1);
+ xcol[0].get (this, c0);
+ xcol[1].get (this, c1);
pix_colors[Color_bottomShadow] = xcol[1].fade (this, 50);
/* topShadowColor */
if (!xcol[1].set (this,
- min (pr0, max (pr0 / 5, pr1) * 7 / 5),
- min (pg0, max (pg0 / 5, pg1) * 7 / 5),
- min (pb0, max (pb0 / 5, pb1) * 7 / 5)))
+ rxvt_rgba (
+ min (c0.r, max (c1.r / 5, c1.r) * 7 / 5),
+ min (c0.g, max (c1.g / 5, c1.g) * 7 / 5),
+ min (c0.b, max (c1.b / 5, c1.b) * 7 / 5),
+ c1.a) // pa1 vs. pa0: arbitrary
+ ));
xcol[1] = pix_colors[Color_White];
pix_colors[Color_topShadow] = xcol[1];
if (XInternAtom (xdisp, "_MOTIF_WM_INFO", True) == None)
{
/* print_warning("Window Manager does not support MWM hints. Bypassing window manager control for borderless window.\n");*/
- attributes.override_redirect = TRUE;
+ attributes.override_redirect = true;
mwmhints.flags = 0;
}
else
dTermGC;
if (color == Color_bg)
- XClearArea (disp, d, x, y, w, h, FALSE);
+ XClearArea (disp, d, x, y, w, h, false);
else if (color >= 0)
{
#if XFT
#endif
extern "C" {
-#include <X11/Intrinsic.h> /* Xlib, Xutil, Xresource, Xfuncproto */
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xresource.h>
}
/*
return (long)mg->mg_ptr;
}
-#define newSVterm(term) SvREFCNT_inc ((SV *)term->perl.self)
-#define SvTERM(sv) (rxvt_term *)SvPTR (sv, "urxvt::term")
+#define newSVterm(term) SvREFCNT_inc ((SV *)(term)->perl.self)
+#define SvTERM(sv) (rxvt_term *)SvPTR ((sv), "urxvt::term")
/////////////////////////////////////////////////////////////////////////////
rxvt_warn ("%s callback evaluation error: %s", type, SvPV_nolen (ERRSV));
}
-#define newSVtimer(timer) new_ref (timer->self, "urxvt::timer")
-#define SvTIMER(sv) (timer *)(perl_watcher *)SvPTR (sv, "urxvt::timer")
+#define newSVtimer(timer) new_ref ((timer)->self, "urxvt::timer")
+#define SvTIMER(sv) (timer *)(perl_watcher *)SvPTR ((sv), "urxvt::timer")
struct timer : perl_watcher, time_watcher
{
}
};
-#define newSViow(iow) new_ref (iow->self, "urxvt::iow")
-#define SvIOW(sv) (iow *)(perl_watcher *)SvPTR (sv, "urxvt::iow")
+#define newSViow(iow) new_ref ((iow)->self, "urxvt::iow")
+#define SvIOW(sv) (iow *)(perl_watcher *)SvPTR ((sv), "urxvt::iow")
struct iow : perl_watcher, io_watcher
{
}
};
-#define newSViw(iw) new_ref (iw->self, "urxvt::iw")
-#define SvIW(sv) (iw *)(perl_watcher *)SvPTR (sv, "urxvt::iw")
+#define newSViw(iw) new_ref ((iw)->self, "urxvt::iw")
+#define SvIW(sv) (iw *)(perl_watcher *)SvPTR ((sv), "urxvt::iw")
struct iw : perl_watcher, idle_watcher
{
}
};
-#define newSVpw(pw) new_ref (pw->self, "urxvt::pw")
-#define SvPW(sv) (pw *)(perl_watcher *)SvPTR (sv, "urxvt::pw")
+#define newSVpw(pw) new_ref ((pw)->self, "urxvt::pw")
+#define SvPW(sv) (pw *)(perl_watcher *)SvPTR ((sv), "urxvt::pw")
struct pw : perl_watcher, child_watcher
{
{
if (THIS->perl.grabtime)
{
- XUngrabKeyboard (THIS->display->display, THIS->perl.grabtime);
- XUngrabPointer (THIS->display->display, THIS->perl.grabtime);
+ XUngrabKeyboard (THIS->xdisp, THIS->perl.grabtime);
+ XUngrabPointer (THIS->xdisp, THIS->perl.grabtime);
THIS->perl.grabtime = 0;
}
}
OUTPUT:
RETVAL
+void
+termlist ()
+ PPCODE:
+{
+ EXTEND (SP, rxvt_term::termlist.size ());
+
+ for (rxvt_term **t = rxvt_term::termlist.begin (); t < rxvt_term::termlist.end (); t++)
+ if ((*t)->perl.self)
+ PUSHs (sv_2mortal (newSVterm (*t)));
+}
+
MODULE = urxvt PACKAGE = urxvt::term
SV *
-_new (...)
+_new (AV *env, AV *arg)
CODE:
{
- if (items < 1 || !SvROK (ST (0)) || SvTYPE (SvRV (ST (0))) != SVt_PVAV)
- croak ("first argument to urxvt::term->_new must be arrayref");
-
rxvt_term *term = new rxvt_term;
stringvec *argv = new stringvec;
stringvec *envv = new stringvec;
- for (int i = 1; i < items; i++)
- argv->push_back (strdup (SvPVbyte_nolen (ST (i))));
+ for (int i = 0; i <= AvFILL (arg); i++)
+ argv->push_back (strdup (SvPVbyte_nolen (*av_fetch (arg, i, 1))));
- AV *env = (AV *)SvRV (ST (0));
for (int i = AvFILL (env) + 1; i--; )
envv->push_back (strdup (SvPVbyte_nolen (*av_fetch (env, i, 1))));
THIS->perl.should_invoke [htype] += inc;
void
-rxvt_term::grab_button (int button, U32 modifiers)
+rxvt_term::grab_button (int button, U32 modifiers, Window window = THIS->vt)
CODE:
- XGrabButton (THIS->display->display, button, modifiers, THIS->vt, 1,
+ XGrabButton (THIS->xdisp, button, modifiers, window, 1,
ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask,
GrabModeSync, GrabModeSync, None, GRAB_CURSOR);
+void
+rxvt_term::ungrab_button (int button, U32 modifiers, Window window = THIS->vt)
+ CODE:
+ XUngrabButton (THIS->xdisp, button, modifiers, window);
+
+#if 0
+
+void
+XGrabKey (rxvt_term *THIS, int keycode, U32 modifiers, Window window = THIS->vt)
+ C_ARGS:
+ THIS->xdisp, keycode, modifiers, window, 1,
+ GrabModeSync, GrabModeSync
+
+void
+XUngrabKey (rxvt_term *THIS, int keycode, U32 modifiers, Window window = THIS->vt)
+ C_ARGS: THIS->xdisp, keycode, modifiers, window
+
+#endif
+
bool
rxvt_term::grab (Time eventtime, int sync = 0)
CODE:
THIS->perl.grabtime = 0;
- if (!XGrabPointer (THIS->display->display, THIS->vt, 0,
+ if (!XGrabPointer (THIS->xdisp, THIS->vt, 0,
ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask,
mode, mode, None, GRAB_CURSOR, eventtime))
- if (!XGrabKeyboard (THIS->display->display, THIS->vt, 0, mode, mode, eventtime))
+ if (!XGrabKeyboard (THIS->xdisp, THIS->vt, 0, mode, mode, eventtime))
THIS->perl.grabtime = eventtime;
else
- XUngrabPointer (THIS->display->display, eventtime);
+ XUngrabPointer (THIS->xdisp, eventtime);
RETVAL = !!THIS->perl.grabtime;
}
void
rxvt_term::allow_events_async ()
CODE:
- XAllowEvents (THIS->display->display, AsyncBoth, THIS->perl.grabtime);
+ XAllowEvents (THIS->xdisp, AsyncBoth, THIS->perl.grabtime);
void
rxvt_term::allow_events_sync ()
CODE:
- XAllowEvents (THIS->display->display, SyncBoth, THIS->perl.grabtime);
+ XAllowEvents (THIS->xdisp, SyncBoth, THIS->perl.grabtime);
void
rxvt_term::allow_events_replay ()
CODE:
- XAllowEvents (THIS->display->display, ReplayPointer, THIS->perl.grabtime);
- XAllowEvents (THIS->display->display, ReplayKeyboard, THIS->perl.grabtime);
+ XAllowEvents (THIS->xdisp, ReplayPointer, THIS->perl.grabtime);
+ XAllowEvents (THIS->xdisp, ReplayKeyboard, THIS->perl.grabtime);
void
rxvt_term::ungrab ()
PPCODE:
{
int count;
- Atom *props = XListProperties (THIS->display->display, window, &count);
+ Atom *props = XListProperties (THIS->xdisp, window, &count);
EXTEND (SP, count);
while (count--)
unsigned long bytes_after;
unsigned char *prop;
- XGetWindowProperty (THIS->display->display, window, property,
+ XGetWindowProperty (THIS->xdisp, window, property,
0, 1<<24, 0, AnyPropertyType,
&type, &format, &nitems, &bytes_after, &prop);
: format == 32 ? sizeof (long)
: 1;
- XChangeProperty (THIS->display->display, window, property,
+ XChangeProperty (THIS->xdisp, window, property,
type, format, PropModeReplace,
(unsigned char *)data_, len / elemsize);
- XSync (THIS->display->display, 0);
+ XSync (THIS->xdisp, 0);
}
Atom
XInternAtom (rxvt_term *term, char *atom_name, int only_if_exists = FALSE)
- C_ARGS: term->display->display, atom_name, only_if_exists
+ C_ARGS: term->xdisp, atom_name, only_if_exists
char *
XGetAtomName (rxvt_term *term, Atom atom)
- C_ARGS: term->display->display, atom
+ C_ARGS: term->xdisp, atom
CLEANUP:
XFree (RETVAL);
void
XDeleteProperty (rxvt_term *term, Window window, Atom property)
- C_ARGS: term->display->display, window, property
+ C_ARGS: term->xdisp, window, property
Window
rxvt_term::DefaultRootWindow ()
Window
XCreateSimpleWindow (rxvt_term *term, Window parent, int x, int y, unsigned int width, unsigned int height)
- C_ARGS: term->display->display, (Window)parent,
+ C_ARGS: term->xdisp, (Window)parent,
x, y, width, height, 0,
term->pix_colors_focused[Color_border],
term->pix_colors_focused[Color_border]
void
XReparentWindow (rxvt_term *term, Window window, Window parent, int x = 0, int y = 0)
- C_ARGS: term->display->display, window, parent, x, y
+ C_ARGS: term->xdisp, window, parent, x, y
void
XMapWindow (rxvt_term *term, Window window)
- C_ARGS: term->display->display, window
+ C_ARGS: term->xdisp, window
void
XUnmapWindow (rxvt_term *term, Window window)
- C_ARGS: term->display->display, window
+ C_ARGS: term->xdisp, window
void
XMoveResizeWindow (rxvt_term *term, Window window, int x, int y, unsigned int width, unsigned int height)
- C_ARGS: term->display->display, window, x, y, width, height
+ C_ARGS: term->xdisp, window, x, y, width, height
void
rxvt_term::XChangeInput (Window window, U32 add_events, U32 del_events = 0)
CODE:
{
XWindowAttributes attr;
- XGetWindowAttributes (THIS->display->display, window, &attr);
- XSelectInput (THIS->display->display, window, attr.your_event_mask | add_events & ~del_events);
+ XGetWindowAttributes (THIS->xdisp, window, &attr);
+ XSelectInput (THIS->xdisp, window, attr.your_event_mask | add_events & ~del_events);
}
void
int dx, dy;
Window child;
- if (XTranslateCoordinates (THIS->display->display, src, dst, x, y, &dx, &dy, &child))
+ if (XTranslateCoordinates (THIS->xdisp, src, dst, x, y, &dx, &dy, &child))
{
EXTEND (SP, 3);
PUSHs (newSViv (dx));
}
void
-rxvt_screen::set (rxvt_display *disp, int depth)
+rxvt_screen::set (rxvt_display *disp, int bitdepth)
{
set (disp);
XVisualInfo vinfo;
- if (XMatchVisualInfo (xdisp, display->screen, depth, TrueColor, &vinfo))
+ if (XMatchVisualInfo (xdisp, display->screen, bitdepth, TrueColor, &vinfo))
{
- this->depth = depth;
- this->visual = vinfo.visual;
- this->cmap = XCreateColormap (xdisp, disp->root, visual, AllocNone);
+ depth = bitdepth;
+ visual = vinfo.visual;
+ cmap = XCreateColormap (xdisp, disp->root, visual, AllocNone);
}
}
/////////////////////////////////////////////////////////////////////////////
bool
-rxvt_color::set (rxvt_screen *screen, Pixel p)
-{
-#if XFT
- XColor xc;
-
- xc.pixel = p;
- if (!XQueryColor (screen->xdisp, screen->cmap, &xc))
- return false;
-
- XRenderColor d;
-
- d.red = xc.red;
- d.green = xc.green;
- d.blue = xc.blue;
- d.alpha = 0xffff;
-
- return
- XftColorAllocValue (screen->xdisp,
- screen->visual,
- screen->cmap,
- &d, &c);
-#else
- this->p = p;
-#endif
-
- return true;
-}
-
-bool
rxvt_color::set (rxvt_screen *screen, const char *name)
{
#if XFT
- return XftColorAllocName (screen->xdisp, screen->visual, screen->cmap, name, &c);
+ int l = strlen (name);
+ rxvt_rgba r;
+ char eos;
+ int mult;
+
+ if ( l == 1+4*1 && 4 == sscanf (name, "#%1hx%1hx%1hx%1hx%c", &r.a, &r.r, &r.g, &r.b, &eos))
+ mult = 0x1111;
+ else if (l == 1+4*2 && 4 == sscanf (name, "#%2hx%2hx%2hx%2hx%c", &r.a, &r.r, &r.g, &r.b, &eos))
+ mult = 0x0101;
+ else if (l == 1+4*4 && 4 == sscanf (name, "#%4hx%4hx%4hx%4hx%c", &r.a, &r.r, &r.g, &r.b, &eos))
+ mult = 0x0001;
+ else
+ return XftColorAllocName (screen->xdisp, screen->visual, screen->cmap, name, &c);
+
+ r.r *= mult; r.g *= mult; r.b *= mult; r.a *= mult;
+ return set (screen, r);
#else
XColor xc;
if (XParseColor (screen->xdisp, screen->cmap, name, &xc))
- return set (screen, xc.red, xc.green, xc.blue);
+ return set (screen, rxvt_rgba (xc.red, xc.green, xc.blue));
return false;
#endif
}
bool
-rxvt_color::set (rxvt_screen *screen, unsigned short cr, unsigned short cg, unsigned short cb)
+rxvt_color::set (rxvt_screen *screen, rxvt_rgba rgba)
{
+#if XFT
+ XRenderColor d;
+
+ d.red = rgba.r;
+ d.green = rgba.g;
+ d.blue = rgba.b;
+ d.alpha = rgba.a;
+
+ return XftColorAllocValue (screen->xdisp, screen->visual, screen->cmap, &d, &c);
+#else
XColor xc;
- xc.red = cr;
- xc.green = cg;
- xc.blue = cb;
+ xc.red = rgba.r;
+ xc.green = rgba.g;
+ xc.blue = rgba.b;
xc.flags = DoRed | DoGreen | DoBlue;
if (XAllocColor (screen->xdisp, screen->cmap, &xc))
- return set (screen, xc.pixel);
+ {
+ p = xc.pixel;
+ return true;
+ }
return false;
+#endif
}
void
-rxvt_color::get (rxvt_screen *screen, unsigned short &cr, unsigned short &cg, unsigned short &cb)
+rxvt_color::get (rxvt_screen *screen, rxvt_rgba &rgba)
{
#if XFT
- cr = c.color.red;
- cg = c.color.green;
- cb = c.color.blue;
+ rgba.r = c.color.red;
+ rgba.g = c.color.green;
+ rgba.b = c.color.blue;
+ rgba.a = c.color.alpha;
#else
XColor c;
c.pixel = p;
XQueryColor (screen->xdisp, screen->cmap, &c);
- cr = c.red;
- cg = c.green;
- cb = c.blue;
+ rgba.r = c.red;
+ rgba.g = c.green;
+ rgba.b = c.blue;
+ rgba.a = rxvt_rgba::MAX_CC;
#endif
}
rxvt_color
rxvt_color::fade (rxvt_screen *screen, int percent)
{
- percent = 100 - percent;
-
- unsigned short cr, cg, cb;
rxvt_color faded;
- get (screen, cr, cg, cb);
+ rxvt_rgba c;
+ get (screen, c);
- faded.set (
- screen,
- cr * percent / 100,
- cg * percent / 100,
- cb * percent / 100
- );
+ c.r = lerp (0, c.r, percent);
+ c.g = lerp (0, c.g, percent);
+ c.b = lerp (0, c.b, percent);
+
+ faded.set (screen, c);
return faded;
}
-#define LERP(a,b,p) (a * p + b * (100 - p)) / 100
-
rxvt_color
rxvt_color::fade (rxvt_screen *screen, int percent, rxvt_color &fadeto)
{
- percent = 100 - percent;
-
- unsigned short cr, cg, cb;
- unsigned short fcr, fcg, fcb;
+ rxvt_rgba c, fc;
rxvt_color faded;
- get (screen, cr, cg, cb);
- fadeto.get (screen, fcr, fcg, fcb);
+ get (screen, c);
+ fadeto.get (screen, fc);
faded.set (
screen,
- LERP (cr, fcr, percent),
- LERP (cg, fcg, percent),
- LERP (cb, fcb, percent)
+ rxvt_rgba (
+ lerp (fc.r, c.r, percent),
+ lerp (fc.g, c.g, percent),
+ lerp (fc.b, c.b, percent),
+ lerp (fc.a, c.a, percent)
+ )
);
return faded;
Colormap cmap;
void set (rxvt_display *disp);
- void set (rxvt_display *disp, int depth);
+ void set (rxvt_display *disp, int bitdepth);
void clear ();
};
typedef unsigned long Pixel;
+struct rxvt_rgba {
+ unsigned short r, g, b, a;
+
+ enum { MIN_CC = 0x0000, MAX_CC = 0xffff };
+
+ rxvt_rgba ()
+ { }
+
+ rxvt_rgba (unsigned short r, unsigned short g, unsigned short b, unsigned short a = MAX_CC)
+ : r(r), g(g), b(b), a(a)
+ { }
+};
+
struct rxvt_color {
#if XFT
XftColor c;
bool operator == (const rxvt_color &b) const { return Pixel (*this) == Pixel (b); }
bool operator != (const rxvt_color &b) const { return Pixel (*this) != Pixel (b); }
- void get (rxvt_screen *screen, unsigned short &cr, unsigned short &cg, unsigned short &cb);
+ void get (rxvt_screen *screen, rxvt_rgba &rgba);
- bool set (rxvt_screen *screen, Pixel p);
bool set (rxvt_screen *screen, const char *name);
- bool set (rxvt_screen *screen, unsigned short cr, unsigned short cg, unsigned short cb);
+ bool set (rxvt_screen *screen, rxvt_rgba rgba);
rxvt_color fade (rxvt_screen *screen, int percent); // fades to black
rxvt_color fade (rxvt_screen *screen, int percent, rxvt_color &fadeto);
template<typename T, typename U> static inline void swap (T& a, U& b) { T t=a; a=(T)b; b=(U)t; }
+// linear interpolation
+template<typename T, typename U, typename P>
+static inline
+T lerp (T a, U b, P p)
+{
+ return (int(a) * int(p) + int(b) * int(100 - p)) / 100;
+}
+
// in range including end
#define IN_RANGE_INC(val,beg,end) \
((unsigned int)(val) - (unsigned int)(beg) <= (unsigned int)(end) - (unsigned int)(beg))
Messages have a size limit of 1023 bytes currently.
+=item @terms = urxvt::termlist
+
+Returns all urxvt::term objects that exist in this process, regardless of
+wether they are started, being destroyed etc., so be careful. Only term
+objects that have perl extensions attached will be returned (because there
+is no urxvt::term objet associated with others).
+
=item $time = urxvt::NOW
Returns the "current time" (as per the event loop).
Croaks (and probably outputs an error message) if the new instance
couldn't be created. Returns C<undef> if the new instance didn't
initialise perl, and the terminal object otherwise. The C<init> and
-C<start> hooks will be called during this call.
+C<start> hooks will be called before this call returns, and are free to
+refer to global data (which is race free).
=cut
sub new {
my ($class, $env, @args) = @_;
- _new ([ map "$_=$env->{$_}", keys %$env ], @args);
+ $env or Carp::croak "environment hash missing in call to urxvt::term->new";
+ @args or Carp::croak "name argument missing in call to urxvt::term->new";
+
+ _new ([ map "$_=$env->{$_}", keys %$env ], \@args);
}
=item $term->destroy
Converts rxvt-unicodes text reprsentation into a perl string. See
C<< $term->ROW_t >> for details.
-=item $success = $term->grab_button ($button, $modifiermask)
+=item $success = $term->grab_button ($button, $modifiermask[, $window = $term->vt])
+
+=item $term->ungrab_button ($button, $modifiermask[, $window = $term->vt])
-Registers a synchronous button grab. See the XGrabButton manpage.
+Register/unregister a synchronous button grab. See the XGrabButton
+manpage.
=item $success = $term->grab ($eventtime[, $sync])