kenoi https://kenoi.dev/ Recent content on kenoi Hugo -- gohugo.io en Mon, 15 Sep 2025 00:00:00 +0000 Karton Virtual Machine Manager Blog #4: Hardware Accelerating the SPICE viewer (with OpenGL) https://kenoi.dev/blogs/2025-09-15/ Mon, 15 Sep 2025 00:00:00 +0000 https://kenoi.dev/blogs/2025-09-15/ <p>I was at <a href="https://akademy.kde.org/2025/">Akademy 2025</a> last-last week where I did some preliminary research on optimizing the VM viewer&rsquo;s display rendering on Karton. After some more work this past week, it&rsquo;s somewhat here! I&rsquo;m still finishing up <a href="https://invent.kde.org/sitter/karton/-/merge_requests/33">the merge request</a>, but exciting news to come!</p> <p>This has been something I&rsquo;ve been planning on for quite a while now and will significantly improve the experience using Karton :)</p> <div style="text-align: center;"> <video src="https://kenoi.dev/blogs/2025-09-15/compare_n.mp4" style="max-width: 100%; height: auto; border-radius: 8px;" controls> Your browser does not support the video tag. </video> </div> <p><em>a comparison with an old video I had.</em></p> <h2 id="old-rendering-pipeline">Old Rendering Pipeline</h2> <p>My original approach for rendering listened to <code>display-primary-create</code> and <code>invalidate-display-primary</code> <a href="https://www.spice-space.org/api/spice-gtk/SpiceDisplayChannel.html#SpiceDisplayChannel-display-invalidate">SPICE signals</a>. Everytime it received a callback, it would create a new QImage and render that to the QQuickItem (the viewer window). As you can imagine, this was very inefficient as it is basically generating new images for every single frame being rendered. It suffered a lot from screen-tearing any time there were sudden changes to the screen.</p> <p>You can read more about my experiences in <a href="https://kenoi.dev/blogs/2025-07-04/">my SPICE client blog</a>.</p> <h2 id="we-can-do-better">We can do better!</h2> <p>Rendering via OpenGL can offload a lot of these tasks to the GPU and can significantly improve performance. I had known about GL properties in <a href="https://www.spice-space.org/">SPICE</a> for a while now, but I kept putting it off since I really didn&rsquo;t want to deal with any more graphics stuff after my last attempt.</p> <p>Fast forward to last-last week, I was attending my first ever KDE Akademy in Berlin and all of a sudden gained some motivation.</p> <img src="https://kenoi.dev/blogs/2025-09-15/akademy.png" style="display: inline-block;" /> <p><em>It was really exciting hearing talks about all the <em>kool</em> things happening in KDE.</em></p> <h3 id="gl-draw">gl-draw</h3> <p>My first order of business was getting the <code>gl-draw</code> signal to properly receive gl-scanouts from my SPICE connection. After setting up the callback, I found out that I had to reconfigure my VMs to properly support it.</p> <p>This was easy enough as I&rsquo;ve made the Karton VM <a href="https://invent.kde.org/sitter/karton/-/merge_requests/8">installation classes</a> a few months ago done through the <a href="https://libvirt.org/formatdomain.html">libvirt domain XML format</a>. VMs need enabling of GL and 3D acceleration through the graphics element in the XML. The socket connection to SPICE also had to be switched from TCP to UNIX, which was set to <code>/tmp/spice-vm{uuid}.sock</code>. As a result, previous VMs configured in Karton will no longer work as the previous rendering pipeline has been removed.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;graphics</span> <span class="na">type=</span><span class="s">&#34;spice&#34;</span> <span class="na">socket=</span><span class="s">&#34;/tmp/spice-vm{uuid}.sock&#34;</span><span class="nt">&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;listen</span> <span class="na">type=</span><span class="s">&#34;socket&#34;</span> <span class="na">socket=</span><span class="s">&#34;/tmp/spice-vm{uuid}.sock&#34;</span><span class="nt">/&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;gl</span> <span class="na">enable=</span><span class="s">&#34;yes&#34;</span><span class="nt">/&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/graphics&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;video&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;model</span> <span class="na">type=</span><span class="s">&#34;virtio&#34;</span> <span class="na">heads=</span><span class="s">&#34;1&#34;</span> <span class="na">primary=</span><span class="s">&#34;yes&#34;</span><span class="nt">&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;acceleration</span> <span class="na">accel3d=</span><span class="s">&#34;yes&#34;</span><span class="nt">/&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;/model&gt;</span> </span></span><span class="line"><span class="cl"> <span class="nt">&lt;address</span> <span class="na">type=</span><span class="s">&#34;pci&#34;</span> <span class="na">domain=</span><span class="s">&#34;0x0000&#34;</span> <span class="na">bus=</span><span class="s">&#34;0x00&#34;</span> <span class="na">slot=</span><span class="s">&#34;0x01&#34;</span> <span class="na">function=</span><span class="s">&#34;0x0&#34;</span><span class="nt">/&gt;</span> </span></span><span class="line"><span class="cl"><span class="nt">&lt;/video&gt;</span> </span></span></code></pre></div><p><em>An example libvirt domain XML snippet generated by Karton</em></p> <p>Once properly configured, I was able to get <code>SpiceGlScanout</code> objects from my callback linked to the <code>gl-draw</code> signal. Now, I needed to render these scanouts onto my QQuickItem canvas.</p> <h3 id="egl-stuff">EGL stuff</h3> <p>Having no background in graphics, I pretty much had no idea what I was doing by this point.</p> <p>The SpiceGlScanout is a struct that looks like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">SpiceGlScanout</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="n">gint</span> <span class="n">fd</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">guint32</span> <span class="n">width</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">guint32</span> <span class="n">height</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">guint32</span> <span class="n">stride</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">guint32</span> <span class="n">format</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="n">gboolean</span> <span class="n">y0top</span><span class="p">;</span> </span></span><span class="line"><span class="cl"><span class="p">};</span> </span></span></code></pre></div><p>The width, height, stride, etc&hellip;, are all parameters that can be used to set your final rendered frame, but the important field is the fd (file descriptor) which is a <a href="https://www.spice-space.org/api/spice-gtk/SpiceDisplayChannel.html#SpiceDisplayChannel--gl-scanout">&ldquo;a drm DMABUF file that can be imported with eglCreateImageKHR&rdquo;</a>. I didn&rsquo;t know what that was; but at least I learned I should be using the <a href="https://registry.khronos.org/EGL/sdk/docs/man/">EGL library</a> to do the processing.</p> <p>I had found some forum articles (<a href="https://forum.qt.io/topic/115805/render-eglimagekhr-into-qopenglframebufferobject/2">Qt forum</a>, <a href="https://developer.arm.com/documentation/ka004859/latest/">Arm developer forum</a>) related to rendering OpenGL textures which used the EGL library and were quite helpful. I also looked at the <a href="https://www.spice-space.org/spice-gtk.html">SPICE GTK widget</a> source code which gave me some ideas on the GL parameters to work with.</p> <p>From these references, I saw that they pretty much followed the same pattern. Very simply put:</p> <pre tabindex="0"><code>-&gt; create egl image from a bunch of attributes/settings -&gt; generate texture from the fd -&gt; bind texture to a texture type -&gt; &#34;glEGLImageTargetTexture2DOES&#34; use this function?? still don&#39;t know what this does lol -&gt; destroy egl image </code></pre><p>I originally tried setting the GL context properties manually, but there were some issues with getting it to detect my display and apparently thread syncronization. Then, I found out that Qt had a <a href="https://doc.qt.io/qt-6/qopenglfunctions.html">QOpenGLFunctions library</a> which had all of the EGL functions and context properties wrapped and made my life a whole bunch easier.</p> <h3 id="opengl-texture---qt">OpenGL texture -&gt; Qt</h3> <p>After a ton of trial and error, it looked like my EGL images were properly being created. Now I needed to render these GL textures to the QQuickItem.</p> <p>How you do so is, within the inherited <code>updatePaintNode()</code> function, you return a <code>QSGNode</code> which has the information for updating that frame. Looking through the Qt documentation, <code>QNativeTexture</code> is a struct that allows you to store a texture ID to an OpenGL image. With that, you can create a wrapper <code>QRhi</code> class from the QNativeTexture with some of the generic context of your display.</p> <p>Finally, you can use the <code>createTextureFromRhiTexture()</code> function under QQuickWindow which allows you to create a QSGTexture from that RHI for a QSGNode that can be returned by <code>updatePaintNode()</code>. And, we&rsquo;re done! Yay!</p> <p>To sum it up, here&rsquo;s the framebuffer pipeline:</p> <p><code>gl-draw signal-&gt;receive gl-scanout-&gt;import GL texture-&gt;GL texture ID-&gt;QNativeTexture-&gt;QRhi-&gt;QSGTexture-&gt;QSGNode-&gt;QQuickItem</code></p> <div style="text-align: center;"> <video src="https://kenoi.dev/blogs/2025-09-15/rendered.mp4" style="max-width: 100%; height: auto; border-radius: 8px;" controls> Your browser does not support the video tag. </video> </div> <p><em>so much smoother! yes, I was very excited.</em></p> <h3 id="socials">Socials</h3> <p>Website: <a href="https://kenoi.dev/">https://kenoi.dev/</a></p> <p>Mastodon: <a href="https://mastodon.social/@kenoi">https://mastodon.social/@kenoi</a></p> <p>GitLab: <a href="https://invent.kde.org/kenoi">https://invent.kde.org/kenoi</a></p> <p>GitHub: <a href="https://github.com/kenoi1">https://github.com/kenoi1</a></p> <p>Matrix: @kenoi:matrix.org</p> <p>Discord: kenyoy</p> GSoC 2025 Final Project Blog: Developing Karton, the KDE Virtual Machine Manager! https://kenoi.dev/blogs/2025-08-28/ Thu, 28 Aug 2025 00:00:00 +0000 https://kenoi.dev/blogs/2025-08-28/ <p>Hello again everyone!</p> <p>I&rsquo;m Derek Lin also known as <a href="https://invent.kde.org/kenoi">kenoi</a>, a second-year Math student at the University of Waterloo.</p> <img style="display: block; margin: 0 auto;" src="https://kenoi.dev/blogs/2025-08-28/konqi.png" width="800" /> <p>Through <a href="https://summerofcode.withgoogle.com/programs/2025/projects/phUCjPUf">Google Summer of Code 2025 (GSoC)</a>, mentored by <a href="https://invent.kde.org/hsitter">Harald Sitter</a>, <a href="https://invent.kde.org/tfella">Tobias Fella</a>, and <a href="https://invent.kde.org/nicolasfella">Nicolas Fella</a>, I have been developing <a href="https://invent.kde.org/sitter/karton">Karton</a>, a virtual machine manager for KDE.</p> <p>As the program wraps up, I thought it would be a good idea to put together what I&rsquo;ve been able to accomplish as well as my plans going forward.</p> <img src="https://kenoi.dev/blogs/2025-08-28/karton.png" width="1000" style="display: block; margin: 0 auto;" /> <p><em>A final look at <a href="https://invent.kde.org/sitter/karton/">Karton</a> after the GSoC period.</em></p> <h2 id="research-and-initial-work">Research and Initial Work</h2> <p>The main motivation behind Karton is to provide KDE users with a more Qt-native alternative to GTK-based virtual machine managers, as well as an easy-to-use experience.</p> <img style="display: block; margin: 0 auto;" src="https://kenoi.dev/blogs/2025-08-28/karqi.png" width="700" /> <p>I had first expressed interest in working on Karton in early Feburary where I made the initial full rewrite (<a href="https://invent.kde.org/sitter/karton/-/merge_requests/4">see MR #4</a>), using <a href="https://libvirt.org/">libvirt</a> and a new UI, wrapping <a href="https://linux.die.net/man/1/virt-install">virt-install</a> and <a href="https://linux.die.net/man/1/virt-viewer">virt-viewer</a> CLIs. During this time, I had been doing research, writing a <a href="https://docs.google.com/document/d/13cVp2gISwdFwQyPr8tRzERKnerJkGQpMFffLZmmd9bQ/edit?usp=sharing">proposal</a>, and trying out different virtual machine managers like <a href="https://apps.gnome.org/Boxes/">GNOME Boxes</a>, <a href="https://virt-manager.org/">virtmanager</a>, and <a href="https://mac.getutm.app/">UTM</a>.</p> <p>You can read more about it in my <a href="https://blogs.kde.org/2025/05/18/gsoc-2025-project-intro-developing-karton-the-kde-virtual-machine-manager/">project introduction blog</a>!</p> <img src="https://kenoi.dev/blogs/2025-08-28/list.png" width="800" style="display: block; margin: 0 auto;" /> <p><em>A screenshot of my <a href="https://invent.kde.org/sitter/karton/-/merge_requests/4">rewrite</a> in March 8, 2025.</em></p> <h3 id="vm-installation">VM Installation</h3> <p>One of my goals for the project was to develop a custom <a href="https://libvirt.org/formatdomain.html">libvirt domain XML</a> generator using Qt libraries and the <a href="https://libosinfo.org/">libosinfo</a> GLib API. I started <a href="https://invent.kde.org/sitter/karton/-/merge_requests/8">working on the feature</a> in advance in April and was able to have it ready for review before the official GSoC coding period.</p> <p>I created a dialogue menu to accept a VM name, installation media, storage, allocated RAM, and CPUs. <code>libosinfo</code> will attempt to identify the ISO file and return a OS short-ID (ex: <code>fedora40</code>, <code>ubuntu24.04</code>, etc), otherwise users will need to select one from the displayed list.</p> <p>Through the OS ID, <code>libosinfo</code> can provide certain specifications needed in the libvirt domain XML. Karton then fills in the rest, generating a UUID, a MAC address to configure a <a href="https://libvirt.org/formatdomain.html#network-interfaces">virtual network</a>, and sets up display, audio, and storage devices. The XML file is assembled through QDomDocument and passed into a libvirt call that verifies it before adding the VM.</p> <p>VM information (id, name, state, paths, etc) in Karton is parsed explicitly from the saved libvirt XML file found in the libvirt QEMU folder, <code>~/.config/libvirt/qemu/{domain_name}.xml</code>.</p> <p>All in all, this addition (<a href="https://invent.kde.org/sitter/karton/-/merge_requests/8">see MR #8</a>) completely removed the <code>virt-install</code> dependency although barebones.</p> <img src="https://kenoi.dev/blogs/2025-08-28/installationdialog.png" width="800" style="display: block; margin: 0 auto;" /> <p><em>A screenshot of the VM installation dialog.</em></p> <p>The easy VM installation process of GNOME Boxes had been an inspiration for me and I&rsquo;d like to improve it in the future by adding a media installer and better error handling later on.</p> <h2 id="official-coding-begins">Official Coding Begins!</h2> <p>A few weeks into the official coding period, I had been addressing feedback and polishing <a href="https://invent.kde.org/sitter/karton/-/merge_requests/8">my VM installer merge request</a>. This introduced much cleaner class interface separation in regards to storing individual VM data.</p> <h3 id="spice-client-and-viewer">SPICE Client and Viewer</h3> <p>My use of <code>virt-viewer</code> previously for interacting with virtual machines was meant as a temporary addition, as it is a separate application and is poorly integrated into Qt/Kirigami and lacks needed customizability.</p> <img src="https://kenoi.dev/blogs/2025-08-28/virtviewer.png" width="800" style="display: inline-block;" /> <p><em>Previously, clicking the <code>view</code> button would open a <code>virtviewer</code> window.</em></p> <p>As such, the bulk of my time was spent working with <a href="https://www.spice-space.org/index.html">SPICE</a> directly, using the <code>spice-client-glib</code> library, in order to create a custom Qt SPICE client and viewer (<a href="https://invent.kde.org/sitter/karton/-/merge_requests/15">see MR #15</a>). This needed to manage the state of connection to VM displays and render them to KDE (Kirigami) windows. Other features such as input forwarding, audio receiving also needed to be implemented.</p> <p>I had configured all Karton-created VMs to be set to <a href="https://libvirt.org/formatdomain.html#id75">autoport for graphics</a> which dynamically assigns a port at runtime. Consequently, I needed to use a CLI tool, <code>virsh domdisplay</code>, to fetch the SPICE URI to establish the initial connection.</p> <p>The viewer display works through a frame buffer. The approach I took was rendering the pixel array I received to a QImage which could be drawn onto a QQuickItem to be displayed on the window. To know when to update, it listens to the SPICE primary display callback.</p> <p>You can read more about it in my <a href="https://blogs.kde.org/2025/07/10/karton-gsoc-2025-blog-%282-qt-spice-client/">Qt SPICE client blog</a>. As noted, this approach is quite inefficient as it needs to create a new QImage for every frame. I plan on improving this in the future.</p> <div style="text-align: center;"> <img src="https://kenoi.dev/blogs/2025-07-04/noice.png" width="350" style="display: inline-block;" /> <img src="https://kenoi.dev/blogs/2025-07-04/nnice.png" width="300" style="display: inline-block;" /> </div> <p><em>Screenshots of my struggles getting the display to work properly.</em></p> <p>I had to manage receiving and forwarding Qt input. Sending QMouseEvents, mouse button clicks, were straightforward and can be mapped directly to <a href="https://www.spice-space.org/spice-protocol.html">SPICE protocol</a> mouse messages when activated. Keystrokes are taken in as QKeyEvents and the received scancodes, in <code>evdev</code>, are converted to <code>PC XT</code> for SPICE through a <a href="https://gitlab.com/qemu-project/keycodemapdb">map generated by QEMU</a>. Implementing scroll and drag followed similarly.</p> <p>I also needed manage receiving audio streams from the SPICE playback callback, writing to a QAudioSink. One thing I found nice is how my approach supported multiple SPICE connections quite nicely. For example, opening multiple VMs will create separate audio sources for each so users can modify volume levels accordingly.</p> <p>Later on, I added display frame resizing when the user resizes the Karton window as well as a fullscreen button. I noticed that doing so still causes resolution to appear quite bad, so proper resizing done through the guest machine will have to be implemented in the future.</p> <div style="text-align: center;"> <video src="https://kenoi.dev/blogs/2025-08-28/view.mp4" style="max-width: 100%; height: auto; border-radius: 8px;" controls> Your browser does not support the video tag. </video> </div> <p><em>Now, we can watch <a href="https://www.peppercarrot.com/">Pepper and Carrot</a> somewhat! (no hardware accelleration yet)</em></p> <h3 id="ui">UI</h3> <p>My final major MR was to rework my UI to make better use of screen space <a href="https://invent.kde.org/sitter/karton/-/merge_requests/25">(see MR #25)</a>. I moved the existing VM ListView into a sidebar displaying only name, state, and OS ID. The right side would then have the detailed information of the selected VM. One my inspirations was MacOS UTM&rsquo;s screenshot of the last active frame.</p> <p>When a user closes the Karton viewer window, the last frame is saved to <code>$HOME/.local/state/KDE/Karton/previews</code>. Implementing cool features like these are much easier now that we have our own viewer! I also added some effects for opacity and hover animation to make it look nice.</p> <img src="https://kenoi.dev/blogs/2025-08-28/manager.png" width="800" style="display: block; margin: 0 auto;" /> <p>Finally, I worked on media disc ejection <a href="https://invent.kde.org/sitter/karton/-/merge_requests/26">(see MR #26)</a>. This uses a <a href="https://libvirt.org/html/libvirt-libvirt-domain.html">libvirt call</a> to simulate the installation media being removed from the VM, so users can boot into their virtual hard drive after installing.</p> <h2 id="demo-usage">Demo Usage</h2> <p>As a final test of the project, I decided to create, configure and use a <a href="https://fedoraproject.org/kde/download">Fedora KDE</a> VM using Karton. After setting specifications, I installed it to the virtual disk, ejected the installation media, and properly booted into it. Then, I tried playing some games. Overall, it worked pretty well!</p> <div style="text-align: center;"> <video src="https://kenoi.dev/blogs/2025-08-28/install.mp4" style="max-width: 100%; height: auto; border-radius: 8px;" controls> Your browser does not support the video tag. </video> </div> <h2 id="list-of-mrs">List of MRs</h2> <h4 id="major-additions">Major Additions:</h4> <ul> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/4">#4 Complete rewrite with libvirt backend, new UI</a></li> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/6">#6 Implement disk path and proper deletion button behavior</a></li> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/8">#8 VM creation through libvirt domain xml format</a></li> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/15">#15 Custom Qt SPICE client and viewer</a></li> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/25">#25 Revamp UI with sidebar and VM preview screencap</a></li> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/26">#26 Implement eject ISO disk button</a></li> </ul> <h4 id="subtle-additions">Subtle Additions:</h4> <ul> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/10">#10 Update stop VM button icon</a></li> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/14">#14 Store XML path of ~/.config/libvirt/qemu </a></li> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/17">#17 Fix fullscreen button anchor margin</a></li> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/22">#22 Extract installation dialog into a new class</a></li> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/28">#28 Error notification/prevention for empty installation fields</a></li> <li><a href="https://invent.kde.org/sitter/karton/-/merge_requests/24">#24 List OS variants through searchable combo box</a></li> </ul> <h2 id="difficulties">Difficulties</h2> <p>My biggest regret was having a study term over this period. I had to really manage my time well, balancing studying, searching for job positions, and contributing. There was a week where I had 2 midterms, 2 interviews, and a final project, and I found myself pulling some late nighters writing code at the school library. Though it&rsquo;s been an exhausting school term, I am still super glad to have been able to contribute to a really cool project and get something work!</p> <p>I was also new to both C++ and Qt development. Funny enough, I had been taking, and struggling on, my first course in C++ while working on Karton. I also spent a lot of time reading documentation to familiarize myself with a lot of the different APIs (libspice, libvirt, and libosinfo).</p> <div style="text-align: center;"> <img src="https://kenoi.dev/blogs/2025-08-28/ram.png" width="350" style="display: inline-block;" /> <img src="https://kenoi.dev/blogs/2025-08-28/storage.png" width="350" style="display: inline-block;" /> </div> <p><em>Left: Karton freezes my computer because I had too many running VMs.</em></p> <p><em>Right: 434.1 GiB of virtual disks; my reminder to implement disk management.</em></p> <h2 id="whats-next">What&rsquo;s Next?</h2> <p>There is still so much to do! Currently, I am on vacation and I will be attending Akademy in Berlin in September so I won&rsquo;t be able to work much until then. In the fall, I will be finally off school for a 4 month internship (yay!!). I&rsquo;m hoping I will have more time to contribute again.</p> <p>There&rsquo;s still a lot left especially with regards to the viewer.</p> <p>Here&rsquo;s a bit of an unorganized list:</p> <ul> <li>Optimize VM display frame buffer with SPICE <code>gl-scanout</code></li> <li>Improved scaling and text rendering in viewer</li> <li>File transfer and clipboard passthrough with SPICE</li> <li>Full VM snapshotting through libvirt (full duplication)</li> <li>Browse and installation tool for commonly installed ISOs through QEMU</li> <li>Error handling in installation process</li> <li>Configuration and allow modifying of existing VMs in the application</li> <li>Others on the <a href="https://invent.kde.org/sitter/karton/-/issues">issue tracker</a></li> </ul> <h2 id="release">Release?</h2> <p>In its current state, Karton is not feature complete, and not ready for officially packaging and releasing. In addition to the missing features listed before, there have been a lot of new and moving parts throughout this coding period, and I&rsquo;d like to have the chance to thoroughly test the code to prevent any major issues.</p> <p>However, I do encourage you to try it out (at your own risk!) <a href="https://invent.kde.org/sitter/karton">by cloning the repo</a>. Let me know what you think and when you find any issues!</p> <p>In other news, there are some discussions of packaging Karton as a <a href="https://flatpak.org/">Flatpak</a> eventually and I will be requesting to add it to the <a href="https://apps.kde.org/">KDE namespace</a> in the coming months, so stay tuned!</p> <h2 id="conclusion">Conclusion</h2> <p>Overall, it has been an amazing experience completing GSoC under KDE and I really recommend it for anyone who is looking to contribute to open-source. I&rsquo;m quite satisfied with what I&rsquo;ve been able to accomplish in this short period of time and hoping to continue to working and learning with the community.</p> <p>Working through MRs has given me a lot of valuable and relevant industry experience going forward. A big thank you to my mentor, <a href="https://invent.kde.org/hsitter">Harald Sitter</a>, who has been reviewing and providing feedback along the way!</p> <p>As mentioned earlier, Karton still definitely has a lot to work on and I plan continuing my work after GSoC as well. If you&rsquo;d like to read more about my work on the project in the future, please check out <a href="https://kenoi.dev/">my personal blog</a> and the development matrix, <a href="https://matrix.to/#/#karton:kde.org">karton:kde.org</a>.</p> <p>Thanks for reading!</p> <img src="https://kenoi.dev/blogs/2025-08-28/qtd.png" width="500" /> <h3 id="socials">Socials</h3> <p>Website: <a href="https://kenoi.dev/">https://kenoi.dev/</a></p> <p>Mastodon: <a href="https://mastodon.social/@kenoi">https://mastodon.social/@kenoi</a></p> <p>GitLab: <a href="https://invent.kde.org/kenoi">https://invent.kde.org/kenoi</a></p> <p>GitHub: <a href="https://github.com/kenoi1">https://github.com/kenoi1</a></p> <p>Matrix: @kenoi:matrix.org</p> <p>Discord: kenyoy</p> Karton Virtual Machine Manager GSoC 2025 Blog #2: Qt SPICE Client https://kenoi.dev/blogs/2025-07-04/ Fri, 04 Jul 2025 00:00:00 +0000 https://kenoi.dev/blogs/2025-07-04/ <p>After my <a href="https://blogs.kde.org/2025/05/18/gsoc-2025-project-intro-developing-karton-the-kde-virtual-machine-manager/">initial status blog</a>, I was really surprised to see so much support and excitement about <a href="https://invent.kde.org/sitter/karton">Karton</a>, and I&rsquo;m grateful for it!</p> <p>A few weeks have gone by since the official coding period for Google Summer of Code began. I wanted to share what I&rsquo;ve been working on with the project!</p> <h2 id="vm-installer">VM Installer</h2> <p>Earlier last month, I was finishing up addressing feedback (big thanks again to <a href="https://invent.kde.org/sitter">Harald</a>) on the <a href="https://invent.kde.org/sitter/karton/-/merge_requests/8">VM installer-related MR</a>. I had made some improvements to memory management, bug fixes related to detecting ISO disks, as well as refactoring of the class structures. I also ported it over to using <a href="https://api.kde.org/ecm/module/ECMQmlModule.html">QML modules</a>, which is much more commonly used in KDE apps, instead of exposing objects at runtime.</p> <p>After a bit more review, this has now been merged into the master branch! This was what was featured in the previous demo video and you can find a list of the full changes on the commit.</p> <h2 id="spice-client">SPICE Client</h2> <p>Two weeks ago, I started to get back to work on my <a href="https://invent.kde.org/sitter/karton/-/merge_requests/15">SPICE viewer branch</a>. This is the main component I had planned for this summer.</p> <p>It took a few days to clean up my code that connects to the SPICE display and inputs channel.</p> <p>However, a lot of my time was spent trying to get a properly working frame buffer that grabs the VM display from <a href="https://www.spice-space.org/spice-gtk.html">SPICE (spice-client-glib)</a> and renders it to a native KDE window. The approach I originally took was rendering the pixel array I received to a QImage which could be drawn onto a QQuickItem to be displayed on the window. It listens to SPICE callbacks to know when to update, and was pretty exciting to see it rendering for the first time!</p> <p>One of the most confusing issues I encountered was when I was encountering weird colour and transparency artifacts in my rendering. I initially thought it was a problem due to the QImage 32-bit RGB format I was labelling the data as, so I ended up going through <a href="https://doc.qt.io/qt-6/qimage.html#Format-enum">a bunch of formats</a> on the Qt documentation. The results were very inconsistent and 24-bit formats were somehow looking better, despite SPICE giving me it in 32-bit. Turns out (unrelatedly), there was some race condition with how I was reading the array while SPICE was writing to it, so manually copying the pixels over to a separate array did the trick.</p> <p>Here are nice pictures from my adventures!</p> <div style="text-align: center;"> <img src="https://kenoi.dev/blogs/2025-07-04/noice.png" width="400" style="display: inline-block;" /> <img src="https://kenoi.dev/blogs/2025-07-04/nnice.png" width="500" style="display: inline-block;" /> </div> <p><em>my first time properly seeing the display&hellip; (also in the wrong format)</em> ( •͈ ૦ •͈ )</p> <p>I have also set up forwarding controls which listens to Qt user input (mouse clicks, hover, keyboard presses) and maps coordinates and events to <a href="https://www.spice-space.org/spice-protocol.html">SPICE messages</a> in the inputs channel. Unfortunately, Qt key event scancodes seem to be in evdev format while SPICE expects PC XT. Currently, I have been manually mapping each scancode, but I might see if I can switch to use some library eventually.</p> <p>Once I polish this up, I hoping to merge this into master soon. It&rsquo;ll likely be very slow and barebones, but I&rsquo;m hoping I can make more improvements later on!</p> <div style="text-align: center;"> <video src="https://kenoi.dev/blogs/2025-07-04/viewer.mp4" muted loop style="max-width: 100%; height: auto; border-radius: 8px;" controls> Your browser does not support the video tag. </video> </div> <p><em>still very lagging scrolling, but now we can read <a href="https://www.peppercarrot.com/">Pepper &amp; Carrot</a>!</em></p> <h2 id="whats-next">What&rsquo;s Next?</h2> <p>While relatively simple, I noticed my approach is quite inefficient, as it has to convert every received frame to a QImage, and suffers from tearing when it has to update quickly (ex: scrolling, videos).</p> <p>SPICE has a <a href="https://www.spice-space.org/api/spice-gtk/SpiceDisplayChannel.html#SpiceGlScanout">gl-scanout property</a> which is likely much more optimized for rendering frames and I plan on looking into switching over to that in the long-term.</p> <p>I also need to implement audio forwarding, sending proper mouse drag events, and resizing the viewing window.</p> <p>On a side note, I also helped review a nice <a href="https://invent.kde.org/sitter/karton/-/merge_requests/16">QoL feature from Vishal</a> to list the OS variants in the installation dialog. I&rsquo;ve just been memorizing them up until now&hellip; :')</p> <p>Hopefully, once I get the SPICE viewer to a reasonable state, I can get back to improving the installation experience further like adding a page to download ISOs from.</p> <p>As I mentioned a bit previously, I also want to rework the UI eventually. This means spending time to redevelop the components to include a sidebar, which is inspired by <a href="https://mac.getutm.app/">UTM</a> and <a href="https://nginx-flathub.apps.openshift.gnome.org/lt/apps/com.ranfdev.DistroShelf">DistroShelf</a>.</p> <h2 id="lastly">Lastly,</h2> <p>I also wanted to make a bit of a note on my plans and hopes throughout the GSoC period. After working on developing these different components of the app, I started to realize how much time goes into polishing, so I believe that I need to prioritse some of the most important features and making them work well.</p> <p>Overall, it&rsquo;s been super busy (since I&rsquo;m also balancing school work), but it has been quite exciting!</p> <p>Come join our matrix channel: <a href="https://matrix.to/#/#karton:kde.org">karton:kde.org</a></p> <p>Another thing, I recently made a personal website, <a href="https://kenoi.dev/">kenoi.dev</a>, where I also plan on blogging!</p> <p>That&rsquo;s all, thank you for reading :D</p> hi, I made a website :> https://kenoi.dev/blogs/2025-06-27/ Fri, 27 Jun 2025 00:00:00 +0000 https://kenoi.dev/blogs/2025-06-27/ <p>Hey! I&rsquo;ve been wanting to make a personal website for a while now, and while I&rsquo;m currently contributing to GSoC @ KDE, I thought it&rsquo;d be nice to have somewhere to share my status also.</p> <p>So, here it is! I hope I can find some time to write something once in a while!</p> <p>Most my blogs will likely about the work I&rsquo;m doing with ongoing projects, but I might write occasionally about travel, music, and maybe drawing!</p> <p>Thanks for reading and have a nice day! :-D</p> GSoC 2025 Project Intro: Developing Karton, the KDE Virtual Machine Manager! https://kenoi.dev/blogs/2025-05-18/ Sun, 18 May 2025 00:00:00 +0000 https://kenoi.dev/blogs/2025-05-18/ <p>(This blog is originally posted to KDE Blogs.)</p> <p>Hi everyone!</p> <p>I&rsquo;m Derek Lin, also known as <a href="https://invent.kde.org/kenoi">kenoi</a>. I&rsquo;m a second-year student at the University of Waterloo and really excited to be working on developing <a href="https://invent.kde.org/sitter/karton">Karton</a>, a virtual machine manager, this summer. This project will be a part of the Google Summer of Code (GSoC) 2025 program and mentored by <a href="https://invent.kde.org/hsitter">Harald Sitter</a>, <a href="https://invent.kde.org/tfella">Tobias Fella</a>, and <a href="https://invent.kde.org/nicolasfella">Nicolas Fella</a>. Over the past few months, I&rsquo;ve been contributing to the project through some merge requests and I hope to get it to a somewhat polished state towards the end of the program!</p> <h2 id="about-karton-virtual-machine-manager">About Karton Virtual Machine Manager</h2> <p>Currently, GTK-based virtual machine managers (virt-manager, GNOME Boxes) are the norm for a lot of KDE users, but they are generally not well integrated into the Plasma environment. Although there has been work done in the past with making a <a href="https://github.com/F1ash/qt-virt-manager">Qt-Widget-based virtual machine manager</a>, it has not been maintained for many years and the UI is quite dated.</p> <p>Karton, as originally started by <a href="https://invent.kde.org/arraybolt">Aaron Rainbolt</a> was planned to be a QEMU frontend for virtualization through its CLI. Eventually, the project ownership was handed over to Harald Sitter and it was made available as a GSoC project. <strong>My aim is to make Karton a native Qt-Quick/Kirigami virtual machine manager, using a libvirt backend</strong>. Through <a href="https://libvirt.org/index.html">libvirt</a>, lower-level tasks can be abstracted and it allows for the app to be potentially <a href="https://libvirt.org/drivers.html">cross-platform</a>.</p> <p>If anyone is interested, I wrote a bit more in detail in my <a href="https://docs.google.com/document/d/13cVp2gISwdFwQyPr8tRzERKnerJkGQpMFffLZmmd9bQ/edit?usp=sharing">GSoC project proposal</a> (although a bit outdated).</p> <h2 id="my-work-so-far">My Work so Far</h2> <p>I originally became interested in the project back in February this year where I tested out GNOME Boxes, virt-manager, and UTM. I also experimented on the virsh CLI, configuring some virtual machines through the <a href="https://libvirt.org/formatdomain.html">libvirt domain XML format</a>.</p> <p>My <a href="https://invent.kde.org/sitter/karton/-/merge_requests/4">first merge request</a> was a proof-of-concept rewrite of the app. I implemented new UI components to list, view, configure, and install libvirt-controlled virtual machines. This used the libvirt API to fetch information on user domains, and wrapped virt-install and virtviewer CLIs for installing and viewing domains respectively. I had spent a big portion of this time getting know Qt frameworks, libvirt, and just C++ overall, so a big thank you to Harald Sitter and Gleb Popov, who have been reviewing my code!</p> <p>A few weeks later I also made a <a href="https://invent.kde.org/sitter/karton/-/merge_requests/6">smaller merge request</a> building off of my rewrite, adding QEMU virtual disk path management which is where the main repository stands as of now.</p> <p>In between my school terms in mid-April, I had the amazing opportunity to attend the Plasma Sprint in Graz where I was able to meet many awesome developers who work on KDE. During this time, I worked on a <a href="https://invent.kde.org/sitter/karton/-/merge_requests/8">merge request to implement a domain installer</a> (in order to replace the virt-install command call). This used the <a href="https://libosinfo.org/">libosinfo</a> GLib API to detect a user-provided OS installer disk image, getting specifications needed for the libvirt XML domain format. Karton is then able to generate a custom XML file which will make it easier to work off of and implement more features in the future. I had to rework a lot of the domain configuration class structure and shifted away from fetching information from libvirt API calls to parsing it directly from XML.</p> <p><a href="https://pastebin.com/wZNp9t26">A libvirt domain XML configuration generated by Karton.</a></p> <h4 id="a-video-demo">A Video Demo</h4> <div style="text-align: center;"> <video src="karton.mp4" muted loop autoplay style="max-width: 100%; height: auto; border-radius: 8px;" controls> Your browser does not support the video tag. </video> </div> <h2 id="current-works-in-progress">Current Works in Progress</h2> <p>As virt-install is very powerful program, my installer is still hardcoded to work with QEMU and I haven&rsquo;t been able to implement a lot of the device configuration yet. I also am currently working on addressing feedback on this merge request.</p> <p>Recently, I also started work on a new custom Qt-Quick virtual machine viewer. It connects to virtual machines through the <a href="https://www.spice-space.org/index.html">spice-client-glib</a> library and renders the frames on a QQuickItem with the images it receives from the active virtual machine. This is still very buggy and has yet to support user input.</p> <div style="text-align: left; max-width: 400px;"> <div style="text-align: center;"> <video src="view.mp4" muted loop autoplay style="max-width: 100%; height: auto; border-radius: 8px;" controls> Your browser does not support the video tag. </video> </div> </div> <p><em>a very cursed viewer&hellip;</em></p> <p><strong>Warning: Karton is still under development. I would not recommend running Karton with any VMs that are important as they may break.</strong></p> <h2 id="my-plans-for-google-summer-of-code-25">My Plans for Google Summer of Code &lsquo;25</h2> <p>Once the domain installer is finished up, I think the majority of my time will be spent on working on and polishing the virtual machine viewer.</p> <p>Some of the other things I would want to get to during the summer are:</p> <ul> <li>Support snapshotting, so users can save the state of their virtual machines.</li> <li>Rework the UI so that it uses space more effectively, possible more similar to UTMs layout.</li> <li>System monitor to graph CPU and RAM usage of VMs, similar to virt-manager.</li> <li>Other configuration options for the installer to support device passthrough and such.</li> <li><a href="https://docs.google.com/document/d/13cVp2gISwdFwQyPr8tRzERKnerJkGQpMFffLZmmd9bQ/edit?usp=sharing">A bunch of more stuff mentioned in the project proposal</a>!</li> </ul> <p>If you have any features you&rsquo;d like to see in the future, let us know in our Matrix, <a href="https://matrix.to/#/#karton:kde.org">karton:kde.org</a>!</p> <h2 id="thats-all">That&rsquo;s all!</h2> <p>Thanks for reading! I&rsquo;m still new to KDE development and virtualization in general, so if you have any suggestions or thoughts on the project, please let me know!</p> <p>Email: <a href="mailto:[email protected]">[email protected]</a></p> <p>Matrix: <a href="https://matrix.to/#/@kenoi:matrix.org">@kenoi:matrix.org</a></p> <p>Discord: kenyoy</p> <p>I also made a Mastodon recently: <a href="https://mastodon.social/@kenoi">mastodon.social/@kenoi</a></p> sketches https://kenoi.dev/gallery/2025-01-01/ Wed, 01 Jan 2025 00:00:00 +0000 https://kenoi.dev/gallery/2025-01-01/ <p>2024-05-24</p> <p><img alt="2023-09-12" loading="lazy" src="https://kenoi.dev//draw/09-12-2023.jpeg"></p> <p>2023-09-12</p> <p><img alt="2022-09-22" loading="lazy" src="https://kenoi.dev//draw/09-17-22.jpg"></p> <p>2022-09-22</p> 2024-07-29 https://kenoi.dev/gallery/2024-07-29/ Mon, 29 Jul 2024 00:00:00 +0000 https://kenoi.dev/gallery/2024-07-29/ 2022-07-20 https://kenoi.dev/gallery/2022-07-20/ Wed, 20 Jul 2022 00:00:00 +0000 https://kenoi.dev/gallery/2022-07-20/ 2021-10-14 https://kenoi.dev/gallery/2021-10-14/ Thu, 14 Oct 2021 00:00:00 +0000 https://kenoi.dev/gallery/2021-10-14/ <p><img alt="2021-10-14" loading="lazy" src="https://kenoi.dev//draw/fallingleaf-wip.png"></p> Digital Artwork https://kenoi.dev/gallery/2000-01-01/ Mon, 01 Jan 1100 00:00:00 +0000 https://kenoi.dev/gallery/2000-01-01/ <p>This is my latest digital artwork. I spent about 5 hours working on this piece. Experimenting with digital painting techniques in Photoshop. This portrait study focuses on light and shadow, using custom brushes and layer blending modes to achieve a painterly effect. Created over multiple sessions, refining details and color harmony throughout the process.</p>