[cairo] Error: Target surface has been finished

Jonathon Jongsma jonathon at quotidian.org
Sat Jan 24 19:28:16 PST 2009


On 01/24/2009 09:47 PM, Evan White wrote:
> This is my first post to a mailing list ever.  I am not a heavy
> programmer I have only had two C++ courses in college.  I am in my
> senior year working on my engineering design project where my group is
> building a replacement for your vehicle's instrument cluster.
>
> I have been able to draw to a window three gauges, but when I try to
> simulate the interface receiving data from our hardware with a text file
> I get the following error:
>
> --------
> glibmm-ERROR **:
> unhandled exception (type std::exception) in signal handler:
> what: the target surface has been finished
>
> aborting...
> Aborted
> --------
>
> Using a couple of cout statements I was able to figure out that I get
> this error when I am calling the stroke() function when I am recalling
> my drawing functions through my signal_io callback.  Does anyone know
> how I could fix this problem and what does this error means.
>
> Here is some of my code I think that might help.
>
> bool Default::on_expose_event(GdkEventExpose* event) {
>     Glib::RefPtr<Gdk::Window>  window = get_window();
>     if(window) {
>         Gtk::Allocation allocation = get_allocation();
>         totalDrawableWidth = allocation.get_width();
>         totalDrawableHeight = allocation.get_height();
>               cr = window->create_cairo_context();
> cr->rectangle(event->area.x, event->area.y, event->area.width,
> event->area.height);
>         cr->clip();
>               // Draw initial gauges
>         drawGauge(gaugePadding*totalDrawableWidth);  // Speed Gauge
>         drawGauge((gaugeWidth + gaugePadding)*totalDrawableWidth);  //
> RPM Gauge
>               drawNeedle(0, gaugePadding*totalDrawableWidth);  // Speed
> Needle
>         drawNeedle(0, (gaugeWidth + gaugePadding)*totalDrawableWidth);
> // RPM Needle
>               drawFuelGauge(0);
>               // start animating the screen
>         file_read = Glib::IOChannel::create_from_file("test.txt","r");
>         Glib::signal_io().connect(sigc::mem_fun(*this,&Default::Update),
> file_read, Glib::IO_IN | Glib::IO_HUP);
>     }
> }
>
> bool Default::Update(Glib::IOCondition io_condition) {
>       if ((io_condition&  Glib::IO_IN) == 0) {
>         cerr<<  "Invalid fifo response"<<  std::endl;
>     }
>     else {
>         Glib::ustring buf;
>         #ifdef GLIBMM_EXCEPTIONS_ENABLED
>         file_read->read_line(buf);
>         #else
>         auto_ptr<Glib::Error>  ex;
>         file_read->read_line(buf, ex);
>         if(ex.get()) {
>             cout<<  "ouch\n";
>             cerr<<  "Error: "<<  ex->what()<<  endl;
>         }
>         #endif //GLIBMM_EXCEPTIONS_ENABLED
>
>         string temp = buf.raw();
>         string tempVal = temp.substr(2, temp.length()-1);
>         temp.erase(1, temp.length()-1);
>         int SensID = atoi(temp.c_str());
>         double value = atof(tempVal.c_str());
>         switch(SensID) {
>             case 0:
>                     cout<<  "speed baby\n";
>                 processSpeed(value);
>                 break;
>             case 1:
>                 processRPM(value);
>                 break;
>             case 2:
>                 processFuel(value);
>                 break;
>             default:
>                 break;
>         }      }
>     return true;
> }
>
> void Default::drawGauge(double Offset) {
>     cout<<  "In gauge\n";
>     cr->set_source_rgb(0,0,0);
>     cr->set_line_width(5);
>     cr->set_line_join(Cairo::LINE_JOIN_ROUND);
>       //draw gauge outlines
>     cr->save();
>     cout<<  "drawing gauge\n";
>     cr->arc(gaugeRadius * totalDrawableWidth + Offset + 1,
> totalDrawableHeight - (gaugePadding * totalDrawableHeight), gaugeRadius
> * totalDrawableWidth, M_PI, 0);
>     cout<<  "stroking arc\n";
>     cr->stroke();    //<- PROBLEM OCCURRS WHEN I GET TO THIS LINE
>     cout<<  "done\n";
>     cr->restore();
> }

Well, I don't see any direct calls to drawGuage in your signal_io callback, so I 
assume that it is called indirectly (via, say, processSpeed, etc).  And I can't 
tell for sure from the code you posted, but it seems that cr is probably a class 
member.  So in your expose handler, it appears that you're creating a cairo 
context (cr) and caching that in the Default class to be used later by your 
signal handler.   There are a couple of problems with this:
1) the cairo context (cr) created in your expose handler is only a short-lived 
object and is not meant to be kept around for later drawing.  In fact, it is 
likely targetting a temporary surface that will no longer exist by the time you 
want to use it next (see [1] for a bit more information)
2) As a general rule of thumb, *never* draw anything to the screen outside of an 
expose event.  If you do, the next time that window gets an expose event, the 
stuff you drew outside of the expose event will get overwritten (See question 
6.2 here [2] for a bit more information).  Instead, you generally want to simply 
invalidate the widget (which will cause a new expose event to be created for 
that widget) and then draw everything within the expose event.

Hope that helps.  good luck
jonner

[1] http://lists.cairographics.org/archives/cairo/2006-November/008412.html
[2] http://library.gnome.org/devel/gtk/stable/gtk-question-index.html#id3104637


More information about the cairo mailing list