0
votes

I'm working on a library that creates transparent X windows and uses cairo to draw on them. There is an event loop implemented in the main thread, while the drawing operations take place in a separate thread within a loop. The latter looks like this

  while (self->_running) {
    PyGILState_Release(gstate);
    usleep(1000); // Sleep 1 ms
    gstate = PyGILState_Ensure();

    if (self->_expiry <= gettime()) {
      draw(self, args_tuple); // All the cairo code is in here
      self->_expiry += interval;
      interval = self->interval;
    }
  }

The event loop calls XNextEvent periodically to trap key/button presses only. The window is mapped before the new UI thread is started from the main thread.

When the interval between iterations on the UI thread (the self->inteval value above) is large (order of seconds), the window stays transparent on the first iteration of the loop, and it only gets painted on from the second iteration onward. Calling draw right before the while loop doesn't help, unless there is a pause of some milliseconds in between calls to draw. For example, if I put interval = 25 right before the while loop, then the second call to draw paints on the window in most of the executions of the application implementing this code.

Things that I have tried:

  • cairo_surface_flush and XFlush right after draw don't seem to work
  • Sending an Expose event doesn't seem to help either.

How can I make sure that my loop starts painting on the window from the first iteration?

2
(1) This is a bad question that cannot really be answered. There is a bug somewhere in your code, but since you do not produce some self-contained reproducing example, there is not much that one can do. (2) What happens if you puts("I live"); right before/after draw and watch the output? Is draw actually called when you think it is called?Uli Schlachter
(3) What do you do when you get an expose event? Are you just ignoring expose events in the main thread?Uli Schlachter
the draw operation gets called because I get the debug statement in output. As I explain in the answer, the problem was that I wasn't using the ExposureMask flag in XSelectInput.Phoenix87

2 Answers

1
votes

What I'm missing is the ExposureMask flag in the call to XSelectInput. With this flag set, one then has to look for Expose events in the event loop with the following pattern:

  switch (e.type) {
  case Expose:
    if (e.xexpose.count == 0) {
      BaseCanvas__redraw(canvas);
    }
    return;
  }

The redraw operation doesn't need to be the full set of draw operations. Having modified the cairo context, it is enough to repaint it on the destination with no more than this

void
BaseCanvas__redraw(BaseCanvas * self) {
  cairo_save(self->context);
  cairo_set_operator(self->context, CAIRO_OPERATOR_SOURCE);
  cairo_paint(self->context);
  cairo_restore(self->context);
}
-1
votes

To me it looks like you are mapping the window and then immediately starting a thread that tries to draw to the window. Thus, if you try to draw before the window manager actually made the window visible, your drawing goes nowhere. If the window manager wins the race and the window actually becomes visible before your draw, the drawing actually works.

Your own answer is not an answer to your question. The question is "why does it stay transparent in the first iteration?" while your answer is (basically) "Don't use threads, just do all drawing in the main loop". (Of course, handling Expose events is the right thing to do, but that's not what the question asked.)