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:
[*] 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:
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 = objTARGET = mainRENAME = install_name_toolGNATMAKE = gnatmake -D $(OBJ)CARGS = -cargs -O3 -gnatp -gnatwuBARGS = -bargsLARGS = -largsSHARED = libgnat-4.3.dylibADA_LIB ?= /usr/local/ada-4.3/lib.PHONEY: clean cleaner dist-clean tarall: @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 += -sharedshared: LARGS += $(shell gtkada-config --libs)shared: LARGS += -dead_stripshared: *.ad[sb] @echo "building with shared libraries:" $(GNATMAKE) $(TARGET) $(INCLUDE) $(CARGS) $(BARGS) $(LARGS)ifeq ($(OS), Darwin)# $(RENAME) -change $(SHARED) $(ADA_LIB)/$(SHARED) $(TARGET)endifstatic: $(OBJ)static: INCLUDE = $(shell gtkada-config --static --cflags)static: BARGS += -staticstatic: LARGS += $(shell gtkada-config --static --libs)static: LARGS += -dead_stripstatic: *.ad[sb] $(GNATMAKE) $(TARGET) $(INCLUDE) $(CARGS) $(BARGS) $(LARGS)debug: $(OBJ)debug: INCLUDE = $(shell gtkada-config --static --cflags)debug: BARGS += -staticdebug: 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-cleanifeq ($(OS), Darwin) (export COPYFILE_DISABLE=true; \ tar --exclude '.svn' -zcvf ~/Sites/HomePage/gtk/lady.tgz ../lady)else tar --exclude '.svn' -zcvf ~/archive/lady.tgz ../ladyendifLady: 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}statusexit $?Copyright 1984, 2004, 2009 John B. Matthews
Distribution permitted under the terms of the GPL.
Last updated 23-Sep-2010