Section 22, 24 and 25 are updated.

This commit is contained in:
Toshio Sekiya 2023-01-05 20:41:02 +09:00
parent d4317f1140
commit a8cf7176df
23 changed files with 2408 additions and 1655 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/image/turtle_snow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -119,19 +119,18 @@ called custom drawing.</p>
<p>GtkDrawingArea provides a cairo drawing context so users can draw <p>GtkDrawingArea provides a cairo drawing context so users can draw
images by using cairo functions. In this section, I will explain:</p> images by using cairo functions. In this section, I will explain:</p>
<ol type="1"> <ol type="1">
<li>Cairo, but only briefly; and</li> <li>Cairo, but only briefly</li>
<li>GtkDrawingArea, with a very simple example.</li> <li>GtkDrawingArea, with a very simple example.</li>
</ol> </ol>
<h2 id="cairo">Cairo</h2> <h2 id="cairo">Cairo</h2>
<p>Cairo is a set of two dimensional graphical drawing functions (or <p>Cairo is a set of two dimensional graphical drawing functions (or
graphics library). There is a lot of documentation on <a graphics library). There are a lot of documents on <a
href="https://www.cairographics.org/">Cairos website</a>. If you arent href="https://www.cairographics.org/">Cairos website</a>. If you arent
familiar with Cairo, it is worth reading their <a familiar with Cairo, it is worth reading the <a
href="https://www.cairographics.org/tutorial/">tutorial</a>.</p> href="https://www.cairographics.org/tutorial/">tutorial</a>.</p>
<p>The following is a gentle introduction to the Cairo library and how <p>The following is an introduction to the Cairo library and how to use
to use it. Firstly, in order to use Cairo you need to know about it. First, you need to know about surfaces, sources, masks,
surfaces, sources, masks, destinations, cairo context and destinations, cairo context and transformations.</p>
transformations.</p>
<ul> <ul>
<li>A surface represents an image. It is like a canvas. We can draw <li>A surface represents an image. It is like a canvas. We can draw
shapes and images with different colors on surfaces.</li> shapes and images with different colors on surfaces.</li>
@ -169,7 +168,8 @@ the paint in the source to the destination.</li>
<li>Save the destination surface to a file if necessary.</li> <li>Save the destination surface to a file if necessary.</li>
</ol> </ol>
<p>Heres a simple example program that draws a small square and saves <p>Heres a simple example program that draws a small square and saves
it as a png file.</p> it as a png file. The path of the file is
<code>src/misc/cairo.c</code>.</p>
<div class="sourceCode" id="cb1"><pre <div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#include </span><span class="im">&lt;cairo.h&gt;</span></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="pp">#include </span><span class="im">&lt;cairo.h&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span> <span id="cb1-2"><a href="#cb1-2"></a></span>
@ -222,10 +222,10 @@ color depth. Width and height are in pixels and given as integers.</li>
<li>14: Creates cairo context. The surface given as an argument will be <li>14: Creates cairo context. The surface given as an argument will be
the destination of the context.</li> the destination of the context.</li>
<li>18: <code>cairo_set_source_rgb</code> creates a source pattern, <li>18: <code>cairo_set_source_rgb</code> creates a source pattern,
which in this case is a solid white paint. The second to fourth argument which in this case is a solid white paint. The second to fourth
are red, green and blue color values respectively, and they are of type arguments are red, green and blue color values respectively, and they
float. The values are between zero (0.0) and one (1.0), with black being are of type float. The values are between zero (0.0) and one (1.0), with
given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).</li> black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).</li>
<li>19: <code>cairo_paint</code> copies everywhere in the source to <li>19: <code>cairo_paint</code> copies everywhere in the source to
destination. The destination is filled with white pixels with this destination. The destination is filled with white pixels with this
command.</li> command.</li>
@ -244,14 +244,15 @@ through the rectangle in the mask.</li>
destroyed.</li> destroyed.</li>
<li>29: Destroys the surface.</li> <li>29: Destroys the surface.</li>
</ul> </ul>
<p>To compile this, type the following.</p> <p>To compile this, change your current directory to
<code>src/misc</code> and type the following.</p>
<pre><code>$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`</code></pre> <pre><code>$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`</code></pre>
<figure> <figure>
<img src="image/rectangle.png" alt="rectangle.png" /> <img src="image/rectangle.png" alt="rectangle.png" />
<figcaption aria-hidden="true">rectangle.png</figcaption> <figcaption aria-hidden="true">rectangle.png</figcaption>
</figure> </figure>
<p>See the <a href="https://www.cairographics.org/">Cairos website</a> <p>See the <a href="https://www.cairographics.org/">Cairos website</a>
for more details.</p> for further information.</p>
<h2 id="gtkdrawingarea">GtkDrawingArea</h2> <h2 id="gtkdrawingarea">GtkDrawingArea</h2>
<p>The following is a very simple example.</p> <p>The following is a very simple example.</p>
<div class="sourceCode" id="cb3"><pre <div class="sourceCode" id="cb3"><pre
@ -302,15 +303,15 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
functions <code>app_activate</code> and <code>draw_function</code> are functions <code>app_activate</code> and <code>draw_function</code> are
important in this example.</p> important in this example.</p>
<ul> <ul>
<li>18: Creates a GtkDrawingArea instance; and</li> <li>22: Creates a GtkDrawingArea instance.</li>
<li>21: Sets a drawing function of the widget. GtkDrawingArea widget <li>25: Sets a drawing function of the widget. GtkDrawingArea widget
uses the function to draw the contents of itself whenever its necessary. uses the function to draw the contents of itself whenever its necessary.
For example, when a user drag a mouse pointer and resize a top-level For example, when a user drag a mouse pointer and resize a top-level
window, GtkDrawingArea also changes the size. Then, the whole window window, GtkDrawingArea also changes the size. Then, the whole window
needs to be redrawn. For the information of needs to be redrawn. For the information of
<code>gtk_drawing_area_set_draw_func</code>, see <a <code>gtk_drawing_area_set_draw_func</code>, see <a
href="https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html">Gtk href="https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html">Gtk
API Reference, gtk_drawing_area_set_draw_func</a>.</li> API Reference gtk_drawing_area_set_draw_func</a>.</li>
</ul> </ul>
<p>The drawing function has five parameters.</p> <p>The drawing function has five parameters.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> drawing_function <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> cairo_t <span class="op">*</span>cr<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span></span> <div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> drawing_function <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> cairo_t <span class="op">*</span>cr<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span></span>
@ -322,20 +323,20 @@ cairo context given by the widget. The destination surface of the
context is connected to the contents of the widget. What you draw to context is connected to the contents of the widget. What you draw to
this surface will appear in the widget on the screen. The third and this surface will appear in the widget on the screen. The third and
fourth parameters are the size of the destination surface. Now, look at fourth parameters are the size of the destination surface. Now, look at
the program example again.</p> the program again.</p>
<ul> <ul>
<li>3-13: The drawing function.</li> <li>3-17: The drawing function.</li>
<li>7-8: Sets the source to be white and paint the destination <li>7-8: Sets the source to be white and paint the destination
white.</li> white.</li>
<li>9: Sets the line width to be 2.</li> <li>9: Sets the line width to be 2.</li>
<li>10: Sets the source to be black.</li> <li>10: Sets the source to be black.</li>
<li>11: Adds a rectangle to the mask.</li> <li>11-15: Adds a rectangle to the mask.</li>
<li>12: Draws the rectangle with black color to the destination.</li> <li>16: Draws the rectangle with black color to the destination.</li>
</ul> </ul>
<p>Compile and run it, then a window with a black rectangle (square) <p>The program is src/misc/da1.c. Compile and run it, then a window with
appears. Try resizing the window. The square always appears at the a black rectangle (square) appears. Try resizing the window. The square
center of the window because the drawing function is invoked each time always appears at the center of the window because the drawing function
the window is resized.</p> is invoked each time the window is resized.</p>
<figure> <figure>
<img src="image/da1.png" alt="Square in the window" /> <img src="image/da1.png" alt="Square in the window" />
<figcaption aria-hidden="true">Square in the window</figcaption> <figcaption aria-hidden="true">Square in the window</figcaption>

View file

@ -121,7 +121,7 @@ of GtkDrawingArea changes to the color given by you.</p>
<img src="image/color.png" alt="color" /> <img src="image/color.png" alt="color" />
<figcaption aria-hidden="true">color</figcaption> <figcaption aria-hidden="true">color</figcaption>
</figure> </figure>
<p>The following colors are available.</p> <p>The following colors are available. (without new line charactor)</p>
<ul> <ul>
<li>white</li> <li>white</li>
<li>black</li> <li>black</li>
@ -141,10 +141,9 @@ turtle graphics language like Logo program language.</p>
<p>In this section, we focus on how to bind the two objects.</p> <p>In this section, we focus on how to bind the two objects.</p>
<h2 id="color.ui-and-color.gresource.xml">Color.ui and <h2 id="color.ui-and-color.gresource.xml">Color.ui and
color.gresource.xml</h2> color.gresource.xml</h2>
<p>First, We need to make the ui file of the widgets. The image in the <p>First, We need to make the ui file of the widgets. Title bar, four
previous subsection gives us the structure of the widgets. Title bar, buttons in the tool bar, textview and drawing area. The ui file is as
four buttons in the tool bar and two widgets textview and drawing area. follows.</p>
The ui file is as follows.</p>
<div class="sourceCode" id="cb1"><pre <div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb1-1"><a href="#cb1-1"></a><span class="fu">&lt;?xml</span><span class="ot"> version=</span><span class="st">&quot;1.0&quot;</span><span class="ot"> encoding=</span><span class="st">&quot;UTF-8&quot;</span><span class="fu">?&gt;</span></span> class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb1-1"><a href="#cb1-1"></a><span class="fu">&lt;?xml</span><span class="ot"> version=</span><span class="st">&quot;1.0&quot;</span><span class="ot"> encoding=</span><span class="st">&quot;UTF-8&quot;</span><span class="fu">?&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2"></a>&lt;<span class="kw">interface</span>&gt;</span> <span id="cb1-2"><a href="#cb1-2"></a>&lt;<span class="kw">interface</span>&gt;</span>
@ -227,21 +226,22 @@ class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><sp
<span id="cb1-79"><a href="#cb1-79"></a> &lt;/<span class="kw">object</span>&gt;</span> <span id="cb1-79"><a href="#cb1-79"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-80"><a href="#cb1-80"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div> <span id="cb1-80"><a href="#cb1-80"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<ul> <ul>
<li>10-53: This part is the tool bar which has four buttons, <li>10-53: The horizontal box <code>boxh1</code> makes a tool bar which
<code>Run</code>, <code>Open</code>, <code>Save</code> and has four buttons, <code>Run</code>, <code>Open</code>, <code>Save</code>
<code>Close</code>. This is similar to the toolbar of tfe text editor in and <code>Close</code>. This is similar to the <code>tfe</code> text
<a href="sec9.html">Section 9</a>. There are two differences. editor in <a href="sec9.html">Section 9</a>. There are two differences.
<code>Run</code> button replaces <code>New</code> button. A signal <code>Run</code> button replaces <code>New</code> button. A signal
element is added to each button object. It has “name” attribute which is element is added to each button object. It has “name” attribute which is
a signal name and “handler” attribute which is the name of its signal a signal name and “handler” attribute which is the name of its signal
handler function. Options “-WI, export-dynamic” CFLAG is necessary when handler. Options “-WI, export-dynamic” CFLAG is necessary when you
you compile the application. You can achieve this by adding compile the application. You can achieve this by adding “export_dynamic:
“export_dynamic: true” argument to executable function in true” argument to the executable function in <code>meson.build</code>.
<code>meson.build</code>. And be careful that the handler must be And be careful that the handler must be defined without static
defined without static class.</li> class.</li>
<li>54-76: Puts GtkScrolledWindow and GtkDrawingArea into GtkBox. GtkBox <li>54-76: The horizontal box <code>boxh2</code> includes
has “homogeneous property” with TRUE value, so the two children have the GtkScrolledWindow and GtkDrawingArea. GtkBox has “homogeneous property”
same width in the box. TfeTextView is a child of GtkScrolledWindow.</li> with TRUE value, so the two children have the same width in the box.
TfeTextView is a child of GtkScrolledWindow.</li>
</ul> </ul>
<p>The xml file for the resource compiler is almost same as before. Just <p>The xml file for the resource compiler is almost same as before. Just
substitute “color” for “tfe”.</p> substitute “color” for “tfe”.</p>
@ -252,216 +252,278 @@ class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><sp
<span id="cb2-4"><a href="#cb2-4"></a> &lt;<span class="kw">file</span>&gt;color.ui&lt;/<span class="kw">file</span>&gt;</span> <span id="cb2-4"><a href="#cb2-4"></a> &lt;<span class="kw">file</span>&gt;color.ui&lt;/<span class="kw">file</span>&gt;</span>
<span id="cb2-5"><a href="#cb2-5"></a> &lt;/<span class="kw">gresource</span>&gt;</span> <span id="cb2-5"><a href="#cb2-5"></a> &lt;/<span class="kw">gresource</span>&gt;</span>
<span id="cb2-6"><a href="#cb2-6"></a>&lt;/<span class="kw">gresources</span>&gt;</span></code></pre></div> <span id="cb2-6"><a href="#cb2-6"></a>&lt;/<span class="kw">gresources</span>&gt;</span></code></pre></div>
<h2 id="tfetextview.h-tfetextview.c-and-color.h">Tfetextview.h, <h2 id="drawing-function-and-surface">Drawing function and surface</h2>
tfetextview.c and color.h</h2> <p>The main point of this program is a drawing function.</p>
<p>First two files are the same as before. Color.h just includes
tfetextview.h.</p>
<div class="sourceCode" id="cb3"><pre <div class="sourceCode" id="cb3"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-2"><a href="#cb3-2"></a></span> <span id="cb3-2"><a href="#cb3-2"></a>draw_func <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> cairo_t <span class="op">*</span>cr<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3"></a><span class="pp">#include </span><span class="im">&quot;../tfetextview/tfetextview.h&quot;</span></span></code></pre></div> <span id="cb3-3"><a href="#cb3-3"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span>
<h2 id="colorapplication.c">Colorapplication.c</h2> <span id="cb3-4"><a href="#cb3-4"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<p>This is the main file. It deals with:</p> <span id="cb3-5"><a href="#cb3-5"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb3-6"><a href="#cb3-6"></a> <span class="op">}</span></span>
<span id="cb3-7"><a href="#cb3-7"></a><span class="op">}</span></span></code></pre></div>
<p>The <code>surface</code> variable in line 3 is a static variable.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> cairo_surface_t <span class="op">*</span>surface <span class="op">=</span> NULL<span class="op">;</span></span></code></pre></div>
<p>The drawing function just copies the <code>surface</code> to its own
surface with the <code>cairo_paint</code> function. The surface (pointed
by the static variable <code>surface</code>) is built by the
<code>run</code> function.</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-2"><a href="#cb5-2"></a>run <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb5-4"><a href="#cb5-4"></a> GtkTextIter start_iter<span class="op">;</span></span>
<span id="cb5-5"><a href="#cb5-5"></a> GtkTextIter end_iter<span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb5-7"><a href="#cb5-7"></a> cairo_t <span class="op">*</span>cr<span class="op">;</span></span>
<span id="cb5-8"><a href="#cb5-8"></a></span>
<span id="cb5-9"><a href="#cb5-9"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> cr <span class="op">=</span> cairo_create <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb5-13"><a href="#cb5-13"></a> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;red&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-14"><a href="#cb5-14"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb5-15"><a href="#cb5-15"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;green&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-16"><a href="#cb5-16"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb5-17"><a href="#cb5-17"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;blue&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-18"><a href="#cb5-18"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb5-19"><a href="#cb5-19"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;white&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-20"><a href="#cb5-20"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb5-21"><a href="#cb5-21"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;black&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-22"><a href="#cb5-22"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb5-23"><a href="#cb5-23"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;light&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-24"><a href="#cb5-24"></a> cairo_set_source_rgba <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="fl">0.5</span><span class="op">);</span></span>
<span id="cb5-25"><a href="#cb5-25"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;dark&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-26"><a href="#cb5-26"></a> cairo_set_source_rgba <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="fl">0.5</span><span class="op">);</span></span>
<span id="cb5-27"><a href="#cb5-27"></a> <span class="cf">else</span></span>
<span id="cb5-28"><a href="#cb5-28"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb5-29"><a href="#cb5-29"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb5-30"><a href="#cb5-30"></a> cairo_destroy <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb5-31"><a href="#cb5-31"></a> <span class="op">}</span></span>
<span id="cb5-32"><a href="#cb5-32"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb5-33"><a href="#cb5-33"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>Building widgets by GtkBuilder.</li> <li>9-10: Gets the string in the GtkTextBuffer and inserts it to
<li>Setting a drawing function of GtkDrawingArea. And connecting a <code>contents</code>.</li>
handler to “resize” signal on GtkDrawingArea.</li> <li>11: If the variable <code>surface</code> points a surface instance,
<li>Implementing each call back functions. Particularly, it is painted as follows.</li>
<code>Run</code> signal handler is the point in this program.</li> <li>12- 30: The source is set based on the string <code>contents</code>
and copied to the surface with <code>cairo_paint</code>.</li>
<li>24,26: Alpha channel is used in “light” and “dark” procedure.</li>
</ul>
<p>The drawing area just reflects the <code>surface</code>. But one
problem is resizing. If a user resizes the main window, the drawing area
is also resized. It makes size difference between the surface and the
drawing area. So, the surface needs to be resized to fit the drawing
area.</p>
<p>It is accomplished by connecting the “resize” signal on the drawing
area to a handler.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>g_signal_connect <span class="op">(</span>GTK_DRAWING_AREA <span class="op">(</span>da<span class="op">),</span> <span class="st">&quot;resize&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>resize_cb<span class="op">),</span> NULL<span class="op">);</span></span></code></pre></div>
<p>The handler is as follows.</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb7-2"><a href="#cb7-2"></a>resize_cb <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-3"><a href="#cb7-3"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span></span>
<span id="cb7-4"><a href="#cb7-4"></a> cairo_surface_destroy <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb7-5"><a href="#cb7-5"></a> surface <span class="op">=</span> cairo_image_surface_create <span class="op">(</span>CAIRO_FORMAT_ARGB32<span class="op">,</span> width<span class="op">,</span> height<span class="op">);</span></span>
<span id="cb7-6"><a href="#cb7-6"></a> run <span class="op">();</span></span>
<span id="cb7-7"><a href="#cb7-7"></a><span class="op">}</span></span></code></pre></div>
<p>If the variable <code>surface</code> sets a surface instance, it is
destroyed. A new surface is created and its size fits the drawing area.
The surface is assigned to the variable <code>surface</code>. The
function <code>run</code> is called and the surface is colored.</p>
<p>The signal is emitted when:</p>
<ul>
<li>The drawing area is realized (it appears on the display).</li>
<li>It is changed (resized) while realized</li>
</ul>
<p>So, the first surface is created when it is realized.</p>
<h2 id="colorapplication.c">Colorapplication.c</h2>
<p>This is the main file.</p>
<ul>
<li>Builds widgets by GtkBuilder.</li>
<li>Sets a drawing function for GtkDrawingArea. And connects a handler
to the “resize” signal on the GtkDrawingArea instance.</li>
<li>Implements each call back function. Particularly, <code>Run</code>
signal handler is the point in this program.</li>
</ul> </ul>
<p>The following is <code>colorapplication.c</code>.</p> <p>The following is <code>colorapplication.c</code>.</p>
<div class="sourceCode" id="cb4"><pre <div class="sourceCode" id="cb8"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a><span class="pp">#include </span><span class="im">&quot;color.h&quot;</span></span> class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb4-2"><a href="#cb4-2"></a></span> <span id="cb8-2"><a href="#cb8-2"></a><span class="pp">#include </span><span class="im">&quot;../tfetextview/tfetextview.h&quot;</span></span>
<span id="cb4-3"><a href="#cb4-3"></a><span class="dt">static</span> GtkWidget <span class="op">*</span>win<span class="op">;</span></span> <span id="cb8-3"><a href="#cb8-3"></a></span>
<span id="cb4-4"><a href="#cb4-4"></a><span class="dt">static</span> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span> <span id="cb8-4"><a href="#cb8-4"></a><span class="dt">static</span> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5"></a><span class="dt">static</span> GtkWidget <span class="op">*</span>da<span class="op">;</span></span> <span id="cb8-5"><a href="#cb8-5"></a><span class="dt">static</span> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb4-6"><a href="#cb4-6"></a></span> <span id="cb8-6"><a href="#cb8-6"></a><span class="dt">static</span> GtkWidget <span class="op">*</span>da<span class="op">;</span></span>
<span id="cb4-7"><a href="#cb4-7"></a><span class="dt">static</span> cairo_surface_t <span class="op">*</span>surface <span class="op">=</span> NULL<span class="op">;</span></span> <span id="cb8-7"><a href="#cb8-7"></a></span>
<span id="cb4-8"><a href="#cb4-8"></a></span> <span id="cb8-8"><a href="#cb8-8"></a><span class="dt">static</span> cairo_surface_t <span class="op">*</span>surface <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb4-9"><a href="#cb4-9"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb8-9"><a href="#cb8-9"></a></span>
<span id="cb4-10"><a href="#cb4-10"></a>run <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span> <span id="cb8-10"><a href="#cb8-10"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span> <span id="cb8-11"><a href="#cb8-11"></a>run <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-12"><a href="#cb4-12"></a> GtkTextIter start_iter<span class="op">;</span></span> <span id="cb8-12"><a href="#cb8-12"></a> GtkTextBuffer <span class="op">*</span>tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb4-13"><a href="#cb4-13"></a> GtkTextIter end_iter<span class="op">;</span></span> <span id="cb8-13"><a href="#cb8-13"></a> GtkTextIter start_iter<span class="op">;</span></span>
<span id="cb4-14"><a href="#cb4-14"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span> <span id="cb8-14"><a href="#cb8-14"></a> GtkTextIter end_iter<span class="op">;</span></span>
<span id="cb4-15"><a href="#cb4-15"></a> cairo_t <span class="op">*</span>cr<span class="op">;</span></span> <span id="cb8-15"><a href="#cb8-15"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb4-16"><a href="#cb4-16"></a></span> <span id="cb8-16"><a href="#cb8-16"></a> cairo_t <span class="op">*</span>cr<span class="op">;</span></span>
<span id="cb4-17"><a href="#cb4-17"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span> <span id="cb8-17"><a href="#cb8-17"></a></span>
<span id="cb4-18"><a href="#cb4-18"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span> <span id="cb8-18"><a href="#cb8-18"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span>
<span id="cb4-19"><a href="#cb4-19"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-19"><a href="#cb8-19"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span>
<span id="cb4-20"><a href="#cb4-20"></a> cr <span class="op">=</span> cairo_create <span class="op">(</span>surface<span class="op">);</span></span> <span id="cb8-20"><a href="#cb8-20"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-21"><a href="#cb4-21"></a> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;red&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span> <span id="cb8-21"><a href="#cb8-21"></a> cr <span class="op">=</span> cairo_create <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb4-22"><a href="#cb4-22"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span> <span id="cb8-22"><a href="#cb8-22"></a> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;red&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb4-23"><a href="#cb4-23"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;green&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span> <span id="cb8-23"><a href="#cb8-23"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb4-24"><a href="#cb4-24"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span> <span id="cb8-24"><a href="#cb8-24"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;green&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb4-25"><a href="#cb4-25"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;blue&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span> <span id="cb8-25"><a href="#cb8-25"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb4-26"><a href="#cb4-26"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span> <span id="cb8-26"><a href="#cb8-26"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;blue&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb4-27"><a href="#cb4-27"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;white&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span> <span id="cb8-27"><a href="#cb8-27"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb4-28"><a href="#cb4-28"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span> <span id="cb8-28"><a href="#cb8-28"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;white&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb4-29"><a href="#cb4-29"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;black&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span> <span id="cb8-29"><a href="#cb8-29"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb4-30"><a href="#cb4-30"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span> <span id="cb8-30"><a href="#cb8-30"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;black&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb4-31"><a href="#cb4-31"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;light&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span> <span id="cb8-31"><a href="#cb8-31"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb4-32"><a href="#cb4-32"></a> cairo_set_source_rgba <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="fl">0.5</span><span class="op">);</span></span> <span id="cb8-32"><a href="#cb8-32"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;light&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb4-33"><a href="#cb4-33"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;dark&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span> <span id="cb8-33"><a href="#cb8-33"></a> cairo_set_source_rgba <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="fl">0.5</span><span class="op">);</span></span>
<span id="cb4-34"><a href="#cb4-34"></a> cairo_set_source_rgba <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="fl">0.5</span><span class="op">);</span></span> <span id="cb8-34"><a href="#cb8-34"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;dark&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb4-35"><a href="#cb4-35"></a> <span class="cf">else</span></span> <span id="cb8-35"><a href="#cb8-35"></a> cairo_set_source_rgba <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="fl">0.5</span><span class="op">);</span></span>
<span id="cb4-36"><a href="#cb4-36"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span> <span id="cb8-36"><a href="#cb8-36"></a> <span class="cf">else</span></span>
<span id="cb4-37"><a href="#cb4-37"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span> <span id="cb8-37"><a href="#cb8-37"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb4-38"><a href="#cb4-38"></a> cairo_destroy <span class="op">(</span>cr<span class="op">);</span></span> <span id="cb8-38"><a href="#cb8-38"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb4-39"><a href="#cb4-39"></a> <span class="op">}</span></span> <span id="cb8-39"><a href="#cb8-39"></a> cairo_destroy <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb4-40"><a href="#cb4-40"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span> <span id="cb8-40"><a href="#cb8-40"></a> <span class="op">}</span></span>
<span id="cb4-41"><a href="#cb4-41"></a><span class="op">}</span></span> <span id="cb8-41"><a href="#cb8-41"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb4-42"><a href="#cb4-42"></a></span> <span id="cb8-42"><a href="#cb8-42"></a><span class="op">}</span></span>
<span id="cb4-43"><a href="#cb4-43"></a><span class="dt">void</span></span> <span id="cb8-43"><a href="#cb8-43"></a></span>
<span id="cb4-44"><a href="#cb4-44"></a>run_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btnr<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-44"><a href="#cb8-44"></a><span class="dt">void</span></span>
<span id="cb4-45"><a href="#cb4-45"></a> run <span class="op">();</span></span> <span id="cb8-45"><a href="#cb8-45"></a>run_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btnr<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-46"><a href="#cb4-46"></a> gtk_widget_queue_draw <span class="op">(</span>GTK_WIDGET <span class="op">(</span>da<span class="op">));</span></span> <span id="cb8-46"><a href="#cb8-46"></a> run <span class="op">();</span></span>
<span id="cb4-47"><a href="#cb4-47"></a><span class="op">}</span></span> <span id="cb8-47"><a href="#cb8-47"></a> gtk_widget_queue_draw <span class="op">(</span>GTK_WIDGET <span class="op">(</span>da<span class="op">));</span></span>
<span id="cb4-48"><a href="#cb4-48"></a></span> <span id="cb8-48"><a href="#cb8-48"></a><span class="op">}</span></span>
<span id="cb4-49"><a href="#cb4-49"></a><span class="dt">void</span></span> <span id="cb8-49"><a href="#cb8-49"></a></span>
<span id="cb4-50"><a href="#cb4-50"></a>open_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btno<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-50"><a href="#cb8-50"></a><span class="dt">void</span></span>
<span id="cb4-51"><a href="#cb4-51"></a> tfe_text_view_open <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span> <span id="cb8-51"><a href="#cb8-51"></a>open_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btno<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-52"><a href="#cb4-52"></a><span class="op">}</span></span> <span id="cb8-52"><a href="#cb8-52"></a> tfe_text_view_open <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb4-53"><a href="#cb4-53"></a></span> <span id="cb8-53"><a href="#cb8-53"></a><span class="op">}</span></span>
<span id="cb4-54"><a href="#cb4-54"></a><span class="dt">void</span></span> <span id="cb8-54"><a href="#cb8-54"></a></span>
<span id="cb4-55"><a href="#cb4-55"></a>save_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btns<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-55"><a href="#cb8-55"></a><span class="dt">void</span></span>
<span id="cb4-56"><a href="#cb4-56"></a> tfe_text_view_save <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span> <span id="cb8-56"><a href="#cb8-56"></a>save_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btns<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-57"><a href="#cb4-57"></a><span class="op">}</span></span> <span id="cb8-57"><a href="#cb8-57"></a> tfe_text_view_save <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb4-58"><a href="#cb4-58"></a></span> <span id="cb8-58"><a href="#cb8-58"></a><span class="op">}</span></span>
<span id="cb4-59"><a href="#cb4-59"></a><span class="dt">void</span></span> <span id="cb8-59"><a href="#cb8-59"></a></span>
<span id="cb4-60"><a href="#cb4-60"></a>close_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btnc<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-60"><a href="#cb8-60"></a><span class="dt">void</span></span>
<span id="cb4-61"><a href="#cb4-61"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span></span> <span id="cb8-61"><a href="#cb8-61"></a>close_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btnc<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-62"><a href="#cb4-62"></a> cairo_surface_destroy <span class="op">(</span>surface<span class="op">);</span></span> <span id="cb8-62"><a href="#cb8-62"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb4-63"><a href="#cb4-63"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span> <span id="cb8-63"><a href="#cb8-63"></a><span class="op">}</span></span>
<span id="cb4-64"><a href="#cb4-64"></a><span class="op">}</span></span> <span id="cb8-64"><a href="#cb8-64"></a></span>
<span id="cb4-65"><a href="#cb4-65"></a></span> <span id="cb8-65"><a href="#cb8-65"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-66"><a href="#cb4-66"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb8-66"><a href="#cb8-66"></a>resize_cb <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-67"><a href="#cb4-67"></a>resize_cb <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-67"><a href="#cb8-67"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span></span>
<span id="cb4-68"><a href="#cb4-68"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span></span> <span id="cb8-68"><a href="#cb8-68"></a> cairo_surface_destroy <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb4-69"><a href="#cb4-69"></a> cairo_surface_destroy <span class="op">(</span>surface<span class="op">);</span></span> <span id="cb8-69"><a href="#cb8-69"></a> surface <span class="op">=</span> cairo_image_surface_create <span class="op">(</span>CAIRO_FORMAT_ARGB32<span class="op">,</span> width<span class="op">,</span> height<span class="op">);</span></span>
<span id="cb4-70"><a href="#cb4-70"></a> surface <span class="op">=</span> cairo_image_surface_create <span class="op">(</span>CAIRO_FORMAT_ARGB32<span class="op">,</span> width<span class="op">,</span> height<span class="op">);</span></span> <span id="cb8-70"><a href="#cb8-70"></a> run <span class="op">();</span></span>
<span id="cb4-71"><a href="#cb4-71"></a> run <span class="op">();</span></span> <span id="cb8-71"><a href="#cb8-71"></a><span class="op">}</span></span>
<span id="cb4-72"><a href="#cb4-72"></a><span class="op">}</span></span> <span id="cb8-72"><a href="#cb8-72"></a></span>
<span id="cb4-73"><a href="#cb4-73"></a></span> <span id="cb8-73"><a href="#cb8-73"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-74"><a href="#cb4-74"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb8-74"><a href="#cb8-74"></a>draw_func <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> cairo_t <span class="op">*</span>cr<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-75"><a href="#cb4-75"></a>draw_func <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> cairo_t <span class="op">*</span>cr<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-75"><a href="#cb8-75"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-76"><a href="#cb4-76"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-76"><a href="#cb8-76"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb4-77"><a href="#cb4-77"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span> <span id="cb8-77"><a href="#cb8-77"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb4-78"><a href="#cb4-78"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span> <span id="cb8-78"><a href="#cb8-78"></a> <span class="op">}</span></span>
<span id="cb4-79"><a href="#cb4-79"></a> <span class="op">}</span></span> <span id="cb8-79"><a href="#cb8-79"></a><span class="op">}</span></span>
<span id="cb4-80"><a href="#cb4-80"></a><span class="op">}</span></span> <span id="cb8-80"><a href="#cb8-80"></a></span>
<span id="cb4-81"><a href="#cb4-81"></a></span> <span id="cb8-81"><a href="#cb8-81"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-82"><a href="#cb4-82"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb8-82"><a href="#cb8-82"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-83"><a href="#cb4-83"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-83"><a href="#cb8-83"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb4-84"><a href="#cb4-84"></a> gtk_widget_show <span class="op">(</span>win<span class="op">);</span></span> <span id="cb8-84"><a href="#cb8-84"></a><span class="op">}</span></span>
<span id="cb4-85"><a href="#cb4-85"></a><span class="op">}</span></span> <span id="cb8-85"><a href="#cb8-85"></a></span>
<span id="cb4-86"><a href="#cb4-86"></a></span> <span id="cb8-86"><a href="#cb8-86"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-87"><a href="#cb4-87"></a><span class="dt">static</span> <span class="dt">void</span></span> <span id="cb8-87"><a href="#cb8-87"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-88"><a href="#cb4-88"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-88"><a href="#cb8-88"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
<span id="cb4-89"><a href="#cb4-89"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span> <span id="cb8-89"><a href="#cb8-89"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb4-90"><a href="#cb4-90"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span> <span id="cb8-90"><a href="#cb8-90"></a> GdkDisplay <span class="op">*</span>display<span class="op">;</span></span>
<span id="cb4-91"><a href="#cb4-91"></a></span> <span id="cb8-91"><a href="#cb8-91"></a></span>
<span id="cb4-92"><a href="#cb4-92"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/color/color.ui&quot;</span><span class="op">);</span></span> <span id="cb8-92"><a href="#cb8-92"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/color/color.ui&quot;</span><span class="op">);</span></span>
<span id="cb4-93"><a href="#cb4-93"></a> win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;win&quot;</span><span class="op">));</span></span> <span id="cb8-93"><a href="#cb8-93"></a> win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;win&quot;</span><span class="op">));</span></span>
<span id="cb4-94"><a href="#cb4-94"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> app<span class="op">);</span></span> <span id="cb8-94"><a href="#cb8-94"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> app<span class="op">);</span></span>
<span id="cb4-95"><a href="#cb4-95"></a> tv <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;tv&quot;</span><span class="op">));</span></span> <span id="cb8-95"><a href="#cb8-95"></a> tv <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;tv&quot;</span><span class="op">));</span></span>
<span id="cb4-96"><a href="#cb4-96"></a> da <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;da&quot;</span><span class="op">));</span></span> <span id="cb8-96"><a href="#cb8-96"></a> da <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;da&quot;</span><span class="op">));</span></span>
<span id="cb4-97"><a href="#cb4-97"></a> g_object_unref<span class="op">(</span>build<span class="op">);</span></span> <span id="cb8-97"><a href="#cb8-97"></a> g_object_unref<span class="op">(</span>build<span class="op">);</span></span>
<span id="cb4-98"><a href="#cb4-98"></a> g_signal_connect <span class="op">(</span>GTK_DRAWING_AREA <span class="op">(</span>da<span class="op">),</span> <span class="st">&quot;resize&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>resize_cb<span class="op">),</span> NULL<span class="op">);</span></span> <span id="cb8-98"><a href="#cb8-98"></a> g_signal_connect <span class="op">(</span>GTK_DRAWING_AREA <span class="op">(</span>da<span class="op">),</span> <span class="st">&quot;resize&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>resize_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb4-99"><a href="#cb4-99"></a> gtk_drawing_area_set_draw_func <span class="op">(</span>GTK_DRAWING_AREA <span class="op">(</span>da<span class="op">),</span> draw_func<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span> <span id="cb8-99"><a href="#cb8-99"></a> gtk_drawing_area_set_draw_func <span class="op">(</span>GTK_DRAWING_AREA <span class="op">(</span>da<span class="op">),</span> draw_func<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb4-100"><a href="#cb4-100"></a></span> <span id="cb8-100"><a href="#cb8-100"></a></span>
<span id="cb4-101"><a href="#cb4-101"></a>GdkDisplay <span class="op">*</span>display<span class="op">;</span></span> <span id="cb8-101"><a href="#cb8-101"></a> display <span class="op">=</span> gdk_display_get_default <span class="op">();</span></span>
<span id="cb4-102"><a href="#cb4-102"></a></span> <span id="cb8-102"><a href="#cb8-102"></a> GtkCssProvider <span class="op">*</span>provider <span class="op">=</span> gtk_css_provider_new <span class="op">();</span></span>
<span id="cb4-103"><a href="#cb4-103"></a> display <span class="op">=</span> gtk_widget_get_display <span class="op">(</span>GTK_WIDGET <span class="op">(</span>win<span class="op">));</span></span> <span id="cb8-103"><a href="#cb8-103"></a> gtk_css_provider_load_from_data <span class="op">(</span>provider<span class="op">,</span> <span class="st">&quot;textview {padding: 10px; font-family: monospace; font-size: 12pt;}&quot;</span><span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb4-104"><a href="#cb4-104"></a> GtkCssProvider <span class="op">*</span>provider <span class="op">=</span> gtk_css_provider_new <span class="op">();</span></span> <span id="cb8-104"><a href="#cb8-104"></a> gtk_style_context_add_provider_for_display <span class="op">(</span>display<span class="op">,</span> GTK_STYLE_PROVIDER <span class="op">(</span>provider<span class="op">),</span> GTK_STYLE_PROVIDER_PRIORITY_USER<span class="op">);</span></span>
<span id="cb4-105"><a href="#cb4-105"></a> gtk_css_provider_load_from_data <span class="op">(</span>provider<span class="op">,</span> <span class="st">&quot;textview {padding: 10px; font-family: monospace; font-size: 12pt;}&quot;</span><span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span> <span id="cb8-105"><a href="#cb8-105"></a><span class="op">}</span></span>
<span id="cb4-106"><a href="#cb4-106"></a> gtk_style_context_add_provider_for_display <span class="op">(</span>display<span class="op">,</span> GTK_STYLE_PROVIDER <span class="op">(</span>provider<span class="op">),</span> GTK_STYLE_PROVIDER_PRIORITY_USER<span class="op">);</span></span> <span id="cb8-106"><a href="#cb8-106"></a></span>
<span id="cb4-107"><a href="#cb4-107"></a><span class="op">}</span></span> <span id="cb8-107"><a href="#cb8-107"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-108"><a href="#cb4-108"></a></span> <span id="cb8-108"><a href="#cb8-108"></a>app_shutdown <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-109"><a href="#cb4-109"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.color&quot;</span></span> <span id="cb8-109"><a href="#cb8-109"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span></span>
<span id="cb4-110"><a href="#cb4-110"></a></span> <span id="cb8-110"><a href="#cb8-110"></a> cairo_surface_destroy <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb4-111"><a href="#cb4-111"></a><span class="dt">int</span></span> <span id="cb8-111"><a href="#cb8-111"></a><span class="op">}</span></span>
<span id="cb4-112"><a href="#cb4-112"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span> <span id="cb8-112"><a href="#cb8-112"></a></span>
<span id="cb4-113"><a href="#cb4-113"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span> <span id="cb8-113"><a href="#cb8-113"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.color&quot;</span></span>
<span id="cb4-114"><a href="#cb4-114"></a> <span class="dt">int</span> stat<span class="op">;</span></span> <span id="cb8-114"><a href="#cb8-114"></a></span>
<span id="cb4-115"><a href="#cb4-115"></a></span> <span id="cb8-115"><a href="#cb8-115"></a><span class="dt">int</span></span>
<span id="cb4-116"><a href="#cb4-116"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_FLAGS_NONE<span class="op">);</span></span> <span id="cb8-116"><a href="#cb8-116"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-117"><a href="#cb4-117"></a></span> <span id="cb8-117"><a href="#cb8-117"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb4-118"><a href="#cb4-118"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_startup<span class="op">),</span> NULL<span class="op">);</span></span> <span id="cb8-118"><a href="#cb8-118"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb4-119"><a href="#cb4-119"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span> <span id="cb8-119"><a href="#cb8-119"></a></span>
<span id="cb4-120"><a href="#cb4-120"></a></span> <span id="cb8-120"><a href="#cb8-120"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
<span id="cb4-121"><a href="#cb4-121"></a> stat <span class="op">=</span>g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span> <span id="cb8-121"><a href="#cb8-121"></a></span>
<span id="cb4-122"><a href="#cb4-122"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span> <span id="cb8-122"><a href="#cb8-122"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_startup<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb4-123"><a href="#cb4-123"></a> <span class="cf">return</span> stat<span class="op">;</span></span> <span id="cb8-123"><a href="#cb8-123"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;shutdown&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_shutdown<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb4-124"><a href="#cb4-124"></a><span class="op">}</span></span></code></pre></div> <span id="cb8-124"><a href="#cb8-124"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb8-125"><a href="#cb8-125"></a></span>
<span id="cb8-126"><a href="#cb8-126"></a> stat <span class="op">=</span>g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
<span id="cb8-127"><a href="#cb8-127"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb8-128"><a href="#cb8-128"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb8-129"><a href="#cb8-129"></a><span class="op">}</span></span></code></pre></div>
<ul> <ul>
<li>109-124: The function <code>main</code> is almost same as before but <li>4-8: Win, tv, da and surface are defined as static variables.</li>
there are some differences. The application ID is <li>10-42: Run function.</li>
“com.github.ToshioCP.color”. <code>G_APPLICATION_FLAGS_NONE</code> is <li>44-63: Handlers for button signals.</li>
specified so no open signal handler is necessary.</li> <li>65-71: Resize handler.</li>
<li>87-107: Startup handler.</li> <li>73-79: Drawing function.</li>
<li>92-97: Builds widgets. The pointers of the top window, TfeTextView <li>81-84: Application activate handler. It just shows the main
and GtkDrawingArea objects are stored to static variables window.</li>
<code>win</code>, <code>tv</code> and <code>da</code> respectively. This <li>86-105: Application startup handler.</li>
is because these objects are often used in handlers. They never be <li>92- 97: It builds widgets according to the ui resource. The static
rewritten so theyre thread safe.</li> variables win, tv and da are assigned instances.</li>
<li>98: connects “resize” signal and the handler.</li> <li>98: Connects “resize” signal and a handler.</li>
<li>99: sets the drawing function.</li> <li>99: Drawing function is set.</li>
<li>82-85: Activate handler, which just shows the widgets.</li> <li>101-104: CSS for textview padding is set.</li>
<li>74-80: The drawing function. It just copies <code>surface</code> to <li>107-111: Application shutdown handler. If there exists a surface
destination.</li> instance, it will be destroyed.</li>
<li>66-72: Resize handler. Re-creates the surface to fit its width and <li>116-129: A function <code>main</code>. It creates a new application
height for the drawing area and paints by calling the function instance. And connects three signals startup, shutdown and activate to
<code>run</code>.</li> their handlers. It runs the application. It releases the reference to
<li>59-64: Close handler. It destroys <code>surface</code> if it exists. the application and returns with <code>stat</code> value.</li>
Then it destroys the top-level window and quits the application.</li>
<li>49-57: Open and save handler. They just call the corresponding
functions of TfeTextView.</li>
<li>43-47: Run handler. It calls run function to paint the surface.
After that <code>gtk_widget_queue_draw</code> is called. This function
adds the widget (GtkDrawingArea) to the queue to be redrawn. It is
important to know that the window is redrawn whenever it is necessary.
For example, when another window is moved and uncovers part of the
widget, or when the window containing it is resized. But repainting
<code>surface</code> is not automatically notified to gtk. Therefore,
you need to call <code>gtk_widget_queue_draw</code> to redraw the
widget.</li>
<li>9-41: Run function paints the surface. First, it gets the contents
of GtkTextBuffer. Then it compares it to “red”, “green” and so on. If it
matches the color, then the surface is painted the color. If it matches
“light” or “dark”, then the color of the surface is lightened or
darkened respectively. Alpha channel is used.</li>
</ul> </ul>
<h2 id="meson.build">Meson.build</h2> <h2 id="meson.build">Meson.build</h2>
<p>This file is almost same as before. An argument “export_dynamic: <p>This file is almost same as before. An argument “export_dynamic:
true” is added to executable function.</p> true” is added to executable function.</p>
<div class="sourceCode" id="cb5"><pre <div class="sourceCode" id="cb9"><pre
class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb5-1"><a href="#cb5-1"></a>project(&#39;color&#39;, &#39;c&#39;)</span> class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb9-1"><a href="#cb9-1"></a>project(&#39;color&#39;, &#39;c&#39;)</span>
<span id="cb5-2"><a href="#cb5-2"></a></span> <span id="cb9-2"><a href="#cb9-2"></a></span>
<span id="cb5-3"><a href="#cb5-3"></a>gtkdep = dependency(&#39;gtk4&#39;)</span> <span id="cb9-3"><a href="#cb9-3"></a>gtkdep = dependency(&#39;gtk4&#39;)</span>
<span id="cb5-4"><a href="#cb5-4"></a></span> <span id="cb9-4"><a href="#cb9-4"></a></span>
<span id="cb5-5"><a href="#cb5-5"></a>gnome=import(&#39;gnome&#39;)</span> <span id="cb9-5"><a href="#cb9-5"></a>gnome=import(&#39;gnome&#39;)</span>
<span id="cb5-6"><a href="#cb5-6"></a>resources = gnome.compile_resources(&#39;resources&#39;,&#39;color.gresource.xml&#39;)</span> <span id="cb9-6"><a href="#cb9-6"></a>resources = gnome.compile_resources(&#39;resources&#39;,&#39;color.gresource.xml&#39;)</span>
<span id="cb5-7"><a href="#cb5-7"></a></span> <span id="cb9-7"><a href="#cb9-7"></a></span>
<span id="cb5-8"><a href="#cb5-8"></a>sourcefiles=files(&#39;colorapplication.c&#39;, &#39;../tfetextview/tfetextview.c&#39;)</span> <span id="cb9-8"><a href="#cb9-8"></a>sourcefiles=files(&#39;colorapplication.c&#39;, &#39;../tfetextview/tfetextview.c&#39;)</span>
<span id="cb5-9"><a href="#cb5-9"></a></span> <span id="cb9-9"><a href="#cb9-9"></a></span>
<span id="cb5-10"><a href="#cb5-10"></a>executable(&#39;color&#39;, sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)</span></code></pre></div> <span id="cb9-10"><a href="#cb9-10"></a>executable(&#39;color&#39;, sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)</span></code></pre></div>
<h2 id="compile-and-execute-it">Compile and execute it</h2> <h2 id="build-and-try">Build and try</h2>
<p>First you need to export some variables (refer to <a <p>Type the following to compile the program.</p>
href="sec2.html">Section 2</a>) if youve installed GTK 4 from the
source. If youve installed GTK 4 from the distribution packages, you
dont need to do this.</p>
<pre><code>$ . env.sh</code></pre>
<p>Then type the following to compile it.</p>
<pre><code>$ meson _build <pre><code>$ meson _build
$ ninja -C _build</code></pre> $ ninja -C _build</code></pre>
<p>The application is made in <code>_build</code> directory. Type the <p>The application is made in <code>_build</code> directory. Type the
following to execute it.</p> following to execute it.</p>
<pre><code>$ _build/color</code></pre> <pre><code>$ _build/color</code></pre>
<p>Type “red”, “green”, “blue”, “white”, black”, “light” or “dark” in <p>Type “red”, “green”, “blue”, “white”, black”, “light” or “dark” in
the TfeTextView. Then, click on <code>Run</code> button. Make sure the the TfeTextView. No new line charactor is needed. Then, click on the
color of GtkDrawingArea changes.</p> <code>Run</code> button. Make sure the color of GtkDrawingArea
changes.</p>
<p>In this program TfeTextView is used to change the color. You can use <p>In this program TfeTextView is used to change the color. You can use
buttons or menus instead of textview. Probably it is more appropriate. buttons or menus instead of textview. Probably it is more appropriate.
Using textview is unnatural. It is a good practice to make such Using textview is unnatural. It is a good practice to make such

File diff suppressed because it is too large Load diff

View file

@ -9,17 +9,17 @@ This is called custom drawing.
GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions. GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions.
In this section, I will explain: In this section, I will explain:
1. Cairo, but only briefly; and 1. Cairo, but only briefly
2. GtkDrawingArea, with a very simple example. 2. GtkDrawingArea, with a very simple example.
## Cairo ## Cairo
Cairo is a set of two dimensional graphical drawing functions (or graphics library). Cairo is a set of two dimensional graphical drawing functions (or graphics library).
There is a lot of documentation on [Cairo's website](https://www.cairographics.org/). There are a lot of documents on [Cairo's website](https://www.cairographics.org/).
If you aren't familiar with Cairo, it is worth reading their [tutorial](https://www.cairographics.org/tutorial/). If you aren't familiar with Cairo, it is worth reading the [tutorial](https://www.cairographics.org/tutorial/).
The following is a gentle introduction to the Cairo library and how to use it. The following is an introduction to the Cairo library and how to use it.
Firstly, in order to use Cairo you need to know about surfaces, sources, masks, destinations, cairo context and transformations. First, you need to know about surfaces, sources, masks, destinations, cairo context and transformations.
- A surface represents an image. - A surface represents an image.
It is like a canvas. It is like a canvas.
@ -50,6 +50,7 @@ This will be the destination.
6. Save the destination surface to a file if necessary. 6. Save the destination surface to a file if necessary.
Here's a simple example program that draws a small square and saves it as a png file. Here's a simple example program that draws a small square and saves it as a png file.
The path of the file is `src/misc/cairo.c`.
~~~C ~~~C
1 #include <cairo.h> 1 #include <cairo.h>
@ -103,7 +104,7 @@ Width and height are in pixels and given as integers.
- 14: Creates cairo context. - 14: Creates cairo context.
The surface given as an argument will be the destination of the context. The surface given as an argument will be the destination of the context.
- 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint. - 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint.
The second to fourth argument are red, green and blue color values respectively, and they are The second to fourth arguments are red, green and blue color values respectively, and they are
of type float. The values are between zero (0.0) and one (1.0), with of type float. The values are between zero (0.0) and one (1.0), with
black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0). black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).
- 19: `cairo_paint` copies everywhere in the source to destination. - 19: `cairo_paint` copies everywhere in the source to destination.
@ -120,13 +121,13 @@ The square is located at the center.
- 28: Destroys the context. At the same time the source is destroyed. - 28: Destroys the context. At the same time the source is destroyed.
- 29: Destroys the surface. - 29: Destroys the surface.
To compile this, type the following. To compile this, change your current directory to `src/misc` and type the following.
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo` $ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
![rectangle.png](../image/rectangle.png) ![rectangle.png](../image/rectangle.png)
See the [Cairo's website](https://www.cairographics.org/) for more details. See the [Cairo's website](https://www.cairographics.org/) for further information.
## GtkDrawingArea ## GtkDrawingArea
@ -181,12 +182,12 @@ The following is a very simple example.
The function `main` is almost same as before. The function `main` is almost same as before.
The two functions `app_activate` and `draw_function` are important in this example. The two functions `app_activate` and `draw_function` are important in this example.
- 18: Creates a GtkDrawingArea instance; and - 22: Creates a GtkDrawingArea instance.
- 21: Sets a drawing function of the widget. - 25: Sets a drawing function of the widget.
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary. GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size. For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size.
Then, the whole window needs to be redrawn. Then, the whole window needs to be redrawn.
For the information of `gtk_drawing_area_set_draw_func`, see [Gtk API Reference, gtk\_drawing\_area\_set\_draw\_func](https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html). For the information of `gtk_drawing_area_set_draw_func`, see [Gtk API Reference -- gtk\_drawing\_area\_set\_draw\_func](https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html).
The drawing function has five parameters. The drawing function has five parameters.
@ -201,15 +202,16 @@ The second parameter is a cairo context given by the widget.
The destination surface of the context is connected to the contents of the widget. The destination surface of the context is connected to the contents of the widget.
What you draw to this surface will appear in the widget on the screen. What you draw to this surface will appear in the widget on the screen.
The third and fourth parameters are the size of the destination surface. The third and fourth parameters are the size of the destination surface.
Now, look at the program example again. Now, look at the program again.
- 3-13: The drawing function. - 3-17: The drawing function.
- 7-8: Sets the source to be white and paint the destination white. - 7-8: Sets the source to be white and paint the destination white.
- 9: Sets the line width to be 2. - 9: Sets the line width to be 2.
- 10: Sets the source to be black. - 10: Sets the source to be black.
- 11: Adds a rectangle to the mask. - 11-15: Adds a rectangle to the mask.
- 12: Draws the rectangle with black color to the destination. - 16: Draws the rectangle with black color to the destination.
The program is [src/misc/da1.c](../src/misc/da1.c).
Compile and run it, then a window with a black rectangle (square) appears. Compile and run it, then a window with a black rectangle (square) appears.
Try resizing the window. Try resizing the window.
The square always appears at the center of the window because the drawing function is invoked each time the window is resized. The square always appears at the center of the window because the drawing function is invoked each time the window is resized.

View file

@ -9,6 +9,7 @@ If you write a name of a color in TfeTextView and click on the `run` button, the
![color](../image/color.png) ![color](../image/color.png)
The following colors are available. The following colors are available.
(without new line charactor)
- white - white
- black - black
@ -30,8 +31,7 @@ In this section, we focus on how to bind the two objects.
## Color.ui and color.gresource.xml ## Color.ui and color.gresource.xml
First, We need to make the ui file of the widgets. First, We need to make the ui file of the widgets.
The image in the previous subsection gives us the structure of the widgets. Title bar, four buttons in the tool bar, textview and drawing area.
Title bar, four buttons in the tool bar and two widgets textview and drawing area.
The ui file is as follows. The ui file is as follows.
~~~xml ~~~xml
@ -117,16 +117,16 @@ The ui file is as follows.
80 </interface> 80 </interface>
~~~ ~~~
- 10-53: This part is the tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`. - 10-53: The horizontal box `boxh1` makes a tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`.
This is similar to the toolbar of tfe text editor in [Section 9](sec9.md). This is similar to the `tfe` text editor in [Section 9](sec9.md).
There are two differences. There are two differences.
`Run` button replaces `New` button. `Run` button replaces `New` button.
A signal element is added to each button object. A signal element is added to each button object.
It has "name" attribute which is a signal name and "handler" attribute which is the name of its signal handler function. It has "name" attribute which is a signal name and "handler" attribute which is the name of its signal handler.
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application. Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
You can achieve this by adding "export_dynamic: true" argument to executable function in `meson.build`. You can achieve this by adding "export_dynamic: true" argument to the executable function in `meson.build`.
And be careful that the handler must be defined without 'static' class. And be careful that the handler must be defined without 'static' class.
- 54-76: Puts GtkScrolledWindow and GtkDrawingArea into GtkBox. - 54-76: The horizontal box `boxh2` includes GtkScrolledWindow and GtkDrawingArea.
GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box. GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box.
TfeTextView is a child of GtkScrolledWindow. TfeTextView is a child of GtkScrolledWindow.
@ -142,121 +142,209 @@ Just substitute "color" for "tfe".
6 </gresources> 6 </gresources>
~~~ ~~~
## Tfetextview.h, tfetextview.c and color.h ## Drawing function and surface
First two files are the same as before. The main point of this program is a drawing function.
Color.h just includes tfetextview.h.
~~~C ~~~C
1 #include <gtk/gtk.h> 1 static void
2 2 draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) {
3 #include "../tfetextview/tfetextview.h" 3 if (surface) {
4 cairo_set_source_surface (cr, surface, 0, 0);
5 cairo_paint (cr);
6 }
7 }
~~~ ~~~
The `surface` variable in line 3 is a static variable.
~~~C
static cairo_surface_t *surface = NULL;
~~~
The drawing function just copies the `surface` to its own surface with the `cairo_paint` function.
The surface (pointed by the static variable `surface`) is built by the `run` function.
~~~C
1 static void
2 run (void) {
3 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
4 GtkTextIter start_iter;
5 GtkTextIter end_iter;
6 char *contents;
7 cairo_t *cr;
8
9 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
10 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
11 if (surface) {
12 cr = cairo_create (surface);
13 if (g_strcmp0 ("red", contents) == 0)
14 cairo_set_source_rgb (cr, 1, 0, 0);
15 else if (g_strcmp0 ("green", contents) == 0)
16 cairo_set_source_rgb (cr, 0, 1, 0);
17 else if (g_strcmp0 ("blue", contents) == 0)
18 cairo_set_source_rgb (cr, 0, 0, 1);
19 else if (g_strcmp0 ("white", contents) == 0)
20 cairo_set_source_rgb (cr, 1, 1, 1);
21 else if (g_strcmp0 ("black", contents) == 0)
22 cairo_set_source_rgb (cr, 0, 0, 0);
23 else if (g_strcmp0 ("light", contents) == 0)
24 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
25 else if (g_strcmp0 ("dark", contents) == 0)
26 cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
27 else
28 cairo_set_source_surface (cr, surface, 0, 0);
29 cairo_paint (cr);
30 cairo_destroy (cr);
31 }
32 g_free (contents);
33 }
~~~
- 9-10: Gets the string in the GtkTextBuffer and inserts it to `contents`.
- 11: If the variable `surface` points a surface instance, it is painted as follows.
- 12- 30: The source is set based on the string `contents` and copied to the surface with `cairo_paint`.
- 24,26: Alpha channel is used in "light" and "dark" procedure.
The drawing area just reflects the `surface`.
But one problem is resizing.
If a user resizes the main window, the drawing area is also resized.
It makes size difference between the surface and the drawing area.
So, the surface needs to be resized to fit the drawing area.
It is accomplished by connecting the "resize" signal on the drawing area to a handler.
~~~C
g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
~~~
The handler is as follows.
~~~C
1 static void
2 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) {
3 if (surface)
4 cairo_surface_destroy (surface);
5 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
6 run ();
7 }
~~~
If the variable `surface` sets a surface instance, it is destroyed.
A new surface is created and its size fits the drawing area.
The surface is assigned to the variable `surface`.
The function `run` is called and the surface is colored.
The signal is emitted when:
- The drawing area is realized (it appears on the display).
- It is changed (resized) while realized
So, the first surface is created when it is realized.
## Colorapplication.c ## Colorapplication.c
This is the main file. This is the main file.
It deals with:
- Building widgets by GtkBuilder. - Builds widgets by GtkBuilder.
- Setting a drawing function of GtkDrawingArea. - Sets a drawing function for GtkDrawingArea.
And connecting a handler to "resize" signal on GtkDrawingArea. And connects a handler to the "resize" signal on the GtkDrawingArea instance.
- Implementing each call back functions. - Implements each call back function.
Particularly, `Run` signal handler is the point in this program. Particularly, `Run` signal handler is the point in this program.
The following is `colorapplication.c`. The following is `colorapplication.c`.
~~~C ~~~C
1 #include "color.h" 1 #include <gtk/gtk.h>
2 2 #include "../tfetextview/tfetextview.h"
3 static GtkWidget *win; 3
4 static GtkWidget *tv; 4 static GtkWidget *win;
5 static GtkWidget *da; 5 static GtkWidget *tv;
6 6 static GtkWidget *da;
7 static cairo_surface_t *surface = NULL; 7
8 8 static cairo_surface_t *surface = NULL;
9 static void 9
10 run (void) { 10 static void
11 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)); 11 run (void) {
12 GtkTextIter start_iter; 12 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
13 GtkTextIter end_iter; 13 GtkTextIter start_iter;
14 char *contents; 14 GtkTextIter end_iter;
15 cairo_t *cr; 15 char *contents;
16 16 cairo_t *cr;
17 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter); 17
18 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE); 18 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
19 if (surface) { 19 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
20 cr = cairo_create (surface); 20 if (surface) {
21 if (g_strcmp0 ("red", contents) == 0) 21 cr = cairo_create (surface);
22 cairo_set_source_rgb (cr, 1, 0, 0); 22 if (g_strcmp0 ("red", contents) == 0)
23 else if (g_strcmp0 ("green", contents) == 0) 23 cairo_set_source_rgb (cr, 1, 0, 0);
24 cairo_set_source_rgb (cr, 0, 1, 0); 24 else if (g_strcmp0 ("green", contents) == 0)
25 else if (g_strcmp0 ("blue", contents) == 0) 25 cairo_set_source_rgb (cr, 0, 1, 0);
26 cairo_set_source_rgb (cr, 0, 0, 1); 26 else if (g_strcmp0 ("blue", contents) == 0)
27 else if (g_strcmp0 ("white", contents) == 0) 27 cairo_set_source_rgb (cr, 0, 0, 1);
28 cairo_set_source_rgb (cr, 1, 1, 1); 28 else if (g_strcmp0 ("white", contents) == 0)
29 else if (g_strcmp0 ("black", contents) == 0) 29 cairo_set_source_rgb (cr, 1, 1, 1);
30 cairo_set_source_rgb (cr, 0, 0, 0); 30 else if (g_strcmp0 ("black", contents) == 0)
31 else if (g_strcmp0 ("light", contents) == 0) 31 cairo_set_source_rgb (cr, 0, 0, 0);
32 cairo_set_source_rgba (cr, 1, 1, 1, 0.5); 32 else if (g_strcmp0 ("light", contents) == 0)
33 else if (g_strcmp0 ("dark", contents) == 0) 33 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
34 cairo_set_source_rgba (cr, 0, 0, 0, 0.5); 34 else if (g_strcmp0 ("dark", contents) == 0)
35 else 35 cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
36 cairo_set_source_surface (cr, surface, 0, 0); 36 else
37 cairo_paint (cr); 37 cairo_set_source_surface (cr, surface, 0, 0);
38 cairo_destroy (cr); 38 cairo_paint (cr);
39 } 39 cairo_destroy (cr);
40 g_free (contents); 40 }
41 } 41 g_free (contents);
42 42 }
43 void 43
44 run_cb (GtkWidget *btnr) { 44 void
45 run (); 45 run_cb (GtkWidget *btnr) {
46 gtk_widget_queue_draw (GTK_WIDGET (da)); 46 run ();
47 } 47 gtk_widget_queue_draw (GTK_WIDGET (da));
48 48 }
49 void 49
50 open_cb (GtkWidget *btno) { 50 void
51 tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (win)); 51 open_cb (GtkWidget *btno) {
52 } 52 tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (win));
53 53 }
54 void 54
55 save_cb (GtkWidget *btns) { 55 void
56 tfe_text_view_save (TFE_TEXT_VIEW (tv)); 56 save_cb (GtkWidget *btns) {
57 } 57 tfe_text_view_save (TFE_TEXT_VIEW (tv));
58 58 }
59 void 59
60 close_cb (GtkWidget *btnc) { 60 void
61 if (surface) 61 close_cb (GtkWidget *btnc) {
62 cairo_surface_destroy (surface); 62 gtk_window_destroy (GTK_WINDOW (win));
63 gtk_window_destroy (GTK_WINDOW (win)); 63 }
64 } 64
65 65 static void
66 static void 66 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) {
67 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) { 67 if (surface)
68 if (surface) 68 cairo_surface_destroy (surface);
69 cairo_surface_destroy (surface); 69 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
70 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); 70 run ();
71 run (); 71 }
72 } 72
73 73 static void
74 static void 74 draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) {
75 draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) { 75 if (surface) {
76 if (surface) { 76 cairo_set_source_surface (cr, surface, 0, 0);
77 cairo_set_source_surface (cr, surface, 0, 0); 77 cairo_paint (cr);
78 cairo_paint (cr); 78 }
79 } 79 }
80 } 80
81 81 static void
82 static void 82 app_activate (GApplication *application) {
83 app_activate (GApplication *application) { 83 gtk_window_present (GTK_WINDOW (win));
84 gtk_widget_show (win); 84 }
85 } 85
86 86 static void
87 static void 87 app_startup (GApplication *application) {
88 app_startup (GApplication *application) { 88 GtkApplication *app = GTK_APPLICATION (application);
89 GtkApplication *app = GTK_APPLICATION (application); 89 GtkBuilder *build;
90 GtkBuilder *build; 90 GdkDisplay *display;
91 91
92 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui"); 92 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui");
93 win = GTK_WIDGET (gtk_builder_get_object (build, "win")); 93 win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
@ -267,67 +355,58 @@ The following is `colorapplication.c`.
98 g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL); 98 g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
99 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL); 99 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL);
100 100
101 GdkDisplay *display; 101 display = gdk_display_get_default ();
102 102 GtkCssProvider *provider = gtk_css_provider_new ();
103 display = gtk_widget_get_display (GTK_WIDGET (win)); 103 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
104 GtkCssProvider *provider = gtk_css_provider_new (); 104 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
105 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1); 105 }
106 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); 106
107 } 107 static void
108 108 app_shutdown (GApplication *application) {
109 #define APPLICATION_ID "com.github.ToshioCP.color" 109 if (surface)
110 110 cairo_surface_destroy (surface);
111 int 111 }
112 main (int argc, char **argv) { 112
113 GtkApplication *app; 113 #define APPLICATION_ID "com.github.ToshioCP.color"
114 int stat; 114
115 115 int
116 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE); 116 main (int argc, char **argv) {
117 117 GtkApplication *app;
118 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); 118 int stat;
119 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); 119
120 120 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
121 stat =g_application_run (G_APPLICATION (app), argc, argv); 121
122 g_object_unref (app); 122 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
123 return stat; 123 g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL);
124 } 124 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
125 125
126 stat =g_application_run (G_APPLICATION (app), argc, argv);
127 g_object_unref (app);
128 return stat;
129 }
130
~~~ ~~~
- 109-124: The function `main` is almost same as before but there are some differences. - 4-8: Win, tv, da and surface are defined as static variables.
The application ID is "com.github.ToshioCP.color". - 10-42: Run function.
`G_APPLICATION_FLAGS_NONE` is specified so no open signal handler is necessary. - 44-63: Handlers for button signals.
- 87-107: Startup handler. - 65-71: Resize handler.
- 92-97: Builds widgets. - 73-79: Drawing function.
The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables `win`, `tv` and `da` respectively. - 81-84: Application activate handler.
This is because these objects are often used in handlers. It just shows the main window.
They never be rewritten so they're thread safe. - 86-105: Application startup handler.
- 98: connects "resize" signal and the handler. - 92- 97: It builds widgets according to the ui resource.
- 99: sets the drawing function. The static variables win, tv and da are assigned instances.
- 82-85: Activate handler, which just shows the widgets. - 98: Connects "resize" signal and a handler.
- 74-80: The drawing function. - 99: Drawing function is set.
It just copies `surface` to destination. - 101-104: CSS for textview padding is set.
- 66-72: Resize handler. - 107-111: Application shutdown handler.
Re-creates the surface to fit its width and height for the drawing area and paints by calling the function `run`. If there exists a surface instance, it will be destroyed.
- 59-64: Close handler. - 116-129: A function `main`.
It destroys `surface` if it exists. It creates a new application instance.
Then it destroys the top-level window and quits the application. And connects three signals startup, shutdown and activate to their handlers.
- 49-57: Open and save handler. It runs the application.
They just call the corresponding functions of TfeTextView. It releases the reference to the application and returns with `stat` value.
- 43-47: Run handler.
It calls run function to paint the surface.
After that `gtk_widget_queue_draw` is called.
This function adds the widget (GtkDrawingArea) to the queue to be redrawn.
It is important to know that the window is redrawn whenever it is necessary.
For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized.
But repainting `surface` is not automatically notified to gtk.
Therefore, you need to call `gtk_widget_queue_draw` to redraw the widget.
- 9-41: Run function paints the surface.
First, it gets the contents of GtkTextBuffer.
Then it compares it to "red", "green" and so on.
If it matches the color, then the surface is painted the color.
If it matches "light" or "dark", then the color of the surface is lightened or darkened respectively.
Alpha channel is used.
## Meson.build ## Meson.build
@ -347,14 +426,9 @@ An argument "export_dynamic: true" is added to executable function.
10 executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true) 10 executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
~~~ ~~~
## Compile and execute it ## Build and try
First you need to export some variables (refer to [Section 2](sec2.md)) if you've installed GTK 4 from the source. Type the following to compile the program.
If you've installed GTK 4 from the distribution packages, you don't need to do this.
$ . env.sh
Then type the following to compile it.
$ meson _build $ meson _build
$ ninja -C _build $ ninja -C _build
@ -365,7 +439,8 @@ Type the following to execute it.
$ _build/color $ _build/color
Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView. Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView.
Then, click on `Run` button. No new line charactor is needed.
Then, click on the `Run` button.
Make sure the color of GtkDrawingArea changes. Make sure the color of GtkDrawingArea changes.
In this program TfeTextView is used to change the color. In this program TfeTextView is used to change the color.

View file

@ -1,37 +1,38 @@
Up: [Readme.md](../Readme.md), Prev: [Section 24](sec24.md), Next: [Section 26](sec26.md) Up: [README.md](../README.md), Prev: [Section 24](sec24.md), Next: [Section 26](sec26.md)
# Tiny turtle graphics interpreter # Tiny turtle graphics interpreter
A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects. A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects.
It is a very small interpreter but it provides a tool to draw fractal curves. It is a very small interpreter but you can draw fractal curves with it.
The following diagram is a Koch curve, which is a famous example of fractal curves. The following diagram is a Koch curve, which is one of famous fractal curves.
![Koch curve](../src/turtle/image/turtle_koch.png) ![Koch curve](../src/turtle/image/turtle_koch.png)
The following is a snow-crystal-shaped curve.
It is composed of six Koch curves.
![Snow](../image/turtle_snow.png)
This program uses flex and bison. This program uses flex and bison.
Flex is a lexical analyzer. Flex is a lexical analyzer.
Bison is a parser generator. Bison is a parser generator.
These two programs are similar to lex and yacc which are proprietary software developed in Bell Laboratory. These two programs are similar to lex and yacc which are proprietary software developed in Bell Laboratory.
However, flex and bison are open source software. However, flex and bison are open source software.
I will write about how to use those software, but they are not topics about gtk. I will write about how to use those software, but they are not topics about GTK 4.
So, readers can skip that part of this sections. So, readers can skip this section.
## How to use turtle ## How to use turtle
The documentation of turtle is [here](turtle_doc.md). The turtle document is [here](turtle_doc.md).
I'll show you a simple example. I'll show you a simple example.
~~~ ~~~
fc (1,0,0) # Foreground color is red, rgb = (1,0,0). fc (1,0,0) # Foreground color is red, rgb = (1,0,0).
pd # Pen down. pd # Pen down.
fd 100 # Go forward by 100 pixels. rp (4) { # Repeat four times.
tr 90 # Turn right by 90 degrees. fd 100 # Go forward by 100 pixels.
fd 100 tr 90 # Turn right by 90 degrees.
tr 90 }
fd 100
tr 90
fd 100
tr 90
~~~ ~~~
1. Compile and install `turtle` (See the documentation above). 1. Compile and install `turtle` (See the documentation above).
@ -41,8 +42,8 @@ Then, run `turtle`.
The side of the square is 100 pixels long. The side of the square is 100 pixels long.
In the same way, you can draw other curves. In the same way, you can draw other curves.
The documentation above shows some fractal curves such as tree, snow and square-koch. The turtle document includes some fractal curves such as tree, snow and square-koch.
The source code in turtle language is located at [src/turtle/example](../src/turtle/example) directory. The source codes are located at [src/turtle/example](../src/turtle/example) directory.
You can read these files into `turtle` editor by clicking on the `Open` button. You can read these files into `turtle` editor by clicking on the `Open` button.
## Combination of TfeTextView and GtkDrawingArea objects ## Combination of TfeTextView and GtkDrawingArea objects
@ -55,10 +56,9 @@ It is similar to `color` program in the previous section.
3. The parser reads the program and generates tree-structured data. 3. The parser reads the program and generates tree-structured data.
4. The interpriter reads the data and executes it step by step. 4. The interpriter reads the data and executes it step by step.
And it draws shapes on a surface. And it draws shapes on a surface.
The surface is different from the surface of the GtkDrawingArea widget. The surface isn't the one in the GtkDrawingArea widget.
5. The widget is added to the queue. 5. The widget is added to the queue.
It will be redrawn with the drawing function. It will be redrawn with the drawing function, which just copies the surface into the one in the GtkDrawingArea.
The function just copies the surface, which is drawn by the interpreter, into the surface of the GtkDrawingArea.
![Parser, interpreter and drawing function](../image/turtle.png) ![Parser, interpreter and drawing function](../image/turtle.png)
@ -74,10 +74,10 @@ So the handler of "clicked" signal of the `Run` button prevents from reentering.
5 GtkTextIter end_iter; 5 GtkTextIter end_iter;
6 char *contents; 6 char *contents;
7 int stat; 7 int stat;
8 static gboolean busy = FALSE; 8 static gboolean busy = FALSE; /* initialized only once */
9 9
10 /* yyparse() and run() are NOT thread safe. */ 10 /* yyparse() and run() are NOT thread safe. */
11 /* The variable busy avoids reentry. */ 11 /* The variable busy avoids reentrance. */
12 if (busy) 12 if (busy)
13 return; 13 return;
14 busy = TRUE; 14 busy = TRUE;
@ -98,16 +98,18 @@ So the handler of "clicked" signal of the `Run` button prevents from reentering.
29 29
30 static void 30 static void
31 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) { 31 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) {
32 if (surface) 32
33 cairo_surface_destroy (surface); 33 if (surface)
34 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); 34 cairo_surface_destroy (surface);
35 } 35 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
36 run_cb (NULL); // NULL is a fake (run button).
37 }
~~~ ~~~
- 8-13: The static value `busy` holds a status of the interpreter. - 8-13: The static value `busy` holds a status of the interpreter.
If it is `TRUE`, the interpreter is running and it is not possible to call the interpreter again because it's not a re-entrant program. If it is `TRUE`, the interpreter is running and it is not possible to call the interpreter again because it's not a re-entrant program.
If it is `FALSE`, it is safe to call the interpreter. If it is `FALSE`, it is safe to call the interpreter.
- 14: Now it is about to call the interpreter so it changes `busy` to TRUE. - 14: Changes `busy` to TRUE to avoid reentrance.
- 15-16: Gets the contents of `tb`. - 15-16: Gets the contents of `tb`.
- 17: The variable `surface` is a static variable. - 17: The variable `surface` is a static variable.
It points to a `cairo_surface_t` instance. It points to a `cairo_surface_t` instance.
@ -116,17 +118,17 @@ Therefore, `surface` isn't NULL usually.
But if it is NULL, the interpreter won't be called. But if it is NULL, the interpreter won't be called.
- 18: Initializes lexical analyzer. - 18: Initializes lexical analyzer.
- 19: Calls parser. - 19: Calls parser.
Parser analyzes the program codes syntactically and generate a tree structured data. Parser analyzes the program codes syntactically and generates a tree structured data.
- 20-22: If the parser successfully parsed, it calls `run` (runtime routine). - 20-22: If the parser successfully parsed, it calls `run` (runtime routine).
- 23: finalizes the lexical analyzer. - 23: finalizes the lexical analyzer.
- 25: frees `contents`. - 25: frees `contents`.
- 26: Adds the drawing area widget to the queue to draw. - 26: Adds the drawing area widget to the queue to draw.
- 27: The interpreter program has finished so `busy` is now changed to FALSE. - 27: The interpreter program has finished so `busy` is now changed to FALSE.
- 29-34: A handler of "resized" signal. - 30-37: A "resized" signal handler.
If `surface` isn't NULL, it destroys the old surface. If the `surface` isn't NULL, it is destroyed.
Then it creates a new surface. A new surface is created.
Its size is the same as the surface of the GtkDrawingArea instance. Its size is the same as the surface of the GtkDrawingArea instance.
Run\_cb is called to redraw the shape on the drawing area.
Other part of `turtleapplication.c` is almost same as the codes of `colorapplication.c` in the previous section. Other part of `turtleapplication.c` is almost same as the codes of `colorapplication.c` in the previous section.
The codes of `turtleapplication.c` is in the [turtle directory](../src/turtle). The codes of `turtleapplication.c` is in the [turtle directory](../src/turtle).
@ -215,7 +217,7 @@ The source files are:
- flex source file => `turtle.lex` - flex source file => `turtle.lex`
- bison source file => `turtle.y` - bison source file => `turtle.y`
- C header file => `turtle.h`, `turtle_lex.h` - C header file => `turtle_lex.h`
- C source file => `turtleapplication.c` - C source file => `turtleapplication.c`
- other files => `turtle.ui`, `turtle.gresources.xml` and `meson.build` - other files => `turtle.ui`, `turtle.gresources.xml` and `meson.build`
@ -225,12 +227,13 @@ The compilation process is a bit complicated.
It also generates `resources.h`. It also generates `resources.h`.
2. bison compiles `turtle.y` to `turtle_parser.c` and generates `turtle_parser.h` 2. bison compiles `turtle.y` to `turtle_parser.c` and generates `turtle_parser.h`
3. flex compiles `turtle.lex` to `turtle_lex.c`. 3. flex compiles `turtle.lex` to `turtle_lex.c`.
4. gcc compiles `application.c`, `resources.c`, `turtle_parser.c` and `turtle_lex.c` with `turtle.h`, `turtle_lex.h`, `resources.h` and `turtle_parser.h`. 4. gcc compiles `application.c`, `resources.c`, `turtle_parser.c` and `turtle_lex.c` with `turtle_lex.h`, `resources.h` and `turtle_parser.h`.
It generates an executable file `turtle`. It generates an executable file `turtle`.
![compile process](../image/turtle_compile_process.png) ![compile process](../image/turtle_compile_process.png)
Meson controls the process and the instruction is described in `meson.build`. Meson controls the process.
The instruction is described in `meson.build`.
~~~meson ~~~meson
1 project('turtle', 'c') 1 project('turtle', 'c')
@ -261,15 +264,13 @@ This program uses trigonometric functions.
They are defined in the math library, but the library is optional. They are defined in the math library, but the library is optional.
So, it is necessary to include it by `#include <math.h>` and also link the library with the linker. So, it is necessary to include it by `#include <math.h>` and also link the library with the linker.
- 6: Gets gtk4 library. - 6: Gets gtk4 library.
- 8: Gets gnome module. - 8: Gets gnome module.See [Meson build system website -- GNUME module](https://mesonbuild.com/Gnome-module.html#gnome-module) for further information.
Module is a system provided by meson.
See [Meson build system website, GNUME module](https://mesonbuild.com/Gnome-module.html#gnome-module) for further information.
- 9: Compiles ui file to C source file according to the XML file `turtle.gresource.xml`. - 9: Compiles ui file to C source file according to the XML file `turtle.gresource.xml`.
- 11: Gets flex. - 11: Gets flex.
- 12: Gets bison. - 12: Gets bison.
- 13: Compiles `turtle.y` to `turtle_parser.c` and `turtle_parser.h` by bison. - 13: Compiles `turtle.y` to `turtle_parser.c` and `turtle_parser.h` by bison.
The function `custom_target` creates a custom top level target. The function `custom_target` creates a custom top level target.
See [Meson build system website, custom target](https://mesonbuild.com/Reference-manual.html#custom_target) for further information. See [Meson build system website -- custom target](https://mesonbuild.com/Reference-manual.html#custom_target) for further information.
- 14: Compiles `turtle.lex` to `turtle_lex.c` by flex. - 14: Compiles `turtle.lex` to `turtle_lex.c` by flex.
- 16: Specifies C source files. - 16: Specifies C source files.
- 18: Compiles C source files including generated files by glib-compile-resources, bison and flex. - 18: Compiles C source files including generated files by glib-compile-resources, bison and flex.
@ -309,79 +310,81 @@ Turtle.lex isn't a big program.
1 %top{ 1 %top{
2 #include <string.h> 2 #include <string.h>
3 #include <stdlib.h> 3 #include <stdlib.h>
4 #include "turtle.h" 4 #include <glib.h>
5 5 #include "turtle_parser.h"
6 static int nline = 1; 6
7 static int ncolumn = 1; 7 static int nline = 1;
8 static void get_location (char *text); 8 static int ncolumn = 1;
9 9 static void get_location (char *text);
10 /* Dinamically allocated memories are added to the single list. They will be freed in the finalize function. */ 10
11 extern GSList *list; 11 /* Dinamically allocated memories are added to the single list. They will be freed in the finalize function. */
12 } 12 extern GSList *list;
13 13 }
14 %option noyywrap 14
15 15 %option noyywrap
16 REAL_NUMBER (0|[1-9][0-9]*)(\.[0-9]+)? 16
17 IDENTIFIER [a-zA-Z][a-zA-Z0-9]* 17 REAL_NUMBER (0|[1-9][0-9]*)(\.[0-9]+)?
18 %% 18 IDENTIFIER [a-zA-Z][a-zA-Z0-9]*
19 /* rules */ 19 %%
20 #.* ; /* comment. Be careful. Dot symbol (.) matches any character but new line. */ 20 /* rules */
21 [ ] ncolumn++; 21 #.* ; /* comment. Be careful. Dot symbol (.) matches any character but new line. */
22 \t ncolumn += 8; /* assume that tab is 8 spaces. */ 22 [ ] ncolumn++;
23 \n nline++; ncolumn = 1; 23 \t ncolumn += 8; /* assume that tab is 8 spaces. */
24 /* reserved keywords */ 24 \n nline++; ncolumn = 1;
25 pu get_location (yytext); return PU; /* pen up */ 25 /* reserved keywords */
26 pd get_location (yytext); return PD; /* pen down */ 26 pu get_location (yytext); return PU; /* pen up */
27 pw get_location (yytext); return PW; /* pen width = line width */ 27 pd get_location (yytext); return PD; /* pen down */
28 fd get_location (yytext); return FD; /* forward */ 28 pw get_location (yytext); return PW; /* pen width = line width */
29 tr get_location (yytext); return TR; /* turn right */ 29 fd get_location (yytext); return FD; /* forward */
30 bc get_location (yytext); return BC; /* background color */ 30 tr get_location (yytext); return TR; /* turn right */
31 fc get_location (yytext); return FC; /* foreground color */ 31 tl get_location (yytext); return TL; /* turn left ver 0.5 */
32 dp get_location (yytext); return DP; /* define procedure */ 32 bc get_location (yytext); return BC; /* background color */
33 if get_location (yytext); return IF; /* if statement */ 33 fc get_location (yytext); return FC; /* foreground color */
34 rt get_location (yytext); return RT; /* return statement */ 34 dp get_location (yytext); return DP; /* define procedure */
35 rs get_location (yytext); return RS; /* reset the status */ 35 if get_location (yytext); return IF; /* if statement */
36 /* constant */ 36 rt get_location (yytext); return RT; /* return statement */
37 {REAL_NUMBER} get_location (yytext); yylval.NUM = atof (yytext); return NUM; 37 rs get_location (yytext); return RS; /* reset the status */
38 /* identifier */ 38 rp get_location (yytext); return RP; /* repeat ver 0.5 */
39 {IDENTIFIER} { get_location (yytext); yylval.ID = g_strdup(yytext); 39 /* constant */
40 list = g_slist_prepend (list, yylval.ID); 40 {REAL_NUMBER} get_location (yytext); yylval.NUM = atof (yytext); return NUM;
41 return ID; 41 /* identifier */
42 } 42 {IDENTIFIER} { get_location (yytext); yylval.ID = g_strdup(yytext);
43 "=" get_location (yytext); return '='; 43 list = g_slist_prepend (list, yylval.ID);
44 ">" get_location (yytext); return '>'; 44 return ID;
45 "<" get_location (yytext); return '<'; 45 }
46 "+" get_location (yytext); return '+'; 46 "=" get_location (yytext); return '=';
47 "-" get_location (yytext); return '-'; 47 ">" get_location (yytext); return '>';
48 "*" get_location (yytext); return '*'; 48 "<" get_location (yytext); return '<';
49 "/" get_location (yytext); return '/'; 49 "+" get_location (yytext); return '+';
50 "(" get_location (yytext); return '('; 50 "-" get_location (yytext); return '-';
51 ")" get_location (yytext); return ')'; 51 "*" get_location (yytext); return '*';
52 "{" get_location (yytext); return '{'; 52 "/" get_location (yytext); return '/';
53 "}" get_location (yytext); return '}'; 53 "(" get_location (yytext); return '(';
54 "," get_location (yytext); return ','; 54 ")" get_location (yytext); return ')';
55 . ncolumn++; return YYUNDEF; 55 "{" get_location (yytext); return '{';
56 %% 56 "}" get_location (yytext); return '}';
57 57 "," get_location (yytext); return ',';
58 static void 58 . ncolumn++; return YYUNDEF;
59 get_location (char *text) { 59 %%
60 yylloc.first_line = yylloc.last_line = nline; 60
61 yylloc.first_column = ncolumn; 61 static void
62 yylloc.last_column = (ncolumn += strlen(text)) - 1; 62 get_location (char *text) {
63 } 63 yylloc.first_line = yylloc.last_line = nline;
64 64 yylloc.first_column = ncolumn;
65 static YY_BUFFER_STATE state; 65 yylloc.last_column = (ncolumn += strlen(text)) - 1;
66 66 }
67 void 67
68 init_flex (const char *text) { 68 static YY_BUFFER_STATE state;
69 state = yy_scan_string (text); 69
70 } 70 void
71 71 init_flex (const char *text) {
72 void 72 state = yy_scan_string (text);
73 finalize_flex (void) { 73 }
74 yy_delete_buffer (state); 74
75 } 75 void
76 76 finalize_flex (void) {
77 yy_delete_buffer (state);
78 }
~~~ ~~~
The file consists of three sections which are separated by "%%" (line 18 and 56). The file consists of three sections which are separated by "%%" (line 18 and 56).
@ -391,17 +394,17 @@ They are definitions, rules and user code sections.
- 1-12: Lines between "%top{" and "}" are C source codes. - 1-12: Lines between "%top{" and "}" are C source codes.
They will be copied to the top of the generated C source file. They will be copied to the top of the generated C source file.
- 2-3: The function `strlen`, in line 62, is defined in `string.h` - 2-3: The function `strlen`, in line 65, is defined in `string.h`
The function `atof`, in line 37, is defined in `stdlib.h`. The function `atof`, in line 40, is defined in `stdlib.h`.
- 6-8: The current input position is pointed by `nline` and `ncolumn`. - 7-9: The current input position is pointed by `nline` and `ncolumn`.
The function `get_location` (line 58-63) sets `yylloc`to point the start and end point of `yytext` in the buffer. The function `get_location` (line 61-66) sets `yylloc`to point the start and end point of `yytext` in the buffer.
This function is declared here so that it can be called before the function is defined. This function is declared here so that it can be called before the function is defined.
- 11: GSlist is used to keep allocated memories. - 12: GSlist is used to keep allocated memories.
- 14: This option (`%option noyywrap`) must be specified when you have only single source file to the scanner. Refer to "9 The Generated Scanner" in the flex documentation in your distribution for further information. - 15: This option (`%option noyywrap`) must be specified when you have only single source file to the scanner. Refer to "9 The Generated Scanner" in the flex documentation in your distribution for further information.
(The documentation is not on the internet.) (The documentation is not on the internet.)
- 16-17: `REAL_NUMBER` and `IDENTIFIER` are names. - 17-18: `REAL_NUMBER` and `IDENTIFIER` are names.
A name begins with a letter or an underscore followed by zero or more letters, digits, underscores (`_`) or dashes (`-`). A name begins with a letter or an underscore followed by zero or more letters, digits, underscores (`_`) or dashes (`-`).
They are followed by regular expressions which are their definition. They are followed by regular expressions which are their definitions.
They will be used in rules section and will expand to the definition. They will be used in rules section and will expand to the definition.
You can leave out such definitions here and use regular expressions in rules section directly. You can leave out such definitions here and use regular expressions in rules section directly.
@ -413,64 +416,66 @@ The patterns are regular expressions or names surrounded by braces.
The names must be defined in the definitions section. The names must be defined in the definitions section.
The definition of the regular expression is written in the flex documentation. The definition of the regular expression is written in the flex documentation.
For example, line 37 is a rule. For example, line 40 is a rule.
- `{REAL_NUMBER}` is a pattern - `{REAL_NUMBER}` is a pattern
- `get_location (yytext); yylval.NUM = atof (yytext); return NUM;` is an action. - `get_location (yytext); yylval.NUM = atof (yytext); return NUM;` is an action.
`{REAL_NUMBER}` is defined in the 16th line, so it expands to `(0|[1-9][0-9]*)(\.[0-9]+)?`. `{REAL_NUMBER}` is defined in the line 17, so it expands to `(0|[1-9][0-9]*)(\.[0-9]+)?`.
This regular expression matches numbers like `0`, `12` and `1.5`. This regular expression matches numbers like `0`, `12` and `1.5`.
If the input is a number, it matches the pattern in line 37. If an input is a number, it matches the pattern in line 40.
Then the matched text is assigned to `yytext` and corresponding action is executed. Then the matched text is assigned to `yytext` and corresponding action is executed.
A function `get_location` changes the location variables. A function `get_location` changes the location variables to the position at the text.
It assigns `atof (yytext)`, which is double sized number converted from `yytext`, to `yylval.NUM` and return `NUM`. It assigns `atof (yytext)`, which is double sized number converted from `yytext`, to `yylval.NUM` and return `NUM`.
`NUM` is an integer defined by `turtle.y`. `NUM` is a token kind and it represents integer.
It is defined in `turtle.y`.
The scanner generated by flex and C compiler has `yylex` function. The scanner generated by flex has `yylex` function.
If `yylex` is called and the input is "123.4", then it works as follows. If `yylex` is called and the input is "123.4", then it works as follows.
1. A string "123.4" matches `{REAL_NUMBER}`. 1. A string "123.4" matches `{REAL_NUMBER}`.
2. Update the location variable `ncolumn` and `yylloc`with `get_location`. 2. Update the location variable `ncolumn` and `yylloc`with `get_location`.
3. `atof` converts the string "123.4" to double type number 123.4. 3. The function `atof` converts the string "123.4" to double type number 123.4.
4. It is assigned to `yylval.NUM`. 4. It is assigned to `yylval.NUM`.
5. `yylex` returns `NUM` to the caller. 5. `yylex` returns `NUM` to the caller.
Then the caller knows the input is `NUM` (number), and its value is 123.4. Then the caller knows the input is a number (`NUM`), and its value is 123.4.
- 19-55: Rules section. - 20-58: Rules section.
- 20: The symbol `.` (dot) matches any character except newline. - 21: The symbol `.` (dot) matches any character except newline.
Therefore, a comment begins `#` followed by any characters except newline. Therefore, a comment begins `#` followed by any characters except newline.
No action happens. No action happens.
- 21: White space just increases a variable `ncolumn` by one. - 22: White space just increases the variable `ncolumn` by one.
- 22: Tab is assumed to be equal to eight spaces. - 23: Tab is assumed to be equal to eight spaces.
- 23: New line increases a variable `nline` by one and resets `ncolumn`. - 24: New line increases a variable `nline` by one and resets `ncolumn`.
- 25-35: Keywords just updates the location variables `ncolumn` and `yylloc`, and return the codes of the keywords. - 26-38: Keywords just updates the location variables `ncolumn` and `yylloc`, and return the token kinds of the keywords.
- 37: Real number constant. - 40: Real number constant.
- 38: `IDENTIFIER` is defined in line 17. - 42: `IDENTIFIER` is defined in line 18.
The location variables are updated and the name of the identifier is assigned to `yylval.ID`. The location variables are updated and the name of the identifier is assigned to `yylval.ID`.
The memory of the name is allocated by the function `g_strdup`. The memory of the name is allocated by the function `g_strdup`.
The memory is registered to the list (GSlist type list). The memory is registered to the list (GSlist type list).
The memory will be freed after the runtime routine finishes. The memory will be freed after the runtime routine finishes.
Returns `ID`. A token kind `ID` is returned.
- 43-54: Symbols just update the location variable and return the codes. - 46-56: Symbols just update the location variable and return the token kinds.
The code is the same as the symbol itself. The token kind is the same as the symbol itself.
- 55: If the input doesn't match above patterns, then it is error. - 58: If the input doesn't match the patterns, then it is an error.
Returns `YYUNDEF`. A special token kind `YYUNDEF` is returned.
### User code section ### User code section
This section is just copied to C source file. This section is just copied to C source file.
- 58-63: A function `get_location`. - 61-66: A function `get_location`.
The location of the input is recorded to `nline` and `ncolumn`. The location of the input is recorded to `nline` and `ncolumn`.
A variable `yylloc` is referred by the parser. A variable `yylloc` is referred by the parser.
It is a C structure and has four members, `first_line`, `first_column`, `last_line` and `last_column`. It is a C structure and has four members, `first_line`, `first_column`, `last_line` and `last_column`.
They point the start and end of the current input text. They point the start and end of the current input text.
- 65: `YY_BUFFER_STATE` is a pointer points the input buffer. - 68: `YY_BUFFER_STATE` is a pointer points the input buffer.
- 67-70: `init_flex` is called by `run_cb` signal handler, which is called when `Run` button is clicked on. - 70-73: A function `init_flex` is called by `run_cb` which is a "clicked" signal handler on the `Run` button.
`run_cb` calls `init_flex` with one argument which is the copy of the content of GtkTextBuffer. It has one string type parameter.
`yy_scan_string` sets the input buffer to read from the text. The caller assigns it with the content of the GtkTextBuffer instance.
- 72-75: `finalize_flex` is called after runtime routine finishes. A function `yy_scan_string` sets the input buffer for the scanner.
- 75-78: A function `finalize_flex` is called after runtime routine finishes.
It deletes the input buffer. It deletes the input buffer.
## Turtle.y ## Turtle.y
@ -480,8 +485,8 @@ So I will explain the key points and leave out other less important parts.
### What does bison do? ### What does bison do?
Bison creates C source file from bison source file. Bison creates C source file of a parser from a bison source file.
Bison source file is a text file. The bison source file is a text file.
A parser analyzes a program source code according to its grammar. A parser analyzes a program source code according to its grammar.
Suppose here is a turtle source file. Suppose here is a turtle source file.
@ -522,7 +527,7 @@ So, the parser gets items in the following table whenever it calls `yylex`.
Bison source code specifies the grammar rules of turtle language. Bison source code specifies the grammar rules of turtle language.
For example, `fc (1,0,0)` is called primary procedure. For example, `fc (1,0,0)` is called primary procedure.
A procedure is like a void type function in C source code. A procedure is like a void type C function.
It doesn't return any values. It doesn't return any values.
Programmers can define their own procedures. Programmers can define their own procedures.
On the other hand, `fc` is a built-in procedure. On the other hand, `fc` is a built-in procedure.
@ -540,7 +545,8 @@ This means:
- expression is ID or NUM. - expression is ID or NUM.
The description above is called BNF (Backus-Naur form). The description above is called BNF (Backus-Naur form).
More precisely, it is similar to BNF. Precisely speaking, it is not exactly the same as BNF.
But the difference is small.
The first line is: The first line is:
@ -550,7 +556,7 @@ FC '(' NUM ',' NUM ',' NUM ')';
The parser analyzes the turtle source code and if the input matches the definition above, the parser recognizes it as a primary procedure. The parser analyzes the turtle source code and if the input matches the definition above, the parser recognizes it as a primary procedure.
The grammar of turtle is described in the [document](turtle_doc.md). The grammar of turtle is described in the [Turtle manual](https://toshiocp.github.io/Gtk4-tutorial/turtle_doc.html).
The following is an extract from the document. The following is an extract from the document.
~~~ ~~~
@ -570,12 +576,14 @@ primary_procedure:
| PW expression | PW expression
| FD expression | FD expression
| TR expression | TR expression
| TL expression
| BC '(' expression ',' expression ',' expression ')' | BC '(' expression ',' expression ',' expression ')'
| FC '(' expression ',' expression ',' expression ')' | FC '(' expression ',' expression ',' expression ')'
| ID '=' expression | ID '=' expression
| IF '(' expression ')' '{' primary_procedure_list '}' | IF '(' expression ')' '{' primary_procedure_list '}'
| RT | RT
| RS | RS
| RP '(' expression ')' '{' primary_procedure_list '}'
| ID '(' ')' | ID '(' ')'
| ID '(' argument_list ')' | ID '(' argument_list ')'
; ;
@ -622,12 +630,12 @@ The grammar rule defines `program` first.
The definition is recursive. The definition is recursive.
- `statement` is program. - `statement` is program.
- `statement statement` is `program statemet`. - `statement statement` is `program statement`.
Therefore, it is program. Therefore, it is program.
- `statement statement statement` is `program statemet`. - `statement statement statement` is `program statement`.
Therefore, it is program. Therefore, it is program.
You can find that a list of statements is program like this. You can find that a sequence of statements is program like this.
`program` and `statement` aren't tokens. `program` and `statement` aren't tokens.
They don't appear in the input. They don't appear in the input.
@ -723,7 +731,17 @@ The following is an extract from `turtle.y`.
#include <stdarg.h> #include <stdarg.h>
#include <setjmp.h> #include <setjmp.h>
#include <math.h> #include <math.h>
#include "turtle.h" #include <glib.h>
#include <cairo.h>
#include "turtle_parser.h"
/* The following line defines 'debug' so that debug information is printed out during the run time. */
/* However it makes the program slow. */
/* If you want to debug on, uncomment the line. */
/* #define debug 1 */
extern cairo_surface_t *surface;
/* error reporting */ /* error reporting */
static void yyerror (char const *s) { /* for syntax error */ static void yyerror (char const *s) { /* for syntax error */
@ -768,7 +786,7 @@ The header file is read by the scanner C source file and other files.
} }
~~~ ~~~
- `yylex` is shared by parser implementation file and scanner file. - `yylex` is shared by the parser implementation file and scanner file.
- `yyparse` and `run` is called by `run_cb` in `turtleapplication.c`. - `yyparse` and `run` is called by `run_cb` in `turtleapplication.c`.
- `node_t` is the type of the semantic value of nterms. - `node_t` is the type of the semantic value of nterms.
The header file defines `YYSTYPE`, which is the semantic value type, with all the token and nterm value types. The header file defines `YYSTYPE`, which is the semantic value type, with all the token and nterm value types.
@ -826,12 +844,14 @@ It also specifies some directives.
%token PW %token PW
%token FD %token FD
%token TR %token TR
%token TL
%token BC %token BC
%token FC %token FC
%token DP %token DP
%token IF %token IF
%token RT %token RT
%token RS %token RS
%token RP
/* constant */ /* constant */
%token <double> NUM %token <double> NUM
/* identirier */ /* identirier */
@ -950,8 +970,9 @@ Be careful.
The operator `=` above is an assignment. The operator `=` above is an assignment.
Assignment is not expression in turtle language. Assignment is not expression in turtle language.
It is primary_procedure. It is primary_procedure.
But if `=` appears in an expression, it is a logical operater, not an assignment. But if `=` appears in an expression, it is a logical operator, not an assignment.
The logical equal '`=`' usually used in the conditional expression, for example, in `if` statement. The logical equal '`=`' usually used in the conditional expression, for example, in `if` statement.
(Turtle language uses '=' instead of '==' in C language).
### Grammar rules ### Grammar rules
@ -984,22 +1005,22 @@ expression:
; ;
~~~ ~~~
- `program` is `statement`. - The first two lines tell that `program` is `statement`.
- Whenever `statement` is reduced to `program`, an action `node_top=$$=$1;` is executed. - Whenever `statement` is reduced to `program`, an action `node_top=$$=$1;` is executed.
- `node_top` is a static variable. - `node_top` is a static variable.
It points the top node of the tree. It points the top node of the tree.
- `$$` is a semantic value of the result, which is `program` in the second line of the example above. - A symbol `$$` is a semantic value of the result.
The semantic value of `program` is a pointer to `node_t` type structure. For example, `$$` in line 2 is the semantic value of `program`.
It was defined in the declaration section. It is a pointer to a `node_t` type structure.
- `$1` is a semantic value of the first component, which is `statement`. - `$1` is a semantic value of the first component.
The semantic value of `statement` is also a pointer to `node_t`. For example, `$1` in line 2 is the semantic value of `statement`.
- `statement` is `primary_procedure`. It is also a pointer to `node_t`.
- The next rule is that `statement` is `primary_procedure`.
There's no action specified. There's no action specified.
Then, the default action is executed. Then, the default action `$$ = $1` is executed.
It is ` $$ = $1`. - The next rule is that `primary_procedure` is `FD` followed by expression.
- `primary_procedure` is `FD` followed by expression.
The action calls `tree1` and assigns its return value to `$$`. The action calls `tree1` and assigns its return value to `$$`.
`tree1` makes a tree node. The function `tree1` makes a tree node.
The tree node has type and union of three pointers to children nodes, string or double. The tree node has type and union of three pointers to children nodes, string or double.
~~~ ~~~
node --+-- type node --+-- type
@ -1009,7 +1030,7 @@ node --+-- type
+---double value +---double value
~~~ ~~~
- `tree1` assigns the four arguments to type, child1, child2 and child3 members. - `tree1` assigns the four arguments to type, child1, child2 and child3 members.
- `expression` is `NUM`. - The last rule is that `expression` is `NUM`.
- `tree2` makes a tree node. The paremeters of `tree2` are a type and a semantic value. - `tree2` makes a tree node. The paremeters of `tree2` are a type and a semantic value.
Suppose the parser reads the following program. Suppose the parser reads the following program.
@ -1062,6 +1083,7 @@ primary_procedure:
| PW expression { $$ = tree1 (N_PW, $2, NULL, NULL); } | PW expression { $$ = tree1 (N_PW, $2, NULL, NULL); }
| FD expression { $$ = tree1 (N_FD, $2, NULL, NULL); } | FD expression { $$ = tree1 (N_FD, $2, NULL, NULL); }
| TR expression { $$ = tree1 (N_TR, $2, NULL, NULL); } | TR expression { $$ = tree1 (N_TR, $2, NULL, NULL); }
| TL expression { $$ = tree1 (N_TL, $2, NULL, NULL); } /* ver 0.5 */
| BC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_BC, $3, $5, $7); } | BC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_BC, $3, $5, $7); }
| FC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_FC, $3, $5, $7); } | FC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_FC, $3, $5, $7); }
/* assignment */ /* assignment */
@ -1070,6 +1092,7 @@ primary_procedure:
| IF '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_IF, $3, $6, NULL); } | IF '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_IF, $3, $6, NULL); }
| RT { $$ = tree1 (N_RT, NULL, NULL, NULL); } | RT { $$ = tree1 (N_RT, NULL, NULL, NULL); }
| RS { $$ = tree1 (N_RS, NULL, NULL, NULL); } | RS { $$ = tree1 (N_RS, NULL, NULL, NULL); }
| RP '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_RP, $3, $6, NULL); }
/* user defined procedure call */ /* user defined procedure call */
| ID '(' ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), NULL, NULL); } | ID '(' ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), NULL, NULL); }
| ID '(' argument_list ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), $3, NULL); } | ID '(' argument_list ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), $3, NULL); }
@ -1219,7 +1242,7 @@ init_table (void) {
~~~ ~~~
`init_table` initializes the table. `init_table` initializes the table.
This must be called before any registrations. This must be called before registrations.
There are five functions to access the table, There are five functions to access the table,
@ -1347,8 +1370,8 @@ The runtime routine stores the name `drawline` and the node of the procedure to
- The second line calls the procedure. - The second line calls the procedure.
First, it looks for the procedure in the symbol table and gets its node. First, it looks for the procedure in the symbol table and gets its node.
Then it searches the node for the parameters and gets `angle` and `distance`. Then it searches the node for the parameters and gets `angle` and `distance`.
- It pushes ("distance", 100.0) to the stack.
- It pushes ("angle", 90.0) to the stack. - It pushes ("angle", 90.0) to the stack.
- It pushes ("distance", 100.0) to the stack.
- It pushes (NULL, 2.0) to the stack. - It pushes (NULL, 2.0) to the stack.
The number 2.0 is the number of parameters (or arguments). The number 2.0 is the number of parameters (or arguments).
It is used when the procedure returns. It is used when the procedure returns.
@ -1356,8 +1379,11 @@ It is used when the procedure returns.
The following diagram shows the structure of the stack. The following diagram shows the structure of the stack.
First, `procedure 1` is called. First, `procedure 1` is called.
The procedure has two parameters. The procedure has two parameters.
In the `procedure 1`, another procedure `procedure 2`, which has one parameter, is called. In the `procedure 1`, another procedure `procedure 2` is called.
And in the `procedure 2`, `procedure 3`, which has three parameters, is called. It has one parameter.
In the `procedure 2`, another procedure `procedure 3` is called.
It has three parameters.
These three procedures are nested.
Programs push data to a stack from a low address memory to a high address memory. Programs push data to a stack from a low address memory to a high address memory.
In the following diagram, the lowest address is at the top and the highest address is at the bottom. In the following diagram, the lowest address is at the top and the highest address is at the bottom.
@ -1948,6 +1974,6 @@ However, the following information is very useful (but old).
- Source code of a language, for example, ruby. - Source code of a language, for example, ruby.
Lately, lots of source codes are in the internet. Lately, lots of source codes are in the internet.
Maybe reading source codes are the most useful for programmers. Maybe reading source codes is the most useful for programmers.
Up: [Readme.md](../Readme.md), Prev: [Section 24](sec24.md), Next: [Section 26](sec26.md) Up: [README.md](../README.md), Prev: [Section 24](sec24.md), Next: [Section 26](sec26.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

BIN
image/turtle_snow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -1,3 +0,0 @@
#include <gtk/gtk.h>
#include "../tfetextview/tfetextview.h"

View file

@ -1,4 +1,5 @@
#include "color.h" #include <gtk/gtk.h>
#include "../tfetextview/tfetextview.h"
static GtkWidget *win; static GtkWidget *win;
static GtkWidget *tv; static GtkWidget *tv;
@ -58,8 +59,6 @@ save_cb (GtkWidget *btns) {
void void
close_cb (GtkWidget *btnc) { close_cb (GtkWidget *btnc) {
if (surface)
cairo_surface_destroy (surface);
gtk_window_destroy (GTK_WINDOW (win)); gtk_window_destroy (GTK_WINDOW (win));
} }
@ -81,13 +80,14 @@ draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpo
static void static void
app_activate (GApplication *application) { app_activate (GApplication *application) {
gtk_widget_show (win); gtk_window_present (GTK_WINDOW (win));
} }
static void static void
app_startup (GApplication *application) { app_startup (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application); GtkApplication *app = GTK_APPLICATION (application);
GtkBuilder *build; GtkBuilder *build;
GdkDisplay *display;
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui"); build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui");
win = GTK_WIDGET (gtk_builder_get_object (build, "win")); win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
@ -98,14 +98,18 @@ app_startup (GApplication *application) {
g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL); g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL); gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL);
GdkDisplay *display; display = gdk_display_get_default ();
display = gtk_widget_get_display (GTK_WIDGET (win));
GtkCssProvider *provider = gtk_css_provider_new (); GtkCssProvider *provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1); gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER); gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
} }
static void
app_shutdown (GApplication *application) {
if (surface)
cairo_surface_destroy (surface);
}
#define APPLICATION_ID "com.github.ToshioCP.color" #define APPLICATION_ID "com.github.ToshioCP.color"
int int
@ -113,9 +117,10 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE); app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv); stat =g_application_run (G_APPLICATION (app), argc, argv);

View file

@ -7,17 +7,17 @@ This is called custom drawing.
GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions. GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions.
In this section, I will explain: In this section, I will explain:
1. Cairo, but only briefly; and 1. Cairo, but only briefly
2. GtkDrawingArea, with a very simple example. 2. GtkDrawingArea, with a very simple example.
## Cairo ## Cairo
Cairo is a set of two dimensional graphical drawing functions (or graphics library). Cairo is a set of two dimensional graphical drawing functions (or graphics library).
There is a lot of documentation on [Cairo's website](https://www.cairographics.org/). There are a lot of documents on [Cairo's website](https://www.cairographics.org/).
If you aren't familiar with Cairo, it is worth reading their [tutorial](https://www.cairographics.org/tutorial/). If you aren't familiar with Cairo, it is worth reading the [tutorial](https://www.cairographics.org/tutorial/).
The following is a gentle introduction to the Cairo library and how to use it. The following is an introduction to the Cairo library and how to use it.
Firstly, in order to use Cairo you need to know about surfaces, sources, masks, destinations, cairo context and transformations. First, you need to know about surfaces, sources, masks, destinations, cairo context and transformations.
- A surface represents an image. - A surface represents an image.
It is like a canvas. It is like a canvas.
@ -48,6 +48,7 @@ This will be the destination.
6. Save the destination surface to a file if necessary. 6. Save the destination surface to a file if necessary.
Here's a simple example program that draws a small square and saves it as a png file. Here's a simple example program that draws a small square and saves it as a png file.
The path of the file is `src/misc/cairo.c`.
@@@include @@@include
misc/cairo.c misc/cairo.c
@ -66,7 +67,7 @@ Width and height are in pixels and given as integers.
- 14: Creates cairo context. - 14: Creates cairo context.
The surface given as an argument will be the destination of the context. The surface given as an argument will be the destination of the context.
- 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint. - 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint.
The second to fourth argument are red, green and blue color values respectively, and they are The second to fourth arguments are red, green and blue color values respectively, and they are
of type float. The values are between zero (0.0) and one (1.0), with of type float. The values are between zero (0.0) and one (1.0), with
black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0). black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).
- 19: `cairo_paint` copies everywhere in the source to destination. - 19: `cairo_paint` copies everywhere in the source to destination.
@ -83,13 +84,13 @@ The square is located at the center.
- 28: Destroys the context. At the same time the source is destroyed. - 28: Destroys the context. At the same time the source is destroyed.
- 29: Destroys the surface. - 29: Destroys the surface.
To compile this, type the following. To compile this, change your current directory to `src/misc` and type the following.
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo` $ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
![rectangle.png](../image/rectangle.png) ![rectangle.png](../image/rectangle.png)
See the [Cairo's website](https://www.cairographics.org/) for more details. See the [Cairo's website](https://www.cairographics.org/) for further information.
## GtkDrawingArea ## GtkDrawingArea
@ -102,12 +103,12 @@ misc/da1.c
The function `main` is almost same as before. The function `main` is almost same as before.
The two functions `app_activate` and `draw_function` are important in this example. The two functions `app_activate` and `draw_function` are important in this example.
- 18: Creates a GtkDrawingArea instance; and - 22: Creates a GtkDrawingArea instance.
- 21: Sets a drawing function of the widget. - 25: Sets a drawing function of the widget.
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary. GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size. For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size.
Then, the whole window needs to be redrawn. Then, the whole window needs to be redrawn.
For the information of `gtk_drawing_area_set_draw_func`, see [Gtk API Reference, gtk\_drawing\_area\_set\_draw\_func](https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html). For the information of `gtk_drawing_area_set_draw_func`, see [Gtk API Reference -- gtk\_drawing\_area\_set\_draw\_func](https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html).
The drawing function has five parameters. The drawing function has five parameters.
@ -122,15 +123,16 @@ The second parameter is a cairo context given by the widget.
The destination surface of the context is connected to the contents of the widget. The destination surface of the context is connected to the contents of the widget.
What you draw to this surface will appear in the widget on the screen. What you draw to this surface will appear in the widget on the screen.
The third and fourth parameters are the size of the destination surface. The third and fourth parameters are the size of the destination surface.
Now, look at the program example again. Now, look at the program again.
- 3-13: The drawing function. - 3-17: The drawing function.
- 7-8: Sets the source to be white and paint the destination white. - 7-8: Sets the source to be white and paint the destination white.
- 9: Sets the line width to be 2. - 9: Sets the line width to be 2.
- 10: Sets the source to be black. - 10: Sets the source to be black.
- 11: Adds a rectangle to the mask. - 11-15: Adds a rectangle to the mask.
- 12: Draws the rectangle with black color to the destination. - 16: Draws the rectangle with black color to the destination.
The program is [src/misc/da1.c](misc/da1.c).
Compile and run it, then a window with a black rectangle (square) appears. Compile and run it, then a window with a black rectangle (square) appears.
Try resizing the window. Try resizing the window.
The square always appears at the center of the window because the drawing function is invoked each time the window is resized. The square always appears at the center of the window because the drawing function is invoked each time the window is resized.

View file

@ -7,6 +7,7 @@ If you write a name of a color in TfeTextView and click on the `run` button, the
![color](../image/color.png){width=7.0cm height=5.13cm} ![color](../image/color.png){width=7.0cm height=5.13cm}
The following colors are available. The following colors are available.
(without new line charactor)
- white - white
- black - black
@ -28,24 +29,23 @@ In this section, we focus on how to bind the two objects.
## Color.ui and color.gresource.xml ## Color.ui and color.gresource.xml
First, We need to make the ui file of the widgets. First, We need to make the ui file of the widgets.
The image in the previous subsection gives us the structure of the widgets. Title bar, four buttons in the tool bar, textview and drawing area.
Title bar, four buttons in the tool bar and two widgets textview and drawing area.
The ui file is as follows. The ui file is as follows.
@@@include @@@include
color/color.ui color/color.ui
@@@ @@@
- 10-53: This part is the tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`. - 10-53: The horizontal box `boxh1` makes a tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`.
This is similar to the toolbar of tfe text editor in [Section 9](sec9.src.md). This is similar to the `tfe` text editor in [Section 9](sec9.src.md).
There are two differences. There are two differences.
`Run` button replaces `New` button. `Run` button replaces `New` button.
A signal element is added to each button object. A signal element is added to each button object.
It has "name" attribute which is a signal name and "handler" attribute which is the name of its signal handler function. It has "name" attribute which is a signal name and "handler" attribute which is the name of its signal handler.
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application. Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
You can achieve this by adding "export_dynamic: true" argument to executable function in `meson.build`. You can achieve this by adding "export_dynamic: true" argument to the executable function in `meson.build`.
And be careful that the handler must be defined without 'static' class. And be careful that the handler must be defined without 'static' class.
- 54-76: Puts GtkScrolledWindow and GtkDrawingArea into GtkBox. - 54-76: The horizontal box `boxh2` includes GtkScrolledWindow and GtkDrawingArea.
GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box. GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box.
TfeTextView is a child of GtkScrolledWindow. TfeTextView is a child of GtkScrolledWindow.
@ -56,24 +56,70 @@ Just substitute "color" for "tfe".
color/color.gresource.xml color/color.gresource.xml
@@@ @@@
## Tfetextview.h, tfetextview.c and color.h ## Drawing function and surface
First two files are the same as before. The main point of this program is a drawing function.
Color.h just includes tfetextview.h.
@@@include @@@include
color/color.h color/colorapplication.c draw_func
@@@ @@@
The `surface` variable in line 3 is a static variable.
~~~C
static cairo_surface_t *surface = NULL;
~~~
The drawing function just copies the `surface` to its own surface with the `cairo_paint` function.
The surface (pointed by the static variable `surface`) is built by the `run` function.
@@@include
color/colorapplication.c run
@@@
- 9-10: Gets the string in the GtkTextBuffer and inserts it to `contents`.
- 11: If the variable `surface` points a surface instance, it is painted as follows.
- 12- 30: The source is set based on the string `contents` and copied to the surface with `cairo_paint`.
- 24,26: Alpha channel is used in "light" and "dark" procedure.
The drawing area just reflects the `surface`.
But one problem is resizing.
If a user resizes the main window, the drawing area is also resized.
It makes size difference between the surface and the drawing area.
So, the surface needs to be resized to fit the drawing area.
It is accomplished by connecting the "resize" signal on the drawing area to a handler.
~~~C
g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
~~~
The handler is as follows.
@@@include
color/colorapplication.c resize_cb
@@@
If the variable `surface` sets a surface instance, it is destroyed.
A new surface is created and its size fits the drawing area.
The surface is assigned to the variable `surface`.
The function `run` is called and the surface is colored.
The signal is emitted when:
- The drawing area is realized (it appears on the display).
- It is changed (resized) while realized
So, the first surface is created when it is realized.
## Colorapplication.c ## Colorapplication.c
This is the main file. This is the main file.
It deals with:
- Building widgets by GtkBuilder. - Builds widgets by GtkBuilder.
- Setting a drawing function of GtkDrawingArea. - Sets a drawing function for GtkDrawingArea.
And connecting a handler to "resize" signal on GtkDrawingArea. And connects a handler to the "resize" signal on the GtkDrawingArea instance.
- Implementing each call back functions. - Implements each call back function.
Particularly, `Run` signal handler is the point in this program. Particularly, `Run` signal handler is the point in this program.
The following is `colorapplication.c`. The following is `colorapplication.c`.
@ -82,40 +128,26 @@ The following is `colorapplication.c`.
color/colorapplication.c color/colorapplication.c
@@@ @@@
- 109-124: The function `main` is almost same as before but there are some differences. - 4-8: Win, tv, da and surface are defined as static variables.
The application ID is "com.github.ToshioCP.color". - 10-42: Run function.
`G_APPLICATION_FLAGS_NONE` is specified so no open signal handler is necessary. - 44-63: Handlers for button signals.
- 87-107: Startup handler. - 65-71: Resize handler.
- 92-97: Builds widgets. - 73-79: Drawing function.
The pointers of the top window, TfeTextView and GtkDrawingArea objects are stored to static variables `win`, `tv` and `da` respectively. - 81-84: Application activate handler.
This is because these objects are often used in handlers. It just shows the main window.
They never be rewritten so they're thread safe. - 86-105: Application startup handler.
- 98: connects "resize" signal and the handler. - 92- 97: It builds widgets according to the ui resource.
- 99: sets the drawing function. The static variables win, tv and da are assigned instances.
- 82-85: Activate handler, which just shows the widgets. - 98: Connects "resize" signal and a handler.
- 74-80: The drawing function. - 99: Drawing function is set.
It just copies `surface` to destination. - 101-104: CSS for textview padding is set.
- 66-72: Resize handler. - 107-111: Application shutdown handler.
Re-creates the surface to fit its width and height for the drawing area and paints by calling the function `run`. If there exists a surface instance, it will be destroyed.
- 59-64: Close handler. - 116-129: A function `main`.
It destroys `surface` if it exists. It creates a new application instance.
Then it destroys the top-level window and quits the application. And connects three signals startup, shutdown and activate to their handlers.
- 49-57: Open and save handler. It runs the application.
They just call the corresponding functions of TfeTextView. It releases the reference to the application and returns with `stat` value.
- 43-47: Run handler.
It calls run function to paint the surface.
After that `gtk_widget_queue_draw` is called.
This function adds the widget (GtkDrawingArea) to the queue to be redrawn.
It is important to know that the window is redrawn whenever it is necessary.
For example, when another window is moved and uncovers part of the widget, or when the window containing it is resized.
But repainting `surface` is not automatically notified to gtk.
Therefore, you need to call `gtk_widget_queue_draw` to redraw the widget.
- 9-41: Run function paints the surface.
First, it gets the contents of GtkTextBuffer.
Then it compares it to "red", "green" and so on.
If it matches the color, then the surface is painted the color.
If it matches "light" or "dark", then the color of the surface is lightened or darkened respectively.
Alpha channel is used.
## Meson.build ## Meson.build
@ -126,14 +158,9 @@ An argument "export_dynamic: true" is added to executable function.
color/meson.build color/meson.build
@@@ @@@
## Compile and execute it ## Build and try
First you need to export some variables (refer to [Section 2](sec2.src.md)) if you've installed GTK 4 from the source. Type the following to compile the program.
If you've installed GTK 4 from the distribution packages, you don't need to do this.
$ . env.sh
Then type the following to compile it.
$ meson _build $ meson _build
$ ninja -C _build $ ninja -C _build
@ -144,7 +171,8 @@ Type the following to execute it.
$ _build/color $ _build/color
Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView. Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView.
Then, click on `Run` button. No new line charactor is needed.
Then, click on the `Run` button.
Make sure the color of GtkDrawingArea changes. Make sure the color of GtkDrawingArea changes.
In this program TfeTextView is used to change the color. In this program TfeTextView is used to change the color.

View file

@ -1,41 +1,42 @@
# Tiny turtle graphics interpreter # Tiny turtle graphics interpreter
A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects. A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects.
It is a very small interpreter but it provides a tool to draw fractal curves. It is a very small interpreter but you can draw fractal curves with it.
The following diagram is a Koch curve, which is a famous example of fractal curves. The following diagram is a Koch curve, which is one of famous fractal curves.
![Koch curve](turtle/image/turtle_koch.png){width=8cm height=5.11cm} ![Koch curve](turtle/image/turtle_koch.png){width=8cm height=5.11cm}
The following is a snow-crystal-shaped curve.
It is composed of six Koch curves.
![Snow](../image/turtle_snow.png){width=8cm height=5.11cm}
This program uses flex and bison. This program uses flex and bison.
Flex is a lexical analyzer. Flex is a lexical analyzer.
Bison is a parser generator. Bison is a parser generator.
These two programs are similar to lex and yacc which are proprietary software developed in Bell Laboratory. These two programs are similar to lex and yacc which are proprietary software developed in Bell Laboratory.
However, flex and bison are open source software. However, flex and bison are open source software.
I will write about how to use those software, but they are not topics about gtk. I will write about how to use those software, but they are not topics about GTK 4.
So, readers can skip that part of this sections. So, readers can skip this section.
## How to use turtle ## How to use turtle
@@@if gfm @@@if gfm
The documentation of turtle is [here](turtle/turtle_doc.src.md). The turtle document is [here](turtle/turtle_doc.src.md).
@@@elif html @@@elif html
The documentation of turtle is [here](turtle/turtle_doc.src.md). The turtle document is [here](https://toshiocp.github.io/Gtk4-tutorial/turtle_doc.html).
@@@elif latex @@@elif latex
The documentation of turtle is in the appendix. The turtle document is in the appendix.
@@@end @@@end
I'll show you a simple example. I'll show you a simple example.
~~~ ~~~
fc (1,0,0) # Foreground color is red, rgb = (1,0,0). fc (1,0,0) # Foreground color is red, rgb = (1,0,0).
pd # Pen down. pd # Pen down.
fd 100 # Go forward by 100 pixels. rp (4) { # Repeat four times.
tr 90 # Turn right by 90 degrees. fd 100 # Go forward by 100 pixels.
fd 100 tr 90 # Turn right by 90 degrees.
tr 90 }
fd 100
tr 90
fd 100
tr 90
~~~ ~~~
1. Compile and install `turtle` (See the documentation above). 1. Compile and install `turtle` (See the documentation above).
@ -45,8 +46,8 @@ Then, run `turtle`.
The side of the square is 100 pixels long. The side of the square is 100 pixels long.
In the same way, you can draw other curves. In the same way, you can draw other curves.
The documentation above shows some fractal curves such as tree, snow and square-koch. The turtle document includes some fractal curves such as tree, snow and square-koch.
The source code in turtle language is located at [src/turtle/example](turtle/example) directory. The source codes are located at [src/turtle/example](turtle/example) directory.
You can read these files into `turtle` editor by clicking on the `Open` button. You can read these files into `turtle` editor by clicking on the `Open` button.
## Combination of TfeTextView and GtkDrawingArea objects ## Combination of TfeTextView and GtkDrawingArea objects
@ -59,10 +60,9 @@ It is similar to `color` program in the previous section.
3. The parser reads the program and generates tree-structured data. 3. The parser reads the program and generates tree-structured data.
4. The interpriter reads the data and executes it step by step. 4. The interpriter reads the data and executes it step by step.
And it draws shapes on a surface. And it draws shapes on a surface.
The surface is different from the surface of the GtkDrawingArea widget. The surface isn't the one in the GtkDrawingArea widget.
5. The widget is added to the queue. 5. The widget is added to the queue.
It will be redrawn with the drawing function. It will be redrawn with the drawing function, which just copies the surface into the one in the GtkDrawingArea.
The function just copies the surface, which is drawn by the interpreter, into the surface of the GtkDrawingArea.
![Parser, interpreter and drawing function](../image/turtle.png) ![Parser, interpreter and drawing function](../image/turtle.png)
@ -77,7 +77,7 @@ turtle/turtleapplication.c run_cb resize_cb
- 8-13: The static value `busy` holds a status of the interpreter. - 8-13: The static value `busy` holds a status of the interpreter.
If it is `TRUE`, the interpreter is running and it is not possible to call the interpreter again because it's not a re-entrant program. If it is `TRUE`, the interpreter is running and it is not possible to call the interpreter again because it's not a re-entrant program.
If it is `FALSE`, it is safe to call the interpreter. If it is `FALSE`, it is safe to call the interpreter.
- 14: Now it is about to call the interpreter so it changes `busy` to TRUE. - 14: Changes `busy` to TRUE to avoid reentrance.
- 15-16: Gets the contents of `tb`. - 15-16: Gets the contents of `tb`.
- 17: The variable `surface` is a static variable. - 17: The variable `surface` is a static variable.
It points to a `cairo_surface_t` instance. It points to a `cairo_surface_t` instance.
@ -86,17 +86,17 @@ Therefore, `surface` isn't NULL usually.
But if it is NULL, the interpreter won't be called. But if it is NULL, the interpreter won't be called.
- 18: Initializes lexical analyzer. - 18: Initializes lexical analyzer.
- 19: Calls parser. - 19: Calls parser.
Parser analyzes the program codes syntactically and generate a tree structured data. Parser analyzes the program codes syntactically and generates a tree structured data.
- 20-22: If the parser successfully parsed, it calls `run` (runtime routine). - 20-22: If the parser successfully parsed, it calls `run` (runtime routine).
- 23: finalizes the lexical analyzer. - 23: finalizes the lexical analyzer.
- 25: frees `contents`. - 25: frees `contents`.
- 26: Adds the drawing area widget to the queue to draw. - 26: Adds the drawing area widget to the queue to draw.
- 27: The interpreter program has finished so `busy` is now changed to FALSE. - 27: The interpreter program has finished so `busy` is now changed to FALSE.
- 29-34: A handler of "resized" signal. - 30-37: A "resized" signal handler.
If `surface` isn't NULL, it destroys the old surface. If the `surface` isn't NULL, it is destroyed.
Then it creates a new surface. A new surface is created.
Its size is the same as the surface of the GtkDrawingArea instance. Its size is the same as the surface of the GtkDrawingArea instance.
Run\_cb is called to redraw the shape on the drawing area.
Other part of `turtleapplication.c` is almost same as the codes of `colorapplication.c` in the previous section. Other part of `turtleapplication.c` is almost same as the codes of `colorapplication.c` in the previous section.
The codes of `turtleapplication.c` is in the [turtle directory](turtle). The codes of `turtleapplication.c` is in the [turtle directory](turtle).
@ -185,7 +185,7 @@ The source files are:
- flex source file => `turtle.lex` - flex source file => `turtle.lex`
- bison source file => `turtle.y` - bison source file => `turtle.y`
- C header file => `turtle.h`, `turtle_lex.h` - C header file => `turtle_lex.h`
- C source file => `turtleapplication.c` - C source file => `turtleapplication.c`
- other files => `turtle.ui`, `turtle.gresources.xml` and `meson.build` - other files => `turtle.ui`, `turtle.gresources.xml` and `meson.build`
@ -195,12 +195,13 @@ The compilation process is a bit complicated.
It also generates `resources.h`. It also generates `resources.h`.
2. bison compiles `turtle.y` to `turtle_parser.c` and generates `turtle_parser.h` 2. bison compiles `turtle.y` to `turtle_parser.c` and generates `turtle_parser.h`
3. flex compiles `turtle.lex` to `turtle_lex.c`. 3. flex compiles `turtle.lex` to `turtle_lex.c`.
4. gcc compiles `application.c`, `resources.c`, `turtle_parser.c` and `turtle_lex.c` with `turtle.h`, `turtle_lex.h`, `resources.h` and `turtle_parser.h`. 4. gcc compiles `application.c`, `resources.c`, `turtle_parser.c` and `turtle_lex.c` with `turtle_lex.h`, `resources.h` and `turtle_parser.h`.
It generates an executable file `turtle`. It generates an executable file `turtle`.
![compile process](../image/turtle_compile_process.png){width=12cm height=9cm} ![compile process](../image/turtle_compile_process.png){width=12cm height=9cm}
Meson controls the process and the instruction is described in `meson.build`. Meson controls the process.
The instruction is described in `meson.build`.
@@@include @@@include
turtle/meson.build turtle/meson.build
@ -213,15 +214,13 @@ This program uses trigonometric functions.
They are defined in the math library, but the library is optional. They are defined in the math library, but the library is optional.
So, it is necessary to include it by `#include <math.h>` and also link the library with the linker. So, it is necessary to include it by `#include <math.h>` and also link the library with the linker.
- 6: Gets gtk4 library. - 6: Gets gtk4 library.
- 8: Gets gnome module. - 8: Gets gnome module.See [Meson build system website -- GNUME module](https://mesonbuild.com/Gnome-module.html#gnome-module) for further information.
Module is a system provided by meson.
See [Meson build system website, GNUME module](https://mesonbuild.com/Gnome-module.html#gnome-module) for further information.
- 9: Compiles ui file to C source file according to the XML file `turtle.gresource.xml`. - 9: Compiles ui file to C source file according to the XML file `turtle.gresource.xml`.
- 11: Gets flex. - 11: Gets flex.
- 12: Gets bison. - 12: Gets bison.
- 13: Compiles `turtle.y` to `turtle_parser.c` and `turtle_parser.h` by bison. - 13: Compiles `turtle.y` to `turtle_parser.c` and `turtle_parser.h` by bison.
The function `custom_target` creates a custom top level target. The function `custom_target` creates a custom top level target.
See [Meson build system website, custom target](https://mesonbuild.com/Reference-manual.html#custom_target) for further information. See [Meson build system website -- custom target](https://mesonbuild.com/Reference-manual.html#custom_target) for further information.
- 14: Compiles `turtle.lex` to `turtle_lex.c` by flex. - 14: Compiles `turtle.lex` to `turtle_lex.c` by flex.
- 16: Specifies C source files. - 16: Specifies C source files.
- 18: Compiles C source files including generated files by glib-compile-resources, bison and flex. - 18: Compiles C source files including generated files by glib-compile-resources, bison and flex.
@ -268,17 +267,17 @@ They are definitions, rules and user code sections.
- 1-12: Lines between "%top{" and "}" are C source codes. - 1-12: Lines between "%top{" and "}" are C source codes.
They will be copied to the top of the generated C source file. They will be copied to the top of the generated C source file.
- 2-3: The function `strlen`, in line 62, is defined in `string.h` - 2-3: The function `strlen`, in line 65, is defined in `string.h`
The function `atof`, in line 37, is defined in `stdlib.h`. The function `atof`, in line 40, is defined in `stdlib.h`.
- 6-8: The current input position is pointed by `nline` and `ncolumn`. - 7-9: The current input position is pointed by `nline` and `ncolumn`.
The function `get_location` (line 58-63) sets `yylloc`to point the start and end point of `yytext` in the buffer. The function `get_location` (line 61-66) sets `yylloc`to point the start and end point of `yytext` in the buffer.
This function is declared here so that it can be called before the function is defined. This function is declared here so that it can be called before the function is defined.
- 11: GSlist is used to keep allocated memories. - 12: GSlist is used to keep allocated memories.
- 14: This option (`%option noyywrap`) must be specified when you have only single source file to the scanner. Refer to "9 The Generated Scanner" in the flex documentation in your distribution for further information. - 15: This option (`%option noyywrap`) must be specified when you have only single source file to the scanner. Refer to "9 The Generated Scanner" in the flex documentation in your distribution for further information.
(The documentation is not on the internet.) (The documentation is not on the internet.)
- 16-17: `REAL_NUMBER` and `IDENTIFIER` are names. - 17-18: `REAL_NUMBER` and `IDENTIFIER` are names.
A name begins with a letter or an underscore followed by zero or more letters, digits, underscores (`_`) or dashes (`-`). A name begins with a letter or an underscore followed by zero or more letters, digits, underscores (`_`) or dashes (`-`).
They are followed by regular expressions which are their definition. They are followed by regular expressions which are their definitions.
They will be used in rules section and will expand to the definition. They will be used in rules section and will expand to the definition.
You can leave out such definitions here and use regular expressions in rules section directly. You can leave out such definitions here and use regular expressions in rules section directly.
@ -290,64 +289,66 @@ The patterns are regular expressions or names surrounded by braces.
The names must be defined in the definitions section. The names must be defined in the definitions section.
The definition of the regular expression is written in the flex documentation. The definition of the regular expression is written in the flex documentation.
For example, line 37 is a rule. For example, line 40 is a rule.
- `{REAL_NUMBER}` is a pattern - `{REAL_NUMBER}` is a pattern
- `get_location (yytext); yylval.NUM = atof (yytext); return NUM;` is an action. - `get_location (yytext); yylval.NUM = atof (yytext); return NUM;` is an action.
`{REAL_NUMBER}` is defined in the 16th line, so it expands to `(0|[1-9][0-9]*)(\.[0-9]+)?`. `{REAL_NUMBER}` is defined in the line 17, so it expands to `(0|[1-9][0-9]*)(\.[0-9]+)?`.
This regular expression matches numbers like `0`, `12` and `1.5`. This regular expression matches numbers like `0`, `12` and `1.5`.
If the input is a number, it matches the pattern in line 37. If an input is a number, it matches the pattern in line 40.
Then the matched text is assigned to `yytext` and corresponding action is executed. Then the matched text is assigned to `yytext` and corresponding action is executed.
A function `get_location` changes the location variables. A function `get_location` changes the location variables to the position at the text.
It assigns `atof (yytext)`, which is double sized number converted from `yytext`, to `yylval.NUM` and return `NUM`. It assigns `atof (yytext)`, which is double sized number converted from `yytext`, to `yylval.NUM` and return `NUM`.
`NUM` is an integer defined by `turtle.y`. `NUM` is a token kind and it represents integer.
It is defined in `turtle.y`.
The scanner generated by flex and C compiler has `yylex` function. The scanner generated by flex has `yylex` function.
If `yylex` is called and the input is "123.4", then it works as follows. If `yylex` is called and the input is "123.4", then it works as follows.
1. A string "123.4" matches `{REAL_NUMBER}`. 1. A string "123.4" matches `{REAL_NUMBER}`.
2. Update the location variable `ncolumn` and `yylloc`with `get_location`. 2. Update the location variable `ncolumn` and `yylloc`with `get_location`.
3. `atof` converts the string "123.4" to double type number 123.4. 3. The function `atof` converts the string "123.4" to double type number 123.4.
4. It is assigned to `yylval.NUM`. 4. It is assigned to `yylval.NUM`.
5. `yylex` returns `NUM` to the caller. 5. `yylex` returns `NUM` to the caller.
Then the caller knows the input is `NUM` (number), and its value is 123.4. Then the caller knows the input is a number (`NUM`), and its value is 123.4.
- 19-55: Rules section. - 20-58: Rules section.
- 20: The symbol `.` (dot) matches any character except newline. - 21: The symbol `.` (dot) matches any character except newline.
Therefore, a comment begins `#` followed by any characters except newline. Therefore, a comment begins `#` followed by any characters except newline.
No action happens. No action happens.
- 21: White space just increases a variable `ncolumn` by one. - 22: White space just increases the variable `ncolumn` by one.
- 22: Tab is assumed to be equal to eight spaces. - 23: Tab is assumed to be equal to eight spaces.
- 23: New line increases a variable `nline` by one and resets `ncolumn`. - 24: New line increases a variable `nline` by one and resets `ncolumn`.
- 25-35: Keywords just updates the location variables `ncolumn` and `yylloc`, and return the codes of the keywords. - 26-38: Keywords just updates the location variables `ncolumn` and `yylloc`, and return the token kinds of the keywords.
- 37: Real number constant. - 40: Real number constant.
- 38: `IDENTIFIER` is defined in line 17. - 42: `IDENTIFIER` is defined in line 18.
The location variables are updated and the name of the identifier is assigned to `yylval.ID`. The location variables are updated and the name of the identifier is assigned to `yylval.ID`.
The memory of the name is allocated by the function `g_strdup`. The memory of the name is allocated by the function `g_strdup`.
The memory is registered to the list (GSlist type list). The memory is registered to the list (GSlist type list).
The memory will be freed after the runtime routine finishes. The memory will be freed after the runtime routine finishes.
Returns `ID`. A token kind `ID` is returned.
- 43-54: Symbols just update the location variable and return the codes. - 46-56: Symbols just update the location variable and return the token kinds.
The code is the same as the symbol itself. The token kind is the same as the symbol itself.
- 55: If the input doesn't match above patterns, then it is error. - 58: If the input doesn't match the patterns, then it is an error.
Returns `YYUNDEF`. A special token kind `YYUNDEF` is returned.
### User code section ### User code section
This section is just copied to C source file. This section is just copied to C source file.
- 58-63: A function `get_location`. - 61-66: A function `get_location`.
The location of the input is recorded to `nline` and `ncolumn`. The location of the input is recorded to `nline` and `ncolumn`.
A variable `yylloc` is referred by the parser. A variable `yylloc` is referred by the parser.
It is a C structure and has four members, `first_line`, `first_column`, `last_line` and `last_column`. It is a C structure and has four members, `first_line`, `first_column`, `last_line` and `last_column`.
They point the start and end of the current input text. They point the start and end of the current input text.
- 65: `YY_BUFFER_STATE` is a pointer points the input buffer. - 68: `YY_BUFFER_STATE` is a pointer points the input buffer.
- 67-70: `init_flex` is called by `run_cb` signal handler, which is called when `Run` button is clicked on. - 70-73: A function `init_flex` is called by `run_cb` which is a "clicked" signal handler on the `Run` button.
`run_cb` calls `init_flex` with one argument which is the copy of the content of GtkTextBuffer. It has one string type parameter.
`yy_scan_string` sets the input buffer to read from the text. The caller assigns it with the content of the GtkTextBuffer instance.
- 72-75: `finalize_flex` is called after runtime routine finishes. A function `yy_scan_string` sets the input buffer for the scanner.
- 75-78: A function `finalize_flex` is called after runtime routine finishes.
It deletes the input buffer. It deletes the input buffer.
## Turtle.y ## Turtle.y
@ -357,8 +358,8 @@ So I will explain the key points and leave out other less important parts.
### What does bison do? ### What does bison do?
Bison creates C source file from bison source file. Bison creates C source file of a parser from a bison source file.
Bison source file is a text file. The bison source file is a text file.
A parser analyzes a program source code according to its grammar. A parser analyzes a program source code according to its grammar.
Suppose here is a turtle source file. Suppose here is a turtle source file.
@ -399,7 +400,7 @@ So, the parser gets items in the following table whenever it calls `yylex`.
Bison source code specifies the grammar rules of turtle language. Bison source code specifies the grammar rules of turtle language.
For example, `fc (1,0,0)` is called primary procedure. For example, `fc (1,0,0)` is called primary procedure.
A procedure is like a void type function in C source code. A procedure is like a void type C function.
It doesn't return any values. It doesn't return any values.
Programmers can define their own procedures. Programmers can define their own procedures.
On the other hand, `fc` is a built-in procedure. On the other hand, `fc` is a built-in procedure.
@ -417,7 +418,8 @@ This means:
- expression is ID or NUM. - expression is ID or NUM.
The description above is called BNF (Backus-Naur form). The description above is called BNF (Backus-Naur form).
More precisely, it is similar to BNF. Precisely speaking, it is not exactly the same as BNF.
But the difference is small.
The first line is: The first line is:
@ -427,7 +429,7 @@ FC '(' NUM ',' NUM ',' NUM ')';
The parser analyzes the turtle source code and if the input matches the definition above, the parser recognizes it as a primary procedure. The parser analyzes the turtle source code and if the input matches the definition above, the parser recognizes it as a primary procedure.
The grammar of turtle is described in the [document](turtle/turtle_doc.src.md). The grammar of turtle is described in the [Turtle manual](https://toshiocp.github.io/Gtk4-tutorial/turtle_doc.html).
The following is an extract from the document. The following is an extract from the document.
~~~ ~~~
@ -447,12 +449,14 @@ primary_procedure:
| PW expression | PW expression
| FD expression | FD expression
| TR expression | TR expression
| TL expression
| BC '(' expression ',' expression ',' expression ')' | BC '(' expression ',' expression ',' expression ')'
| FC '(' expression ',' expression ',' expression ')' | FC '(' expression ',' expression ',' expression ')'
| ID '=' expression | ID '=' expression
| IF '(' expression ')' '{' primary_procedure_list '}' | IF '(' expression ')' '{' primary_procedure_list '}'
| RT | RT
| RS | RS
| RP '(' expression ')' '{' primary_procedure_list '}'
| ID '(' ')' | ID '(' ')'
| ID '(' argument_list ')' | ID '(' argument_list ')'
; ;
@ -499,12 +503,12 @@ The grammar rule defines `program` first.
The definition is recursive. The definition is recursive.
- `statement` is program. - `statement` is program.
- `statement statement` is `program statemet`. - `statement statement` is `program statement`.
Therefore, it is program. Therefore, it is program.
- `statement statement statement` is `program statemet`. - `statement statement statement` is `program statement`.
Therefore, it is program. Therefore, it is program.
You can find that a list of statements is program like this. You can find that a sequence of statements is program like this.
`program` and `statement` aren't tokens. `program` and `statement` aren't tokens.
They don't appear in the input. They don't appear in the input.
@ -606,7 +610,17 @@ The following is an extract from `turtle.y`.
#include <stdarg.h> #include <stdarg.h>
#include <setjmp.h> #include <setjmp.h>
#include <math.h> #include <math.h>
#include "turtle.h" #include <glib.h>
#include <cairo.h>
#include "turtle_parser.h"
/* The following line defines 'debug' so that debug information is printed out during the run time. */
/* However it makes the program slow. */
/* If you want to debug on, uncomment the line. */
/* #define debug 1 */
extern cairo_surface_t *surface;
/* error reporting */ /* error reporting */
static void yyerror (char const *s) { /* for syntax error */ static void yyerror (char const *s) { /* for syntax error */
@ -657,7 +671,7 @@ The header file is read by the scanner C source file and other files.
} }
~~~ ~~~
- `yylex` is shared by parser implementation file and scanner file. - `yylex` is shared by the parser implementation file and scanner file.
- `yyparse` and `run` is called by `run_cb` in `turtleapplication.c`. - `yyparse` and `run` is called by `run_cb` in `turtleapplication.c`.
- `node_t` is the type of the semantic value of nterms. - `node_t` is the type of the semantic value of nterms.
The header file defines `YYSTYPE`, which is the semantic value type, with all the token and nterm value types. The header file defines `YYSTYPE`, which is the semantic value type, with all the token and nterm value types.
@ -715,12 +729,14 @@ It also specifies some directives.
%token PW %token PW
%token FD %token FD
%token TR %token TR
%token TL
%token BC %token BC
%token FC %token FC
%token DP %token DP
%token IF %token IF
%token RT %token RT
%token RS %token RS
%token RP
/* constant */ /* constant */
%token <double> NUM %token <double> NUM
/* identirier */ /* identirier */
@ -839,8 +855,9 @@ Be careful.
The operator `=` above is an assignment. The operator `=` above is an assignment.
Assignment is not expression in turtle language. Assignment is not expression in turtle language.
It is primary_procedure. It is primary_procedure.
But if `=` appears in an expression, it is a logical operater, not an assignment. But if `=` appears in an expression, it is a logical operator, not an assignment.
The logical equal '`=`' usually used in the conditional expression, for example, in `if` statement. The logical equal '`=`' usually used in the conditional expression, for example, in `if` statement.
(Turtle language uses '=' instead of '==' in C language).
### Grammar rules ### Grammar rules
@ -873,22 +890,22 @@ expression:
; ;
~~~ ~~~
- `program` is `statement`. - The first two lines tell that `program` is `statement`.
- Whenever `statement` is reduced to `program`, an action `node_top=$$=$1;` is executed. - Whenever `statement` is reduced to `program`, an action `node_top=$$=$1;` is executed.
- `node_top` is a static variable. - `node_top` is a static variable.
It points the top node of the tree. It points the top node of the tree.
- `$$` is a semantic value of the result, which is `program` in the second line of the example above. - A symbol `$$` is a semantic value of the result.
The semantic value of `program` is a pointer to `node_t` type structure. For example, `$$` in line 2 is the semantic value of `program`.
It was defined in the declaration section. It is a pointer to a `node_t` type structure.
- `$1` is a semantic value of the first component, which is `statement`. - `$1` is a semantic value of the first component.
The semantic value of `statement` is also a pointer to `node_t`. For example, `$1` in line 2 is the semantic value of `statement`.
- `statement` is `primary_procedure`. It is also a pointer to `node_t`.
- The next rule is that `statement` is `primary_procedure`.
There's no action specified. There's no action specified.
Then, the default action is executed. Then, the default action `$$ = $1` is executed.
It is ` $$ = $1`. - The next rule is that `primary_procedure` is `FD` followed by expression.
- `primary_procedure` is `FD` followed by expression.
The action calls `tree1` and assigns its return value to `$$`. The action calls `tree1` and assigns its return value to `$$`.
`tree1` makes a tree node. The function `tree1` makes a tree node.
The tree node has type and union of three pointers to children nodes, string or double. The tree node has type and union of three pointers to children nodes, string or double.
~~~ ~~~
node --+-- type node --+-- type
@ -898,7 +915,7 @@ node --+-- type
+---double value +---double value
~~~ ~~~
- `tree1` assigns the four arguments to type, child1, child2 and child3 members. - `tree1` assigns the four arguments to type, child1, child2 and child3 members.
- `expression` is `NUM`. - The last rule is that `expression` is `NUM`.
- `tree2` makes a tree node. The paremeters of `tree2` are a type and a semantic value. - `tree2` makes a tree node. The paremeters of `tree2` are a type and a semantic value.
Suppose the parser reads the following program. Suppose the parser reads the following program.
@ -957,6 +974,7 @@ primary_procedure:
| PW expression { $$ = tree1 (N_PW, $2, NULL, NULL); } | PW expression { $$ = tree1 (N_PW, $2, NULL, NULL); }
| FD expression { $$ = tree1 (N_FD, $2, NULL, NULL); } | FD expression { $$ = tree1 (N_FD, $2, NULL, NULL); }
| TR expression { $$ = tree1 (N_TR, $2, NULL, NULL); } | TR expression { $$ = tree1 (N_TR, $2, NULL, NULL); }
| TL expression { $$ = tree1 (N_TL, $2, NULL, NULL); } /* ver 0.5 */
| BC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_BC, $3, $5, $7); } | BC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_BC, $3, $5, $7); }
| FC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_FC, $3, $5, $7); } | FC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_FC, $3, $5, $7); }
/* assignment */ /* assignment */
@ -965,6 +983,7 @@ primary_procedure:
| IF '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_IF, $3, $6, NULL); } | IF '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_IF, $3, $6, NULL); }
| RT { $$ = tree1 (N_RT, NULL, NULL, NULL); } | RT { $$ = tree1 (N_RT, NULL, NULL, NULL); }
| RS { $$ = tree1 (N_RS, NULL, NULL, NULL); } | RS { $$ = tree1 (N_RS, NULL, NULL, NULL); }
| RP '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_RP, $3, $6, NULL); }
/* user defined procedure call */ /* user defined procedure call */
| ID '(' ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), NULL, NULL); } | ID '(' ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), NULL, NULL); }
| ID '(' argument_list ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), $3, NULL); } | ID '(' argument_list ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), $3, NULL); }
@ -1114,7 +1133,7 @@ init_table (void) {
~~~ ~~~
`init_table` initializes the table. `init_table` initializes the table.
This must be called before any registrations. This must be called before registrations.
There are five functions to access the table, There are five functions to access the table,
@ -1242,8 +1261,8 @@ The runtime routine stores the name `drawline` and the node of the procedure to
- The second line calls the procedure. - The second line calls the procedure.
First, it looks for the procedure in the symbol table and gets its node. First, it looks for the procedure in the symbol table and gets its node.
Then it searches the node for the parameters and gets `angle` and `distance`. Then it searches the node for the parameters and gets `angle` and `distance`.
- It pushes ("distance", 100.0) to the stack.
- It pushes ("angle", 90.0) to the stack. - It pushes ("angle", 90.0) to the stack.
- It pushes ("distance", 100.0) to the stack.
- It pushes (NULL, 2.0) to the stack. - It pushes (NULL, 2.0) to the stack.
The number 2.0 is the number of parameters (or arguments). The number 2.0 is the number of parameters (or arguments).
It is used when the procedure returns. It is used when the procedure returns.
@ -1251,8 +1270,11 @@ It is used when the procedure returns.
The following diagram shows the structure of the stack. The following diagram shows the structure of the stack.
First, `procedure 1` is called. First, `procedure 1` is called.
The procedure has two parameters. The procedure has two parameters.
In the `procedure 1`, another procedure `procedure 2`, which has one parameter, is called. In the `procedure 1`, another procedure `procedure 2` is called.
And in the `procedure 2`, `procedure 3`, which has three parameters, is called. It has one parameter.
In the `procedure 2`, another procedure `procedure 3` is called.
It has three parameters.
These three procedures are nested.
Programs push data to a stack from a low address memory to a high address memory. Programs push data to a stack from a low address memory to a high address memory.
In the following diagram, the lowest address is at the top and the highest address is at the bottom. In the following diagram, the lowest address is at the top and the highest address is at the bottom.
@ -1843,4 +1865,4 @@ However, the following information is very useful (but old).
- Source code of a language, for example, ruby. - Source code of a language, for example, ruby.
Lately, lots of source codes are in the internet. Lately, lots of source codes are in the internet.
Maybe reading source codes are the most useful for programmers. Maybe reading source codes is the most useful for programmers.

View file

@ -1,12 +1,8 @@
dp sq (side) { dp sq (side) {
fd side rp (4) {
tr 90 fd side
fd side tr 90
tr 90 }
fd side
tr 90
fd side
tr 90
} }
sq (100) sq (100)

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -1,13 +0,0 @@
#include <gtk/gtk.h>
#include "../tfetextview/tfetextview.h"
#include "turtle_lex.h"
#include "turtle_parser.h"
/* The following line defines 'debug' so that debug information is printed during the run time. */
/* However it makes the program slow. */
/* If you don't want to see such information, remove the line. */
/*#define debug 1*/
extern cairo_surface_t *surface;

View file

@ -1,7 +1,8 @@
%top{ %top{
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "turtle.h" #include <glib.h>
#include "turtle_parser.h"
static int nline = 1; static int nline = 1;
static int ncolumn = 1; static int ncolumn = 1;
@ -27,12 +28,14 @@ pd get_location (yytext); return PD; /* pen down */
pw get_location (yytext); return PW; /* pen width = line width */ pw get_location (yytext); return PW; /* pen width = line width */
fd get_location (yytext); return FD; /* forward */ fd get_location (yytext); return FD; /* forward */
tr get_location (yytext); return TR; /* turn right */ tr get_location (yytext); return TR; /* turn right */
tl get_location (yytext); return TL; /* turn left ver 0.5 */
bc get_location (yytext); return BC; /* background color */ bc get_location (yytext); return BC; /* background color */
fc get_location (yytext); return FC; /* foreground color */ fc get_location (yytext); return FC; /* foreground color */
dp get_location (yytext); return DP; /* define procedure */ dp get_location (yytext); return DP; /* define procedure */
if get_location (yytext); return IF; /* if statement */ if get_location (yytext); return IF; /* if statement */
rt get_location (yytext); return RT; /* return statement */ rt get_location (yytext); return RT; /* return statement */
rs get_location (yytext); return RS; /* reset the status */ rs get_location (yytext); return RS; /* reset the status */
rp get_location (yytext); return RP; /* repeat ver 0.5 */
/* constant */ /* constant */
{REAL_NUMBER} get_location (yytext); yylval.NUM = atof (yytext); return NUM; {REAL_NUMBER} get_location (yytext); yylval.NUM = atof (yytext); return NUM;
/* identifier */ /* identifier */
@ -73,4 +76,3 @@ void
finalize_flex (void) { finalize_flex (void) {
yy_delete_buffer (state); yy_delete_buffer (state);
} }

View file

@ -12,7 +12,17 @@
#include <stdarg.h> #include <stdarg.h>
#include <setjmp.h> #include <setjmp.h>
#include <math.h> #include <math.h>
#include "turtle.h" #include <glib.h>
#include <cairo.h>
#include "turtle_parser.h"
/* The following line defines 'debug' so that debug information is printed out during the run time. */
/* However it makes the program slow. */
/* If you want to debug on, uncomment the line. */
/* #define debug 1 */
extern cairo_surface_t *surface;
/* error reporting */ /* error reporting */
static void yyerror (char const *s) { /* for syntax error */ static void yyerror (char const *s) { /* for syntax error */
@ -25,6 +35,7 @@
N_PW, N_PW,
N_FD, N_FD,
N_TR, N_TR,
N_TL, /* Turn Left version 0.5 */
N_BC, N_BC,
N_FC, N_FC,
N_DP, N_DP,
@ -32,6 +43,7 @@
N_IF, N_IF,
N_RT, N_RT,
N_RS, N_RS,
N_RP, /* Repeat version 0.5 */
N_NUM, N_NUM,
N_ID, N_ID,
N_program, N_program,
@ -59,6 +71,7 @@
"N_PW", "N_PW",
"N_FD", "N_FD",
"N_TR", "N_TR",
"N_TL", /* ver o.5 */
"N_BC", "N_BC",
"N_FC", "N_FC",
"N_DP", "N_DP",
@ -66,6 +79,7 @@
"N_IF", "N_IF",
"N_RT", "N_RT",
"N_RS", "N_RS",
"N_RP", /* ver 0.5 */
"N_NUM", "N_NUM",
"N_ID", "N_ID",
"N_program", "N_program",
@ -132,12 +146,14 @@
%token PW %token PW
%token FD %token FD
%token TR %token TR
%token TL /* ver 0.5 */
%token BC %token BC
%token FC %token FC
%token DP %token DP
%token IF %token IF
%token RT %token RT
%token RS %token RS
%token RP /* ver 0.5 */
/* constant */ /* constant */
%token <double> NUM %token <double> NUM
/* identirier */ /* identirier */
@ -180,6 +196,7 @@ primary_procedure:
| PW expression { $$ = tree1 (N_PW, $2, NULL, NULL); } | PW expression { $$ = tree1 (N_PW, $2, NULL, NULL); }
| FD expression { $$ = tree1 (N_FD, $2, NULL, NULL); } | FD expression { $$ = tree1 (N_FD, $2, NULL, NULL); }
| TR expression { $$ = tree1 (N_TR, $2, NULL, NULL); } | TR expression { $$ = tree1 (N_TR, $2, NULL, NULL); }
| TL expression { $$ = tree1 (N_TL, $2, NULL, NULL); } /* ver 0.5 */
| BC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_BC, $3, $5, $7); } | BC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_BC, $3, $5, $7); }
| FC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_FC, $3, $5, $7); } | FC '(' expression ',' expression ',' expression ')' { $$ = tree1 (N_FC, $3, $5, $7); }
/* assignment */ /* assignment */
@ -188,6 +205,7 @@ primary_procedure:
| IF '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_IF, $3, $6, NULL); } | IF '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_IF, $3, $6, NULL); }
| RT { $$ = tree1 (N_RT, NULL, NULL, NULL); } | RT { $$ = tree1 (N_RT, NULL, NULL, NULL); }
| RS { $$ = tree1 (N_RS, NULL, NULL, NULL); } | RS { $$ = tree1 (N_RS, NULL, NULL, NULL); }
| RP '(' expression ')' '{' primary_procedure_list '}' { $$ = tree1 (N_RP, $3, $6, NULL); }
/* user defined procedure call */ /* user defined procedure call */
| ID '(' ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), NULL, NULL); } | ID '(' ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), NULL, NULL); }
| ID '(' argument_list ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), $3, NULL); } | ID '(' argument_list ')' { $$ = tree1 (N_procedure_call, tree3 (N_ID, $1), $3, NULL); }
@ -614,6 +632,7 @@ execute (node_t *node) {
double d, x, y; double d, x, y;
char *name; char *name;
int n, i; int n, i;
int counter; /* ver 0.5, for repeat procedure */
if (node == NULL) if (node == NULL)
runtime_error ("Node is NULL.\n"); runtime_error ("Node is NULL.\n");
@ -677,6 +696,11 @@ g_print ("fd: New Y coordinate is %f.\n", cur_y);
for (; angle < 0; angle += 360.0); for (; angle < 0; angle += 360.0);
for (; angle>360; angle -= 360.0); for (; angle>360; angle -= 360.0);
break; break;
case N_TL: /* ver 0.5 */
angle += eval (child1(node));
for (; angle < 0; angle += 360.0);
for (; angle>360; angle -= 360.0);
break;
case N_BC: case N_BC:
bc.red = eval (child1(node)); bc.red = eval (child1(node));
bc.green = eval (child2(node)); bc.green = eval (child2(node));
@ -722,6 +746,15 @@ g_print ("fc: Foreground color is (%f, %f, %f).\n", fc.red, fc.green, fc.blue);
fc.red = 0.0; fc.green = 0.0; fc.blue = 0.0; fc.red = 0.0; fc.green = 0.0; fc.blue = 0.0;
/* To change background color, use bc. */ /* To change background color, use bc. */
break; break;
case N_RP: /* ver 0.5 */
counter = (int) eval (child1(node));
if (counter < 0)
runtime_error ("Repeat number %d is negative.\n", counter);
if (counter > 100)
runtime_error ("Repeat number %d is too big.\n", counter);
for (i=0; i<counter; ++i)
execute (child2(node));
break;
case N_procedure_call: case N_procedure_call:
name = name(child1(node)); name = name(child1(node));
node_t *proc = proc_lookup (name); node_t *proc = proc_lookup (name);

View file

@ -1,4 +1,7 @@
#include "turtle.h" #include <gtk/gtk.h>
#include "../tfetextview/tfetextview.h"
#include "turtle_lex.h"
#include "turtle_parser.h"
static GtkWidget *win; static GtkWidget *win;
static GtkWidget *tv; static GtkWidget *tv;
@ -13,10 +16,10 @@ run_cb (GtkWidget *btnr) {
GtkTextIter end_iter; GtkTextIter end_iter;
char *contents; char *contents;
int stat; int stat;
static gboolean busy = FALSE; static gboolean busy = FALSE; /* initialized only once */
/* yyparse() and run() are NOT thread safe. */ /* yyparse() and run() are NOT thread safe. */
/* The variable busy avoids reentry. */ /* The variable busy avoids reentrance. */
if (busy) if (busy)
return; return;
busy = TRUE; busy = TRUE;
@ -79,9 +82,11 @@ show_filename (TfeTextView *tv) {
static void static void
resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) { resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) {
if (surface) if (surface)
cairo_surface_destroy (surface); cairo_surface_destroy (surface);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
run_cb (NULL); // NULL is a fake (run button).
} }
static void static void
@ -128,7 +133,7 @@ main (int argc, char **argv) {
GtkApplication *app; GtkApplication *app;
int stat; int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE); app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
@ -137,4 +142,3 @@ main (int argc, char **argv) {
g_object_unref (app); g_object_unref (app);
return stat; return stat;
} }

1
src/turtle/version.txt Normal file
View file

@ -0,0 +1 @@
0.5