2 Copyright (C) 2003-2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 #include <gdkmm/rectangle.h>
31 #include <gtkmm2ext/fastmeter.h>
32 #include <gtkmm2ext/utils.h>
34 #define UINT_TO_RGB(u,r,g,b) { (*(r)) = ((u)>>16)&0xff; (*(g)) = ((u)>>8)&0xff; (*(b)) = (u)&0xff; }
35 #define UINT_TO_RGBA(u,r,g,b,a) { UINT_TO_RGB(((u)>>8),r,g,b); (*(a)) = (u)&0xff; }
39 using namespace Gtkmm2ext;
42 int FastMeter::min_pattern_metric_size = 16;
43 int FastMeter::max_pattern_metric_size = 1024;
44 bool FastMeter::no_rgba_overlay = false;
46 FastMeter::Pattern10Map FastMeter::vm_pattern_cache;
47 FastMeter::PatternBgMap FastMeter::vb_pattern_cache;
49 FastMeter::Pattern10Map FastMeter::hm_pattern_cache;
50 FastMeter::PatternBgMap FastMeter::hb_pattern_cache;
52 FastMeter::FastMeter (long hold, unsigned long dimen, Orientation o, int len,
53 int clr0, int clr1, int clr2, int clr3,
54 int clr4, int clr5, int clr6, int clr7,
58 float stp0, float stp1,
59 float stp2, float stp3,
64 , _styleflags(styleflags)
73 last_peak_rect.width = 0;
74 last_peak_rect.height = 0;
78 no_rgba_overlay = ! Glib::getenv("NO_METER_SHADE").empty();
102 set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
110 if (orientation == Vertical) {
113 fgpattern = request_vertical_meter(pixwidth + 2, pixheight + 2, _clr, _stp, _styleflags);
114 bgpattern = request_vertical_background (pixwidth + 2, pixheight + 2, _bgc, false);
119 fgpattern = request_horizontal_meter(pixwidth + 2, pixheight + 2, _clr, _stp, _styleflags);
120 bgpattern = request_horizontal_background (pixwidth + 2, pixheight + 2, _bgc, false);
123 pixrect.width = pixwidth;
124 pixrect.height = pixheight;
126 request_width = pixrect.width + 2;
127 request_height= pixrect.height + 2;
132 FastMeter::~FastMeter ()
136 Cairo::RefPtr<Cairo::Pattern>
137 FastMeter::generate_meter_pattern (
138 int width, int height, int *clr, float *stp, int styleflags, bool horiz)
142 const double soft = 3.0 / (double) height;
143 const double offs = -1.0 / (double) height;
145 cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
148 Cairo coordinate space goes downwards as y value goes up, so invert
149 knee-based positions by using (1.0 - y)
152 UINT_TO_RGBA (clr[9], &r, &g, &b, &a); // top/clip
153 cairo_pattern_add_color_stop_rgb (pat, 0.0,
154 r/255.0, g/255.0, b/255.0);
156 knee = offs + stp[3] / 115.0f; // -0dB
158 UINT_TO_RGBA (clr[8], &r, &g, &b, &a);
159 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
160 r/255.0, g/255.0, b/255.0);
162 UINT_TO_RGBA (clr[7], &r, &g, &b, &a);
163 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
164 r/255.0, g/255.0, b/255.0);
166 knee = offs + stp[2]/ 115.0f; // -3dB || -2dB
168 UINT_TO_RGBA (clr[6], &r, &g, &b, &a);
169 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
170 r/255.0, g/255.0, b/255.0);
172 UINT_TO_RGBA (clr[5], &r, &g, &b, &a);
173 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
174 r/255.0, g/255.0, b/255.0);
176 knee = offs + stp[1] / 115.0f; // -9dB
178 UINT_TO_RGBA (clr[4], &r, &g, &b, &a);
179 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
180 r/255.0, g/255.0, b/255.0);
182 UINT_TO_RGBA (clr[3], &r, &g, &b, &a);
183 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
184 r/255.0, g/255.0, b/255.0);
186 knee = offs + stp[0] / 115.0f; // -18dB
188 UINT_TO_RGBA (clr[2], &r, &g, &b, &a);
189 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee,
190 r/255.0, g/255.0, b/255.0);
192 UINT_TO_RGBA (clr[1], &r, &g, &b, &a);
193 cairo_pattern_add_color_stop_rgb (pat, 1.0 - knee + soft,
194 r/255.0, g/255.0, b/255.0);
196 UINT_TO_RGBA (clr[0], &r, &g, &b, &a); // bottom
197 cairo_pattern_add_color_stop_rgb (pat, 1.0,
198 r/255.0, g/255.0, b/255.0);
200 if ((styleflags & 1) && !no_rgba_overlay) {
201 cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
202 cairo_pattern_add_color_stop_rgba (shade_pattern, 0, 0.0, 0.0, 0.0, 0.15);
203 cairo_pattern_add_color_stop_rgba (shade_pattern, 0.4, 1.0, 1.0, 1.0, 0.05);
204 cairo_pattern_add_color_stop_rgba (shade_pattern, 1, 0.0, 0.0, 0.0, 0.25);
206 cairo_surface_t* surface;
208 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
209 tc = cairo_create (surface);
210 cairo_set_source (tc, pat);
211 cairo_rectangle (tc, 0, 0, width, height);
213 cairo_pattern_destroy (pat);
215 cairo_set_source (tc, shade_pattern);
216 cairo_rectangle (tc, 0, 0, width, height);
218 cairo_pattern_destroy (shade_pattern);
220 if (styleflags & 2) { // LED stripes
222 cairo_set_line_width(tc, 1.0);
223 cairo_set_source_rgba(tc, .0, .0, .0, 0.4);
224 //cairo_set_operator (tc, CAIRO_OPERATOR_SOURCE);
225 for (float y=0.5; y < height; y+= 2.0) {
226 cairo_move_to(tc, 0, y);
227 cairo_line_to(tc, width, y);
233 pat = cairo_pattern_create_for_surface (surface);
235 cairo_surface_destroy (surface);
239 cairo_surface_t* surface;
241 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width);
242 tc = cairo_create (surface);
245 cairo_matrix_init_rotate (&m, -M_PI/2.0);
246 cairo_matrix_translate (&m, -height, 0);
247 cairo_pattern_set_matrix (pat, &m);
248 cairo_set_source (tc, pat);
249 cairo_rectangle (tc, 0, 0, height, width);
251 cairo_pattern_destroy (pat);
252 pat = cairo_pattern_create_for_surface (surface);
254 cairo_surface_destroy (surface);
256 Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
262 Cairo::RefPtr<Cairo::Pattern>
263 FastMeter::generate_meter_background (
264 int width, int height, int *clr, bool shade, bool horiz)
266 guint8 r0,g0,b0,r1,g1,b1,a;
268 cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, height);
270 UINT_TO_RGBA (clr[0], &r0, &g0, &b0, &a);
271 UINT_TO_RGBA (clr[1], &r1, &g1, &b1, &a);
273 cairo_pattern_add_color_stop_rgb (pat, 0.0,
274 r1/255.0, g1/255.0, b1/255.0);
276 cairo_pattern_add_color_stop_rgb (pat, 1.0,
277 r0/255.0, g0/255.0, b0/255.0);
279 if (shade && !no_rgba_overlay) {
280 cairo_pattern_t* shade_pattern = cairo_pattern_create_linear (0.0, 0.0, width, 0.0);
281 cairo_pattern_add_color_stop_rgba (shade_pattern, 0.0, 1.0, 1.0, 1.0, 0.15);
282 cairo_pattern_add_color_stop_rgba (shade_pattern, 0.6, 0.0, 0.0, 0.0, 0.10);
283 cairo_pattern_add_color_stop_rgba (shade_pattern, 1.0, 1.0, 1.0, 1.0, 0.20);
285 cairo_surface_t* surface;
287 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
288 tc = cairo_create (surface);
289 cairo_set_source (tc, pat);
290 cairo_rectangle (tc, 0, 0, width, height);
292 cairo_set_source (tc, shade_pattern);
293 cairo_rectangle (tc, 0, 0, width, height);
296 cairo_pattern_destroy (pat);
297 cairo_pattern_destroy (shade_pattern);
299 pat = cairo_pattern_create_for_surface (surface);
302 cairo_surface_destroy (surface);
306 cairo_surface_t* surface;
308 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, height, width);
309 tc = cairo_create (surface);
312 cairo_matrix_init_rotate (&m, -M_PI/2.0);
313 cairo_matrix_translate (&m, -height, 0);
314 cairo_pattern_set_matrix (pat, &m);
315 cairo_set_source (tc, pat);
316 cairo_rectangle (tc, 0, 0, height, width);
318 cairo_pattern_destroy (pat);
319 pat = cairo_pattern_create_for_surface (surface);
321 cairo_surface_destroy (surface);
324 Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
329 Cairo::RefPtr<Cairo::Pattern>
330 FastMeter::request_vertical_meter(
331 int width, int height, int *clr, float *stp, int styleflags)
333 height = max(height, min_pattern_metric_size);
334 height = min(height, max_pattern_metric_size);
336 const Pattern10MapKey key (width, height,
337 stp[0], stp[1], stp[2], stp[3],
338 clr[0], clr[1], clr[2], clr[3],
339 clr[4], clr[5], clr[6], clr[7],
340 clr[8], clr[9], styleflags);
342 Pattern10Map::iterator i;
343 if ((i = vm_pattern_cache.find (key)) != vm_pattern_cache.end()) {
346 // TODO flush pattern cache if it gets too large
348 Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
349 width, height, clr, stp, styleflags, false);
350 vm_pattern_cache[key] = p;
355 Cairo::RefPtr<Cairo::Pattern>
356 FastMeter::request_vertical_background(
357 int width, int height, int *bgc, bool shade)
359 height = max(height, min_pattern_metric_size);
360 height = min(height, max_pattern_metric_size);
363 const PatternBgMapKey key (width, height, bgc[0], bgc[1], shade);
364 PatternBgMap::iterator i;
365 if ((i = vb_pattern_cache.find (key)) != vb_pattern_cache.end()) {
368 // TODO flush pattern cache if it gets too large
370 Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (
371 width, height, bgc, shade, false);
372 vb_pattern_cache[key] = p;
377 Cairo::RefPtr<Cairo::Pattern>
378 FastMeter::request_horizontal_meter(
379 int width, int height, int *clr, float *stp, int styleflags)
381 width = max(width, min_pattern_metric_size);
382 width = min(width, max_pattern_metric_size);
384 const Pattern10MapKey key (width, height,
385 stp[0], stp[1], stp[2], stp[3],
386 clr[0], clr[1], clr[2], clr[3],
387 clr[4], clr[5], clr[6], clr[7],
388 clr[8], clr[9], styleflags);
390 Pattern10Map::iterator i;
391 if ((i = hm_pattern_cache.find (key)) != hm_pattern_cache.end()) {
394 // TODO flush pattern cache if it gets too large
396 Cairo::RefPtr<Cairo::Pattern> p = generate_meter_pattern (
397 height, width, clr, stp, styleflags, true);
399 hm_pattern_cache[key] = p;
403 Cairo::RefPtr<Cairo::Pattern>
404 FastMeter::request_horizontal_background(
405 int width, int height, int *bgc, bool shade)
407 width = max(width, min_pattern_metric_size);
408 width = min(width, max_pattern_metric_size);
411 const PatternBgMapKey key (width, height, bgc[0], bgc[1], shade);
412 PatternBgMap::iterator i;
413 if ((i = hb_pattern_cache.find (key)) != hb_pattern_cache.end()) {
416 // TODO flush pattern cache if it gets too large
418 Cairo::RefPtr<Cairo::Pattern> p = generate_meter_background (
419 height, width, bgc, shade, true);
421 hb_pattern_cache[key] = p;
429 FastMeter::set_hold_count (long val)
443 FastMeter::on_size_request (GtkRequisition* req)
445 if (orientation == Vertical) {
446 vertical_size_request (req);
448 horizontal_size_request (req);
453 FastMeter::vertical_size_request (GtkRequisition* req)
455 req->height = request_height;
456 req->height = max(req->height, min_pattern_metric_size);
457 req->height = min(req->height, max_pattern_metric_size);
460 req->width = request_width;
464 FastMeter::horizontal_size_request (GtkRequisition* req)
466 req->width = request_width;
467 req->width = max(req->width, min_pattern_metric_size);
468 req->width = min(req->width, max_pattern_metric_size);
471 req->height = request_height;
475 FastMeter::on_size_allocate (Gtk::Allocation &alloc)
477 if (orientation == Vertical) {
478 vertical_size_allocate (alloc);
480 horizontal_size_allocate (alloc);
486 FastMeter::vertical_size_allocate (Gtk::Allocation &alloc)
488 if (alloc.get_width() != request_width) {
489 alloc.set_width (request_width);
492 int h = alloc.get_height();
493 h = max (h, min_pattern_metric_size + 2);
494 h = min (h, max_pattern_metric_size + 2);
496 if (h != alloc.get_height()) {
497 alloc.set_height (h);
500 if (pixheight != h) {
501 fgpattern = request_vertical_meter (request_width, h, _clr, _stp, _styleflags);
502 bgpattern = request_vertical_background (request_width, h, highlight ? _bgh : _bgc, highlight);
504 pixwidth = request_width - 2;
507 CairoWidget::on_size_allocate (alloc);
511 FastMeter::horizontal_size_allocate (Gtk::Allocation &alloc)
513 if (alloc.get_height() != request_height) {
514 alloc.set_height (request_height);
517 int w = alloc.get_width();
518 w = max (w, min_pattern_metric_size + 2);
519 w = min (w, max_pattern_metric_size + 2);
521 if (w != alloc.get_width()) {
526 fgpattern = request_horizontal_meter (w, request_height, _clr, _stp, _styleflags);
527 bgpattern = request_horizontal_background (w, request_height, highlight ? _bgh : _bgc, highlight);
529 pixheight = request_height - 2;
532 CairoWidget::on_size_allocate (alloc);
536 FastMeter::render (cairo_t* cr, cairo_rectangle_t* area)
538 if (orientation == Vertical) {
539 return vertical_expose (cr, area);
541 return horizontal_expose (cr, area);
546 FastMeter::vertical_expose (cairo_t* cr, cairo_rectangle_t* area)
549 GdkRectangle intersection;
550 GdkRectangle background;
551 GdkRectangle eventarea;
553 cairo_set_source_rgb (cr, 0, 0, 0); // black
554 rounded_rectangle (cr, 0, 0, pixwidth + 2, pixheight + 2, 2);
557 top_of_meter = (gint) floor (pixheight * current_level);
559 /* reset the height & origin of the rect that needs to show the pixbuf
562 pixrect.height = top_of_meter;
563 pixrect.y = 1 + pixheight - top_of_meter;
567 background.width = pixrect.width;
568 background.height = pixheight - top_of_meter;
570 eventarea.x = area->x;
571 eventarea.y = area->y;
572 eventarea.width = area->width;
573 eventarea.height = area->height;
575 if (gdk_rectangle_intersect (&background, &eventarea, &intersection)) {
576 cairo_set_source (cr, bgpattern->cobj());
577 cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
581 if (gdk_rectangle_intersect (&pixrect, &eventarea, &intersection)) {
582 // draw the part of the meter image that we need. the area we draw is bounded "in reverse" (top->bottom)
583 cairo_set_source (cr, fgpattern->cobj());
584 cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
591 last_peak_rect.x = 1;
592 last_peak_rect.width = pixwidth;
593 last_peak_rect.y = max(1, 1 + pixheight - (gint) floor (pixheight * current_peak));
594 if (bright_hold || (_styleflags & 2)) {
595 last_peak_rect.height = max(0, min(3, pixheight - last_peak_rect.y - 1 ));
597 last_peak_rect.height = max(0, min(2, pixheight - last_peak_rect.y - 1 ));
600 cairo_set_source (cr, fgpattern->cobj());
601 cairo_rectangle (cr, last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height);
603 if (bright_hold && !no_rgba_overlay) {
604 cairo_fill_preserve (cr);
605 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.3);
610 last_peak_rect.width = 0;
611 last_peak_rect.height = 0;
616 FastMeter::horizontal_expose (cairo_t* cr, cairo_rectangle_t* area)
619 GdkRectangle intersection;
620 GdkRectangle background;
621 GdkRectangle eventarea;
623 cairo_set_source_rgb (cr, 0, 0, 0); // black
624 rounded_rectangle (cr, 0, 0, pixwidth + 2, pixheight + 2, 2);
627 right_of_meter = (gint) floor (pixwidth * current_level);
629 /* reset the height & origin of the rect that needs to show the pixbuf
632 pixrect.width = right_of_meter;
634 background.x = 1 + right_of_meter;
636 background.width = pixwidth - right_of_meter;
637 background.height = pixheight;
639 eventarea.x = area->x;
640 eventarea.y = area->y;
641 eventarea.width = area->width;
642 eventarea.height = area->height;
644 if (gdk_rectangle_intersect (&background, &eventarea, &intersection)) {
645 cairo_set_source (cr, bgpattern->cobj());
646 cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
650 if (gdk_rectangle_intersect (&pixrect, &eventarea, &intersection)) {
651 cairo_set_source (cr, fgpattern->cobj());
652 cairo_rectangle (cr, intersection.x, intersection.y, intersection.width, intersection.height);
659 last_peak_rect.y = 1;
660 last_peak_rect.height = pixheight;
661 const int xpos = floor (pixwidth * current_peak);
662 if (bright_hold || (_styleflags & 2)) {
663 last_peak_rect.width = min(3, xpos );
665 last_peak_rect.width = min(2, xpos );
667 last_peak_rect.x = 1 + max(0, xpos - last_peak_rect.width);
669 cairo_set_source (cr, fgpattern->cobj());
670 cairo_rectangle (cr, last_peak_rect.x, last_peak_rect.y, last_peak_rect.width, last_peak_rect.height);
672 if (bright_hold && !no_rgba_overlay) {
673 cairo_fill_preserve (cr);
674 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.3);
679 last_peak_rect.width = 0;
680 last_peak_rect.height = 0;
685 FastMeter::set (float lvl, float peak)
687 float old_level = current_level;
688 float old_peak = current_peak;
690 if (pixwidth <= 0 || pixheight <=0) return;
693 if (lvl >= current_peak) {
695 hold_state = hold_cnt;
698 if (hold_state > 0) {
699 if (--hold_state == 0) {
712 if (current_level == old_level && current_peak == old_peak && (hold_state == 0 || peak != -1)) {
716 Glib::RefPtr<Gdk::Window> win;
718 if ((win = get_window()) == 0) {
723 if (orientation == Vertical) {
724 queue_vertical_redraw (win, old_level);
726 queue_horizontal_redraw (win, old_level);
731 FastMeter::queue_vertical_redraw (const Glib::RefPtr<Gdk::Window>& win, float old_level)
735 gint new_top = (gint) floor (pixheight * current_level);
738 rect.width = pixwidth;
739 rect.height = new_top;
740 rect.y = 1 + pixheight - new_top;
742 if (current_level > old_level) {
743 /* colored/pixbuf got larger, just draw the new section */
744 /* rect.y stays where it is because of X coordinates */
745 /* height of invalidated area is between new.y (smaller) and old.y
747 X coordinates just make my brain hurt.
749 rect.height = pixrect.y - rect.y;
751 /* it got smaller, compute the difference */
752 /* rect.y becomes old.y (the smaller value) */
754 /* rect.height is the old.y (smaller) minus the new.y (larger)
756 rect.height = pixrect.height - rect.height;
759 GdkRegion* region = 0;
762 if (rect.height != 0) {
764 /* ok, first region to draw ... */
766 region = gdk_region_rectangle (&rect);
770 /* redraw the last place where the last peak hold bar was;
771 the next expose will draw the new one whether its part of
772 expose region or not.
775 if (last_peak_rect.width * last_peak_rect.height != 0) {
777 region = gdk_region_new ();
780 gdk_region_union_with_rect (region, &last_peak_rect);
783 if (hold_state && current_peak > 0) {
785 region = gdk_region_new ();
789 rect.y = max(1, 1 + pixheight - (gint) floor (pixheight * current_peak));
790 if (bright_hold || (_styleflags & 2)) {
791 rect.height = max(0, min(3, pixheight - last_peak_rect.y -1 ));
793 rect.height = max(0, min(2, pixheight - last_peak_rect.y -1 ));
795 rect.width = pixwidth;
796 gdk_region_union_with_rect (region, &rect);
800 gdk_window_invalidate_region (win->gobj(), region, true);
803 gdk_region_destroy(region);
809 FastMeter::queue_horizontal_redraw (const Glib::RefPtr<Gdk::Window>& win, float old_level)
813 gint new_right = (gint) floor (pixwidth * current_level);
815 rect.height = pixheight;
818 if (current_level > old_level) {
819 rect.x = 1 + pixrect.width;
820 /* colored/pixbuf got larger, just draw the new section */
821 rect.width = new_right - pixrect.width;
823 /* it got smaller, compute the difference */
824 rect.x = 1 + new_right;
825 /* rect.height is the old.x (smaller) minus the new.x (larger) */
826 rect.width = pixrect.width - new_right;
829 GdkRegion* region = 0;
832 if (rect.height != 0) {
834 /* ok, first region to draw ... */
836 region = gdk_region_rectangle (&rect);
840 /* redraw the last place where the last peak hold bar was;
841 the next expose will draw the new one whether its part of
842 expose region or not.
845 if (last_peak_rect.width * last_peak_rect.height != 0) {
847 region = gdk_region_new ();
850 gdk_region_union_with_rect (region, &last_peak_rect);
853 if (hold_state && current_peak > 0) {
855 region = gdk_region_new ();
859 rect.height = pixheight;
860 const int xpos = floor (pixwidth * current_peak);
861 if (bright_hold || (_styleflags & 2)) {
862 rect.width = min(3, xpos);
864 rect.width = min(2, xpos);
866 rect.x = 1 + max(0, xpos - rect.width);
867 gdk_region_union_with_rect (region, &rect);
871 gdk_window_invalidate_region (win->gobj(), region, true);
874 gdk_region_destroy(region);
880 FastMeter::set_highlight (bool onoff)
882 if (highlight == onoff) {
886 if (orientation == Vertical) {
887 bgpattern = request_vertical_background (pixwidth + 2, pixheight + 2, highlight ? _bgh : _bgc, highlight);
889 bgpattern = request_horizontal_background (pixwidth + 2, pixheight + 2, highlight ? _bgh : _bgc, highlight);