Lady
Download the Ada source for the demo.
Visit Apple's Open Source support page.
Visit the GNAT for Mac OS X home page.
Show me how to use shared libraries.
A GtkAda animation demo.
Here's a simple demonstration of buffered animation using GtkAda. It builds on either Linux or Mac OS X using the included Makefile.
The original GNAT port to Mac OS featured a demo of Lady Ada, Countess of Lovelace, bouncing around in a window. I still remember sitting at a trade show in front of a screen full of bouncing ladies, while passing developers stared enviously. I couldn't resist resurrecting it. Here is a static picture of the demo:
To build the demo on Mac OS X, you need several components. If you already have X11, Gtk+, GNAT and GtkAda installed, skip to the Makefile, below:
1. Apple's X11. This program lets you run X11 applications on the Mac desktop. It's included with Xcode on Mac OS X 10.5 "Leopard". It's located in the "Optional Installs.mpkg" package on the Mac OS X 10.4 "Tiger" DVD. It's on disk 3 of the Mac OS X 10.3 "Jaguar" CD set. Don't forget to install the X11 SDK, too. It is not included by default on earlier systems, and it is required by everything else.
2. Gtk+ 2.x. Under Mac OS X 10.5, there are several approaches:
- a) use MacPorts, or
- b) download a prebuilt binary located here[*], or
- c) build Gtk and its dependencies yourself, as shown here and below.
[*] Note: the prebuilt binary includes an earlier version of GtkAda, but you can checkout the newest version and build it against the prebuilt Gtk+.
Don't change the default MacPorts installation prefix "/opt/local" to something else. The glade port, a graphical Gtk interface development tool, is also worthwhile.
3. GNAT. This is the Mac OS X port of GNAT, the GNU Ada compiler. If you have installed GNAT prior to installing Gtk, you may need to revert to the stock Apple gcc compiler to build certain dependencies, e.g. gettext. With previous versions, you had to change the softlink /usr/bin/gcc to point to Apple's gcc, and put it back when you were done. More recent versions allow you to switch compilers by altering the PATH environment variable. A simple script to manage the PATH is shown here.
4. GtkAda 2.x. GtkAda is a full-featured, object-oriented binding to Gtk. See the Installation notes below.
Installation notes:
Under Mac OS X 10.5, use MacPorts:
[switch to Apple gcc] $ port install gtk2 [switch to GNAT 4.3] $ svn checkout http://svn.eu.adacore.com/anonsvn/Dev/trunk/GtkAda $ cd GtkAda/ $ ./configure --prefix=/opt/local --with-GL=GL --with-GL-prefix=/usr/X11R6 $ make $ make install
When building manually, you have to assemble the required source archives; a typical set of dependencies are shown; see gtkada/docs/gtk-build.txt for additional details:
- atk-1.2.4
- gettext-0.11.5
- glade-2.0.0
- glib-2.2.3
- gtk+-2.2.4
- gtkada-2.8.0 (via svn)
- jpegsrc.v6b
- libpng-1.2.7
- libxml2-2.5.7
- pango-1.2.5
- pkgconfig-0.15.0
Lady: Makefile.
Once you've installed GtkAda, it's time to build the application. A typical Makfile is shown. By default, it shows the build targets available. Note in particular that the shared library path defaults to ada-4.3, if not set. You'll have to alter the relevant variables for a prior version of GNAT. This script also shows how to use install_name_tool to change the path to a shared library after linking, as suggested by Jim Hopper. The static binary is nearly twice the size of the shared.
# Lady Makefile
#
# Make shared, static or debug targets.
#
# Author: John B. Matthews
# Lady Makefile
# John B. Matthews
# Created: 15-Oct-2007
# Modified: 18-Oct-2007 Isolate Darwin specific commands.
# Modified: 10-Apr-2009 New shared library path.
# Modified: 23-Sep-2010 Use gtkada-config options; obj directory.
# Modified: 25-Jul-2011 Upddate debug target.
# Distribution: GPL
OS := $(shell uname)
OBJ = obj
TARGET = main
RENAME = install_name_tool
GNATMAKE = gnatmake -D $(OBJ)
CARGS = -cargs -O3 -gnatp -gnatwu
BARGS = -bargs
LARGS = -largs
SHARED = libgnat-4.3.dylib
ADA_LIB ?= /usr/local/ada-4.3/lib
.PHONEY: clean cleaner dist-clean tar
all:
@echo ""
@echo "Lady build targets:"
@echo ""
@echo " shared Use the shared Ada libraries."
@echo " static Link the Ada libraries statically."
@echo " debug Enable debugging."
@echo ""
@echo "Support targets:"
@echo ""
@echo " clean Remove *.ali *.o b~.*"
@echo " cleaner Remove target, too."
@echo " dist-clean Remove Xcode build directory, too."
@echo " tar Build a clean distribution tarball."
@echo ""
shared: $(OBJ)
shared: INCLUDE = $(shell gtkada-config --cflags)
shared: BARGS += -shared
shared: LARGS += $(shell gtkada-config --libs)
shared: LARGS += -dead_strip
shared: *.ad[sb]
@echo "building with shared libraries:"
$(GNATMAKE) $(TARGET) $(INCLUDE) $(CARGS) $(BARGS) $(LARGS)
ifeq ($(OS), Darwin)
# $(RENAME) -change $(SHARED) $(ADA_LIB)/$(SHARED) $(TARGET)
endif
static: $(OBJ)
static: INCLUDE = $(shell gtkada-config --static --cflags)
static: BARGS += -static
static: LARGS += $(shell gtkada-config --static --libs)
static: LARGS += -dead_strip
static: *.ad[sb]
$(GNATMAKE) $(TARGET) $(INCLUDE) $(CARGS) $(BARGS) $(LARGS)
debug: $(OBJ)
debug: INCLUDE = $(shell gtkada-config --static --cflags)
debug: BARGS += -static
debug: LARGS += $(shell gtkada-config --static --libs)
debug: *.ad[sb]
$(GNATMAKE) -g $(TARGET) $(INCLUDE) $(LARGS)
$(OBJ):
mkdir $(OBJ)
clean:
${RM} $(OBJ)/* b~*
cleaner: clean
${RM} $(TARGET)
dist-clean: cleaner
${RM} -r $(OBJ)
tar: dist-clean
ifeq ($(OS), Darwin)
(export COPYFILE_DISABLE=true; \
tar --exclude '.svn' -zcvf ~/Sites/HomePage/gtk/lady.tgz ../lady)
else
tar --exclude '.svn' -zcvf ~/archive/lady.tgz ../lady
endif
Lady: Core source code.
------------------------------------------------------------------
--|
--| Lady: A Gtk Animation Demo
--|
--| Author: John B. Matthews, Wright State University
--| Last Modified: 17-Jul-2005
--|
------------------------------------------------------------------
with Ada.Text_IO;
with Double_Buffer;
with Gdk.Color;
with Gdk.Drawable;
with Gdk.GC;
with Gdk.Pixbuf;
with Gdk.Rectangle;
with Glib;
use Glib; use type Glib.Gint;
with Glib.Error;
use type Glib.Error.GError;
with Gtk.Box;
with Gtk.Button;
with Gtk.Drawing_Area;
with Gtk.Enums;
with Gtk.Handlers;
pragma Elaborate_All (Gtk.Handlers);
with Gtk.Hbutton_Box;
with Gtk.Label;
with Gtk.Main;
pragma Elaborate_All (Gtk.Main);
with Gtk.Style;
with Gtk.Widget;
with Gtk.Window;
with Pango.Font;
package body Lady is
White_Gc : Gdk.GC.Gdk_GC;
Black_Gc : Gdk.GC.Gdk_GC;
Image_Width : Gint;
Image_Height : Gint;
Image : Gdk.Pixbuf.Gdk_Pixbuf;
X_Pos : Gint := 13; -- current position
Y_Pos : Gint := 17;
Vmin : Gint := 2; -- min. velocity
Vmax : Gint := 20; -- max. velocity
Dh : Gint := 4; -- horizontal change
Dv : Gint := 4; -- vertical change
package Void_Cb is new
Gtk.Handlers.Callback (Gtk.Window.Gtk_Window_Record);
package Button_Cb is new
Gtk.Handlers.Callback (Gtk.Button.Gtk_Button_Record);
package Timeout is new
Gtk.Main.Timeout (Gtk.Drawing_Area.Gtk_Drawing_Area);
procedure Inset_Rect (R : in out Gdk.Rectangle.Gdk_Rectangle;
Dh, Dv : Gint) is
begin
R.X := Gint'Min(R.X - (Dh / 2), R.X + R.Width / 2);
R.Y := Gint'Min(R.Y - (Dv / 2), R.Y + R.Height / 2);
R.Width := Gint'Max(R.Width + Dh, 0);
R.Height := Gint'Max(R.Height + Dv, 0);
end Inset_Rect;
------------------
-- Draw_Content --
------------------
procedure Draw_Content (Pixmap : Gdk.Gdk_Drawable) is
Width, Height : Gint;
Rect : Gdk.Rectangle.Gdk_Rectangle;
begin
-- Erase the old
Gdk.Drawable.Get_Size(Pixmap, Width, Height);
Gdk.Drawable.Draw_Rectangle(Pixmap, White_Gc, True, 0, 0, Width, Height);
-- Draw the new
Rect := (0, 0, Width, Height);
for i in Gint'(2) .. 10 loop
Inset_Rect(Rect, -(i * 10), -(i * 10));
Gdk.Drawable.Draw_Rectangle(Pixmap, Black_Gc, False,
Rect.X, Rect.Y, Rect.Width, Rect.Height);
end loop;
Gdk.Pixbuf.Render_To_Drawable (
Pixbuf => Image,
Drawable => Pixmap,
Gc => Black_Gc,
Src_X => 0,
Src_Y => 0,
Dest_X => X_Pos,
Dest_Y => Y_Pos,
Width => Image_Width,
Height => Image_Height);
Gdk.Drawable.Draw_Rectangle (Pixmap, Black_Gc, False,
X_Pos, Y_Pos, Image_Width, Image_Height);
if X_Pos < 0 then Dh := -Dh; end if;
if Y_Pos < 0 then Dv := -Dv; end if;
if X_Pos > Width - Image_Width then
X_Pos := Width - Image_Width;
Dh := -Dh;
end if;
if Y_Pos > Height - Image_Height then
Y_Pos := Height - Image_Height;
Dv := -Dv;
end if;
X_Pos := X_Pos + Dh;
Y_Pos := Y_Pos + Dv;
end Draw_Content;
-----------------
-- Draw_Buffer --
-----------------
function Draw_Buffer (
Area : Gtk.Drawing_Area.Gtk_Drawing_Area) return Boolean is
Buffer : Double_Buffer.Gtk_Double_Buffer :=
Double_Buffer.Gtk_Double_Buffer (Area);
begin
Draw_Content (Double_Buffer.Get_Pixmap (Buffer));
Double_Buffer.Draw (Buffer);
return True;
end Draw_Buffer;
----------
-- Sign --
----------
function Sign (n : Gint) return Gint is
begin
if n < 0 then return -1;
else return 1;
end if;
end Sign;
------------
-- Slower --
------------
procedure Slower (
Button : access Gtk.Button.Gtk_Button_Record'Class) is
pragma Warnings (Off, Button);
begin
Dh := Sign(Dh) * Gint'Max(Abs(Dh) - 1, Vmin);
Dv := Sign(Dv) * Gint'Max(Abs(Dv) - 1, Vmin);
end Slower;
------------
-- Faster --
------------
procedure Faster (
Button : access Gtk.Button.Gtk_Button_Record'Class) is
pragma Warnings (Off, Button);
begin
Dh := Sign(Dh) * Gint'Min(Abs(Dh) + 1, Vmax);
Dv := Sign(Dv) * Gint'Min(Abs(Dv) + 1, Vmax);
end Faster;
----------
-- Quit --
----------
procedure Quit (Win : access Gtk.Window.Gtk_Window_Record'Class) is
pragma Warnings (Off, Win);
begin
Gtk.Main.Gtk_Exit (0);
end Quit;
----------
-- Init --
----------
procedure Init is
Win : Gtk.Window.Gtk_Window;
Buffer : Double_Buffer.Gtk_Double_Buffer;
Hbox : Gtk.Hbutton_Box.Gtk_HButton_Box;
Vbox : Gtk.Box.Gtk_Box;
Label : Gtk.Label.Gtk_Label;
Style : Gtk.Style.Gtk_Style;
Button : Gtk.Button.Gtk_Button;
Id : Gtk.Main.Timeout_Handler_Id;
Error : Glib.Error.GError;
begin
-- Double buffer demo
Gtk.Window.Gtk_New (Win, Gtk.Enums.Window_Toplevel);
Gtk.Window.Set_Title (Win, "GtkAda Animation");
Void_Cb.Connect (Win, "destroy", Void_Cb.To_Marshaller (Quit'Access));
-- A vertical box with three rows
Gtk.Box.Gtk_New_Vbox (Vbox, Homogeneous => False, Spacing => 5);
-- Row 1: a label with style
Gtk.Label.Gtk_New (Label, "Lady Ada Lovelace");
Style := Gtk.Style.Copy (Gtk.Window.Get_Style (Win));
Gtk.Style.Set_Font_Description (Style,
Pango.Font.From_String ("Helvetica Bold 14"));
Gtk.Label.Set_Style (Label, Style);
-- Row 2: a double buffered drawing area
Double_Buffer.Gtk_New (Buffer);
Double_Buffer.Set_USize (Buffer, 450, 325);
Double_Buffer.Set_Back_Store (Buffer, True);
-- Row 3: A horizontal row of buttons
Gtk.HButton_Box.Gtk_New (Hbox);
Gtk.HButton_Box.Set_Border_Width (Hbox, 10);
Gtk.HButton_Box.Set_Spacing (Hbox, 10);
Gtk.HButton_Box.Set_Layout (Hbox, Gtk.Enums.Buttonbox_Spread);
Gtk.Button.Gtk_New (Button, "Slower");
Gtk.HButton_Box.Pack_Start (HBox, Button);
Button_Cb.Object_Connect (Button, "clicked",
Button_Cb.To_Marshaller (Slower'Access), Button);
Gtk.Button.Gtk_New (Button, "Faster");
Gtk.HButton_Box.Pack_Start (HBox, Button);
Button_Cb.Object_Connect (Button, "clicked",
Button_Cb.To_Marshaller (Faster'Access), Button);
-- Pack them in
Gtk.Box.Pack_Start (Vbox, Label, Expand => False);
Gtk.Box.Pack_Start (Vbox, Buffer);
Gtk.Box.Pack_Start (Vbox, HBox, Expand => False);
Gtk.Window.Add (Win, Vbox);
Gtk.Window.Show_All (Win);
-- The window needs to be created before creating the GCs
Gdk.GC.Gdk_New (White_Gc, Double_Buffer.Get_Window (Buffer));
Gdk.GC.Set_Foreground
(White_Gc, Gdk.Color.White (Gtk.Widget.Get_Default_Colormap));
Gdk.GC.Gdk_New (Black_Gc, Double_Buffer.Get_Window (Buffer));
Gdk.GC.Set_Foreground
(Black_Gc, Gdk.Color.Black (Gtk.Widget.Get_Default_Colormap));
-- Load the image
Gdk.Pixbuf.Gdk_New_From_File (Image, "lady.png", Error);
if Error = null then
Image_Width := Gdk.Pixbuf.Get_Width (Image);
Image_Height := Gdk.Pixbuf.Get_Height (Image);
else
Ada.Text_IO.Put_Line("Error: " & Glib.Error.Get_Message(Error));
Glib.Error.Error_Free(Error);
Image_Width := 45; Image_Height := 60;
Image := Gdk.Pixbuf.Gdk_New(Bits_Per_Sample => 24,
Width => Image_Width, Height => Image_Height);
Gdk.Pixbuf.Fill(Image, 16#0000FF00#);
end if;
-- Draw a frame every 40 ms (25 frames/sec)
Id := Timeout.Add (40, Draw_Buffer'Access,
Gtk.Drawing_Area.Gtk_Drawing_Area (Buffer));
end Init;
end Lady;
ada: a script to manage the PATH.
#!/bin/sh
#
# Manage Ada in $PATH
#
ADA_BIN=${ADA_BIN:-/usr/local/ada-4.3/bin}
function checkPath() {
echo ${PATH} | grep -q -s $1
}
function removePath() {
newPath=`echo $PATH | sed -e "s;$1;;" \
-e "s/^://" -e "s/::/:/" -e "s/:$//"`
}
function status() {
echo `gcc --version | head -n 1`
if checkPath ${ADA_BIN} ; then
echo "${ADA_BIN} \033[1min\033[0m \$PATH"
removePath ${ADA_BIN}
echo "export PATH=${newPath} ; $0"
else
echo "${ADA_BIN} \033[1mnot in\033[0m \$PATH"
echo "export PATH=${ADA_BIN}:${PATH} ; $0"
fi
}
status
exit $?
Copyright 1984, 2004, 2009 John B. Matthews
Distribution permitted under the terms of the GPL.
Last updated 23-Sep-2010