Book HomeMastering Perl/TkSearch this book

15.9. Coexisting with Other GUI Main Loops

It's perfectly possible to have more than one GUI main loop running concurrently. It's a simple matter of cooperation and balance. By balance, we mean how the events are portioned out. It's very easy for one main loop to "take control" and "starve" the other loop of processing time. In this section, we'll demonstrate how to use both OpenGL and Tk widgets in the same application. We've found that, generally, to keep Tk events flowing, it's sufficient to call update once in a while. If update starves OpenGL, we fall back to DoOneEvent.

DoOneEvent allows us to fine tune a Tk event loop by processing only selected events, which we specify by bit pattern. We can inclusively OR the following symbols together and define the desired bit pattern: WINDOW_EVENTS, FILE_EVENTS, TIMER_EVENTS, and IDLE_EVENTS. To specify all possible events, use ALL_EVENTS, and to make the DoOneEvent call nonblocking, add DONT_WAIT.

When passed ALL_EVENTS, DoOneEvent processes events as they arise and puts the application to sleep when no further events are outstanding. DoOneEvent first looks for a window or I/O event and, if found, calls the handler and returns. If there is no window or I/O event, it looks for a single timer event, invokes the callback, and returns. If no window, I/O, or timer event is ready, all pending idle callbacks are executed, if any. In all cases, DoOneEvent returns 1.

When passed DONT_WAIT, DoOneEvent works as described, except that, if there are no events to process, it returns immediately with a value of 0, indicating it didn't find any work to do.

It's actually rather difficult to find a use for DoOneEvent. One example is the bouncing ball widget demonstration, although it might have been better written using timer callbacks. But it is simulating a simulation, and simulations typically want to run as fast as possible, so we can't fault the implementation.

Even games don't usually require DoOneEvent. Here are two scenarios in which you might use it. Example one probably never reaches the MainLoop statement. It runs as fast as possible, consuming all available CPU time, and depends on update to process events.

&run;
MainLoop;

sub run {
    while (1) {
        &dogame;
        $mw->update;
    }
}

Example two establishes a repeating timer event, then enters MainLoop to process events. The game progresses at a more or less stately speed, with an update occurring every 50 milliseconds. Unlike example one, this example does not consume all available CPU time.

$mw->repeat(50 => \&run);
MainLoop;

sub run {
    &dogame;
    $mw->update;
}

15.9.1. Embedding OpenGL in a Perl/Tk Window

Before we delve into the difficult stuff, here's a really simple static OpenGL program that draws into a Tk window. OpenGL's glpOpenWindow command lets us specify a parent window. This example stuffs the OpenGL window in a Tk Toplevel widget. We use waitVisibility to ensure that the Toplevel is mapped, so it has a valid window identifier.

use OpenGL;

$mw = MainWindow->new;
$mw->Button(-text => 'OpenGL Demo', -command => \&opengl)->pack;
$mw->Button(-text => 'Quit', -command => \&exit)->pack;

sub opengl {
    $top = $mw->Toplevel(qw/-width 500 -height 500 -background pink/);
    $top->title('OpenGL Demo');
    $top->waitVisibility;
    glpOpenWindow(parent=> hex($top->id), width => 450, height => 450);
    glClearColor(0, 0, 1, 1);
    glClear(GL_COLOR_BUFFER_BIT);
    glOrtho(-1, 1, -1, 1, -1, 1);
    glColor3f(0, 1, 0);
    glBegin(GL_POLYGON);
   
    $pi =  3.141592654;
    $d2r = $pi / 180.0;
    $nvert = 8;
    $dangle = 360 / $nvert;
    for ($angle = 0; $angle <= 359; $angle += $dangle) {
        $x = cos($angle * $d2r);
        $y = sin($angle * $d2r);
        glVertex2f($x, $y);
    }
    glEnd;
    glFlush;
}

The results are shown in Figure 15-10.

Figure 15-10

Figure 15-10. Embedding OpenGL in a Tk window

15.9.2. Flying the Enterprise

OpenGL is the de facto 3D graphics package, created by SGI. Ports and look-alikes are widely available. For Linux, install the MESA graphics library and install the Perl interface from CPAN. Bundled with the Perl interface is an OpenGL program that flies the Starship Enterprise in a 3D world.

As with the previous example, we've embedded the flying simulation in a Tk Toplevel widget. Then we enter the OpenGL main loop, which processes all Tk events followed by all OpenGL events.

use Tk qw/:eventtypes/;

$mw = MainWindow->new;
$b = $mw->Button(-text => 'Quit', -command => \&exit);
$b->pack;
$mw->waitVisibility;

$gl = $mw->Toplevel(-width => 400, -height => 400);
$gl->waitVisibility;
&gl_init( hex($gl->id) );

while( 1 ){ # gl_MainLoop

    # ...

    while (my $stat = $mw->DoOneEvent( DONT_WAIT | ALL_EVENTS )){}

    while($p=XPending) {
			@e=&glpXNextEvent;
			&$s(@e) if($s=$cb{$e[0]});
    }

    # ...
    
} # end gl_mainLoop

Figure 15-11 shows the results.

Figure 15-11

Figure 15-11. Perl/Tk and OpenGL main loops can coexist

The DoOneEvent statement was an experiment in which we tried various event masks, in an attempt to determine the optimal combination. You see what we arrived at, which, interestingly, is exactly equivalent to:

$mw->update; 


Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.