Prechádzať zdrojové kódy

+ First part of GTK article

michael 24 rokov pred
rodič
commit
ecd4fbace8
1 zmenil súbory, kde vykonal 368 pridanie a 0 odobranie
  1. 368 0
      docs/gtk5.tex

+ 368 - 0
docs/gtk5.tex

@@ -0,0 +1,368 @@
+\documentclass[10pt]{article}
+\usepackage{a4}
+\usepackage{epsfig}
+\usepackage{listings}
+\usepackage{tabularx}
+\lstset{language=Delphi}%
+\lstset{basicstyle=\sffamily\small}%
+\lstset{commentstyle=\itshape}%
+\lstset{keywordstyle=\bfseries}%
+%\lstset{blankstring=true}%
+\newcommand{\file}[1]{\textsf{#1}}
+\newcommand{\var}[1]{\texttt{#1}}
+\usepackage[pdftex]{hyperref}
+\newif\ifpdf
+\ifx\pdfoutput\undefined
+  \pdffalse
+\else
+  \pdfoutput=1
+  \pdftrue
+\fi
+\begin{document}
+\title{Programming GTK in Free Pascal: Using GDK}
+\author{Florian Kl\"ampfl\\and\\Micha\"el Van Canneyt}
+\date{July 2001}
+\maketitle
+\section{Introduction}
+In this article, some of the graphics primitives from the gdk toolkit will
+be demonstrated in a small game - breakout.
+
+The GTK toolkit widgets are built upon the GDK: Graphics Drawing Kit. 
+The GDK does not know anything about buttons, menus checkboxes and so on.
+Instead, it knows how to create windows, draw on them, handle mouse clicks
+and keypresses. This functionality is used by the GTK widget set to create
+usable widgets.
+
+Sometimes, the widgets offered by GTK are not enough, and one has to fall
+back on the graphics functionality of the GDK to be able to do what is 
+needed for a program.
+
+Fortunately, it is not necessary to create a GTK window and handle all
+GDK events to be able to use the GDK functions. The GTK widget set has a
+special widget, which can be used to draw upon. This widget is the 
+\var{TGtkDrawingArea} widget. The use of the \var{TGtkDrawingArea} is what
+will be explained below.
+
+The GDK graphics functions will be explained using a simple arcade game,
+to demonstrate that the speed of the GDK is sufficient for the creation of
+simple games. The breakout game is chosen because it is conceptually simple, 
+requires moving graphics and can be extended in many ways.
+
+\section{The drawing area widget}
+The drawing area widget (\var{TGTKDrawingArea}) is a simple widget which
+just provides a drawing window. It responds to all widget events, and adds
+additionally the 'configure\_event', which is called when the widget is
+realized (i.e. when the window handle is created.)
+
+The widget has only 1 method: \var{gtk\_drawing\_area\_size}, which sets
+the size of the drawing area. It is defined as follows:
+\begin{verbatim}
+procedure gtk_drawing_area_size(Area:PGtkDrawingArea; width:gint;height:gint) 
+\end{verbatim}
+The arguments to this function are self-explaining.
+
+To use the drawing area widget, one should respond to the 'expose\_event'.
+This event is triggered whenever a part of the window that was invisible,
+becomes visible. The event handler gets an \var{PGDKEventExpose} parameter,
+which describes which area was exposed. This can be used for optimization
+purposes.
+
+To draw in the drawing area widget, the \var{Window} field of the
+\var{TGTKWidget} parent can be used. This is of type \var{TGDKWindow}.
+All drawing functions require a parameter of type \var{TGdkDrawable}
+which can be one of the \var{TGdkWindow} or \var{TGdkPixMap} types.
+
+\section{Graphics contexts}
+Most drawing functions do not only require a drawable to draw on, they also
+require a {\em Graphics Context}. A graphics context is a series of
+parameters that determine how lines are drawn, what colors and font are
+used etc.
+
+The Graphics Context is an opaque record, and its members cannot be
+accessed. The relevant parameters are set in a \var{TGdkGCValues} record,
+which is defined as follows:
+\begin{verbatim}
+foreground : TGdkColor;
+background : TGdkColor;
+font : PGdkFont;
+thefunction : TGdkfunction;
+fill : TGdkFill;
+tile : PGdkPixmap;
+stipple : PGdkPixmap;
+clip_mask : PGdkPixmap;
+subwindow_mode : TGdkSubwindowMode;
+ts_x_origin : gint;
+ts_y_origin : gint;
+clip_x_origin : gint;
+clip_y_origin : gint;
+graphics_exposures : gint;
+line_width : gint;
+line_style : TGdkLineStyle;
+cap_style : TGdkCapStyle;
+join_style : TGdkJoinStyle;
+\end{verbatim}
+The \var{ForeGround} and \var{Background} parameters determine the foreground
+and background colors. \var{Font} is the default font. The \var{Fill} field
+describes how areas are filled. It can be one of the following:
+\begin{description}
+\item[GDK\_SOLID] fill with the foreground color.
+\item[GDK\_TILED] Use the pixmap specified in \var{Tile} to fill the area.
+\item[GDK\_STIPPLED] Use the pixmap specified in \var{Stipple} to draw
+pixels that are in the bitmap in the foreground color. Other bits are not
+drawn.
+\item[GDK\_OPAQUE\_STIPPLED] Same as \var{GDK\_STIPPLED} except that bits 
+not in the pixmap will be drawn in the background color.
+\end{description}
+The \var{clip\_bitmap} is used to define a clip area. The
+\var{ts\_x\_origin} and \var{ts\_y\_origin} define the stipple or tile
+origin.  The \var{clip\_x\_origin} and \var{clip\_y\_origin} fields define 
+the origin of the clipping region.
+\var{LineWidth} is the linewidth used when drawing lines. \var{Line\_Style}
+determines how dashed lines are drawn. It can have one of the following
+values:
+\begin{description}
+\item[GDK\_LINE\_SOLID] Lines are drawn solid.
+\item[GDK\_LINE\_ON\_OFF\_DASH] Even segments are drawn, odd segments are
+not.
+\item[GDK\_LINE\_DOUBLE\_DASH] Even segments are drawn, Odd segments are
+drawn in the background color if the fill style is \var{GDK\_SOLID}.
+\end{description}
+\var{cap\_style} determines how line ends are drawn. The following values are
+defined:
+\begin{description}
+\item[GDK\_CAP\_BUTT] The lines are drawn with square ends.  
+\item[GDK\_CAP\_NOT\_LAST] Idem as \var{GDK\_CAP\_BUTT}, only for zero-width
+lines, the last dot is not drawn.
+\item[GDK\_CAP\_ROUND] The end of the line is a semicircle. The circle has
+diameter equal to the linewidth, and the center is the endpoint of the line.
+\item[GDK\_CAP\_PROJECTING] Idem as [GDK\_CAP\_BUTT], only the line extends
+half the linewidth outside the endpoint.
+\end{description}
+
+The effect of these elements will be shown in the next section.
+
+To set a color, a \var{TGDkColor} record must be allocated. Colors are
+specified using a RGB value. Unfortunately, not all graphics cards can
+show all colors. In order to find out which screen color corresponds 
+to the RGB-specified color, the GDK uses a colormap, and allocates a
+color that matches the closest to the specified color values. 
+When allocating a new color, the colormap should be specified.
+
+A colormap can be obtained from a \var{TGTKWidget} object using the
+\var{gtk\_widget\_get\_colormap} function; A color can then be allocated
+using the \var{gdk\_colormap\_alloc\_color} function:
+\begin{verbatim}
+function gdk_colormap_alloc_color(colormap:PGdkColormap; 
+                                  color:PGdkColor;
+                                  writeable:gboolean; 
+                                  best_match:gboolean):gboolean;
+\end{verbatim}
+The \var{writeable} parameter specifies whether changes in the
+\var{color} using \var{gdk\_color\_change} are allowed.
+\var{best\_match} specifies whether a best match should be attempted 
+on existing colors or an exact value is required.
+The function returns \var{True} if the allocation succeeded, 
+\var{False} otherwise.
+
+\section{Drawing primitives}
+Using the properties introduced in the previous section, drawing can be
+attempted using the drawing primitives offered by GDK. GDK offers drawing
+functions for points, lines, segments, rectangles, polygons, circles, text
+and bitmaps.
+
+All functions accept as the first two parameters a \var{PGDKdrawable}, which
+can be a \var{TGDKWindow} or \var{TGDkPixmap}, and a \var{PGdkGC}, a pointer
+to a graphics context. These parameters are omitted from the following
+declarations:
+\begin{verbatim}
+procedure gdk_draw_point(x,y:gint);
+procedure gdk_draw_line(x1,y1,x2,y2:gint);
+procedure gdk_draw_rectangle(filled,x,y,width,height:gint);
+\end{verbatim}
+The meaning of the parameters for these functions is obvious.
+For the rectangle, care must be taken. If \var{Filled} is false (-1) then
+the drawn rectangle is actually \var{Width+1}, \var{Height+1}. If it is
+filled, then the width are height are as specified.
+
+The following functions can be used to draw a series of lines:
+\begin{verbatim}
+procedure gdk_draw_polygon(filled:gint;points:PGdkPoint; npoints:gint);
+procedure gdk_draw_segments(segs:PGdkSegment; nsegs:gint);
+procedure gdk_draw_lines(points:PGdkPoint; npoints:gint);
+\end{verbatim}
+The \var{gdk\_draw\_polygon} polygon takes a series of dots and connects 
+them using lines, optionally filling them. A \var{TGDKPoint} record contains
+ 2 fields \var{X,Y} which specify the location of a point. If needed, the
+first and last points are also connected using a line.
+The \var{gdk\_draw\_lines} does the same, only it cannot be filled, and it 
+will not connect the first and last points.
+The \var{gdk\_draw\_segments} requires a series of \var{TGDKSegment}
+records. These consist of 4 fields: \var{x1,y1,x2,y2}, each describing
+the start and end point of a line segment. The segments will not be
+connected.
+
+The \var{gdk\_draw\_arc} can be used to draw a circle or a segment of
+the circle, or an ellipse.
+\begin{verbatim}
+procedure gdk_draw_arc(filled,x,y,width,height,angle1,angle2 : gint);
+\end{verbatim}
+The \var{x,y, width} and \var{height} parameters describe a bounding
+rectangle for the circle. The angles describe the start and extending 
+angle of the segment to be drawn: The circle segment starts at angle
+\var{angle1} and ends at \var{angle1+angle2}. These angles are specified 
+in 1/64ths of a degree and are measured counterclockwise, starting at 
+the 3 o'clock direction. A circle segment drawn from 90 to 270 degrees 
+should therefore have as angles 90*64=5760 and 270*64=17280.
+
+If filled is \var{True} (-1), then the segment will be connected to
+the circle centre, and filled, in effect drawing a pie-slice.
+
+Finally, for the \var{gdk\_draw\_string} function, the graphics context comes
+before the graphics context:
+\begin{verbatim}
+procedure gdk_draw_string(drawable:PGdkDrawable; font:PGdkFont; gc:PGdkGC;
+                          x:gint; y:gint; thestring:Pgchar);
+\end{verbatim}
+The meaning of the parameters for this functions should be obvious.
+
+All this is demonstrated in the following program:
+\begin{lstlisting}{}
+program graphics;
+
+{$mode objfpc}
+{$h+}
+
+uses glib,gdk,gtk,sysutils;
+
+var 
+  window, 
+  area : PGtkWidget;
+
+Function CloseApp(widget : PGtkWidget ;
+                  event : PGdkEvent;
+                  data : gpointer) : boolean; cdecl;
+Begin
+  gtk_main_quit();
+  close_application := false;
+End;
+
+Function AllocateColor(R,G,B : Integer; 
+                       Widget : PGtkWidget) : PGdkColor;
+
+begin
+  Result:=New(PgdkColor);
+  With Result^ do
+    begin
+    Pixel:=0;
+    Red:=R;
+    Blue:=B;
+    Green:=G;  
+    end;
+  gdk_colormap_alloc_color(gtk_widget_get_colormap(Widget),
+                           Result,true,False);
+end;
+
+function Exposed(Widget: PGtkWidget;
+                 event : PGdkEventExpose; 
+                 Data : gpointer) : Integer; cdecl;
+
+Const 
+  Triangle : Array[1..4] of TgdkPoint = 
+            ((X:10;Y:195),
+             (X:110;Y:195),
+             (X:55;Y:145),
+             (X:10;Y:195));
+  LineStyles : Array[1..5] of TgdkLineStyle = 
+          (GDK_LINE_SOLID, GDK_LINE_ON_OFF_DASH, 
+           GDK_LINE_DOUBLE_DASH, GDK_LINE_ON_OFF_DASH, 
+           GDK_LINE_SOLID);
+  capstyles : Array[1..5] of TgdkCapStyle = 
+          (GDK_CAP_ROUND,GDK_CAP_NOT_LAST, GDK_CAP_BUTT,  
+           GDK_CAP_PROJECTING, GDK_CAP_NOT_LAST);
+          
+Var
+  SegTriangle : Array[1..3] of TgdkSegment;
+  Win : pgdkWindow;
+  gc : PgdkGC;
+  i,seg : Integer; 
+  font : PgdkFont;
+  Angle1,Angle2 : Longint;
+    
+begin
+  gc:=gdk_gc_new(widget^.Window);
+  Win:=widget^.window;
+  With Event^.area do
+    gdk_window_clear_area (win,x,y,width,height);
+  gdk_gc_set_foreground(gc,allocatecolor(0,0,0,Widget));
+  gdk_draw_rectangle(win,gc,0,5,5,590,390);
+  gdk_gc_set_foreground(gc,allocatecolor(0,0,$ffff,Widget));
+  for I:=10 to 50 do
+    gdk_draw_point(win,gc,I*10,100);
+  gdk_gc_set_foreground(gc,allocatecolor($ffff,0,0,Widget));
+  for I:=10 to 50 do
+    begin
+    gdk_gc_set_line_attributes(gc,6,LineStyles[i div 10],CapStyles[i div 10],GDK_JOIN_MITER);
+    gdk_draw_line(win,gc,I*10,20,I*10,90)
+    end;
+  gdk_gc_set_line_attributes(gc,1,GDK_LINE_SOLID,GDK_CAP_BUTT,GDK_JOIN_MITER);
+  gdk_gc_set_foreground(gc,allocatecolor($ffff,0,$ffff,Widget));
+  seg:=(360 div 20) * 64;
+  For I:=1 to 20 do
+    gdk_draw_arc(win,gc,0,220-I*4,200-i*4,8*i,8*i,i*seg,seg*19);
+  For I:=1 to 20 do
+    gdk_draw_arc(win,gc,-1,380-I*4,200-i*4,8*i,8*i,(i-1)*seg,seg);
+  gdk_gc_set_foreground(gc,allocatecolor(0,$ffff,$ffff,Widget));
+  gdk_draw_polygon(win,gc,0,@triangle[1],4);  
+  For I:=1 to 4 do
+    Triangle[i].Y:=400-Triangle[i].y;
+  gdk_draw_polygon(win,gc,-1,@triangle[1],4);  
+  gdk_gc_set_foreground(gc,allocatecolor(0,$ffff,0,Widget));
+  For I:=1 to 4 do
+    Triangle[i].X:=600-Triangle[i].x;
+  gdk_draw_lines(win,gc,@triangle[1],4);
+  For I:=1 to 3 do
+    begin
+    SegTriangle[i].X1:=Triangle[i].X;
+    SegTriangle[i].Y1:=400-Triangle[i].Y;
+    SegTriangle[i].X2:=Triangle[i+1].X;
+    SegTriangle[i].Y2:=400-Triangle[i+1].Y;
+    end;
+  gdk_draw_segments(win,gc,@segtriangle[1],3);
+  font:=gdk_font_load('-*-helvetica-bold-r-normal--*-120-*-*-*-*-iso8859-1');
+  gdk_gc_set_foreground(gc,allocatecolor($ffff,$ffff,0,Widget));
+  For I:=1 to 4 do
+    gdk_draw_string(win,font,gc,I*100,300,Pchar(format('String %d',[i])));
+  result:=0;
+end;
+
+Begin
+  // Initialize GTK and create the main window
+  gtk_init( @argc, @argv );
+  window := gtk_window_new( GTK_WINDOW_TOPLEVEL );
+  gtk_window_set_policy(PgtkWindow(Window),0,0,1);
+  gtk_signal_connect (GTK_OBJECT (window), 'delete_event',
+          GTK_SIGNAL_FUNC( @CloseApp ), NIL);
+  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
+  area := gtk_drawing_area_new();
+  gtk_container_add( GTK_CONTAINER(window), Area);
+  gtk_signal_connect (GTK_OBJECT (area),'expose_event',
+                      GTK_SIGNAL_FUNC(@Exposed),Nil);
+  gtk_drawing_area_size (PGTKDRAWINGAREA(area),600,400);
+  gtk_widget_show_all( window ); 
+  gtk_main();
+end.
+\end{lstlisting}
+The main program startsby creating a main window,
+and adding a \var{TGTKDrawingArea} to it. It then connects 2 event handlers,
+one to stop the application if the window is closed (\var{CloseApp}),  
+the other to draw the \var{TGTKDrawingArea} when it is exposed 
+(\var{Exposed}). This latter contains the actual drawing routines, and is
+pretty self-explaining.
+
+Note that the allocated colors are not freed again, so this program does
+contain a memory leak.
+
+\section{Animation}
+
+
+\end{document}