Niecza Perl 6 and Gtk

..er, Tetris

by Martin Berends

London Perl Workshop

12 November 2011




Graphical User Interfaces

(Event driven programming)


* Like in Soviet Russia:

* You don't call the GUI libraries.

* The GUI libraries call you.




Events == user actions

and system signals


Examples:

* Mouse and keyboard input

* Clock ticks and file I/O




Event handlers are a form

of callback routine

my $window = Window.new("helloworld");
$window.add_DeleteEvent: sub ($obj, $args) { #OK
    # runs when the user deletes the window using the "close
    # window" widget in the window frame.
    Application.Quit;
};                
Application.Run;  



BTW, that was part of

niecza/examples/gtk1.pl

About Niecza:

* a recent Perl 6 implementation by Stefan O'Rear

* uses Mono or .NET, on Linux, OS X and Windows




Niecza uses most CLR libraries (from GAC)

and auto-marshals params & return values

# Translated from http://www.mono-project.com/GtkSharp:_Hello_World

constant $GTK = "gtk-sharp,Version=2.12.0.0,Culture=neutral,PublicKeyToken=35e10195dab3c99f";

constant Application = CLR::("Gtk.Application,$GTK");
constant Window      = CLR::("Gtk.Window,$GTK");
constant Button      = CLR::("Gtk.Button,$GTK");

Application.Init; 
my $window = Window.new("helloworld"); 



Live demo 1

niecza/examples/gtk1.pl




Gtk (and Gdk)

in 'Gnome Libraries' at http://docs.go-mono.com

Widgets including: AboutDialog Arrow Box Button Calendar Clipboard ComboBox Curve Dialog Drag DrawingArea FileChooserDialog Menu Printer ProgressBar Scrollbar Statusbar Timeout Toolbar TrayIcon VolumeButton Win32EmbedWidget WindowGroup and over 200 others




Drawing with Gdk and Cairo

constant GdkCairoHelper = CLR::("Gdk.CairoHelper,$GDK");
constant GtkDrawingArea = CLR::("Gtk.DrawingArea,$GTK");
my $drawingarea = GtkDrawingArea.new;
$drawingarea.add_ExposeEvent: sub ($obj, $args) {
    my $cc = GdkCairoHelper.Create($obj.GdkWindow);  # Cairo Context
    my $windowX=0; my $windowY=0; my $windowWidth=0; my $windowHeight=0; my $windowDepth=0;
    $obj.GdkWindow.GetGeometry($windowX, $windowY, $windowWidth, $windowHeight, $windowDepth);
    $cc.SetSourceRGB(0.95.Num, 0.90.Num, 0.85.Num); $cc.Paint;  # background color
    ...           



Time event handler

constant GLibTimeout    = CLR::("GLib.Timeout,$GLIB"); 
GLibTimeout.Add: 60000, sub () {  # Update once per minute
    $drawingarea.QueueDrawArea(0,0,$windowSizeX,$windowSizeY);
    return True;  # meaning please continue calling this timeout handler
};                



Live demo 2

niecza/examples/gtk-clock.pl




Introducing Tetris (Tetromino-tennis)

constant matrixRows = 20; constant matrixColumns = 10;
my @tetrominoes =  # cell coordinates relative to the "middle" cell
    [ -1,  0,  0, 0, 1, 0, 2, 0 ],  # I
    [ -1,  0,  0, 0, 1, 0, 1, 1 ],  # J
    [ -1,  1, -1, 0, 0, 0, 1, 0 ],  # L
    [ -1,  1, -1, 0, 0, 0, 0, 1 ],  # O
    [ -1,  1,  0, 1, 0, 0, 1, 0 ],  # S
    [ -1,  0,  0, 0, 0, 1, 1, 0 ],  # T
    [ -1,  0,  0, 0, 0, 1, 1, 1 ];  # Z   



Colours

(must convert Rat -> Num to marshal to Float)

my @colors =  # Copied approximately from the PC version
    [0,       0,       0      ], # 0: black background
    [0.5.Num, 0,       0      ], # 1: I maroon
    [1,       1,       1      ], # 2: J white
    [0.9.Num, 0,       0.9.Num], # 3: L dark magenta
    [0,       0,       0.6.Num], # 4: O dark blue
    [0,       0.8.Num, 0      ], # 5: S green
    [0.7.Num, 0.7.Num, 0      ], # 6: T brown
    [0,       0.8.Num, 0.8.Num]; # 7: Z dark cyan 



Physics (1)

- pieces should obey laws of nature

sub CanMovePiece($deltaX, $deltaY) {
    my $canMove = True;
    for @piece -> $x, $y {
        if $x+$pieceX+$deltaX < 0 || $x+$pieceX+$deltaX >= matrixColumns ||
           $y+$pieceY+$deltaY < 0 || $y+$pieceY+$deltaY >= matrixRows ||
           @matrix[$y+$pieceY+$deltaY][$x+$pieceX+$deltaX] != 0
        { $canMove = False; }
    }
    $canMove;
}                 



Physics (2)

- pieces should obey laws of nature

sub TryRotatePiece() {
    my @p = @piece;
    @piece .= map({ $^b, -$^a });
    if ! CanMovePiece(0, 0) {
        @piece = @p;
    }
}                 



Keyboard input

(give the user the illusion of control)

sub KeyPressEvent($sender, $eventargs) { #OK not used
    given $eventargs.Event.Key {
        when 'Up'    { if $colorindex != 4 { TryRotatePiece() } }
        when 'Down'  { while CanMovePiece(0,1) {++$pieceY;} }
        when 'Left'  { if CanMovePiece(-1,0)   {--$pieceX;} }
        when 'Right' { if CanMovePiece( 1,0)   {++$pieceX;} }
    }
    return True;  # means this keypress is now handled
}                 



Live demo 3

niecza/examples/gtk-tetris.pl




Thanks!

Questions?

github.com/sorear/niecza