<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://remcokranenburg.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://remcokranenburg.com/" rel="alternate" type="text/html" /><updated>2025-12-28T10:38:22+00:00</updated><id>https://remcokranenburg.com/feed.xml</id><title type="html">Remco’s Blog</title><subtitle>Code and stuff</subtitle><author><name>Remco Kranenburg</name></author><entry><title type="html">Google Summer of Code 2021, Retrospective</title><link href="https://remcokranenburg.com/2021/08/22/gsoc-retrospective.html" rel="alternate" type="text/html" title="Google Summer of Code 2021, Retrospective" /><published>2021-08-22T00:00:00+00:00</published><updated>2021-08-22T00:00:00+00:00</updated><id>https://remcokranenburg.com/2021/08/22/gsoc-retrospective</id><content type="html" xml:base="https://remcokranenburg.com/2021/08/22/gsoc-retrospective.html"><![CDATA[<p>In this post, we look back on the virtual keyboard project that I worked on for
xrdesktop as a participant in Google Summer of Code 2021. It was a great
experience and a good introduction to open source development.</p>

<p><img src="/assets/2021/07-26-keyboard-mode4-emoji.png" alt="Keyboard emoji mode" title="Keyboard emoji mode" /></p>

<h2 id="current-status">Current Status</h2>

<p>The project was very successful in its goal of providing a text input method for
xrdesktop independent of SteamVR’s virtual keyboard. The keyboard is usable for
people in 56 languages and it works with KDE. You can try it out by compiling
the ‘next’ branches of the various libraries. See below for detailed
instructions.</p>

<p><img src="/assets/2021/08-19-arabic-keyboard.png" alt="Arabic keyboard layout" title="Arabic keyboard layout" /></p>

<h2 id="how-to-run">How to Run</h2>

<p>You will need to compile and run it for KDE. Follow
<a href="https://gitlab.freedesktop.org/xrdesktop/xrdesktop/-/wikis/Installation-from-source">this how-to guide</a>
to install xrdesktop from source. However, you need to use different branches in
a few cases:</p>

<ul>
  <li>gulkan: <a href="https://gitlab.freedesktop.org/xrdesktop/gulkan/-/tree/next">gulkan:next</a></li>
  <li>gxr: <a href="https://gitlab.freedesktop.org/xrdesktop/gxr/-/tree/next">gxr:next</a></li>
  <li>xrdesktop: <a href="https://gitlab.freedesktop.org/remcokranenburg/xrdesktop/-/tree/keyboard-internationalization">remcokranenburg:keyboard-internationalization</a></li>
  <li>libinputsynth: <a href="https://gitlab.freedesktop.org/remcokranenburg/libinputsynth/-/tree/keyvals-input">remcokranenburg:keyvals-input</a></li>
  <li>kwin-effect-xrdesktop: <a href="https://gitlab.freedesktop.org/remcokranenburg/kwin-effect-xrdesktop/-/tree/virtual-keyboard-support">remcokranenburg:virtual-keyboard-support</a></li>
</ul>

<p><img src="/assets/2021/08-13-kde-in-vr-with-keyboard.png" alt="KDE in VR with keyboard" title="KDE in VR with keyboard" /></p>

<h2 id="future-work">Future Work</h2>

<p>During our feedback sessions, my mentors and I decided to focus first on making
the basic keyboard experience complete, by implementing it for a desktop
environment and providing keyboard layouts for a lot of languages. In the
revised planning, the prediction mode was postponed.</p>

<p>A start was made on a predictive mode that would work like the swipe keyboard
mode on Android. Post-GSoC, I’d like to continue this work. When finished, I’d
also like to investigate whether the same technology can be used to add a swipe
mode to GNOME Shell’s on-screen keyboard.</p>

<p>The third and most experimental mode was dropped for the time being. It entailed
a <a href="https://en.wikipedia.org/wiki/Dasher_(software)">Dasher-like</a> interface adapted for a virtual reality environment. Since VR has
such a different way of interacting, a keyboard input method might not make the
most use of your abilities. The hypothesis is that a Dasher-like method might
be faster when used with six-degrees-of-freedom controllers.</p>

<p>While implementing keyboard layouts, I learned that languages with
non-alphabetic writing systems, such as Chinese, need a different way of
handling keyboard input. They need to be able to write tentative words, that can
be <em>committed</em> by additional key presses. This feature could potentially be
based on the prediction system, because that also predicts tentative words,
which can be committed by choosing one of the proposals.</p>

<h2 id="merge-requests">Merge Requests</h2>

<p>Some warm-up bugfixes / improvements:</p>

<ul>
  <li>MERGED <a href="https://gitlab.freedesktop.org/xrdesktop/xrdesktop/-/merge_requests/24">xrdesktop!24</a> Improve menu position when attached to controller</li>
  <li>MERGED <a href="https://gitlab.freedesktop.org/xrdesktop/xrdesktop/-/merge_requests/27">xrdesktop!27</a> Bump minimum meson version</li>
  <li>MERGED <a href="https://gitlab.freedesktop.org/xrdesktop/xrdesktop/-/merge_requests/28">xrdesktop!28</a> Client to shell example</li>
  <li>MERGED <a href="https://gitlab.freedesktop.org/xrdesktop/xrdesktop/-/merge_requests/30">xrdesktop!30</a> g3k-button: correct calculation of background circle radius</li>
  <li>MERGED <a href="https://gitlab.freedesktop.org/xrdesktop/gulkan/-/merge_requests/15">gulkan!15</a> Add pango example</li>
  <li>MERGED <a href="https://gitlab.freedesktop.org/xrdesktop/libinputsynth/-/merge_requests/4">libinputsynth!4</a> gitignore: ignore build directory</li>
</ul>

<p>The main merge requests, implementing the G3kKeyboard and making it work in KDE:</p>

<ul>
  <li>MERGED <a href="https://gitlab.freedesktop.org/xrdesktop/xrdesktop/-/merge_requests/31">xrdesktop!31</a> Add virtual keyboard</li>
  <li>OPEN <a href="https://gitlab.freedesktop.org/xrdesktop/kwin-effect-xrdesktop/-/merge_requests/4">kwin-effect-xrdesktop!4</a> Support G3kKeyboard</li>
</ul>

<p>Implementing multiple keyboard layouts:</p>

<ul>
  <li>DECLINED <a href="https://gitlab.freedesktop.org/xrdesktop/libinputsynth/-/merge_requests/5">libintputsynth!5</a> Keysequence input</li>
  <li>OPEN <a href="https://gitlab.freedesktop.org/xrdesktop/libinputsynth/-/merge_requests/6">libinputsynth!6</a> Keyvals input</li>
  <li>OPEN <a href="https://gitlab.freedesktop.org/xrdesktop/xrdesktop/-/merge_requests/33">xrdesktop!33</a> Keyboard internationalization</li>
</ul>

<h2 id="blog-posts">Blog Posts</h2>

<p>This project also prompted me to finally start a blog:</p>

<ul>
  <li><a href="/2021/05/30/getting-started-with-glib.html">Getting Started with GLib</a></li>
  <li><a href="/2021/06/14/introduction-to-xrdesktop.html">Introduction to xrdesktop</a></li>
  <li><a href="/2021/06/27/keyboard-event-handling.html">Keyboard Event Handling</a></li>
  <li><a href="/2021/07/26/emoji-mode.html">Emoji Mode ✨</a></li>
  <li><a href="/2021/08/13/kde-in-vr-with-keyboard.html">KDE in VR with Keyboard Support</a></li>
  <li><a href="/2021/08/19/keyboard-layouts.html">Keyboard Layouts</a></li>
</ul>

<h2 id="final-words">Final Words</h2>

<p>I loved working on this project. I’ve always been interested in computer
graphics and virtual reality, so this was the perfect fit for a start in open
source development.</p>

<p>I would like to thank my mentors Christoph Haag and Lubosz Sarnecki for their
fantastic support, and Google and Collabora for providing the opportunity to
work on such a cool project. We will see each other again, I hope!</p>]]></content><author><name>Remco Kranenburg</name></author><category term="GSoC2021" /><summary type="html"><![CDATA[In this post, we look back on the virtual keyboard project that I worked on for xrdesktop as a participant in Google Summer of Code 2021. It was a great experience and a good introduction to open source development.]]></summary></entry><entry><title type="html">Keyboard Layouts</title><link href="https://remcokranenburg.com/2021/08/19/keyboard-layouts.html" rel="alternate" type="text/html" title="Keyboard Layouts" /><published>2021-08-19T00:00:00+00:00</published><updated>2021-08-19T00:00:00+00:00</updated><id>https://remcokranenburg.com/2021/08/19/keyboard-layouts</id><content type="html" xml:base="https://remcokranenburg.com/2021/08/19/keyboard-layouts.html"><![CDATA[<p>We now support 56 languages in xrdesktop!</p>

<p><img src="/assets/2021/08-19-arabic-keyboard.png" alt="Arabic keyboard layout" /></p>

<p>The Unicode Common Locale Data Repository specifies many keyboard layouts, so we
convert this data to our internal JSON format.</p>

<p>Currently, we only support languages with alphabetic writing systems, such as
English, Arabic or Russian. Languages with logographic writing systems, such as
Chinese, Japanese or Korean, are not supported yet. They require a more
complicated interaction model, because a character is built by multiple
keystrokes before being committed.</p>]]></content><author><name>Remco Kranenburg</name></author><category term="GSoC2021" /><summary type="html"><![CDATA[We now support 56 languages in xrdesktop!]]></summary></entry><entry><title type="html">KDE in VR with Keyboard Support</title><link href="https://remcokranenburg.com/2021/08/13/kde-in-vr-with-keyboard.html" rel="alternate" type="text/html" title="KDE in VR with Keyboard Support" /><published>2021-08-13T00:00:00+00:00</published><updated>2021-08-13T00:00:00+00:00</updated><id>https://remcokranenburg.com/2021/08/13/kde-in-vr-with-keyboard</id><content type="html" xml:base="https://remcokranenburg.com/2021/08/13/kde-in-vr-with-keyboard.html"><![CDATA[<p>The xrdesktop project comes with built-in support for the two most popular
desktop environments: GNOME and KDE. I’ve managed to get my virtual keyboard
working for KDE!</p>

<p><img src="/assets/2021/08-13-kde-in-vr-with-keyboard.png" alt="KDE in VR with Keyboard" /></p>

<p>KDE’s xrdesktop support comes in the form of a KWin ‘desktop effects’ plugin.
It is in this plugin that windows are mirrored in the VR environment. This is
also the place where I needed to add support for input through the virtual
keyboard.</p>

<p>Luckily, there was existing code that worked with SteamVR’s keyboard. Adding
xrdesktop’s own keyboard support was a matter of connecting to its
<code class="language-plaintext highlighter-rouge">key-pressed-event</code> event with a callback, which synthesizes a key press using
<code class="language-plaintext highlighter-rouge">libinputsynth</code>.</p>]]></content><author><name>Remco Kranenburg</name></author><category term="GSoC2021" /><summary type="html"><![CDATA[The xrdesktop project comes with built-in support for the two most popular desktop environments: GNOME and KDE. I’ve managed to get my virtual keyboard working for KDE!]]></summary></entry><entry><title type="html">Emoji Mode ✨</title><link href="https://remcokranenburg.com/2021/07/26/emoji-mode.html" rel="alternate" type="text/html" title="Emoji Mode ✨" /><published>2021-07-26T00:00:00+00:00</published><updated>2021-07-26T00:00:00+00:00</updated><id>https://remcokranenburg.com/2021/07/26/emoji-mode</id><content type="html" xml:base="https://remcokranenburg.com/2021/07/26/emoji-mode.html"><![CDATA[<p>We now have multiple keyboard modes in xrdesktop: lowercase, uppercase, numbers,
special characters and emojis!</p>

<p><img src="/assets/2021/07-26-keyboard-mode1-main.png" alt="Keyboard lowercase mode" /></p>

<p>The modes are specified in JSON format and loaded as a GResource. The following
shows an abbreviated example of how each key is specified. Each key is part of
a mode and has a <em>label</em>, <em>x</em> and <em>y</em> coordinates and a <em>width</em>. An optional
<em>target</em> makes a key switch to a different mode.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Small"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"modes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Main"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"keys"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"q"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"w"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"e"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"r"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"t"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">4.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"y"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">5.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="err">...</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"⇧"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"target"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Capitals"</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"z"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"x"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"c"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"v"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">4.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"b"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">5.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"n"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">6.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="err">...</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"⌫"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">8.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"?123"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"target"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Numbers"</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">","</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"☺"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"target"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Emoji"</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">" "</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">4.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"."</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">7.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"↵"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">8.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="w"> </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Capitals"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"keys"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Q"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"W"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"E"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"R"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"T"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">4.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Y"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">5.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="err">...</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"⇧"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"target"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Main"</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Z"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"X"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"C"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"V"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">4.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"B"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">5.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"N"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">6.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="err">...</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"⌫"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">8.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"?123"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"target"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Numbers"</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">","</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"☺"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"target"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Emoji"</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">" "</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">4.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"."</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">7.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"↵"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">8.5</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.5</span><span class="w"> </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="err">...</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Numeric mode:</p>

<p><img src="/assets/2021/07-26-keyboard-mode2-numbers.png" alt="Keyboard uppercase mode" /></p>

<p>Special character mode is accessible from the numeric mode.</p>

<p><img src="/assets/2021/07-26-keyboard-mode3-special.png" alt="Keyboard special mode" /></p>

<p>And we have support for emojis!</p>

<p><img src="/assets/2021/07-26-keyboard-mode4-emoji.png" alt="Keyboard emoji mode" /></p>

<p>The emojis are specified literally in the JSON file:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Emoji"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"keys"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"😀"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"😃"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"😄"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"😁"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"😆"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">4.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w"> </span><span class="nl">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"😅"</span><span class="p">,</span><span class="w"> </span><span class="nl">"x"</span><span class="p">:</span><span class="w"> </span><span class="mf">5.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"y"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="nl">"w"</span><span class="p">:</span><span class="w"> </span><span class="mf">1.0</span><span class="w"> </span><span class="p">},</span><span class="w">
    </span><span class="err">...</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>To get it all working, I had to add Pango as a dependency, because it supports
font fallbacks. Most fonts do not support all possible characters, so when you
want to draw text in many languages, you will need to use several backup fonts.</p>

<p>I added a small pango example to gulkan, showing Arabic, Chinese and Hindi text
plus emojis:</p>

<p><img src="/assets/2021/07-26-gulkan-pango.png" alt="Gulkan pango example" /></p>

<p>That’s it for now! A future post will showcase Kwin support for xrdesktop with
a virtual keyboard.</p>]]></content><author><name>Remco Kranenburg</name></author><category term="GSoC2021" /><summary type="html"><![CDATA[We now have multiple keyboard modes in xrdesktop: lowercase, uppercase, numbers, special characters and emojis!]]></summary></entry><entry><title type="html">Keyboard Event Handling</title><link href="https://remcokranenburg.com/2021/06/27/keyboard-event-handling.html" rel="alternate" type="text/html" title="Keyboard Event Handling" /><published>2021-06-27T00:00:00+00:00</published><updated>2021-06-27T00:00:00+00:00</updated><id>https://remcokranenburg.com/2021/06/27/keyboard-event-handling</id><content type="html" xml:base="https://remcokranenburg.com/2021/06/27/keyboard-event-handling.html"><![CDATA[<p>First things first, I made sure that the keyboard is always in view by using
some nice existing functionality of xrdesktop and grouping all keyboard buttons
into one G3kContainer and attaching it to the user’s head:</p>

<video src="/assets/2021/06-27-keyboard-attachment.webm" autoplay="" loop=""></video>

<h2 id="event-handling">Event Handling</h2>

<p>In the past few days of working on the virtual keyboard for xrdesktop, I’ve been
trying to make the keyboard behave like a keyboard. That is, it should notice
when one of its keys is clicked by the user, and it should generate the proper
key event when this happens. This is the result:</p>

<video src="/assets/2021/06-27-keyboard-events.webm" autoplay="" loop=""></video>

<p>In my <a href="/2021/06/14/introduction-to-xrdesktop.html">previous blog post</a>, you
could already see the keys react to clicks, but this is not enough for a fully
functioning keyboard system. For one, you don’t want to handle the events for
each button separately; you would have to connect more than 100 events for a
standard keyboard! You just want to connect a single <code class="language-plaintext highlighter-rouge">key-press-event</code> to your
active window, which would contain all the information of the pressed key.</p>

<p>The second reason for handling the click events inside the keyboard, is that we
need to keep track of the modifier keys (Shift, Ctrl, Alt, things like that).
The keyboard should maintain the state of these keys (on or off) internally, so
the <code class="language-plaintext highlighter-rouge">key-press-event</code> would send the key capitalized when Shift is active, or
‘Ctrl + X’ when Ctrl is active, etc.</p>

<p>So how is this done technically? We need to register a <code class="language-plaintext highlighter-rouge">key-press-event</code>
<em>signal</em> for G3kKeyboard, which we can then <em>emit</em> at the right time. The user
of the keyboard will write an event <em>callback</em>, and will <em>connect</em> the signal
to it.</p>

<p>What is the right time to emit the <code class="language-plaintext highlighter-rouge">key-press-event</code>? Well, when a button has
been clicked. So, we need to <em>connect</em> the button’s <code class="language-plaintext highlighter-rouge">grab-start-event</code> to a
callback that emits the <code class="language-plaintext highlighter-rouge">key-press-event</code>.</p>

<p>You might be familiar with event handling in other languages, but in GLib style
this is a fairly verbose affair. Once I understand the GObject system a bit
better, I will write that part 2 of ‘Getting Started with GLib’ with a short
tutorial on how to create objects with it. Find the raw event handling code
below:</p>

<p><code class="language-plaintext highlighter-rouge">g3k_keyboard.c</code>:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span>
<span class="nf">g3k_keyboard_class_init</span><span class="p">(</span><span class="n">G3kKeyboardClass</span> <span class="o">*</span><span class="n">klass</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">keyboard_signals</span><span class="p">[</span><span class="n">KEY_PRESS_EVENT</span><span class="p">]</span> <span class="o">=</span>
    <span class="n">g_signal_new</span> <span class="p">(</span><span class="s">"key-press-event"</span><span class="p">,</span>
                  <span class="n">G_TYPE_FROM_CLASS</span> <span class="p">(</span><span class="n">klass</span><span class="p">),</span>
                  <span class="n">G_SIGNAL_RUN_LAST</span><span class="p">,</span>
                  <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">G_TYPE_NONE</span><span class="p">,</span>
                  <span class="mi">1</span><span class="p">,</span> <span class="n">G_TYPE_POINTER</span> <span class="o">|</span> <span class="n">G_SIGNAL_TYPE_STATIC_SCOPE</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span>
<span class="nf">g3k_keyboard_click_cb</span> <span class="p">(</span><span class="n">XrdWindow</span>     <span class="o">*</span><span class="n">window</span><span class="p">,</span>
                       <span class="n">GxrController</span> <span class="o">*</span><span class="n">controller</span><span class="p">,</span>
                       <span class="n">gpointer</span>       <span class="n">user_data</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">G3kKeyboard</span> <span class="o">*</span><span class="n">keyboard</span> <span class="o">=</span> <span class="n">G3K_KEYBOARD</span> <span class="p">(</span><span class="n">user_data</span><span class="p">);</span>

  <span class="n">g_autofree</span> <span class="n">gchar</span> <span class="o">*</span><span class="n">title</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="n">g_object_get</span> <span class="p">(</span><span class="n">window</span><span class="p">,</span> <span class="s">"title"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">title</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>

  <span class="n">G3kKeyEvent</span> <span class="n">event</span> <span class="o">=</span> <span class="p">{</span> <span class="p">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">title</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">};</span>

  <span class="n">g_signal_emit</span> <span class="p">(</span><span class="n">keyboard</span><span class="p">,</span> <span class="n">keyboard_signals</span><span class="p">[</span><span class="n">KEY_PRESS_EVENT</span><span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">event</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Example usage:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span>
<span class="nf">keyboard_pressed_cb</span><span class="p">(</span><span class="n">G3kKeyboard</span> <span class="o">*</span><span class="n">keyboard</span><span class="p">,</span>
                    <span class="n">G3kKeyEvent</span> <span class="o">*</span><span class="n">event</span><span class="p">,</span>
                    <span class="n">gpointer</span>     <span class="n">user_data</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">g_print</span> <span class="p">(</span><span class="s">"Yay, key %c was pressed!</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">event</span><span class="o">-&gt;</span><span class="n">key</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span>
<span class="nf">_init_keyboard</span><span class="p">(</span><span class="n">XrdShell</span> <span class="o">*</span><span class="n">shell</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">G3kContext</span> <span class="o">*</span><span class="n">context</span> <span class="o">=</span> <span class="n">xrd_shell_get_g3k</span> <span class="p">(</span><span class="n">shell</span><span class="p">);</span>
  <span class="n">G3kKeyboard</span> <span class="o">*</span><span class="n">keyboard</span> <span class="o">=</span> <span class="n">g3k_keyboard_new</span> <span class="p">(</span><span class="n">context</span><span class="p">);</span>

  <span class="n">G3kContainer</span> <span class="o">*</span><span class="n">container</span> <span class="o">=</span> <span class="n">g3k_keyboard_get_container</span><span class="p">(</span><span class="n">keyboard</span><span class="p">);</span>
  <span class="n">GSList</span> <span class="o">*</span><span class="n">buttons</span> <span class="o">=</span> <span class="n">g3k_container_get_windows</span> <span class="p">(</span><span class="n">container</span><span class="p">);</span>

  <span class="k">for</span> <span class="p">(</span><span class="n">GSList</span> <span class="o">*</span><span class="n">button</span> <span class="o">=</span> <span class="n">buttons</span><span class="p">;</span> <span class="n">button</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="n">button</span> <span class="o">=</span> <span class="n">button</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="n">g_signal_connect</span> <span class="p">(</span><span class="n">button</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">,</span> <span class="s">"grab-start-event"</span><span class="p">,</span>
                        <span class="p">(</span><span class="n">GCallback</span><span class="p">)</span> <span class="n">g3k_keyboard_click_cb</span><span class="p">,</span> <span class="n">keyboard</span><span class="p">);</span>
    <span class="p">}</span>

  <span class="n">g_signal_connect</span> <span class="p">(</span><span class="n">keyboard</span><span class="p">,</span> <span class="s">"key-press-event"</span><span class="p">,</span>
                    <span class="n">G_CALLBACK</span> <span class="p">(</span><span class="n">keyboard_pressed_cb</span><span class="p">),</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>That’s it for now. Up next, let’s start on the Swype prediction mode! For this,
we need to show the path that the controller traced over the keyboard’s keys and
we need to calculate the word that was most likely meant to be typed.</p>]]></content><author><name>Remco Kranenburg</name></author><category term="GSoC2021" /><summary type="html"><![CDATA[First things first, I made sure that the keyboard is always in view by using some nice existing functionality of xrdesktop and grouping all keyboard buttons into one G3kContainer and attaching it to the user’s head:]]></summary></entry><entry><title type="html">Introduction to xrdesktop</title><link href="https://remcokranenburg.com/2021/06/14/introduction-to-xrdesktop.html" rel="alternate" type="text/html" title="Introduction to xrdesktop" /><published>2021-06-14T00:00:00+00:00</published><updated>2021-06-14T00:00:00+00:00</updated><id>https://remcokranenburg.com/2021/06/14/introduction-to-xrdesktop</id><content type="html" xml:base="https://remcokranenburg.com/2021/06/14/introduction-to-xrdesktop.html"><![CDATA[<p>Hello again! It has been a week and we still haven’t been formally introduced!
I’m Remco Kranenburg, and I’m implementing a <em>virtual keyboard</em> for
<a href="https://gitlab.freedesktop.org/xrdesktop/xrdesktop">xrdesktop</a> this summer, for
<abbr title="Google Summer of Code">GSoC</abbr> 2021. In this post I’ll talk a
bit about the project and show some initial results. But first, a screenshot!</p>

<p><img src="/assets/2021/06-14-keyboard-drawing.png" alt="Keyboard drawing" /></p>

<h2 id="predictive-text-input-for-virtual-reality">Predictive Text Input for Virtual Reality</h2>

<p><em>xrdesktop</em> is a project that brings your Linux desktop into a VR space, with
support for KDE and GNOME. It is written in the form of a GLib library. You
could easily write your own VR window manager, in theory in any language (though
language bindings are currently not finished).</p>

<p>One area in which xrdesktop is lacking, is a virtual keyboard. SteamVR provides
one, but it is not available when you are in a virtual environment without
SteamVR, such as when you are using a standalone headset, or when you are using
the fully open source OpenXR implementation
<a href="https://monado.freedesktop.org/">Monado</a>. Since xrdesktop is intended to work
in all such environments, we need our own keyboard implementation.</p>

<p>During GSoC 2021, that is exactly what I will implement. The project is divided
into three broad milestones:</p>

<ol>
  <li>a basic QWERTY keyboard implementation</li>
  <li>a <a href="https://en.wikipedia.org/wiki/Swype">Swype</a>-like predictive mode</li>
  <li>a <a href="https://en.wikipedia.org/wiki/Dasher_(software)">Dasher</a>-like predictive
mode</li>
</ol>

<p>The first milestone is the important one, since it enables text input in
xrdesktop at all. Any of the other modes are nice-to-have, but those are the
modes I’m personally most excited about!</p>

<h2 id="first-week">First Week</h2>

<p>It was quite a rush to be able to make that first screenshot! In fact, I was so
excited that I forgot to make a ‘C’ key.</p>

<p>Let’s look a bit into the structure of the xrdesktop project! xrdesktop is in
the middle of a major refactoring currently, with much of the code that is not
desktop-specific being moved out to a separate namespace called G3k. It should
result in a very easy to learn system, but right now it is a bit of a mess.</p>

<p>The project uses <em>Vulkan</em> to draw graphics and <em>OpenXR</em> to interface with VR
hardware. Instead of writing directly against these APIs, two GLib wrappers were
created: <em>Gulkan</em> and <em>GXR.</em> GXR used to have support for both OpenVR (SteamVR,
basically) and OpenXR, but the refactoring simplifies this to just OpenXR. We
also have a vector math library called <em>graphene</em>, which provides your basic
matrix operations. Then there is <em>G3k</em>, which you can think of as Gtk for VR. It
should provide things like buttons, containers, stuff like that. I say ‘should’,
because it is only a few days old at this point! Finally, there is <em>xrdesktop</em>,
which contains the code that positions your desktop windows in VR.</p>

<p>At first, I created a class XrdKeyboard (more on how to create classes with GLib
in an upcoming part 2 of <em>Getting Started with GLib</em>), but it seemed like this
was the wrong level of abstraction, so I renamed it G3kKeyboard in the end. But
because the refactoring is not complete yet, it still depends on XrdClient for
now. This is also true for G3kButton, which is implemented as an XrdWindow at
the moment. The buttons of G3kKeyboard are currently XrdWindows, but that will
surely change in the future.</p>

<p>Currently, when you click on a key, it prints a message to <em>stdout</em>. Next up,
I’ll read up a bit more about GObject signals and properties, so we can connect
the keyboard events to the active window. Let’s end with another screenshot
showing our amazing VR stdout printer!</p>

<p><img src="/assets/2021/06-14-keyboard-with-callbacks.png" alt="Keyboard with callback" /></p>]]></content><author><name>Remco Kranenburg</name></author><category term="GSoC2021" /><summary type="html"><![CDATA[Hello again! It has been a week and we still haven’t been formally introduced! I’m Remco Kranenburg, and I’m implementing a virtual keyboard for xrdesktop this summer, for GSoC 2021. In this post I’ll talk a bit about the project and show some initial results. But first, a screenshot!]]></summary></entry><entry><title type="html">Getting Started with GLib</title><link href="https://remcokranenburg.com/2021/05/30/getting-started-with-glib.html" rel="alternate" type="text/html" title="Getting Started with GLib" /><published>2021-05-30T00:00:00+00:00</published><updated>2021-05-30T00:00:00+00:00</updated><id>https://remcokranenburg.com/2021/05/30/getting-started-with-glib</id><content type="html" xml:base="https://remcokranenburg.com/2021/05/30/getting-started-with-glib.html"><![CDATA[<p>For GSoC 2021, I will be working on xrdesktop, which is written with the GLib library. Since
learning by explaining is even better than learning by doing, I’ll show the basics of using GLib in
this blog post. We’ll start with a plain C program and gradually replace parts of it with GLib
functionality.</p>

<h2 id="what-is-glib">What is GLib?</h2>

<p>First things first, GLib is a C library designed with two goals:</p>

<ol>
  <li>making C programmers’ lives easier by adding high-level language constructs to C</li>
  <li>adding an interoperability layer to C libraries so they can automatically be used in other
programming languages</li>
</ol>

<p>Initially, we’ll only be concerned with the first goal. GLib extends the standard C environment in
many ways, such as safer string handling, abstract data types, and a fully-featured object-oriented
type system.</p>

<h2 id="a-simple-program">A Simple Program</h2>

<p>As an example program, let’s look at a solution for the
<a href="https://adventofcode.com/2019/day/1">first puzzle</a> of the wonderful <em>Advent of Code 2019</em> puzzles.
If you want to do these puzzles yourself, you might want to stop reading further now, although the
first exercise is just an introduction of course!</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;math.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">calculate_fuel</span><span class="p">(</span><span class="kt">int</span> <span class="n">weight</span><span class="p">,</span> <span class="n">gboolean</span> <span class="n">include_fuel</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">additional_weight</span> <span class="o">=</span> <span class="n">fmax</span><span class="p">(</span><span class="n">weight</span> <span class="o">/</span> <span class="mi">3</span> <span class="o">-</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

    <span class="k">if</span><span class="p">(</span><span class="n">include_fuel</span> <span class="o">&amp;&amp;</span> <span class="n">additional_weight</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">additional_weight</span> <span class="o">+=</span> <span class="n">calculate_fuel</span><span class="p">(</span><span class="n">additional_weight</span><span class="p">,</span> <span class="n">include_fuel</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">additional_weight</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
    <span class="kt">_Bool</span> <span class="n">include_fuel</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">argc</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span><span class="p">(</span><span class="n">strcmp</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="s">"--include-fuel"</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">include_fuel</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">total_fuel_required</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">for</span><span class="p">(;;)</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">mass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="kt">int</span> <span class="n">tokens_scanned</span> <span class="o">=</span> <span class="n">scanf</span><span class="p">(</span><span class="s">"%d"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mass</span><span class="p">);</span>

        <span class="k">if</span><span class="p">(</span><span class="n">tokens_scanned</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="kt">int</span> <span class="n">fuel_required</span> <span class="o">=</span> <span class="n">calculate_fuel</span><span class="p">(</span><span class="n">mass</span><span class="p">,</span> <span class="n">include_fuel</span><span class="p">);</span>
        <span class="n">total_fuel_required</span> <span class="o">+=</span> <span class="n">fuel_required</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">total_fuel_required</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This program must be compiled with <code class="language-plaintext highlighter-rouge">gcc -lm main.c</code>, to link C’s math library to our program.</p>

<p>The first thing we need to do to convert this to a GLib-based program, is to add the <code class="language-plaintext highlighter-rouge">glib.h</code>
include to the top of the file:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;glib.h&gt;</span><span class="cp">
</span></code></pre></div></div>

<p>We will also need to add some more options to the compiler. You can find out which ones we need
exactly by running <code class="language-plaintext highlighter-rouge">pkg-config --cflags --libs glib-2.0</code>. On my system, this gives the following
output:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-I/usr/include/glib-2.0
-I/usr/lib64/glib-2.0/include
-I/usr/include/sysprof-4
-pthread
-lglib-2.0
</code></pre></div></div>

<p>You see a few include directories that tell the compiler where to find GLib’s header files, as well
as <code class="language-plaintext highlighter-rouge">-pthread</code> which enables thread support, and <code class="language-plaintext highlighter-rouge">-lglib-2.0</code>, which links the GLib library to our
program.</p>

<h2 id="glib-conversion">GLib Conversion</h2>

<p>Now that we have GLib, we can start thinking about improving the code.</p>

<h3 id="math">Math</h3>

<p>One thing that comes to mind immediately is that we need to include <code class="language-plaintext highlighter-rouge">math.h</code> and link C’s math
library only for the <code class="language-plaintext highlighter-rouge">fmax()</code> function. If we are using GLib anyway, we can use its <code class="language-plaintext highlighter-rouge">MAX()</code> macro
instead.</p>

<h3 id="booleans">Booleans</h3>

<p>Another peculiarity of C is its boolean logic. It is essentially integer comparison: <code class="language-plaintext highlighter-rouge">0</code> means
false and any other value means true. Traditional C code would look like this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">done</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="k">while</span><span class="p">(</span><span class="o">!</span><span class="n">done</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">do_cool_things</span><span class="p">();</span>

    <span class="k">if</span><span class="p">(</span><span class="n">is_done</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">done</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is fairly weak from a type system perspective, since it is now easily possible to mix integer
arithmetic and boolean logic. For example, the values <code class="language-plaintext highlighter-rouge">1</code> and <code class="language-plaintext highlighter-rouge">42</code> are both <em>true</em>, but they do not
equal each other, and it is possible to turn a false value into true by adding to it <code class="language-plaintext highlighter-rouge">value += 1</code>.
A <code class="language-plaintext highlighter-rouge">_Bool</code> type was added to the language at a relatively late stage, and it is even possible to use
<code class="language-plaintext highlighter-rouge">bool</code>, <code class="language-plaintext highlighter-rouge">true</code> and <code class="language-plaintext highlighter-rouge">false</code> if you include <code class="language-plaintext highlighter-rouge">stdbool.h</code>. However, this does not change the fact that
it is all integer logic underneath.</p>

<p>GLib has its own <code class="language-plaintext highlighter-rouge">gboolean</code> type with <code class="language-plaintext highlighter-rouge">TRUE</code> and <code class="language-plaintext highlighter-rouge">FALSE</code> values, so we will use that here.</p>

<h3 id="other-basic-types">Other Basic Types</h3>

<p>GLib has a bunch of other types, such as types with explicit sizes (<code class="language-plaintext highlighter-rouge">gint32</code>, <code class="language-plaintext highlighter-rouge">gint64</code>), easier
alternatives to some types (<code class="language-plaintext highlighter-rouge">guint</code> instead of <code class="language-plaintext highlighter-rouge">unsigned integer</code>, <code class="language-plaintext highlighter-rouge">gconstpointer</code> instead of
<code class="language-plaintext highlighter-rouge">const void *</code>), and a few cosmetic types that just exist for completeness (<code class="language-plaintext highlighter-rouge">gint</code>, <code class="language-plaintext highlighter-rouge">gfloat</code>).</p>

<h3 id="safer-strings">Safer Strings</h3>

<p>Nul-terminated strings in C are notoriously difficult to use safely, because a string’s length is
not stored in the object. It is simply an array of bytes that ends in an all-zeroes byte (a nul).
All string-related functions that need to know the length will search the array until they find such
a nul byte. Often, this byte is accidentally replaced with another character and the string
functions will start operating on memory that is unrelated to the string! This usually leads to code
that is vulnerable to attack.</p>

<p>GLib includes a <code class="language-plaintext highlighter-rouge">GString</code> type that makes it easier to use strings correctly. It does this by
storing the length alongside the character array. You can use it like this:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">print_hello_world</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">GString</span> <span class="o">*</span><span class="n">message</span> <span class="o">=</span> <span class="n">g_string_new</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span>
    <span class="n">g_string_append</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="s">", world!"</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">message</span><span class="o">-&gt;</span><span class="n">str</span><span class="p">);</span>
    <span class="n">g_string_free</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This makes it much easier to do string operations like inserting and removing parts without making
memory management mistakes. Note that you must still remember to free the memory you allocated with
<code class="language-plaintext highlighter-rouge">g_string_new()</code>.</p>

<h3 id="automatic-memory-management">Automatic Memory Management</h3>

<p>Way back in the 1990s, Java was developed as an alternative to C that was not only easier to use,
but also safer. One of these safety features is automatic memory management. Instead of manually
marking an area of memory as being used by an object in our program, the runtime would decide by
itself whether an object and its memory was still reachable in the current scope of the program.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">printBookTitles</span><span class="o">()</span> <span class="o">{</span>
    <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Book</span><span class="o">&gt;</span> <span class="n">books</span> <span class="o">=</span> <span class="n">get_books</span><span class="o">();</span>

    <span class="k">for</span><span class="o">(</span><span class="nc">Book</span> <span class="n">book</span> <span class="o">:</span> <span class="n">books</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">book</span><span class="o">.</span><span class="na">title</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>In the above snippet, a list of Book objects is retrieved from the <code class="language-plaintext highlighter-rouge">get_books()</code> function. Each
book title is printed and then the function ends. As programmers we don’t need to know whether
references to <code class="language-plaintext highlighter-rouge">books</code> still exist after the function ends: if this was the last reference, the Java
runtime will automatically free its memory, but if other references exist, the runtime will keep
the memory marked as being in use. This makes two undesirable things impossible: using the memory
after it has been freed, and keeping the memory marked as used even though it is unreachable by
the program.</p>

<p>GLib has a type <code class="language-plaintext highlighter-rouge">g_autoptr</code>, which does something similar: the system keeps track of all references
to the object, and if the last reference is removed, the memory is freed. An example:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">print_hello_world</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">g_autoptr</span><span class="p">(</span><span class="n">GString</span><span class="p">)</span> <span class="n">message</span> <span class="o">=</span> <span class="n">g_string_new</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span>
    <span class="n">g_string_append</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="s">", world!"</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">message</span><span class="o">-&gt;</span><span class="n">str</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Note that the memory behind <code class="language-plaintext highlighter-rouge">message</code> is never explicitly freed. GLib will automatically free the
memory at the end of the function, because <code class="language-plaintext highlighter-rouge">message</code> is the only reference and it is removed at the
end of the function.</p>

<h3 id="the-result">The Result</h3>

<p>Combining all of the above, we get the following program:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;glib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="n">gint</span> <span class="nf">calculate_fuel</span><span class="p">(</span><span class="n">gint</span> <span class="n">weight</span><span class="p">,</span> <span class="n">gboolean</span> <span class="n">include_fuel</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">gint</span> <span class="n">additional_weight</span> <span class="o">=</span> <span class="n">MAX</span><span class="p">(</span><span class="n">weight</span> <span class="o">/</span> <span class="mi">3</span> <span class="o">-</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

    <span class="k">if</span><span class="p">(</span><span class="n">include_fuel</span> <span class="o">&amp;&amp;</span> <span class="n">additional_weight</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">additional_weight</span> <span class="o">+=</span> <span class="n">calculate_fuel</span><span class="p">(</span><span class="n">additional_weight</span><span class="p">,</span> <span class="n">include_fuel</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">additional_weight</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">gint</span> <span class="nf">main</span><span class="p">(</span><span class="n">gint</span> <span class="n">argc</span><span class="p">,</span> <span class="n">gchar</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
    <span class="n">gboolean</span> <span class="n">include_fuel</span> <span class="o">=</span> <span class="n">FALSE</span><span class="p">;</span>

    <span class="n">g_autoptr</span><span class="p">(</span><span class="n">GString</span><span class="p">)</span> <span class="n">include_fuel_argument</span> <span class="o">=</span> <span class="n">g_string_new</span><span class="p">(</span><span class="s">"--include-fuel"</span><span class="p">);</span>

    <span class="k">for</span><span class="p">(</span><span class="n">gint</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">argc</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">g_autoptr</span><span class="p">(</span><span class="n">GString</span><span class="p">)</span> <span class="n">argument</span> <span class="o">=</span> <span class="n">g_string_new</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>

        <span class="k">if</span><span class="p">(</span><span class="n">g_string_equal</span><span class="p">(</span><span class="n">argument</span><span class="p">,</span> <span class="n">include_fuel_argument</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">include_fuel</span> <span class="o">=</span> <span class="n">TRUE</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">gint</span> <span class="n">total_fuel_required</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">for</span><span class="p">(;;)</span> <span class="p">{</span>
        <span class="n">gint</span> <span class="n">mass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">gint</span> <span class="n">tokens_scanned</span> <span class="o">=</span> <span class="n">scanf</span><span class="p">(</span><span class="s">"%d"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mass</span><span class="p">);</span>

        <span class="k">if</span><span class="p">(</span><span class="n">tokens_scanned</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">gint</span> <span class="n">fuel_required</span> <span class="o">=</span> <span class="n">calculate_fuel</span><span class="p">(</span><span class="n">mass</span><span class="p">,</span> <span class="n">include_fuel</span><span class="p">);</span>
        <span class="n">total_fuel_required</span> <span class="o">+=</span> <span class="n">fuel_required</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"%d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">total_fuel_required</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You might have noticed that I still use <code class="language-plaintext highlighter-rouge">scanf()</code> and <code class="language-plaintext highlighter-rouge">printf()</code>, even though GLib provides
alternatives such as <code class="language-plaintext highlighter-rouge">g_printf()</code> and <code class="language-plaintext highlighter-rouge">GUnixInputStream</code>. It should be possible to use these
constructs, but it requires a different compiler configuration because we then need to use a
Unix-specific version of GLib.</p>

<p>With such a simple example, the conversion to a GLib-based program is maybe not so useful. However,
you will quickly see the benefit when you start working with more complicated data types and their
memory management.</p>

<p>You can find the code for my solution
<a href="https://github.com/remcokranenburg/advent-of-glib-2019/tree/main/day01-tyranny">here</a>. You might
see an interesting file called <code class="language-plaintext highlighter-rouge">meson.build</code> there, which is an increasingly popular way of
building your programs. But that is a topic for another post! Let’s see what the next puzzle brings.</p>]]></content><author><name>Remco Kranenburg</name></author><category term="GSoC2021" /><summary type="html"><![CDATA[For GSoC 2021, I will be working on xrdesktop, which is written with the GLib library. Since learning by explaining is even better than learning by doing, I’ll show the basics of using GLib in this blog post. We’ll start with a plain C program and gradually replace parts of it with GLib functionality.]]></summary></entry><entry><title type="html">Hello, world!</title><link href="https://remcokranenburg.com/2021/05/21/hello-world.html" rel="alternate" type="text/html" title="Hello, world!" /><published>2021-05-21T00:00:00+00:00</published><updated>2021-05-21T00:00:00+00:00</updated><id>https://remcokranenburg.com/2021/05/21/hello-world</id><content type="html" xml:base="https://remcokranenburg.com/2021/05/21/hello-world.html"><![CDATA[<p>Welcome to my blog. Testing some stuff! Might delete later.</p>]]></content><author><name>Remco Kranenburg</name></author><summary type="html"><![CDATA[Welcome to my blog. Testing some stuff! Might delete later.]]></summary></entry></feed>