<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet href="/feed_style.xsl" type="text/xsl"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="https://www.rssboard.org/media-rss">
  <channel>
    <title>GabMus&#39;s Dev Log</title>
    <link>https://gabmus.org/</link>
    <description>Recent content on GabMus&#39;s Dev Log</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <copyright>Gabriele Musco - [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/).</copyright>
    <lastBuildDate>Wed, 11 Mar 2026 08:52:20 +0100</lastBuildDate><atom:link href="https://gabmus.org/index.xml" rel="self" type="application/rss+xml" /><icon>https://gabmus.org/logo.svg</icon>
    
    
    <item>
      <title>The Only Two Linux Distros You Will Ever Need</title>
      <link>https://gabmus.org/posts/the_only_two_linux_distros_you_will_ever_need/</link>
      <pubDate>Wed, 11 Mar 2026 08:52:20 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/the_only_two_linux_distros_you_will_ever_need/</guid>
      <description><![CDATA[<p>With the release of the 2026 edition of the <a href="https://www.youtube.com/watch?v=kluoZ9RhmVo">LTT Linux Challenge</a>, comes the usual fuss about which distro they should or shouldn&rsquo;t have chosen, I decided to throw my hat into the ring and give you my final and definitive recommendation on <strong>what Linux distro you should use in 2026</strong>.</p>
<p>Let me tell you a little about myself first, in case you&rsquo;re new here: I&rsquo;m a software engineer and I&rsquo;ve been using Linux as my primary operating system since 2009. That&rsquo;s 17 years ago at the time of writing this, back when <a href="https://en.wikipedia.org/wiki/Steam_(service)#Linux">Steam on Linux</a> didn&rsquo;t exist, not to mention Proton. The opinions expressed in this article are the result of my experience, of trial and error and hands-on experience on what works and what doesn&rsquo;t.</p>
<hr>
<h2 id="my-recommendation">My recommendation</h2>
<p>Alright, onto the fun part. It really boils down to these <strong>two</strong>:</p>
<ol>
<li><a href="https://universal-blue.org/">Universal Blue</a> based distros (<a href="https://getaurora.dev/">Aurora</a>, <a href="https://bazzite.gg/">Bazzite</a> and <a href="https://projectbluefin.io/">Bluefin</a>)</li>
<li><a href="https://archlinux.org/">Arch Linux</a> and its direct derivatives (more on this later)</li>
</ol>
<p>That&rsquo;s it. These are the only two options that make sense <em>for most people</em>.</p>
<h3 id="but-what-about">&ldquo;But what about&hellip;&rdquo;</h3>
<p>There is one caveat: if you&rsquo;re using a more advanced distro like <a href="https://nixos.org/">NixOS</a> or <a href="https://www.gentoo.org/">Gentoo</a>, or a special-purpose distro such as one for pen-testing or one for supporting a specific set of hardware, then you already know what you need from your system, and your choice is likely dictated by experience and deliberate reasoning, and this article is <strong>not for you</strong>. If you&rsquo;re happy with your choice, you don&rsquo;t need to change it.</p>
<hr>
<p>Before moving on to why I chose these two families of distros in particular, I want to explain why I don&rsquo;t recommend distros like Ubuntu, Linux Mint, Debian, Pop!_OS or Fedora.</p>
<h3 id="versioned-releases-and-broken-upgrades">Versioned releases and broken upgrades</h3>
<p>One of the most overlooked aspects of running Linux for a long time is dealing with updates, and in particular how updates work in what I like to call <em>the legacy versioned release model</em>.</p>
<p>Excluding immutable, image based distros, once installed an operating system is a <strong>state machine</strong> that moves from state to state with every action the user takes, such as installing and uninstalling applications, changing configuration files and adding third party repositories. By nature of the immeasurable amount of things you can do to an operating system, it&rsquo;s impossible to know and account for all the possible combinations of states that could exist out there.</p>
<p>This becomes potentially catastrophic during a <em>version upgrade</em>: by making the big changes required for a full system upgrade, including kernel, drivers, libraries and applications all at once, you are inevitably asking for trouble.</p>
<p><strong>When the operating system cannot control its own state</strong>, big changes like these should be avoided, in favor of gradual ones, introduced bit by bit as they come.</p>
<p>It&rsquo;s quite hard to nail down exactly all the things that <em>can</em> go wrong during a version upgrade, and if it wasn&rsquo;t, there wouldn&rsquo;t be a problem. Let&rsquo;s just describe a few:</p>
<ul>
<li>Any third party repository you might have added (ie: PPAs for Ubuntu, Copr repos and RPM Fusion for Fedora) might not work on the new version</li>
<li>A fundamental structural change in the new version (ie: what filesystem or bootloader to use) requires making assumptions on the state of the system that are often not correct</li>
<li>A new version of the kernel or a low-level system package might introduce a regression that makes some of your hardware inoperable</li>
<li>User-created system configurations might get overwritten</li>
</ul>
<p>This is a huge problem, because when doing a version upgrade you&rsquo;re essentially rolling the dice on whether or not it is going to succeed, or fail and leave you with a broken system and a weekend of troubleshooting ahead of you.</p>
<p>And trust me, I&rsquo;ve seen it happen countless times, to my systems and to other people&rsquo;s. It&rsquo;s not fun and it&rsquo;s not something that can be allowed to happen.</p>
<p>This is the main reason why I cannot recommend distributions that follow a versioned release model, because even if they work great, sooner or later they&rsquo;re going to break and you&rsquo;re going to be mad about it.</p>
<h2 id="option-1-universal-blue----aka-aurora-bazzite-and-bluefin">Option 1: Universal Blue &ndash; aka Aurora, Bazzite and Bluefin</h2>
<p><img src="/images/post_pics/the_only_two_linux_distros_you_will_ever_need/ublue.png" alt=""></p>
<p><a href="https://universal-blue.org/">Universal Blue</a> is the easiest Linux distribution for almost everyone. Let me explain why.</p>
<p>You see, this problem with system upgrades, and the operating system getting messier as time goes by, doesn&rsquo;t exist only on the desktop, but in the server and enterprise world as well.</p>
<p>However here&rsquo;s the fun part: in the server world we solved it! How? Well, in the silliest and most obvious way possible of course: every time you so much as <em>want to touch</em> the system, you throw the entire thing away and rebuild it completely from scratch!</p>
<p>It makes sense: how many times have you been plagued by absurd errors or bugs on your computer? Those you could only solve by <strong>nuking the entire system and reinstalling it from scratch</strong>?</p>
<p>This sounds idiotic at best, but it does work, and we&rsquo;ve found very convenient ways of automating this process. In the server space we have <em>declarative infrastructure</em> and <em>containers</em>, and they&rsquo;re basically the industry standard at this point.</p>
<p>The clever people of the Universal Blue project have essentially done the same thing: they ship their operating systems as immutable glorified container images, and every time you update your system it&rsquo;s just reinstalled from scratch, while your home folder and your configurations are kept intact.</p>
<p>It&rsquo;s not even unheard of in the consumer space: both Android and iOS use a similar approach, where your system partition is locked down and immutable, and OS updates are provided as a full system image that replaces the one you&rsquo;re currently running. It&rsquo;s a tried and true method, and while it might admittedly still be novel in the desktop computing space, there&rsquo;s little reason why it shouldn&rsquo;t work out just fine.</p>
<p>While this is a good way of doing things, it does come with one caveat by definition: your system is immutable.</p>
<p>This means that you are limited in <em>how</em> you can install software. The Bazzite documentation has <a href="https://docs.bazzite.gg/Installing_and_Managing_Software/">a very well put together list</a> that should apply to all distros in the Universal Blue family, but I also want to give you my recommended methods, in order of preference:</p>
<ul>
<li><a href="https://flatpak.org/">Flatpak</a> (mostly via <a href="https://flathub.org/">Flathub</a>)
<ul>
<li>Most apps work great, some like Steam and code editors don&rsquo;t</li>
<li>More on this in the <a href="#installing-apps">Installing apps</a> section of this article</li>
</ul>
</li>
<li><a href="https://appimage.org/">AppImage</a>
<ul>
<li>Annoying to update apps</li>
<li>The ecosystem around the format can be problematic for developers</li>
</ul>
</li>
<li>In a container via <a href="https://distrobox.it/">Distrobox</a>
<ul>
<li>Really cool and extremely versatile, but not very easy for a beginner</li>
</ul>
</li>
<li>Via <a href="https://brew.sh/">Homebrew</a>
<ul>
<li>Good idea, I don&rsquo;t really like the implementation all that much on a technical level and it&rsquo;s primarily made for macOS, but it does work</li>
</ul>
</li>
</ul>
<p>Additionally, if you need to make any changes at the system level, like adding kernel command line arguments, installing out-of-tree drivers or system-wide background services, you&rsquo;re going to be straight out of luck, unless Universal Blue (or the particular flavor you choose) decides to support it.</p>
<p>But <em>most people</em> don&rsquo;t need to do any of that.</p>
<p>What <em>most people</em> need is something that just works, gets out of your way and lets you use your goddamn computer.</p>
<p>And for that use case, Aurora, Bazzite and Bluefin <em>are</em> the best options.</p>
<hr>
<p>But what if your use case is a bit different? What if you <em>do</em> need to mess around with your system, even if just a little? Maybe you need particular drivers for a special piece of hardware?</p>
<p>If that&rsquo;s the case and you&rsquo;re willing to take up a little more responsibility in exchange for full control over the system, that&rsquo;s where my second option comes in.</p>
<h2 id="option-2-arch-linux">Option 2: Arch Linux</h2>
<p><img src="/images/post_pics/the_only_two_linux_distros_you_will_ever_need/arch.png" alt=""></p>
<p>Arch can be a fun and fairly predictable distro, but <strong>only if you know exactly what you&rsquo;re doing</strong>.</p>
<p>Vanilla Arch is fundamentally a DIY distro: you&rsquo;ll be expected to read documentation, prepare for updates and do regular system maintenance. <strong>If you choose Arch, you&rsquo;ll essentially be building your own distro</strong>.</p>
<p>And this is an incredible prospect for people like me that enjoy this side of using computers, but it&rsquo;s absolutely not a good choice for new users.</p>
<p>Fortunately, there are a number of very good Arch derivatives that solve exactly this problem. We&rsquo;ll get to them in a moment; first let&rsquo;s see what makes Arch a &ldquo;good&rdquo; distro.</p>
<p>In Arch, versioning is thrown completely out the window. Arch is a <strong>rolling-release</strong> distribution. This means that there are no versions of Arch, just one: the current one.</p>
<p>Packages are released into the stable repositories as soon as they are available, but not without some rigorous testing by the maintainers.</p>
<p>To be fair, sometimes some bugs do slip through the cracks, but so do they on versioned distros. The big difference here is that with updates being small, frequent and incremental, what <em>can</em> break is measured not by <em>entire system versions</em> but by <em>individual packages</em> that are easily downgraded.</p>
<p>For example: a while ago a regression was introduced in <a href="https://mesa3d.org/">mesa</a> (open source 3D graphics library, the built-in user-space driver that&rsquo;s used by AMD and Intel GPUs) that created problems with running VR. I&rsquo;ve been hit by this problem, and the solution was just to downgrade mesa to the last-known-good version, until the new release with the fix came around.</p>
<p>If I&rsquo;d happened to encounter a similar bug on a versioned distro during a major version upgrade, I would have had to downgrade the entire system, and that&rsquo;s not an easy task.</p>
<p>Also, it was just one package that was broken, hindering one (unimportant I might add) functionality in my otherwise perfectly working system.</p>
<p>It&rsquo;s a good idea, in case you end up using Arch or an Arch based distro, to keep an eye on the <a href="https://archlinux.org/news/">Arch Linux official news section</a>, particularly before running updates. Any (rare) issues that you might find during regular updates that might require particular attention or manual intervention are meticulously reported here, with simple instructions on how to proceed depending on the particular situation you might find yourself in.</p>
<p>Another advantage of Arch over Universal Blue is wider software availability:</p>
<ul>
<li>First of all, you can use all of the aforementioned installation options (Flatpak, AppImage, Distrobox and Homebrew) on Arch without any problems (and in fact you might be doing so if you have a Steam Deck running SteamOS, since it&rsquo;s just immutable Arch).</li>
<li>Second, you can use regular packages from the system repositories, which offer quite the ample variety.</li>
<li>Third, you can resort to the <a href="https://wiki.archlinux.org/title/Arch_User_Repository">Arch User Repository</a> or AUR for short.</li>
</ul>
<p>An important note on using the AUR tho, quoting directly from the Wiki:</p>
<blockquote>
<p>AUR packages are user-produced content. These PKGBUILDs are completely unofficial and have not been thoroughly vetted. Any use of the provided files is at your own risk.</p>
</blockquote>
<p>In other words:</p>
<ol>
<li>Use the AUR only as a last ditch option, if and only if all the other options fail</li>
<li>Make sure the package you&rsquo;re interested in comes from a trusted user, or has been submitted or approved by the developer of the program you&rsquo;re trying to install</li>
<li>If you don&rsquo;t know what you&rsquo;re doing, please ask the community for help</li>
</ol>
<hr>
<p>Let&rsquo;s have a look at some distros that are based on Arch, and offer an easier and friendlier out-of-the-box experience.</p>
<h3 id="cachyos">CachyOS</h3>
<p><img src="/images/post_pics/the_only_two_linux_distros_you_will_ever_need/cachyos.png" alt=""></p>
<p><a href="https://cachyos.org/">CachyOS</a> is a lot of fun. It&rsquo;s very close to base Arch, with the following differences:</p>
<ul>
<li>It uses its own repositories that <strong>follow closely the upstream Arch release schedule</strong>, offering packages that are specifically optimized for your particular CPU architecture
<ul>
<li>In other words: it provides a small but measurable performance edge by making use of most if not all of the advanced features that are built into your CPU</li>
<li>It also uses a slightly tweaked kernel, more suited for real-time applications (aka desktop/gaming usage as opposed to server usage)</li>
</ul>
</li>
<li>The installation process is very simple and straightforward thanks to the graphical installer, while providing a lot of customization options</li>
<li>Some of the extra packages it provides are quite useful, like for example <code>gamescope-git</code> for beating some misbehaving games into shape (see <a href="https://gabmus.org/posts/fix-games-losing-focus-on-multimonitor/">this post</a> for an example) and <code>gamescope-session-cachyos</code>, which allows you to log straight into Steam Big Picture instead of your regular desktop, which is quite convenient for a TV setup.</li>
</ul>
<p>Overall you should choose CachyOS if you want to squeeze every last drop of performance out of a recent PC (Intel Haswell or AMD Excavator or newer).</p>
<h4 id="the-flavor-of-the-week-allegations">The &ldquo;flavor of the week&rdquo; allegations</h4>
<p>CachyOS is getting quite a lot of media coverage, and has been described as &ldquo;flavor of the week&rdquo;.</p>
<p>I assume this is meant to imply that its popularity is a temporary trend, and that it will &ldquo;fall of out fashion&rdquo; at some point.</p>
<p>But CachyOS is at its core just Arch. Arch has been around forever and is not going anywhere any time soon.</p>
<p>CachyOS on the other hand is fairly new, we don&rsquo;t know if it&rsquo;s going to disappear tomorrow. I don&rsquo;t think it will, but it doesn&rsquo;t matter, because if that happens all you need to do is switch from the CachyOS system repositories to the Arch Linux ones. Your system will keep on working and you can go about your day as if nothing happened. I&rsquo;m sure that if that happened, there would be much more detailed instructions on how to do this exactly, but it simply boils down to this.</p>
<p><img src="/images/post_pics/the_only_two_linux_distros_you_will_ever_need/cachy_to_arch.png" alt=""></p>
<p>To some extent I can attest to the fact that there isn&rsquo;t much more to it than this. In fact, I&rsquo;ve done the opposite: I&rsquo;ve configured my Arch system to use the CachyOS repositories, and it was a simple and straightforward process, fully sanctioned and accounted for by the CachyOS project and further simplified by <a href="https://wiki.cachyos.org/features/optimized_repos/#adding-our-repositories-to-an-existing-arch-linux-install">the excellent entry in the their Wiki</a>.</p>
<h3 id="endeavouros">EndeavourOS</h3>
<p><img src="/images/post_pics/the_only_two_linux_distros_you_will_ever_need/endeavouros.png" alt=""></p>
<p><a href="https://endeavouros.com/">EndeavourOS</a> is very similar to CachyOS in principle, but more conservative. <strong>It uses the same exact repositories as Arch</strong>, with I believe an additional one for branding and other small tools.</p>
<p>This means you won&rsquo;t get the performance benefits of CachyOS, but otherwise the experience will be pretty much exactly the same: a preconfigured, polished and ready to go Arch distro.</p>
<p>The installer is the same CachyOS uses, so there should be no big difference on that front either, and EndeavourOS also offers a similar number of customization options during the initial setup.</p>
<p>EndeavourOS is great if you want something simple, that stays very close to traditional Arch Linux, or if your hardware is not new enough to make use of the optimizations CachyOS provides (older than Intel Haswell or AMD Excavator).</p>
<h3 id="a-note-on-why-not-manjaro">A note on why NOT Manjaro</h3>
<p>Given its history I can not recommend Manjaro.</p>
<p>From a technical standpoint, they have a history of causing problems by shipping unstable or unreleased packages, or holding off some updates for too long, breaking packages installed from the AUR, among other things.</p>
<p>With all the options I already mentioned, and setting aside any technical mishaps here and there, I cannot see Manjaro as an interesting or compelling option for any use case. I have no personal issue with the Manjaro project in particular, nor the people that run it. This is a technical preference and since this article is about my opinions on things anyway, I think it&rsquo;s worth mentioning.</p>
<h2 id="some-tips-on-daily-usage">Some tips on daily usage</h2>
<p>In this section I want to cover some suggestions, regardless of which distribution you choose, for a smooth Linux experience.</p>
<h3 id="make-sure-you-use-wayland">Make sure you use Wayland</h3>
<p>In Linux there are two main <em>display protocols</em>. You don&rsquo;t need to know what a display protocol is, but it&rsquo;s a good idea to get a general understanding anyway.</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/X_Window_System">X11</a> (sometimes called Xorg, after its most popular implementation) is the <strong>legacy</strong> display protocol that&rsquo;s been in use on UNIX systems since the &rsquo;80s and on Linux systems up until today
<ul>
<li>Old and clunky</li>
<li>Insecure by design, allows any application to behave like a keylogger or grab the content of the screen with no restrictions</li>
<li>New display features such as Variable Refresh Rate (VRR), HDR and display scaling are unsupported or barely supported with caveats</li>
<li>Can suffer from tearing if not configured properly</li>
<li>Most desktop environments are dropping support for it</li>
<li>Frustratingly, technically more widely supported since it&rsquo;s been around forever</li>
</ul>
</li>
<li><a href="https://en.wikipedia.org/wiki/Wayland_(protocol)">Wayland</a> is the new display protocol, developed by the same people that used to develop Xorg
<ul>
<li>Wide adoption is only fairly recent (as of the last couple years or so)</li>
<li>Feature availability depends on the specific desktop environment</li>
<li>Secure by design, disallowing keylogger behavior (but this causes some annoyances, like applications being unable to set their own global key bindings) and unrestricted screen recording</li>
<li>Good support for VRR, HDR and display scaling</li>
<li>No tearing by default
<ul>
<li>It seems like some people want tearing. I think it&rsquo;s something to do with lower input latency. Anyway you can enable it on some desktops like KDE Plasma if desired</li>
</ul>
</li>
<li>New display features will get support on Wayland first</li>
</ul>
</li>
</ul>
<p>To summarize, Wayland is the current recommended option for the vast majority of use cases, since it supports all modern features. Most desktop environments and application toolkits are slowly dropping support for X11 in favor of Wayland, so if nothing else it&rsquo;s a good idea to use it to future proof your system.</p>
<p>The choice of your display server is tightly bound to the choice of desktop environment you make.</p>
<h3 id="desktop-environment-choice">Desktop Environment choice</h3>
<p>My recommendation of desktop environments for new users boils down to these two options:</p>
<ul>
<li>KDE Plasma</li>
<li>GNOME</li>
</ul>
<p><img src="/images/post_pics/the_only_two_linux_distros_you_will_ever_need/desktop_environments.png" alt=""></p>
<p><strong>KDE Plasma</strong> is a stable desktop that offers many advanced features in a fairly easy to use package. Its default layout is also very familiar if you&rsquo;re coming from Windows, but can easily be adapted for whatever workflow you prefer.</p>
<p>On the other hand, if you&rsquo;re looking for a little paradigm shift in how you use your computer, you might be interested in GNOME. At first glance it looks a bit more similar to macOS, but don&rsquo;t let that fool you, because it&rsquo;s a very different way to interact with your desktop and applications. It lacks some more advanced customization options that KDE Plasma offers and overall it&rsquo;s less adaptable.</p>
<p>Ultimately, each desktop has its pros and cons, but at the end of the day they are very comparable feature-wise, and your choice will depend on your personal preference. My own choice here is GNOME, but if you&rsquo;re unsure, <strong>I recommend KDE Plasma</strong> as an option that is more suited for general usage (and so does Valve, since that&rsquo;s the desktop that ships with SteamOS).</p>
<p>I made this table of <em>some</em> of the desktop environments that are out there, with a small description and the reasons behind my recommendation.</p>
<table>
  <thead>
      <tr>
          <th>Desktop Environment</th>
          <th>Recommended for new users</th>
          <th>X11 Support</th>
          <th>Wayland Support</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://kde.org/plasma-desktop/">KDE Plasma</a></td>
          <td>✅ Yes</td>
          <td>⚠️ Yes (but <a href="https://blogs.kde.org/2025/11/26/going-all-in-on-a-wayland-future/">will be dropped soon</a>)</td>
          <td>✅ Yes</td>
          <td>Familiar, very customizable, a bit advanced but has a lot of functionality. SteamOS ships with KDE Plasma</td>
      </tr>
      <tr>
          <td><a href="https://www.gnome.org/">GNOME</a></td>
          <td>✅ Yes</td>
          <td>❌ No</td>
          <td>✅ Yes</td>
          <td>Unique, simple and straight to the point. Lacks some features compared to KDE Plasma, offers an opinionated and streamlined experience</td>
      </tr>
      <tr>
          <td><a href="https://en.wikipedia.org/wiki/Cinnamon_(desktop_environment)">Cinnamon</a></td>
          <td>⚠️ Maybe, lack of Wayland support makes this hard to recommend</td>
          <td>✅ Yes</td>
          <td>❌ No (work in progress)</td>
          <td>The desktop that ships with Linux Mint. Familiar, very easy to use, but lacks more modern functionality</td>
      </tr>
      <tr>
          <td><a href="https://xfce.org/">XFCE</a></td>
          <td>❌ No, lack of Wayland support, a bit too barebones for regular users</td>
          <td>✅ Yes</td>
          <td>❌ No (work in progress)</td>
          <td>Old reliable, it&rsquo;s quite stable but may look a bit outdated, and lacks modern amenities</td>
      </tr>
      <tr>
          <td><a href="https://system76.com/cosmic">COSMIC</a></td>
          <td>❌ No, still immature</td>
          <td>❌ No</td>
          <td>✅ Yes</td>
          <td>Cool features in a simple and fairly customizable package, promising but very new and still not polished enough for general usage (see <a href="https://youtu.be/kluoZ9RhmVo?si=4mBNU4sPHkPG6xSk&amp;t=1420">Linus&rsquo;s weird issue with Steam</a>)</td>
      </tr>
      <tr>
          <td><a href="https://hypr.land/">Hyprland</a></td>
          <td>❌ No, too advanced</td>
          <td>❌ No</td>
          <td>✅ Yes</td>
          <td>Cool animations, excellent tiling system, extremely customizable since you build it from the ground up via configuration files, does not provide a usable out-of-the-box experience and requires a lot of tinkering</td>
      </tr>
      <tr>
          <td><a href="https://github.com/niri-wm/niri">Niri</a></td>
          <td>❌ No, too advanced</td>
          <td>❌ No</td>
          <td>✅ Yes</td>
          <td>Novel concept based on an infinite scrolling horizontal surface, combined with tiling, extremely customizable since you build it from the ground up via configuration files, does not provide a usable out-of-the-box experience and requires a lot of tinkering</td>
      </tr>
  </tbody>
</table>
<h3 id="installing-apps">Installing apps</h3>
<p>I strongly suggest you install most applications (<strong>except Steam</strong>) from <a href="https://flathub.org/en">Flathub</a>. Depending on the distribution you choose, you&rsquo;ll find one of these &ldquo;App Store&rdquo; like frontends for installing apps from Flathub:</p>
<table>
  <thead>
      <tr>
          <th>Discover</th>
          <th>Bazaar</th>
          <th>GNOME Software</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><img src="/images/post_pics/the_only_two_linux_distros_you_will_ever_need/discover.png" alt=""></td>
          <td><img src="/images/post_pics/the_only_two_linux_distros_you_will_ever_need/bazaar.png" alt=""></td>
          <td><img src="/images/post_pics/the_only_two_linux_distros_you_will_ever_need/gnome-software.png" alt=""></td>
      </tr>
  </tbody>
</table>
<p>Flatpak is a universal packaging format, meaning you can install Flatpak apps on virtually any Linux distro.</p>
<p>It also runs applications inside of a sandboxed environment, which provides some security benefits (random applications usually aren&rsquo;t able to invasively access your system) but also some headaches since you might want some applications to be able to break out of the sandbox.</p>
<p>Fortunately it&rsquo;s very easy to manage the permissions of your Flatpak apps using <a href="https://flathub.org/en/apps/com.github.tchx84.Flatseal">Flatseal</a>.</p>
<h3 id="random-commands-from-the-internet">Random commands from the internet</h3>
<figure><img src="/images/post_pics/the_only_two_linux_distros_you_will_ever_need/unix-magic.jpg"
    alt="UNIX Magic"><figcaption>
      <p>&quot;UNIX Magic&quot; poster, created by Gary Overacre, published by UniTech Software. Image from <a href="https://github.com/drio/unixmagic">Unix Magic Poster Reference Tracker</a></p>
    </figcaption>
</figure>

<p>It&rsquo;s very common, particularly when searching how to fix various miscellaneous problems or installing obscure pieces of software, to find random commands in forum threads or blog posts.</p>
<p>Most people just blindly copy &amp; paste them in their terminal, without so much as reading them. Please, <strong>do not do this</strong>.</p>
<p>Do not run random terminal commands you don&rsquo;t understand, for a variety of reasons:</p>
<ul>
<li>If you don&rsquo;t understand what they do, you effectively have no idea if they apply to your distribution, or to your particular problem
<ul>
<li>Even if you read that a certain command is specifically written for your distribution, and while <em>technically</em> it might be the case, there&rsquo;s a high likelyhood that it&rsquo;s old and not up to date and therefore useless at best and actively harmful at worst</li>
</ul>
</li>
<li>It might be malicious, you might install malware without even realizing it</li>
<li>Even if not malicious, you could break your system beyond repair, forcing you to reinstall (this is <em>less likely</em> on immutable distros, like the ones based on Universal Blue)</li>
<li>In the remote chance that it actually works, you don&rsquo;t know why it worked</li>
</ul>
<p>This is a very, very bad habit of the Linux community and something we need to get rid of. The reason we do it is trivial really: it&rsquo;s easier to say &ldquo;copy this and paste it in your terminal&rdquo; than to explain your way through a graphical program, or to explain what the command does.</p>
<p>It gets even worse when these commands start using fancy UNIX tooling like <code>sed</code>, <code>grep</code>, <code>awk</code> and <code>tee</code>. What you get on the other end is some fabulous UNIX Magic soup. Truly delicious if you understand it, but to a novice it&rsquo;s just a mess of magical incantations. Besides, someone who is not a programmer shouldn&rsquo;t necessarily have to know the intricacies of the shell to enjoy using Linux properly.</p>
<hr>
<p>And this concludes the article, it&rsquo;s long and verbose but my hope is that it gives you all the information you might need to get started on your Linux adventure without surprises.</p>
<p>All of these opinions are my own, and I&rsquo;m sure someone will disagree with me, but that&rsquo;s fine.</p>
<p>This is the culmination of a lifetime of using Linux on the desktop and beyond, and the result of all the lessons that I&rsquo;ve learned along the way.</p>
]]></description>
      
        <media:thumbnail url="https://gabmus.org/images/post_pics/the_only_two_linux_distros_you_will_ever_need/thumb.jpg" />
      
    </item>
    
    
    
    <item>
      <title>MSI/ASUS Motherboard Fan Control on Linux</title>
      <link>https://gabmus.org/posts/msi-motherboard-fan-control/</link>
      <pubDate>Wed, 03 Dec 2025 13:33:38 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/msi-motherboard-fan-control/</guid>
      <description><![CDATA[<p>It seems like some modern motherboards use fan control systems are not fully compatible out of the box with Linux (what a surprise).</p>
<p>In particular this relates to my recently acquired <a href="https://us.msi.com/Motherboard/MAG-X870E-TOMAHAWK-WIFI/Overview">MSI MAG X870E TOMAHAWK WIFI</a> motherboard, but as I understand this same problem also happens on other MSI and ASUS motherboards.</p>
<p>More specifically, this is an issue with the <em>Nuvoton NCT6687-R</em> fan control chipset and its various implementations.</p>
<p>The solution to allow for software fan control is decently easy, but has a few steps. It consists in installing <a href="https://github.com/Fred78290/nct6687d">this out of tree driver</a>, ensuring it&rsquo;s correctly loaded with the appropriate options and tweaking some settings in the UEFI/BIOS.</p>
<h3 id="1-installing-the-driver">1. Installing the driver</h3>
<p>If you have access to the AUR, simply install <code>nct6687d-dkms-git</code>. Otherwise, follow <a href="https://github.com/Fred78290/nct6687d?tab=readme-ov-file#installation">the official guide on the driver&rsquo;s README</a>.</p>
<h3 id="2-ensuring-the-driver-loads">2. Ensuring the driver loads</h3>
<p>Create a <code>.conf</code> file inside <code>/etc/modules-load.d/</code>. I called mine <code>gabmus-msi-fan-control.conf</code>. Add the following string inside it: <code>nct6687</code>.</p>
<h3 id="3-configure-lm_sensors">3. Configure lm_sensors</h3>
<p>Run <code>sudo sensors-detect</code> and follow the interactive wizard to discover the hardware you have available.</p>
<p>Once it&rsquo;s done pay attention: it will give you a summary and ask you to (over)write <code>/etc/conf.d/lm_sensors</code>. You can allow it to do so, but you also need to check what driver it will try to use. Chances are it will default to <code>nct6683</code>, which is the in-tree driver that doesn&rsquo;t support manual control.</p>
<p>If that&rsquo;s the case, once it has written the file (<code>/etc/conf.d/lm_sensors</code>), go edit it yourself. You will need to replace <code>nct6683</code> inside the <code>HWMON_MODULES</code> variable with <code>nct6687</code>.</p>
<h3 id="4-uefibios-tweaks">4. UEFI/BIOS tweaks</h3>
<p>Inside your UEFI, in the fan control section, you should ensure all of the detected fans have <strong>Fan Type Auto Detect</strong> enabled and <strong>Smart Fan Mode</strong> disabled. Note that disabling Smart Fan Mode will lock your fans on a static speed. I recommend setting the speed to 100% to ensure that your PC stays cool before booting into Linux and having the fans controlled by your favorite fan control system.</p>
<p>My source for this particular section is <a href="https://github.com/Fred78290/nct6687d/issues/141">this issue on the driver&rsquo;s repo</a>.</p>
<h3 id="5-check-the-speed-and-possibly-add-a-parameter-to-the-driver">5. Check the speed and possibly add a parameter to the driver</h3>
<p>Open up your fan control application. For reference I use and currently recommend <a href="https://docs.coolercontrol.org/">CoolerControl</a>.</p>
<p>Check if your fans show an RPM reading. If they do congrats, you&rsquo;re done.</p>
<p>If they don&rsquo;t, you might need to use an alternative configuration for the driver as described below.</p>
<p>Create a new <code>.conf</code> file in <code>/etc/modprobe.d/</code>, I called mine <code>gabmus-msi-fan-control.conf</code>. In it, write the following: <code>options nct6687 fan_config=msi_alt1</code>. This instructs the driver to use an alternative fan control mechanism that should be specific to <em>some</em> MSI motherboards.</p>
<p>Reboot and check the RPM readings again. If they show up then you&rsquo;re all set, otherwise your particular hardware might not be supported yet. In this case it might be a good idea to open an issue on the driver&rsquo;s repo.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Full disk encryption with USB unlock on Arch Linux</title>
      <link>https://gabmus.org/posts/full-disc-encryption-with-usb-unlock/</link>
      <pubDate>Mon, 10 Nov 2025 13:36:50 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/full-disc-encryption-with-usb-unlock/</guid>
      <description><![CDATA[<p><em>Disclaimer: I didn&rsquo;t invent anything new here, this is just a case of scattered information that&rsquo;s annoying to put together. All sources are linked throughout the article.</em></p>
<p><em>Another disclaimer: I&rsquo;m not a security expert and I don&rsquo;t claim to be. This is just a solution that I&rsquo;m using, if following anything that I write causes any problems including but not limited to data loss or theft, I don&rsquo;t take any responsibility. You&rsquo;re on your own.</em></p>
<p>I find myself in this very particular situation: I have a server running Arch in which I want to have full disk encryption, but being a server I want to be able to reboot it without having to insert the password manually.</p>
<p>A solution to this problem is using TPM to store a decryption key. This works but it&rsquo;s not ideal if someone unauthorized takes possession of the server, since they will have access to the unencrypted disk by just turning the computer on.</p>
<p>Another solution is to store a decryption key on a regular USB stick, and in case you hear weird noises in the night or suspect someone might be out to take your server from you, you just remove the USB stick and either hide it or destroy it.</p>
<p>But let&rsquo;s get some things out of the way first.</p>
<blockquote>
<p>Are you running Arch on a server?!?!</p>
</blockquote>
<p>Yes, get over it.</p>
<blockquote>
<p>Is this secure?</p>
</blockquote>
<p>As for any time you ask this question, the answer is <em>it depends on your threat model</em>. In general, no this isn&rsquo;t very secure, but it&rsquo;s good enough for what I need and better than any alternatives I found. If you think you know a better way to achieve a similar result, please do let me know.</p>
<hr>
<h2 id="what-youll-need">What you&rsquo;ll need</h2>
<ul>
<li>Arch install with full disk encryption</li>
<li>USB drive</li>
<li>Shell access</li>
</ul>
<h2 id="steps">Steps</h2>
<ol>
<li>Boot up your system using password unlock (hopefully for the last time)</li>
<li>Insert a USB drive that you&rsquo;re willing to sacrifice</li>
<li>Format it as a single FAT32 partition on top of a MBR partition table</li>
<li>Mount it to a location of your choosing, I&rsquo;ll use <code>/mnt</code></li>
<li>Setup a key file as follows (<a href="https://www.tqdev.com/2022-luks-with-usb-unlock/">Source</a>)
<ul>
<li>Create a new random key in the USB drive, named anything you want, I&rsquo;ll call it <code>key.lek</code>: <code>dd if=/dev/urandom of=/mnt/key.lek bs=4096 count=1</code></li>
<li>Identify your encrypted volume: <code>blkid --match-token TYPE=crypto_LUKS -o device</code>
<ul>
<li>You should end up with a device path, for example <code>/dev/sdx2</code>. I will use this in the following command so make sure to replace it on your end</li>
</ul>
</li>
<li>Add this new random key as a valid key to decrypt your disk: <code>cryptsetup luksAddKey /dev/sdx2 /mnt/key.lek</code>
<ul>
<li>You will be asked for one of the passphrases already set up for your encrypted volume, provide it to continue</li>
</ul>
</li>
</ul>
</li>
<li>Modify your <code>/etc/mkinitcpio.conf</code> to include the <code>vfat</code> module, so that the USB drive will be readable by the initramfs (<a href="https://wiki.archlinux.org/title/Dm-crypt/Device_encryption#Configuring_mkinitcpio">Source</a>)
<ul>
<li>For clarity, the <code>MODULES</code> row in your <code>mkinitcpio.conf</code> file will look something like this: <code>MODULES=(vfat)</code></li>
<li>In case you already have other modules, don&rsquo;t remove them of course, just add this one</li>
</ul>
</li>
<li>Regenerate your initramfs with <code>mkinitcpio -P</code></li>
<li>Modify your kernel command line arguments to instruct it on how to decrypt using the USB drive (<a href="https://wiki.archlinux.org/title/Dm-crypt/System_configuration#cryptkey">Source</a>)
<ul>
<li>Take note of the UUID of the USB drive partition in which you stored the key with <code>lsblk -f</code>. Since it&rsquo;s a FAT32 it should be quite short compared to other partition UUIDs.</li>
<li>Add this at the beginning of your kernel options: <code>cryptkey=UUID=YOUR-USB-DISK-PARTITION-UUID-HERE:vfat:/key.lek</code></li>
</ul>
</li>
</ol>
<p>That should be it, reboot and your disk should decrypt automatically. Enjoy!</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Fix games losing focus when using multiple monitors on Linux</title>
      <link>https://gabmus.org/posts/fix-games-losing-focus-on-multimonitor/</link>
      <pubDate>Wed, 05 Nov 2025 17:10:41 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/fix-games-losing-focus-on-multimonitor/</guid>
      <description><![CDATA[<p>Alright, let me give you the magic string right away:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gamescope --mangoapp -w <span style="color:#bd93f9">2560</span> -h <span style="color:#bd93f9">1440</span> -W <span style="color:#bd93f9">2560</span> -H <span style="color:#bd93f9">1440</span> --fullscreen --force-grab-cursor --adaptive-sync --backend<span style="color:#ff79c6">=</span>wayland -- env <span style="color:#8be9fd;font-style:italic">ENABLE_LAYER_MESA_ANTI_LAG</span><span style="color:#ff79c6">=</span><span style="color:#bd93f9">1</span> %command%
</span></span></code></pre></div><p>Put this in your Steam game launch options, adjusting the resolution and you should be good to go.</p>
<p>Just a couple more things:</p>
<ol>
<li>Don&rsquo;t use the &ldquo;stable&rdquo; version of gamescope, use <code>gamescope-git</code> instead
<ul>
<li>This is easiest on <a href="https://cachyos.org/">CachyOS</a> since <a href="https://packages.cachyos.org/package/cachyos/x86_64/gamescope-git">it&rsquo;s already packaged in the main repos</a>, medium-easy on Arch since you can just use <a href="https://aur.archlinux.org/packages/gamescope-git">the AUR version</a>, and <em>&ldquo;good luck, you&rsquo;re on your own&rdquo;</em> everywhere else</li>
</ul>
</li>
<li>After installing gamescope, and every time you update it, run <code>sudo setcap 'CAP_SYS_NICE=eip' $(which gamescope)</code>
<ul>
<li><a href="https://wiki.archlinux.org/title/Gamescope#Setting_Gamescopes_priority">Source</a></li>
</ul>
</li>
<li>Ensure you have <em>variable refresh rate</em> turned on on your monitor and DE/compositor</li>
</ol>
<hr>
<p>Now, if you&rsquo;re still here you likely want to know what&rsquo;s going on, and what the magical incantation above is doing.</p>
<h2 id="whats-going-on">What&rsquo;s going on</h2>
<p>So, for a while now some games have had issues with losing focus on Linux. In particular this is happening to me on GNOME (Wayland, considering X11 support has been dropped) with a monitor arrangement consisting of two 1440p monitors, horizontally arranged, with the primary one being to the right of the secondary one.</p>
<p>Note that similar issues seem to have been observed on Plasma, as well as various WM-like compositors, so it&rsquo;s possible that it&rsquo;s not a problem with GNOME itself.</p>
<p>The issue is sneaky, and it only seems to happen when rapidly moving the mouse, followed by clicking. You know, how one normally plays shooter games. By losing focus, I consistently lose in any direct confrontation, and consistently lose my patience.</p>
<p>I don&rsquo;t know exactly when this started happening, nor why it&rsquo;s happening, but it&rsquo;s annoying and I&rsquo;ve been trying to fix this on and off for a while.</p>
<h2 id="gamescope-and-stuttering">Gamescope and stuttering</h2>
<p>The common answer to similar issues is to just use <a href="https://github.com/ValveSoftware/gamescope">gamescope</a> with <code>--force-grab-cursor</code>, and while that works, I found that <strong>gamescope introduces problems of its own</strong>.</p>
<p>Notably, it seems that gamescope in nested mode (aka running it inside another compositor) introduces some anomalous stuttering and performance degradation in general.</p>
<p>Fixing this is multi-fold.</p>
<p>First, you need to run the <code>setcap</code> command above. This ensures that gamescope&rsquo;s process is <em>super non-nice</em>, meaning it will have a very high scheduling priority.</p>
<p>Second, you want to turn on variable refresh rate/adaptive sync, in your monitor, in your DE AND in gamescope (the launch options above have the flag already set). This seems to make all the difference for me, but maybe if your monitor doesn&rsquo;t support VRR at all you won&rsquo;t be affected by this particular problem.</p>
<p>Third, you want to use the <code>-git</code> version of gamescope. It seems that the currently tagged version still doesn&rsquo;t include many important fixes and patches.</p>
<p>And that should be it, gamescope should now behave properly and your game shouldn&rsquo;t lose focus anymore.</p>
<h3 id="a-dumb-alternative-approach">A dumb alternative approach</h3>
<p>This might sound obvious but turning off your secondary monitor(s) is an option and fixes the issue without any complicated setup. It&rsquo;s dumb, it works, but it&rsquo;s annoying and less than ideal. Still, worth mentioning.</p>
<h2 id="explaining-the-command">Explaining the command</h2>
<ul>
<li><code>gamescope</code>: well, that&rsquo;s what we need to run</li>
<li><code>--mangoapp</code>: optional, runs mangohud on top of the game; note that the usual <code>MANGOHUD=1</code> works but is unsupported and may report inaccurate information, so just use this flag instead</li>
<li><code>-w 2560 -h 1440 -W 2560 -H 1440</code>
<ul>
<li><code>-w</code> and <code>-h</code> are the width and height your game should run at; <strong>gamescope will tell the game that this is your monitor&rsquo;s resolution</strong> to force it to run at said maximum resolution</li>
<li><code>-W</code> and <code>-H</code> are the width and height of the window that gamescope will render; this can be lower than your monitor resolution if you want your game to run windowed, or the same if you want to run it fullscreen</li>
</ul>
</li>
<li><code>--fullscreen</code>: optional, makes the game fullscreen</li>
<li><code>--force-grab-cursor</code>: the reason we&rsquo;re here, this captures the mouse so that it doesn&rsquo;t escape gamescope&rsquo;s window</li>
<li><code>--adaptive-sync</code>: makes sure gamescope plays nicely with VRR monitors as explained above; disable this if you don&rsquo;t have VRR</li>
<li><code>--backend=wayland</code>: the backend gamescope will use to render; your choice on the matter seems to be between <code>wayland</code> and <code>sdl</code>, and it seems that at least for me <code>wayland</code> works best</li>
<li><code>--</code>: this is some old school shell trickery to avoid parsing what&rsquo;s after it as command line options, just put it in there</li>
<li><code>env ENABLE_LAYER_MESA_ANTI_LAG=1</code>: optional, this turns on an implementation in mesa of AMD anti lag; this might improve latency for you, make things worse or do nothing at all</li>
<li><code>%command%</code>: the command Steam uses to run your game</li>
</ul>
<hr>
<p>Alright, I&rsquo;m putting this out there since there doesn&rsquo;t seem to be anything out there that fully explains this problem. If you have any more insight on what&rsquo;s going on, why it&rsquo;s happening or if there is a way to fix it without resorting to gamescope, let me know via email or on fedi.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Command line options with Relm4</title>
      <link>https://gabmus.org/posts/command_line_options_with_relm4/</link>
      <pubDate>Wed, 07 Feb 2024 07:32:14 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/command_line_options_with_relm4/</guid>
      <description><![CDATA[<p>I&rsquo;ve been using <a href="https://relm4.org/">Relm4</a> for a project and it&rsquo;s been pretty great. Relm4 is basically a wrapper around the GTK API that offers a more declarative layer of abstraction for building applications.</p>
<p>The only problem I find is that the project seems a little bit niche. Don&rsquo;t get me wrong, it seems very polished and so far I haven&rsquo;t found any bugs, so that&rsquo;s exceptional considering the scope of the project. The real problem with it being niche is that of course not every use case can be fully covered and documented, so some stuff you just have to figure out for yourself.</p>
<p>This was the case for me with command line options.</p>
<p>You might know that GTK offers a way to manage command line options out of the box, but Relm4 simply doesn&rsquo;t wrap that particular API. After a bit of research I found a good way to make it all work and here&rsquo;s how.</p>
<p>So given a struct named <code>MyAppComponent</code> being your root Relm4 component:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">let</span> app <span style="color:#ff79c6">=</span> gtk4::Application::builder()
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4">// set other properties as needed...
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>    .flags(gtk4::gio::ApplicationFlags::HANDLES_COMMAND_LINE)
</span></span><span style="display:flex;"><span>    .build();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// don&#39;t know if this really MUST be static, but that&#39;s what works for me
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span><span style="color:#ff79c6">static</span> BROKER: <span style="color:#50fa7b">relm4</span>::MessageBroker<span style="color:#ff79c6">&lt;</span>MyAppComponent::Input<span style="color:#ff79c6">&gt;</span> <span style="color:#ff79c6">=</span> relm4::MessageBroker::new();
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// this is just an example, here&#39;s the documentation for the Gio.Application API
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">// https://docs.gtk.org/gio/method.Application.add_main_option.html
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>app.add_main_option(
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#34;foo&#34;</span>,
</span></span><span style="display:flex;"><span>    gtk4::glib::Char::try_from(<span style="color:#f1fa8c">&#39;f&#39;</span>).unwrap(),
</span></span><span style="display:flex;"><span>    gtk4::glib::OptionFlags::IN_MAIN,
</span></span><span style="display:flex;"><span>    gtk4::glib::OptionArg::<span style="color:#8be9fd;font-style:italic">None</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#34;foo option description here&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">None</span>
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// add as many options as you want
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">let</span> sender <span style="color:#ff79c6">=</span> BROKER.sender(); <span style="color:#6272a4">// we&#39;ll move this to the signal handler below
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>app.connect_command_line(<span style="color:#ff79c6">move</span> <span style="color:#ff79c6">|</span>this, cmdline<span style="color:#ff79c6">|</span> {
</span></span><span style="display:flex;"><span>    this.activate();
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4">// very basic example, what I actually did was parse the
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>    <span style="color:#6272a4">// ApplicationCommandLine object and save my flags in an ad-hoc struct
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>    <span style="color:#6272a4">// but this should also just work
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>    sender.emit(MyAppComponent::Input::HandleCommandLine(cmdline));
</span></span><span style="display:flex;"><span>    <span style="color:#bd93f9">0</span>
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">let</span> relm_app <span style="color:#ff79c6">=</span> relm4::RelmApp::from_app(app).with_broker(<span style="color:#ff79c6">&amp;</span>BROKER);
</span></span><span style="display:flex;"><span>relm_app.run::<span style="color:#ff79c6">&lt;</span>MyAppComponent<span style="color:#ff79c6">&gt;</span>(MyAppComponent::Init {
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4">// your init data here
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4"></span>});
</span></span></code></pre></div><p>All you need now is a handler for the <code>HandleCommandLine</code> input signal in your app component and you&rsquo;re pretty much all set.</p>
<p>This is very much a simplified example of what I&rsquo;m actually doing, but it should serve as a good base to build on top of. Being that this was previously completely undocumented, at least it&rsquo;s a starting point.</p>
<p>Feel free to comment if you have found a better solution or if somehow I missed some critical piece of documentation that explains the actual way to do this.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>RDNA3 Fan Control in Linux - Why are my GPU fans not spinning under load?</title>
      <link>https://gabmus.org/posts/rdna3_fan_control_in_linux/</link>
      <pubDate>Sat, 20 Jan 2024 08:42:01 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/rdna3_fan_control_in_linux/</guid>
      <description><![CDATA[<p>I&rsquo;ve recently upgraded my GPU from a <a href="/posts/early-adopter-experience-with-the-new-radeon-rx-5700-xt-on-arch-linux/">5700XT</a> to a 7800XT, and of course using a new GPU close to launch on Linux would reveal some paper cuts.</p>
<p>Turns out my GPU fans weren&rsquo;t spinning, even under load. It helps that it&rsquo;s winter and I have some very effective case fans, but it didn&rsquo;t really make sense that at 88°C junction temperature the fans wouldn&rsquo;t spin.</p>
<p>First, you might think of reaching for <a href="https://gitlab.com/corectrl/corectrl">CoreCtrl</a> to tweak the fan curve, but you&rsquo;ll be disappointed to find no fan control settings for your new GPU.</p>
<p>Turns out RDNA3 GPUs, unlike previous Radeon cards, don&rsquo;t have manual fan control at the firmware level. What they do have is manual fan curve control.</p>
<p>The funny thing is that for whatever reason the default fan curve (at least for my particular card) looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>$ cat /sys/class/drm/card1/device/gpu_od/fan_ctrl/fan_curve
</span></span><span style="display:flex;"><span>OD_FAN_CURVE:
</span></span><span style="display:flex;"><span>0: 0C 0%
</span></span><span style="display:flex;"><span>1: 0C 0%
</span></span><span style="display:flex;"><span>2: 0C 0%
</span></span><span style="display:flex;"><span>3: 0C 0%
</span></span><span style="display:flex;"><span>4: 0C 0%
</span></span><span style="display:flex;"><span>OD_RANGE:
</span></span><span style="display:flex;"><span>FAN_CURVE(hotspot temp): 25C 100C
</span></span><span style="display:flex;"><span>FAN_CURVE(fan speed): 15% 100%
</span></span></code></pre></div><p>Basically 0% at any temperature, which shouldn&rsquo;t really even be allowed if you look at the valid ranges just below.</p>
<p>After scouring the internet I managed to find how to actually tweak these values, and I made a simple script to set my custom fan curve. The syntax is <code>node_index temperature fan_speed_percent</code>, and finally <code>c</code> as in &ldquo;commit&rdquo; to confirm the settings.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#ff79c6">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#ff79c6"></span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">if</span> <span style="color:#ff79c6">[[</span> <span style="color:#f1fa8c">&#34;</span><span style="color:#8be9fd;font-style:italic">$USER</span><span style="color:#f1fa8c">&#34;</span> !<span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#34;root&#34;</span> <span style="color:#ff79c6">]]</span>; <span style="color:#ff79c6">then</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;You need to run this as root&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">exit</span> <span style="color:#bd93f9">1</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># you might need to adjust this, in my case it&#39;s card1,</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># for you it might be card0 or something else entirely</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">cd</span> /sys/class/drm/card1/device/gpu_od/fan_ctrl
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;0 30 20&#34;</span> &gt; fan_curve
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;1 50 25&#34;</span> &gt; fan_curve
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;2 60 50&#34;</span> &gt; fan_curve
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;3 70 60&#34;</span> &gt; fan_curve
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;4 80 100&#34;</span> &gt; fan_curve
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;c&#34;</span> &gt; fan_curve
</span></span></code></pre></div>]]></description>
      
    </item>
    
    
    
    <item>
      <title>Nvpunk: A Modern Neovim Distribution with Batteries Included</title>
      <link>https://gabmus.org/posts/nvpunk_a_modern_neovim_distribution_with_batteries_included/</link>
      <pubDate>Tue, 06 Dec 2022 07:36:27 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/nvpunk_a_modern_neovim_distribution_with_batteries_included/</guid>
      <description><![CDATA[<p>I&rsquo;ve been using <a href="https://neovim.io">neovim</a> for a while now, and for me, it&rsquo;s the single best text editor out there.</p>
<p>But it&rsquo;s got a big problem: it&rsquo;s a bit of a hassle, and you have to know what you&rsquo;re doing. There&rsquo;s a wide ecosystem of plugins that integrate into each other, most of them requiring custom configuration code to work. It took a while, but I managed to put together a pretty good config.</p>
<p>Over time this config, that was initially living in my dotfiles repo, grew larger and more complex. So came the refactorings, then the generalizations, modularization, custom settings systems&hellip;</p>
<p>It outgrew its role as a humble user config, it became its own thing. At some point I had to give it its own repo, and along with it, a proper name: <strong>Nvpunk</strong>.</p>
<p><img src="/images/post_pics/nvpunk_a_modern_neovim_distribution_with_batteries_included/greeter.jpg" alt="Screenshot of Nvpunk’s greeter"></p>
<p>Yeah, that sounds about right.</p>
<hr>
<p>Romantic storytelling aside, let me tell you what Nvpunk is: a complete, out of the box and ready to go neovim distribution.</p>
<p>In other words, it&rsquo;s a fully featured neovim configuration you can use without having to learn how to select, install and manage plugins, and maintain the configuration glue that holds it all together.</p>
<p>Some highlights of what&rsquo;s included:</p>
<ul>
<li>LSP support with completions, snippets, diagnostics and formatting</li>
<li>DAP support for debugging with a familiar UI</li>
<li>Advanced syntax highlighting with TreeSitter</li>
<li>Interactive file tree with git support</li>
<li>Code outline panel</li>
<li>Fancy greeter with customizable header graphics</li>
<li>Theme chooser with a selection of popular preconfigured themes</li>
<li>Categorized right click menu to access some useful features</li>
</ul>
<p>I&rsquo;ve been heavily inspired by other projects like <a href="https://www.lunarvim.org/">LunarVim</a>, <a href="https://nvchad.com/">NvChad</a> and <a href="https://astronvim.github.io/">AstroNvim</a>, with the difference that Nvpunk tries to offer a ready to go experience, while these distros give you more of a base to build on yourself, still requiring manual configuration to be effective.</p>
<p>Nvpunk still offers some degree of custom configuration, but it&rsquo;s more tailored towards advanced users that know what they&rsquo;re doing, and it&rsquo;s very much optional.</p>
<p>Most of the things you may want to change are configurable via a UI preferences menu, accessible from the greeter.</p>
<p>I understand that Nvpunk may not be for everyone: some may call it bloated, and typically (neo)vim fans like to roll their own config anyway (just like I did), but that&rsquo;s fine. Nvpunk is mostly targeted at people that are looking for a good, feature-rich text editor, but that don&rsquo;t have the time, interest or knowledge to maintain their own config.</p>
<p>You can find out more on <a href="https://nvpunk.gabmus.org">Nvpunk&rsquo;s website</a> and on the <a href="https://gitlab.com/gabmus/nvpunk">GitLab repository</a>.</p>
]]></description>
      
        <media:thumbnail url="https://gabmus.org/images/post_pics/nvpunk_a_modern_neovim_distribution_with_batteries_included/cover.jpg" />
      
    </item>
    
    
    
    <item>
      <title>Swatch: a color palette manager</title>
      <link>https://gabmus.org/posts/swatch_a_color_palette_manager/</link>
      <pubDate>Mon, 14 Feb 2022 11:31:20 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/swatch_a_color_palette_manager/</guid>
      <description><![CDATA[<p>Between one thing and the other, I found myself in need for a palette manager: something to let me categorize different palettes and easily pick and name colors depending on their use.</p>
<p>I had a look at some already available options, but nothing seemed to fit the bill, so (as it&rsquo;s often the case) I decided to make my own.</p>
<hr>
<p>I want to make a quick tangent on how easy it has become for me to quickly <del>hack together</del> build a GTK app.</p>
<p>A big part of the reason is reusing existing code and tooling from my other projects, as well as learning the proper way to do things.</p>
<p>Another big reason is the great ecosystem that&rsquo;s been growing around GNOME technologies recently, particularly after the release of GTK4.</p>
<p>First of all, libadwaita is a huge help for building any kind of application. It ships a bunch of widgets and other useful tidbits such as pre-built style classes, implementing the rules defined in the GNOME HIG. This allows for quick building, clean code and great looks out of the box.</p>
<p>But libadwaita has already been praised enough by me and other developers, so that&rsquo;s not really anything new.</p>
<p>The other incredibly useful tool that I&rsquo;m using for my apps is <a href="https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/">Blueprint</a> by <a href="https://www.jwestman.net/">James Westman</a>. Without going into too much detail, it&rsquo;s a markup language for building GTK4 UIs in a clean and simple way. You then use the blueprint compiler to compile it to regular XML without ever having to look at it yourself.</p>
<hr>
<p>Going back to Swatch, it&rsquo;s really as simple as it gets: you can create as many palettes as you want, and you can add colors to each palette. You can give custom names to everything, and you can quickly copy the color in hexadecimal (or <code>rgb()</code>) format. I also decided to give Swatch two view modes: list and grid, similar to what you&rsquo;d see in a file manager.</p>
<p><img src="/images/post_pics/Swatch_a_color_palette_manager/scrot1.png" alt=""></p>
<p>And with that said, this little tool does exactly what I need. Hopefully you can find it useful as well.</p>
<p><a href="https://gitlab.gnome.org/gabmus/swatch">The code is available on GNOME GitLab</a> as usual, and you can try it for yourself using the latest flatpak CI build.</p>
<p>As for a proper release, I&rsquo;m planning to tag one and submit it to Flathub as soon as the GNOME 42 runtime comes out, so stay tuned for that!</p>
]]></description>
      
        <media:thumbnail url="https://gabmus.org/images/post_pics/Swatch_a_color_palette_manager/scrot0.png" />
      
    </item>
    
    
    
    <item>
      <title>Block ads in WebKitGtk</title>
      <link>https://gabmus.org/posts/block_ads_in_webkitgtk/</link>
      <pubDate>Sat, 27 Nov 2021 19:08:53 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/block_ads_in_webkitgtk/</guid>
      <description><![CDATA[<p>One of the most requested features (and for a good reason) in Feeds has always been ad blocking.</p>
<p>I never came around to implement it, mostly because I couldn&rsquo;t find a guide or any similar resource, except for <a href="https://gitlab.gnome.org/GNOME/Epiphany">GNOME Web&rsquo;s source code</a>, and that&rsquo;s not exactly easy to navigate. At least, it isn&rsquo;t for me.</p>
<p>Today I decided to come back to it, and I found that it&rsquo;s actually a rather easy task! That&rsquo;s why I wanted to document this process, in hope that someone might find it useful one day.</p>
<p>First off, you&rsquo;ll need one or more <strong>blocklists</strong>, in JSON format. I used <a href="https://easylist.to/">EasyList</a>, in particular <a href="https://easylist-downloads.adblockplus.org/easylist_min_content_blocker.json">here&rsquo;s the link to the JSON block list</a>. Blocklists should be updated frequently, so it&rsquo;s advisable to have some sort of mechanism to download and update them at runtime automatically.</p>
<p>Let&rsquo;s now jump to the code, which will be Python in this case, but hopefully can be easily adapted to other languages.</p>
<p>I&rsquo;ll use some closures for convenience.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python3" data-lang="python3"><span style="display:flex;"><span><span style="color:#6272a4"># have a function that downloads text</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">def</span> <span style="color:#50fa7b">download_text</span>(url: <span style="color:#8be9fd;font-style:italic">str</span>) <span style="color:#ff79c6">-&gt;</span> <span style="color:#8be9fd;font-style:italic">str</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4"># ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># create the filter store somewhere in your code, ideally you should</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># only have one in your application</span>
</span></span><span style="display:flex;"><span>my_filter_store <span style="color:#ff79c6">=</span> WebKit2<span style="color:#ff79c6">.</span>UserContentFilterStore<span style="color:#ff79c6">.</span>new(<span style="color:#f1fa8c">&#39;some/cache/path&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># a name that will be used to save/retrieve blocklists from the store</span>
</span></span><span style="display:flex;"><span>BLOCKLIST_ID <span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#39;blocklist&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">def</span> <span style="color:#50fa7b">apply_adblock</span>(
</span></span><span style="display:flex;"><span>        webview: WebKit2<span style="color:#ff79c6">.</span>WebView,
</span></span><span style="display:flex;"><span>        filter_store: WebKit2<span style="color:#ff79c6">.</span>UserContentFilterStore,
</span></span><span style="display:flex;"><span>        blocklist_url: <span style="color:#8be9fd;font-style:italic">str</span>
</span></span><span style="display:flex;"><span>) <span style="color:#ff79c6">-&gt;</span> <span style="color:#ff79c6">None</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4"># 0: Retrieve the WebKit2.UserContentManager from the WebView</span>
</span></span><span style="display:flex;"><span>    content_manager <span style="color:#ff79c6">=</span> webview<span style="color:#ff79c6">.</span>get_user_content_manager()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> <span style="color:#50fa7b">save_blocklist_cb</span>(caller, res, <span style="color:#ff79c6">*</span>args):
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4"># 6: Once saving is done we can retrieve the newly created</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">#    WebKit2.UserContentFilter with save_finish and finally add th</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">#    filter to the WebKit2.UserContentManager</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">try</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd;font-style:italic">filter</span> <span style="color:#ff79c6">=</span> filter_store<span style="color:#ff79c6">.</span>save_finish(res)
</span></span><span style="display:flex;"><span>            content_manager<span style="color:#ff79c6">.</span>add_filter(<span style="color:#8be9fd;font-style:italic">filter</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">except</span> GLib<span style="color:#ff79c6">.</span>Error:
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd;font-style:italic">print</span>(<span style="color:#f1fa8c">&#39;Error saving blocklist&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> <span style="color:#50fa7b">download_blocklist_cb</span>(blocklist: <span style="color:#8be9fd;font-style:italic">str</span>):
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4"># 5: Now that we have the blocklist (as a string containing a json),</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">#    we&#39;ll need to convert it to GLib.Bytes, then save it in the store.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">#    Yes, we need to save it even if we want to refresh it every time,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">#    this is just the way this works.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">#    This is also async and when it&#39;s done it will call continue on to</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">#    save_blocklist_cb</span>
</span></span><span style="display:flex;"><span>        filter_store<span style="color:#ff79c6">.</span>save(
</span></span><span style="display:flex;"><span>            BLOCKLIST_ID, GLib<span style="color:#ff79c6">.</span>Bytes<span style="color:#ff79c6">.</span>new(blocklist<span style="color:#ff79c6">.</span>encode()), <span style="color:#ff79c6">None</span>,
</span></span><span style="display:flex;"><span>            save_blocklist_cb
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> <span style="color:#50fa7b">download_blocklist</span>():
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4"># 4: Download the blocklist and continue on to download_blocklist_cb</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">try</span>:
</span></span><span style="display:flex;"><span>            res <span style="color:#ff79c6">=</span> download_text(blocklist_url)
</span></span><span style="display:flex;"><span>            GLib<span style="color:#ff79c6">.</span>idle_add(download_blocklist_cb, res)
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">except</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd;font-style:italic">print</span>(<span style="color:#f1fa8c">&#39;Error downloading the blocklist&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> <span style="color:#50fa7b">filter_load_cb</span>(caller, res, <span style="color:#ff79c6">*</span>args):
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4"># 2: load_finish will either succeed, and return a</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">#    WebKit2.UserContentFilter object or raise a GLib.Error in case</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4">#    it cannot find the blocklist in the store (ie: on the first run)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">try</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd;font-style:italic">filter</span> <span style="color:#ff79c6">=</span> filter_store<span style="color:#ff79c6">.</span>load_finish(res)
</span></span><span style="display:flex;"><span>            content_manager<span style="color:#ff79c6">.</span>add_filter(<span style="color:#8be9fd;font-style:italic">filter</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">except</span> GLib<span style="color:#ff79c6">.</span>Error:
</span></span><span style="display:flex;"><span>            <span style="color:#6272a4"># 3: If loading the blocklist from the store fails, download the</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6272a4">#    blocklist (async to avoid blocking of course) and save it</span>
</span></span><span style="display:flex;"><span>            <span style="color:#8be9fd;font-style:italic">print</span>(<span style="color:#f1fa8c">&#39;blocklist store not found, downloading...&#39;</span>)
</span></span><span style="display:flex;"><span>            Thread(target<span style="color:#ff79c6">=</span>download_blocklist, daemon<span style="color:#ff79c6">=</span><span style="color:#ff79c6">True</span>)<span style="color:#ff79c6">.</span>start()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4"># 1: try to load the blocklist from the store, this is async and will call</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4">#    filter_load_cb when it&#39;s done</span>
</span></span><span style="display:flex;"><span>    filter_store<span style="color:#ff79c6">.</span>load(BLOCKLIST_ID, <span style="color:#ff79c6">None</span>, filter_load_cb, <span style="color:#ff79c6">None</span>)
</span></span></code></pre></div><p>I decided to comment the example code above instead of writing disconnected descriptions here, hopefully it makes sense.</p>
<p>This said, if you want to look at the actual code that I&rsquo;m using in Feeds, <a href="https://gitlab.gnome.org/World/gfeeds/-/blob/0a3d3da66d7d4ede155db24fb0f83f03b47f5489/gfeeds/webview.py#L78">you can find it here</a> (this is a snapshot to today&rsquo;s current latest commit, in the future you may want to look at newer code). It contains some logic to automatically refresh or even remove the blocklist.</p>
<p>Let me know what you think in the comments, and feel free to point out any errors, or if something is unclear.</p>
]]></description>
      
        <media:thumbnail url="https://gabmus.org/images/post_pics/Block_ads_in_WebKitGtk/block_ads_webkit.avif" />
      
    </item>
    
    
    
    <item>
      <title>Gtk4, LibAdwaita and the new Feeds</title>
      <link>https://gabmus.org/posts/gtk4_libadwaita_and_the_new_feeds/</link>
      <pubDate>Sun, 21 Nov 2021 11:52:10 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/gtk4_libadwaita_and_the_new_feeds/</guid>
      <description><![CDATA[<p>So, something big is coming for my news reader app <a href="https://gfeeds.gabmus.org">Feeds</a>.</p>
<p>Most of this year, as far as personal projects go, I spent at close contact with Gtk4, <a href="https://gitlab.gnome.org/gnome/libadwaita">libadwaita</a> and the awesome people over at the various matrix chat rooms.</p>
<p>I&rsquo;ve spent this time porting <a href="https://whatip.gabmus.org">What IP</a>, <a href="https://hydrapaper.gabmus.org">HydraPaper</a> and <a href="https://giara.gabmus.org">Giara</a> to Gtk4+libadwaita.</p>
<p><img src="/images/post_pics/Gtk4_LibAdwaita_and_the_new_Feeds/whatip_hydrapaper_giara.avif" alt="What IP, HydraPaper and Giara at their latest iteration, side by side"></p>
<p>I&rsquo;m really happy of the results, and I&rsquo;m even happier with how this new stack is shaping up.</p>
<p>The porting process for the most part has been relatively painless across the board, with only minor changes (albeit, many of them) needed for the actual porting. Along with the updated stack, I&rsquo;ve been able to introduce new features and improvements, for the most part thanks to the new widgets available in libadwaita making my life a lot easier.</p>
<p>What IP being the simpler of the bunch didn&rsquo;t really change much from its original iteration, except for some code cleanups. But hey, the theme is different and it looks amazing!</p>
<p>HydraPaper also stayed pretty much the same, with the exception of a new wallpaper folder selector (that you can see in the image above) in the form of a brand new widget, the mighty Flap! It&rsquo;s a sidebar that can open above other widgets, mostly a glorified overlay, but with extra bells and whistles. On a regular monitor you can open it with the usual button on the headerbar, the same that opened a Popover before, but on a touchscreen you can swipe to open it! Plus, compared to the previous Popover implementation, the new Flap is part of the main window, meaning it can scale to fill the entire vertical space of the window, making this selector/filter much easier to interact with thanks to the increased size.</p>
<p>Giara being one of the more complex apps, received a lot more work and attention. First off, the post views have been re-implemented using ListView, a brand new (still somewhat janky) widget in Gtk4 that allows for better optimized lists, where rows get recycled instead of adding up, with performance quickly grinding to a halt. This change required a lot of work, since my usual terrible implementation of ListBox is 100% incompatible with the way ListView works. But with a lot of help and elbow grease, I managed to land it, making the whole experience of mindlessly scrolling through reddit a little less frustrating (hopefully). Also, thanks to another awesome widget, AdwCarousel, I was able to add support for image galleries. And they support touch gestures, too! Along with these changes, I also added an internal fullscreen image viewer, very much inspired by the one in telegram-desktop.</p>
<hr>
<p>But let&rsquo;s jump to the actual reason why I&rsquo;m writing this post, <em>The new Feeds</em>.</p>
<p><img src="/images/post_pics/Gtk4_LibAdwaita_and_the_new_Feeds/feeds.avif" alt="What the new Feeds will look like"></p>
<p>You see, I wanted to start porting Feeds to Gtk4 sooner than it ended up happening. Unfortunately, I wasn&rsquo;t able to up until recently. The reason being WebKit. WebKit support for Gtk4 has been broken for a while, and just recently (not all that recently, tho) it got to a point where it was usable enough for me to start the port. At the time of writing this, you still need to enable Gtk4 support in WebKit by using custom build flags, making development possible by building WebKit myself (which, by the way, takes <em>a lot</em> of time), but at the same time making distribution unfeasible. Flatpak being the main way I distribute my apps, it could work in theory, but having a 1GB+ bundle for a simple news reader isn&rsquo;t really a great user experience, so for now I&rsquo;m holding off this release, waiting for it to become available in the GNOME runtime.</p>
<p>But let&rsquo;s move on to what&rsquo;s actually new in Feeds.</p>
<p>First of all, from a user facing perspective, The whole look of the app is quite different. Apart from the new Adwaita theme in libadwaita, the article list uses the new <code>navigation-sidebar</code> style class, with cool rounded corners for the rows and no separators between them, but still providing enough visual separation. Label sizes in the rows have also been tweaked slightly.</p>
<p>Oh, and of course, I added article pictures! The code is based on the custom picture widget I made for Giara, and let me tell you: they make any article that more interesting!</p>
<p>As for visual changes, another big one is the new filter view, again based on the Flap widget.</p>
<p><img src="/images/post_pics/Gtk4_LibAdwaita_and_the_new_Feeds/feeds_filters.avif" alt=""></p>
<p>It&rsquo;s a similar story to HydraPaper, the old Popover implementation was hard to use and unintuitive. A sidebar is a much more common pattern, plus the added vertical size makes it easier to find the feed you wanna look at.</p>
<p>As for the articles list, you may think ListView would be perfect here as well, and that&rsquo;s what I thought as well. Unfortunately due to some technical issues with how ListView row selection and activation work at this current time, while it does work, it&rsquo;s not really all that convenient. I added the ListView to Feeds, and you can enable it in the preferences, in the advanced section, but I kept the ListBox around as a default, and I also changed its API so that it&rsquo;s the same as the ListView implementation. You can switch between one or the other and try them out for yourself, hopefully at some point I can get rid of the ListBox and have the ListView be the default.</p>
<p>With all the nice changes and cool things this port brought with it, I decided to tackle one of the most annoying issues I&rsquo;ve had with Feeds: the actual feed parsing.</p>
<p>Up until recently I&rsquo;ve been using the very popular <a href="https://github.com/kurtmckee/feedparser/">feedparser</a> python library, but I&rsquo;ve always been dissatisfied with both its performance and its weird quirks. I came to the conclusion that it&rsquo;s not the kind of library I was looking for.</p>
<p>I tried searching for alternatives, and I broadened my search to any language, but unfortunately I couldn&rsquo;t find something I really liked.</p>
<p>So I decided to write my own! And for the best performance I wrote it in&hellip; C++! Maybe some of you were expecting Rust? Maybe one day, but I don&rsquo;t really know Rust right now 😕</p>
<p>This new library is called <a href="https://gitlab.com/gabmus/syndication-domination">Sydndication Domination</a> or syndom for short (it&rsquo;s a bit cheesy, I know). Syndom is tailor made for Feeds, but of course it can be used for any application.</p>
<p>It&rsquo;s based around an awesome XML parser for C++ called <a href="https://pugixml.org/">pugixml</a>, if you ever need to parse XML I suggest you take a look at it. Syndom is able to parse an RSS or Atom file and extract all the useful information a news reader would need, and most importantly, <em>it</em> does the heavy lifting of trying all the different places a certain information can be found in. It can also parse Opml files for importing feeds, as well as Html files for extracting other useful information from, say a blog post, like the featured image, the feed URL, the title or the description.</p>
<p>The whole thing comes together inside Feeds with python bindings, created using another library called <a href="https://github.com/pybind/pybind11/">pybind11</a>.</p>
<p>Honestly this has probably been the biggest change to ever come to Feeds. The performance difference is enormous, it&rsquo;s like night and day, and I couldn&rsquo;t be happier about it.</p>
<p>So that&rsquo;s all I wanted you to know, hopefully I can release Feeds sooner rather than later as I really, <em>really</em> want people to use this new version rather than the crusty old one.</p>
<p>If you want to try it for yourself <a href="https://cloud.disroot.org/s/2Dj94NxWn6HRAZC">you can download a recent snapshot I made manually from this link</a>. Please do let me know what you think in the comments or <a href="https://matrix.to/#/#org.gabmus:matrix.org">in the matrix room</a> if you prefer. And feel free to report any bugs you encounter along the way!</p>
]]></description>
      
        <media:thumbnail url="https://gabmus.org/images/post_pics/Gtk4_LibAdwaita_and_the_new_Feeds/feeds.avif" />
      
    </item>
    
    
    
    <item>
      <title>Switching to Cactus Comments</title>
      <link>https://gabmus.org/posts/switching_to_cactus_comments/</link>
      <pubDate>Wed, 16 Jun 2021 10:24:30 +0200</pubDate>
      
      <guid>https://gabmus.org/posts/switching_to_cactus_comments/</guid>
      <description><![CDATA[<p>I&rsquo;ve been using Commento for a while for comments (without much adoption to be honest), and while I really love the project, and the simplicity of its approach, there&rsquo;s always been one unavoidable thing I never really liked too much: if you want to comment, you have to register to my commento instance.</p>
<p>That&rsquo;s less than ideal, considering that if I did have lots of people who wanted to comment, that&rsquo;d have been quite a strain on my humble home server, where my commento instance is hosted.</p>
<p>Today I found <a href="https://karmanyaah.malhotra.cc/tech/2021/06/website-things/">this post</a> on Mastodon (feel free to <a href="https://linuxrocks.online/@gabmus">follow me there</a> by the way) by Karmanyaah Malhotra about how he reworked his website recently. I always enjoy reading about other people&rsquo;s websites, so I quickly read through it, just to find out about this comment system called <a href="https://cactus.chat">Cactus Comments</a>.</p>
<p>Cactus Comments uses <a href="https://matrix.org/">Matrix</a> to create a federated comment section on your website. If you have a Matrix account on any instance, you can comment on Cactus! Heck you can even use a full-fledged Matrix client to do so! That&rsquo;s just great.</p>
<p>It&rsquo;s free to use and self hostable as well, so there&rsquo;s really not much of an excuse not to try it out. Plus if you already have a Matrix account you&rsquo;re already halfway there.</p>
<p>I integrated it in my <a href="https://gitlab.com/gabmus/hugo-ficurinia">Ficurinia Hugo theme</a> without much effort, so there&rsquo;s another bonus point.</p>
<p>And that&rsquo;s it, I&rsquo;m really happy about this new comment system, it&rsquo;s one less service running on my home server and due to its decentralized and federated nature it will hopefully encourage more people to leave interesting comments and get a conversation going!</p>
]]></description>
      
        <media:thumbnail url="https://gabmus.org/images/post_pics/Switching_to_Cactus_Comments/cactus.avif" />
      
    </item>
    
    
    
    <item>
      <title>Raspberry Pi Pico pinout in your terminal</title>
      <link>https://gabmus.org/posts/raspberry_pi_pico_pinout_in_your_terminal/</link>
      <pubDate>Tue, 09 Mar 2021 13:14:27 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/raspberry_pi_pico_pinout_in_your_terminal/</guid>
      <description><![CDATA[<p>I&rsquo;m playing around with the new <a href="https://www.raspberrypi.org/products/raspberry-pi-pico">Raspberry Pi Pico</a>, and I quickly realized that I&rsquo;d really love to have a quick pinout reference in my terminal.</p>
<p>I thought of making a man page, but I would have lost the color (I don&rsquo;t know how to use arbitrary colors in groff), so I just created a simple text file with ascii escape sequences for colors and called it a day. It works, and it&rsquo;s pretty!</p>
<p>I&rsquo;m uploading it here for your convenience (and mine as well), you should be able to grab it from your *NIX box by just typing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>curl https://gabmus.org/pico_pinout
</span></span></code></pre></div><p>If you want to save it in a file, just add <code>&gt; some_file_name</code> to the above.</p>
]]></description>
      
        <media:thumbnail url="https://gabmus.org/images/post_pics/Raspberry_Pi_Pico_pinout_in_your_terminal/screenshot.avif" />
      
    </item>
    
    
    
    <item>
      <title>Making a D-Bus Daemon with Python and Flatpak</title>
      <link>https://gabmus.org/posts/making-a-dbus-daemon/</link>
      <pubDate>Sat, 02 Jan 2021 09:42:43 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/making-a-dbus-daemon/</guid>
      <description><![CDATA[<p><em>I learned most of this stuff from <a href="https://larry-price.com/blog/categories/dbus/">this awesome series of articles by Larry Price</a>. Give it a read, it&rsquo;s worth it.</em></p>
<h2 id="why">Why</h2>
<p>D-Bus daemons are quite useful tools. What would you need them for? Well, I decided to add one to <a href="https://hydrapaper.gabmus.org">HydraPaper</a> to do two jobs:</p>
<ol>
<li>change the wallpaper periodically</li>
<li>listen for monitor configuration changes and adapt to it, so that when you plug or unplug a monitor, the wallpaper doesn&rsquo;t get all messed up</li>
</ol>
<p>There could be other reasons, like provide an always active &ldquo;server&rdquo; component to your application, or if you need to send notifications to the user even when your main app isn&rsquo;t running.</p>
<p>Of course possibilities are limitless, it&rsquo;s just another way to create software. <strong>Using D-Bus to create this daemon allows your graphical application to communicate with the main application in an easy and predictable way</strong>.</p>
<p>If you want to read more about D-Bus, I suggest you give a look at <a href="https://venam.nixers.net/blog/unix/2020/07/06/dbus-polkit.html">this nicely written article by Patrick Louis</a>.</p>
<h2 id="how">How</h2>
<p>Now what languages are we gonna use? Ha! Trick question! If you know me (or can read the title) you know we&rsquo;ll be using Python. Besides I&rsquo;m pretty sure you can use whatever language you prefer, but today that&rsquo;s what we&rsquo;re gonna use.</p>
<p>Before we begin, it&rsquo;s good practice to choose a unique id for our daemon using the <a href="https://en.wikipedia.org/wiki/Reverse_domain_name_notation">reverse domain name notation</a>. For the HydraPaper Daemon I chose <code>org.gabmus.hydrapaper.Daemon</code>. It&rsquo;s important that your name is unique, so it&rsquo;s a good practice to name it something like <code>ext.yourPersonalHandle.yourApplicationName.Daemon</code>. Another good idea is if you have a registered domain like I do for <code>gabmus.org</code>, to just use that, followed by the name of your app and some other indication that this service will be a daemon. <a href="https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names">You can read more about valid names here</a>.</p>
<p>Once you have decided this id, just put it in a constant, along with the same but separated by slashes as if it was a path, like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>UID         <span style="color:#ff79c6">=</span>  <span style="color:#f1fa8c">&#39;org.gabmus.myapp.Daemon&#39;</span>
</span></span><span style="display:flex;"><span>UID_AS_PATH <span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#39;/org/gabmus/myapp/Daemon&#39;</span>  <span style="color:#6272a4"># notice the leading slash</span>
</span></span></code></pre></div><p>Let&rsquo;s now start with the basics: we need a class extending <code>dbus.service.Object</code>. This will be our <strong>D-Bus Object</strong>, and some of the methods inside of it will be exposed as <strong>D-Bus methods</strong>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff79c6">import</span> dbus
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">import</span> dbus.service  <span style="color:#6272a4"># yes, you need to import this as well</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>UID         <span style="color:#ff79c6">=</span>  <span style="color:#f1fa8c">&#39;org.gabmus.hydrapaper.Daemon&#39;</span>
</span></span><span style="display:flex;"><span>UID_AS_PATH <span style="color:#ff79c6">=</span> <span style="color:#f1fa8c">&#39;/org/gabmus/hydrapaper/Daemon&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">class</span> <span style="color:#50fa7b">MyappDaemon</span>(dbus<span style="color:#ff79c6">.</span>service<span style="color:#ff79c6">.</span>Object):
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> __init__(self, bus_name):
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">super</span>()<span style="color:#ff79c6">.</span>__init__(
</span></span><span style="display:flex;"><span>            bus_name, UID_AS_PATH
</span></span><span style="display:flex;"><span>        )
</span></span></code></pre></div><p>Now before moving on, I decided to include the final part here: bootstrapping our main loop and all of the things we need to get this bad boy running, so that you can actually try things out and see what&rsquo;s going on instead of just blindly copy-pasting.</p>
<p>You&rsquo;ll need these additional imports:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff79c6">from</span> dbus.mainloop.glib <span style="color:#ff79c6">import</span> DBusGMainLoop
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">from</span> gi.repository <span style="color:#ff79c6">import</span> GLib
</span></span></code></pre></div><p>Let&rsquo;s move <strong>to the bottom of the file</strong> and create a main function, just to do things cleanly:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff79c6">def</span> <span style="color:#50fa7b">main</span>():
</span></span><span style="display:flex;"><span>    DBusGMainLoop(set_as_default<span style="color:#ff79c6">=</span><span style="color:#ff79c6">True</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">try</span>:
</span></span><span style="display:flex;"><span>        bus_name <span style="color:#ff79c6">=</span> dbus<span style="color:#ff79c6">.</span>service<span style="color:#ff79c6">.</span>BusName(
</span></span><span style="display:flex;"><span>            UID, bus<span style="color:#ff79c6">=</span>dbus<span style="color:#ff79c6">.</span>SessionBus(), do_not_queue<span style="color:#ff79c6">=</span><span style="color:#ff79c6">True</span>
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">except</span> dbus<span style="color:#ff79c6">.</span>exceptions<span style="color:#ff79c6">.</span>NameExistsException:
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">print</span>(<span style="color:#f1fa8c">f</span><span style="color:#f1fa8c">&#39;Service with id </span><span style="color:#f1fa8c">{</span>UID<span style="color:#f1fa8c">}</span><span style="color:#f1fa8c"> is already running&#39;</span>)
</span></span><span style="display:flex;"><span>        exit(<span style="color:#bd93f9">1</span>)
</span></span><span style="display:flex;"><span>    loop <span style="color:#ff79c6">=</span> GLib<span style="color:#ff79c6">.</span>MainLoop()
</span></span><span style="display:flex;"><span>    daemon <span style="color:#ff79c6">=</span> MyappDaemon(bus_name)
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">try</span>:
</span></span><span style="display:flex;"><span>        loop<span style="color:#ff79c6">.</span>run()
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">except</span> KeyboardInterrupt:
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">print</span>(<span style="color:#f1fa8c">&#39;KeyboardInterrupt received&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">except</span> Exception <span style="color:#ff79c6">as</span> e:
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">print</span>(<span style="color:#f1fa8c">&#39;Unhandled exception: `</span><span style="color:#f1fa8c">{}</span><span style="color:#f1fa8c">`&#39;</span><span style="color:#ff79c6">.</span>format(<span style="color:#8be9fd;font-style:italic">str</span>(e)))
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">finally</span>:
</span></span><span style="display:flex;"><span>        loop<span style="color:#ff79c6">.</span>quit()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">if</span> __name__ <span style="color:#ff79c6">==</span> <span style="color:#f1fa8c">&#39;__main__&#39;</span>:
</span></span><span style="display:flex;"><span>    main()
</span></span></code></pre></div><p>Alright, let&rsquo;s resume from where we left.</p>
<p>We have a D-Bus object, and the thing can run doing nothing now, awesome.</p>
<p>Let&rsquo;s give this object a method we can invoke from outside.</p>
<p>To do this we&rsquo;ll create a method in our class and decorate it with <code>@dbus.service.method</code>. This decorator takes some parameters:</p>
<ul>
<li><code>dbus_interface</code>: the interface we want this method to be attached to (interfaces are another nesting level for D-Bus, honestly I think they&rsquo;re overkill for this kind of use, so we&rsquo;re just going to use our previously declared <code>UID</code> as the interface, it&rsquo;s gonna work).</li>
<li><code>in_signature</code>: what kind of parameters our method requires</li>
<li><code>out_signature</code>: what kind of output our method returns</li>
</ul>
<p>For the signatures, you can read more about data types you can use <a href="https://dbus.freedesktop.org/doc/dbus-python/tutorial.html#data-types">in this documentation page</a>.</p>
<p>Let&rsquo;s create a simple <code>hello</code> method that takes a string and returns another string.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff79c6">class</span> <span style="color:#50fa7b">MyappDaemon</span>(dbus<span style="color:#ff79c6">.</span>service<span style="color:#ff79c6">.</span>Object):
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4"># ...</span>
</span></span><span style="display:flex;"><span>    @dbus.service.method(
</span></span><span style="display:flex;"><span>        dbus_interface<span style="color:#ff79c6">=</span>UID,
</span></span><span style="display:flex;"><span>        in_signature<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;s&#39;</span>, out_signature<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;s&#39;</span>
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> <span style="color:#50fa7b">hello</span>(self, your_name: <span style="color:#8be9fd;font-style:italic">str</span>) <span style="color:#ff79c6">-&gt;</span> <span style="color:#8be9fd;font-style:italic">str</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">return</span> <span style="color:#f1fa8c">f</span><span style="color:#f1fa8c">&#39;Hi there, </span><span style="color:#f1fa8c">{</span>your_name<span style="color:#f1fa8c">}</span><span style="color:#f1fa8c">!&#39;</span>
</span></span></code></pre></div><p>That&rsquo;s it! Easy, wasn&rsquo;t it?</p>
<p>Now if like me you want to have some action to run every x seconds or something, you can just use the standard threads and sleep model of Python, not much else you need as far as D-Bus goes.</p>
<p>Now, let&rsquo;s see what we need to do to package this new Daemon in a Flatpak and have it being recognized by the system as proper and activatable D-Bus service.</p>
<h2 id="plumbing">Plumbing</h2>
<p>First off, we&rsquo;ll be using meson, so I assume you already are using it for your base application.</p>
<p>Assuming we have our daemon inside a sub-folder called <code>myapp_daemon</code>. For packages reasons I suggest you call the actual daemon Python file something like <code>myappd.in.py</code>. Notice the trailing <code>d</code>, it means daemon. Another thing: <strong>make sure to give this file executable permissions!</strong> (<code>chmod +x myapp_daemon/myappd.in.py</code>)</p>
<p>At the beginning of <code>myappd.in.py</code> we&rsquo;re going to insert our shebang like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6272a4">#!@PYTHON@</span>
</span></span></code></pre></div><p>In our main <code>meson.build</code> file, the one in the root of the project, let&rsquo;s put the following somewhere you feel is appropriate (skip anything that is already there of course):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-meson" data-lang="meson"><span style="display:flex;"><span>prefix <span style="color:#ff79c6">=</span> <span style="color:#8be9fd;font-style:italic">get_option</span>(<span style="color:#f1fa8c">&#39;prefix&#39;</span>)
</span></span><span style="display:flex;"><span>datadir <span style="color:#ff79c6">=</span> <span style="color:#8be9fd;font-style:italic">get_option</span>(<span style="color:#f1fa8c">&#39;datadir&#39;</span>)
</span></span><span style="display:flex;"><span>libexecdir <span style="color:#ff79c6">=</span> <span style="color:#8be9fd;font-style:italic">join_paths</span>(prefix, <span style="color:#8be9fd;font-style:italic">get_option</span>(<span style="color:#f1fa8c">&#39;libexecdir&#39;</span>))
</span></span><span style="display:flex;"><span>etcdir <span style="color:#ff79c6">=</span> <span style="color:#8be9fd;font-style:italic">get_option</span>(<span style="color:#f1fa8c">&#39;sysconfdir&#39;</span>)
</span></span><span style="display:flex;"><span>dbus_service_dir <span style="color:#ff79c6">=</span> <span style="color:#8be9fd;font-style:italic">dependency</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#39;dbus-1&#39;</span>
</span></span><span style="display:flex;"><span>).get_pkgconfig_variable(
</span></span><span style="display:flex;"><span>    <span style="color:#f1fa8c">&#39;session_bus_services_dir&#39;</span>,
</span></span><span style="display:flex;"><span>    define_variable: [<span style="color:#f1fa8c">&#39;datadir&#39;</span>, <span style="color:#8be9fd;font-style:italic">join_paths</span>(prefix, datadir)]
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>python <span style="color:#ff79c6">=</span> import(<span style="color:#f1fa8c">&#39;python&#39;</span>)
</span></span><span style="display:flex;"><span>py_installation <span style="color:#ff79c6">=</span> python.find_installation(<span style="color:#f1fa8c">&#39;python3&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">if</span> <span style="color:#ff79c6">not</span> py_installation.found()
</span></span><span style="display:flex;"><span>    <span style="color:#8be9fd;font-style:italic">error</span>(<span style="color:#f1fa8c">&#39;No valid python3 binary found&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">endif</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">subdir</span>(<span style="color:#f1fa8c">&#39;myapp_daemon&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">subdir</span>(<span style="color:#f1fa8c">&#39;data&#39;</span>)
</span></span></code></pre></div><p>Now, let&rsquo;s create <code>myapp_daemon/meson.build</code>, and put this inside:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-meson" data-lang="meson"><span style="display:flex;"><span>daemon_conf <span style="color:#ff79c6">=</span> <span style="color:#8be9fd;font-style:italic">configuration_data</span>()
</span></span><span style="display:flex;"><span>daemon_conf.set(<span style="color:#f1fa8c">&#39;PYTHON&#39;</span>, py_installation.path())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">configure_file</span>(
</span></span><span style="display:flex;"><span>    input: meson.project_name() <span style="color:#ff79c6">+</span> <span style="color:#f1fa8c">&#39;d.in.py&#39;</span>,
</span></span><span style="display:flex;"><span>    output: meson.project_name() <span style="color:#ff79c6">+</span> <span style="color:#f1fa8c">&#39;d&#39;</span>,
</span></span><span style="display:flex;"><span>    install: <span style="color:#ff79c6">true</span>,
</span></span><span style="display:flex;"><span>    install_dir: libexecdir,
</span></span><span style="display:flex;"><span>    configuration: daemon_conf
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>That&rsquo;s it for the daemon itself, but we still need some other files to let Flatpak (or your host operating system for that matter if you install this with a regular package manager) recognize this as an activatable service.</p>
<p><em>Note: an activatable service means a service that will be started automagically as soon as an application tries to connect to it.</em></p>
<p>For this, I like to work inside of a new folder called <code>data</code> inside the project root.</p>
<p>Inside <code>data</code> let&rsquo;s create:</p>
<p><code>org.gabmus.myapp.Daemon.desktop.in</code> containing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-desktop" data-lang="desktop"><span style="display:flex;"><span><span style="color:#ff79c6">[Desktop Entry]</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">Name</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">org.gabmus.myapp.Daemon</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">Comment</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">Daemon for myapp</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">Icon</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">org.gabmus.myapp.Daemon</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">Exec</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">@libexecdir@/myappd</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">X-GNOME-Autostart-Delay</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">10</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">StartupNotify</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">false</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">NoDisplay</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">true</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">Type</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">Application</span>
</span></span></code></pre></div><p><code>org.gabmus.myapp.Daemon.service.in</code> containing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-desktop" data-lang="desktop"><span style="display:flex;"><span><span style="color:#ff79c6">[D-BUS Service]</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">Name</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">org.gabmus.myapp.Daemon</span>
</span></span><span style="display:flex;"><span><span style="color:#50fa7b">Exec</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">@libexecdir@/myappd</span>
</span></span></code></pre></div><p><code>meson.build</code> (if it exists already, just add to it) containing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-meson" data-lang="meson"><span style="display:flex;"><span>data_conf <span style="color:#ff79c6">=</span> <span style="color:#8be9fd;font-style:italic">configuration_data</span>()
</span></span><span style="display:flex;"><span>data_conf.set(<span style="color:#f1fa8c">&#39;libexecdir&#39;</span>, libexecdir)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">configure_file</span>(
</span></span><span style="display:flex;"><span>    input: <span style="color:#f1fa8c">&#39;org.gabmus.myapp.Daemon.desktop.in&#39;</span>,
</span></span><span style="display:flex;"><span>    output: <span style="color:#f1fa8c">&#39;org.gabmus.myapp.Daemon.desktop&#39;</span>,
</span></span><span style="display:flex;"><span>    configuration: data_conf,
</span></span><span style="display:flex;"><span>    install: <span style="color:#ff79c6">true</span>,
</span></span><span style="display:flex;"><span>    install_dir: <span style="color:#8be9fd;font-style:italic">join_paths</span>(etcdir, <span style="color:#f1fa8c">&#39;xdg&#39;</span>, <span style="color:#f1fa8c">&#39;autostart&#39;</span>)
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">configure_file</span>(
</span></span><span style="display:flex;"><span>    input: <span style="color:#f1fa8c">&#39;org.gabmus.myapp.Daemon.service.in&#39;</span>,
</span></span><span style="display:flex;"><span>    output: <span style="color:#f1fa8c">&#39;org.gabmus.myapp.Daemon.service&#39;</span>,
</span></span><span style="display:flex;"><span>    configuration: data_conf,
</span></span><span style="display:flex;"><span>    install: <span style="color:#ff79c6">true</span>,
</span></span><span style="display:flex;"><span>    install_dir: dbus_service_dir
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>Finally, we need to tell Flatpak that the <code>org.gabmus.myapp.Daemon</code> name is owned by our application. To do this let&rsquo;s add the following to our Flatpak manifest, inside the <code>finish-args</code> array:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#f1fa8c">&#34;--own-name=org.gabmus.myapp&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#f1fa8c">&#34;--own-name=org.gabmus.myapp.Daemon&#34;</span>
</span></span></code></pre></div><p>Great! Now in theory everything should be in place, of course let me know if anything is broken or doesn&rsquo;t work, the comment section is there to be used!</p>
<p>For the final section of this small tutorial, let&rsquo;s see how to call the method we created.</p>
<h2 id="summon-the-daemon">Summon the daemon</h2>
<p>For this last section, I&rsquo;ll just leave you with some very brief example code in Python, hopefully it&rsquo;s easy enough to follow:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff79c6">import</span> dbus
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">def</span> <span style="color:#50fa7b">summon_hello</span>(name):
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">try</span>:
</span></span><span style="display:flex;"><span>        bus <span style="color:#ff79c6">=</span> dbus<span style="color:#ff79c6">.</span>SessionBus()
</span></span><span style="display:flex;"><span>        obj <span style="color:#ff79c6">=</span> bus<span style="color:#ff79c6">.</span>get_object(
</span></span><span style="display:flex;"><span>            <span style="color:#f1fa8c">&#39;org.gabmus.myapp.Daemon&#39;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f1fa8c">&#39;/org/gabmus/myapp/Daemon&#39;</span>
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>        interface <span style="color:#ff79c6">=</span> dbus<span style="color:#ff79c6">.</span>Interface(
</span></span><span style="display:flex;"><span>            obj, dbus_interface<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#39;org.gabmus.myapp.Daemon&#39;</span>
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">return</span> interface<span style="color:#ff79c6">.</span>hello(name)
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">except</span> dbus<span style="color:#ff79c6">.</span>exceptions<span style="color:#ff79c6">.</span>DBusException:
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">print</span>(<span style="color:#f1fa8c">&#39;Failed to communicate with `org.gabmus.myapp.Daemon`!&#39;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">return</span> <span style="color:#f1fa8c">&#39;&#39;</span>
</span></span></code></pre></div><hr>
<p>Hope you can find this tutorial useful. Mind you, I&rsquo;m not an expert in this matter, I just did this and failed to find a single tutorial summarizing all of what I needed, so I decided to make one myself.</p>
<p>If you find that anything doesn&rsquo;t work as expected (maybe I missed a step or two?) please let me know in the comments, or contact me directly by using one of the contacts you can find <a href="/pages/about">in the About page</a>.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Giara is a reddit app for Linux</title>
      <link>https://gabmus.org/posts/giara_is_a_reddit_app_for_linux/</link>
      <pubDate>Fri, 30 Oct 2020 09:55:23 +0100</pubDate>
      
      <guid>https://gabmus.org/posts/giara_is_a_reddit_app_for_linux/</guid>
      <description><![CDATA[<p>I am a quite avid reddit user. Like it or not, reddit is a very central place where communities for stuff that I care about tend to aggregate.</p>
<p>For the longest time, I have felt the need for a reddit app on my Linux desktop, and I waited and kept waiting for one that would finally allow me to break free of using the web version.</p>
<p>Some honorable mentions are due to projects like the now seemingly abandoned <a href="https://github.com/samdroid-apps/something-for-reddit">Something for reddit</a> and the terminal app <a href="https://gitlab.com/ajak/tuir/">Tuir</a>.</p>
<p>Unfortunately they were not what I was looking for.</p>
<p>At some point I just decided to start scratching this itch of mine, and so my quest began.</p>
<p><img src="/images/post_pics/Giara_is_a_reddit_app_for_Linux/screenshot.avif" alt="Screenshot of Giara"></p>
<p>And this is how Giara got to be!</p>
<p>At this current stage Giara is already usable, with most of the main features you&rsquo;d expect from a reddit app to work.</p>
<p><img src="/images/post_pics/Giara_is_a_reddit_app_for_Linux/screenshot2.avif" alt="Screenshot of a comment tree"></p>
<p>Many people, including some familiar faces, stepped up and helped me in the development process, and I have already received some contributions.</p>
<p>It&rsquo;s not 100% done, tho. It still needs to reach feature parity with the reddit web client, as features like awards, polls, galleries and private messages are still to be implemented. I&rsquo;m also working on improving performance, as the app tends to be slower than I would prefer sometimes. All of these missing features and improvements are up next in my to-do list.</p>
<p>The app has also been recently moved to the <a href="https://gitlab.gnome.org/World">World group in GNOME&rsquo;s GitLab</a>, and I&rsquo;m incredibly happy for it! The next step now is to add support for the <a href="https://l10n.gnome.org/"><em>Damned Lies</em></a> translation platform, so that it can be even easier to contribute new translations to the project.</p>
<p>As always any help and contribution is highly appreciated!</p>
<p>You can find the repo on <a href="https://gitlab.gnome.org/World/giara">GNOME&rsquo;s GitLab</a> and you can download the latest stable version of Giara from <a href="https://flathub.org/apps/details/org.gabmus.giara">Flathub</a>. You may also want to take a look at <a href="https://giara.gabmus.org">Giara&rsquo;s website</a>.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Map Mouse Back and Forward Buttons in Your GTK App</title>
      <link>https://gabmus.org/posts/map_mouse_back_and_forward_buttons_in_your_gtk_app/</link>
      <pubDate>Thu, 08 Oct 2020 11:39:57 +0200</pubDate>
      
      <guid>https://gabmus.org/posts/map_mouse_back_and_forward_buttons_in_your_gtk_app/</guid>
      <description><![CDATA[<p>The back and forward buttons on your mouse are incredibly useful, at least for me. I use them all the time!</p>
<p>I couldn&rsquo;t find a proper way to map it on my app until I asked on the GTK Matrix chatroom, there <a href="https://github.com/baedert">baedert</a> sent me a couple of links to how he did it in his app corebird.</p>
<p>I translated the code to Python and decided to make a blog post to archive the procedure.</p>
<p>First, here&rsquo;s a function that creates the &ldquo;gesture&rdquo; and assigns it to a widget and a callback function</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff79c6">def</span> <span style="color:#50fa7b">add_mouse_button_accel</span>(widget, function):
</span></span><span style="display:flex;"><span>    gesture <span style="color:#ff79c6">=</span> Gtk<span style="color:#ff79c6">.</span>GestureMultiPress<span style="color:#ff79c6">.</span>new(widget)
</span></span><span style="display:flex;"><span>    gesture<span style="color:#ff79c6">.</span>set_button(<span style="color:#bd93f9">0</span>)
</span></span><span style="display:flex;"><span>    gesture<span style="color:#ff79c6">.</span>set_propagation_phase(Gtk<span style="color:#ff79c6">.</span>PropagationPhase<span style="color:#ff79c6">.</span>CAPTURE)
</span></span><span style="display:flex;"><span>    gesture<span style="color:#ff79c6">.</span>connect(<span style="color:#f1fa8c">&#39;pressed&#39;</span>, function)
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">return</span> gesture
</span></span></code></pre></div><p>As you may have noticed, this function returns the created gesture. You may think there is no need for it and ideally you&rsquo;d be right.</p>
<p>There&rsquo;s just a small issue: due to something that I could only identify as a bug in GTK, you need to keep the <code>Gtk.GestureMultiPress</code> object around, otherwise it gets destroyed and the button clicks are no longer registered.</p>
<p>Once the gesture is returned, you can assign it to something that stays in memory (or you can modify this function and do something like <code>widget.mouse_accel_gesture = gesture</code> and be done with it).</p>
<p>As for the function associated with the mouse click, here&rsquo;s a template you can use:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff79c6">def</span> <span style="color:#50fa7b">on_mouse_event</span>(gesture, n_press, x, y):
</span></span><span style="display:flex;"><span>    btn <span style="color:#ff79c6">=</span> gesture<span style="color:#ff79c6">.</span>get_current_button()
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">if</span> btn <span style="color:#ff79c6">==</span> <span style="color:#bd93f9">8</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4"># Handle the Back button here</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">elif</span> btn <span style="color:#ff79c6">==</span> <span style="color:#bd93f9">9</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4"># Handle the Forward button here</span>
</span></span></code></pre></div><p>There you go, hope it helps. If you have any trouble feel free to leave a comment or contact me (contact info can be found <a href="/pages/about">in the about page</a>)</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Create an auto resizing image widget with GTK3 and Python</title>
      <link>https://gabmus.org/posts/create_an_auto-resizing_image_widget_with_gtk3_and_python/</link>
      <pubDate>Sat, 03 Oct 2020 10:23:54 +0200</pubDate>
      
      <guid>https://gabmus.org/posts/create_an_auto-resizing_image_widget_with_gtk3_and_python/</guid>
      <description><![CDATA[<p>One of the most common things you might wanna do with images, that is making them automatically resize to adapt to the available space they have, is not possible with <code>Gtk.Image</code>.</p>







<figure>
    <video autoplay loop controls style="max-width: 100%;">
        
        
        
        
    </video>
    
</figure>

<p>So after some hacking, I seem to have found a solution that works:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff79c6">from</span> gi.repository <span style="color:#ff79c6">import</span> Gtk, GdkPixbuf, Gdk
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">class</span> <span style="color:#50fa7b">PictureView</span>(Gtk<span style="color:#ff79c6">.</span>DrawingArea):
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> __init__(self, path, <span style="color:#ff79c6">*</span>args, <span style="color:#ff79c6">**</span>kwargs):
</span></span><span style="display:flex;"><span>        <span style="color:#8be9fd;font-style:italic">super</span>()<span style="color:#ff79c6">.</span>__init__(<span style="color:#ff79c6">*</span>args, <span style="color:#ff79c6">**</span>kwargs)
</span></span><span style="display:flex;"><span>        self<span style="color:#ff79c6">.</span>path <span style="color:#ff79c6">=</span> path
</span></span><span style="display:flex;"><span>        self<span style="color:#ff79c6">.</span>pixbuf <span style="color:#ff79c6">=</span> GdkPixbuf<span style="color:#ff79c6">.</span>Pixbuf<span style="color:#ff79c6">.</span>new_from_file(self<span style="color:#ff79c6">.</span>path)
</span></span><span style="display:flex;"><span>        self<span style="color:#ff79c6">.</span>img_surface <span style="color:#ff79c6">=</span> Gdk<span style="color:#ff79c6">.</span>cairo_surface_create_from_pixbuf(
</span></span><span style="display:flex;"><span>            self<span style="color:#ff79c6">.</span>pixbuf, <span style="color:#bd93f9">1</span>, <span style="color:#ff79c6">None</span>
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> <span style="color:#50fa7b">get_useful_height</span>(self):
</span></span><span style="display:flex;"><span>        aw <span style="color:#ff79c6">=</span> self<span style="color:#ff79c6">.</span>get_allocated_width()
</span></span><span style="display:flex;"><span>        pw <span style="color:#ff79c6">=</span> self<span style="color:#ff79c6">.</span>pixbuf<span style="color:#ff79c6">.</span>get_width()
</span></span><span style="display:flex;"><span>        ph <span style="color:#ff79c6">=</span> self<span style="color:#ff79c6">.</span>pixbuf<span style="color:#ff79c6">.</span>get_height()
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">return</span> aw<span style="color:#ff79c6">/</span>pw <span style="color:#ff79c6">*</span> ph
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> <span style="color:#50fa7b">get_scale_factor</span>(self):
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">return</span> self<span style="color:#ff79c6">.</span>get_allocated_width() <span style="color:#ff79c6">/</span> self<span style="color:#ff79c6">.</span>pixbuf<span style="color:#ff79c6">.</span>get_width()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> <span style="color:#50fa7b">do_draw</span>(self, context):
</span></span><span style="display:flex;"><span>        sf <span style="color:#ff79c6">=</span> self<span style="color:#ff79c6">.</span>get_scale_factor()
</span></span><span style="display:flex;"><span>        context<span style="color:#ff79c6">.</span>scale(sf, sf)
</span></span><span style="display:flex;"><span>        context<span style="color:#ff79c6">.</span>set_source_surface(self<span style="color:#ff79c6">.</span>img_surface, <span style="color:#bd93f9">0</span>, <span style="color:#bd93f9">0</span>)
</span></span><span style="display:flex;"><span>        context<span style="color:#ff79c6">.</span>paint()
</span></span><span style="display:flex;"><span>        height <span style="color:#ff79c6">=</span> self<span style="color:#ff79c6">.</span>get_useful_height(self<span style="color:#ff79c6">.</span>get_allocated_width())
</span></span><span style="display:flex;"><span>        self<span style="color:#ff79c6">.</span>set_size_request(<span style="color:#ff79c6">-</span><span style="color:#bd93f9">1</span>, height)
</span></span></code></pre></div><p>This code is actually simpler than I imagined, and that&rsquo;s a good thing. Let&rsquo;s see what&rsquo;s going on here.</p>
<p>I&rsquo;m doing all of this in a self contained class I unoriginally called <em>PictureView</em>, of course you can rename it to your liking. It takes a path to a picture as an argument.</p>
<p>I&rsquo;m subclassing <code>Gtk.DrawingArea</code> to be able to draw arbitrary stuff with cairo.</p>
<p>The first thing is creating a base <code>GdkPixbuf.Pixbuf</code> to store the original image, create a cairo surface from that image and then tell the widget what to do inside the <code>do_draw</code> method.</p>
<p>This will in turn find out the size at which it wants to be rendered.</p>
<p>For the width, I want all of it, or in other words all of the allocated width that the widget has.</p>
<p>As for the height, I simply calculate the target height (indicated as <code>allocated_height</code> in the formula below) by solving a simple proportion between the picture width and the allocated width:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>pixbuf_width / allocated_width = pixbuf_height / allocated_height
</span></span></code></pre></div><p>Once I have this useful data, I set the widget size request with the new values I found (I leave the width as -1 so that it doesn&rsquo;t change).</p>
<p>Then, I scale the whole cairo context with the <code>scale</code> method, calculating the scale factor as <code>widget_width / pixbuf_width</code>, and finally set the cairo context source to the image surface I created in the constructor.</p>
<p>Oh, and don&rsquo;t forget to call <code>cairo.paint()</code> as well.</p>
<p>All in all this is pretty straight forward, but you need to know what you&rsquo;re doing, and I certainly did not up to about a couple of hour ago.</p>
<p>Hopefully if you need to do this, you will find this blog post and avoid wasting two hours of your time trying to figure this out from sparse C code floating around the internet.</p>
<hr>
<p>As a final note, I&rsquo;m being told that in GTK4 GtkPicture is able to do this, but since I&rsquo;m still stuck with GTK3 (as you probably are as well if you&rsquo;re reading this), this is the best solution I could find.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Man pages with Markdown and Pandoc</title>
      <link>https://gabmus.org/posts/man_pages_with_markdown_and_pandoc/</link>
      <pubDate>Mon, 31 Aug 2020 19:49:06 +0200</pubDate>
      
      <guid>https://gabmus.org/posts/man_pages_with_markdown_and_pandoc/</guid>
      <description><![CDATA[<p>Man pages are awesome. They genuinely are. In any *nix system you can just get documentation on your programs or even some programming things (namely a lot of C stuff, this was very useful in University) <strong>offline</strong> by just using a tiny simple command.</p>
<p>Of course writing man pages is a different thing. They are written in <em>groff</em>. I am not a fan of it, although I know people who do enjoy using it.</p>
<h3 id="markdown--pandoc">Markdown ❤ Pandoc</h3>
<p>Of course the obvious thing to do is write your docs in <strong>Markdown</strong>, since it&rsquo;s the best simple markup language out there. Fortunately, it&rsquo;s very easy to convert Markdown to groff, and consequently into a man page.</p>
<p>And of course you can just use <a href="https://pandoc.org/">Pandoc</a> to do that. It&rsquo;s as simple and straight forward as you can imagine, it&rsquo;s just one command away:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pandoc --standalone --to man mymanpage.1.md -o mymanpage.1
</span></span></code></pre></div><p>It&rsquo;s actually not the first time I&rsquo;ve done something like this. The first was with <a href="https://gitlab.com/gabmus/glorious-man-pages">the handy manuals I wrote for the Glorious GMMK keyboard and Model O mouse</a>. Most of the fancy things you&rsquo;d wanna tweak with these peripherals are done with weird key combinations, and having the paper manuals or even worse PDF copies of those around isn&rsquo;t very practical. So of course I wrote man pages for those.</p>
<p>If you know Markdown, the syntax you have to use is of course very simple, but if you wanna do it right, you may want to know about a couple of dialect forms of Markdown:</p>
<h3 id="definition-lists">Definition lists</h3>
<p><a href="https://pandoc.org/MANUAL.html#definition-lists">Definition lists</a> are perfect for describing particular commands or arguments in your man page, and they get rendered just as you think they would in a man page. They are not part of the original Markdown spec, but Pandoc supports them no problem.</p>
<p>They&rsquo;re really easy to use, here&rsquo;s an example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#f1fa8c">`-h, --help`</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>:   Shows the help message
</span></span></code></pre></div><p>The backticks are optional but they help escaping weird characters and besides they mark a section that is effectively code.</p>
<h3 id="metadata-blocks">Metadata blocks</h3>
<p><a href="https://pandoc.org/MANUAL.html#metadata-blocks">Metadata blocks</a> are also useful to show the title of the man page. Definitely one of those small touches that you might want to have in your man page, as every proper one has them.</p>
<p>Again, the syntax is fairly straight forward. You want to have this at the beginning of your file and looking something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>% mymanual(1) Version 0.9.99 | Example manual for this blog post
</span></span></code></pre></div><h3 id="example">Example</h3>
<p>Finally I want to include an example Markdown man page I recently made for <a href="https://hydrapaper.gabmus.org">HydraPaper</a>, so that you can get a better grasp of what a full file could look like. Of course you can also find this file in HydraPaper&rsquo;s repository if you&rsquo;re interested.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>% HydraPaper(1) | General Commands Manual
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>NAME
</span></span><span style="display:flex;"><span>====
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>HydraPaper - Wallpaper manager with multi monitor support
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>SYNOPSIS
</span></span><span style="display:flex;"><span>========
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f1fa8c">`hydrapaper [-h] [-c WALLPAPER_PATH [WALLPAPER_PATH ...]] [-m WALLPAPER_MODES [WALLPAPER_MODES ...]] [-r] [-l]`</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>DESCRIPTION
</span></span><span style="display:flex;"><span>===========
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>HydraPaper is a wallpaper manager, specifically designed to work around the lack of functionality of many desktop environments to set a different wallpaper for each monitor in a multi monitor setup. It accomplishes this by scaling and merging different wallpapers into a single one and setting it as spanned.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>It currently supports various desktop environments, including GNOME, MATE, Cinnamon and Budgie. Experimental support is included for the sway window manager.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>OPTIONS
</span></span><span style="display:flex;"><span>=======
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f1fa8c">`-h, --help`</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>:   Show the help message and exit
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f1fa8c">`-c, --cli WALLPAPER_PATHS...`</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>:   Set wallpapers from the command line
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f1fa8c">`-m, --modes WALLPAPER_MODES...`</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>:   Specify the modes for the wallpapers (`zoom`, <span style="color:#f1fa8c">`center_black`</span>, <span style="color:#f1fa8c">`center_blur`</span>, <span style="color:#f1fa8c">`fit_black`</span>, <span style="color:#f1fa8c">`fit_blur`</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f1fa8c">`-r, --random`</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>:   Set wallpapers randomly
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f1fa8c">`-l, --lockscreen`</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>:   Set lockscreen wallpapers instead of desktop ones (for supported desktop environments)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>BUGS
</span></span><span style="display:flex;"><span>====
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Bugs can be reported and filed at https://gitlab.gnome.org/gabmus/hydrapaper/issues
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>If you are not using the flatpak version of HydraPaper, or if you are using an otherwise out of date or downstream version of it, please make sure that the bug you want to report hasn&#39;t been already fixed or otherwise caused by a downstream patch.
</span></span></code></pre></div>]]></description>
      
    </item>
    
    
    
    <item>
      <title>Back to Hugo and on static site generators and custom themes</title>
      <link>https://gabmus.org/posts/back-to-hugo/</link>
      <pubDate>Mon, 24 Aug 2020 09:19:02 +0200</pubDate>
      
      <guid>https://gabmus.org/posts/back-to-hugo/</guid>
      <description><![CDATA[<p>As you can see, this website is kinda different. I remade it, just because why not.</p>
<p>I was previously using Hexo with a nice theme called cactus (properly altered to match my preference), but at some point I got tired of that as well.</p>
<p>I wanted to create something simple and straight forward. This is the kind of website without too many bells and whistles. Just a blog, period.</p>
<p>The initial idea of static site generators kinda goes well with this concept, but I don&rsquo;t exactly know when or how things changed. Maybe people liked these tools so much they tried to make them more general purpose, or they just wanted to use them for different things. Doesn&rsquo;t matter, point is that these generators, once simple and easy ways to make a decent blog without the burden and subsequent psychological trauma of a LAMP stack, are now bloated and messy.</p>
<p>Well, to be fair it&rsquo;s not really an issue with the generators themselves, but with the themes. It&rsquo;s the theme that has the responsibility for how the content is meant to be laid out, what is allowed and how you should do things. Honestly calling them themes is an understatement. If the generator is the skeleton, the <em>theme</em> is muscles, flesh and nervous system. The so called generator is useless without one.</p>
<p>Making themes is one of those things that I don&rsquo;t like to do. I didn&rsquo;t like it when I was a kid playing around with Wordpress and Joomla, and I don&rsquo;t like it now. I&rsquo;d rather make a plain website instead. But that&rsquo;s not feasible for a blog because of pagination, tags, RSS feed and whatever else that doesn&rsquo;t come to mind right now.</p>
<p>But at some point I just said, well F it, let&rsquo;s do it. It&rsquo;s a thing I&rsquo;ll do once and basically forget it, and the gain I get from it is a website I actually like and that I can easily customize and change around whenever I feel like it.</p>
<p>I decided to use Hugo since it seems to be the most popular static site generator at this point in time. I&rsquo;m not new to it, and I somewhat knew my way around it (or so I thought) since I already modified another theme for <a href="https://techpills.technology">the latest iteration of the Tech Pills website</a>. After watching some YouTube tutorials and digging through <a href="https://gohugo.io/documentation/">the lackluster documentation</a> what came out was a new theme I decided to call <a href="https://gitlab.com/gabmus/hugo-ficurinia">Ficurinia</a>.</p>
<p>The name choice was pretty easy. This theme is inspired by the theme I was using before with Hexo called cactus. This being sort of a grittier version of cactus (if not a more personal vision on the same idea) I decided to call it after the Sicilian word for Indian fig aka prickly pear, <em>Ficurinia</em>.</p>
<p>I&rsquo;m happy to have made this theme. It&rsquo;s simple, no weird layouts or messy animations, no javascript, and it works on mobile without any modification or special rules. Heck it even looks pretty damn good in the links command line browser.</p>
<p>So this is my new home now. Hope you like it.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Python unittest &#43; Meson</title>
      <link>https://gabmus.org/posts/python-unittest-meson/</link>
      <pubDate>Mon, 11 May 2020 11:27:41 +0000</pubDate>
      
      <guid>https://gabmus.org/posts/python-unittest-meson/</guid>
      <description><![CDATA[<p>Alright, I started writing some unit tests for <a href="https://gabmus.gitlab.io/gnome-feeds">Feeds</a> and I wanted to integrate them into meson, so that I can just run <code>meson test</code> or <code>ninja test</code> during build and have them run automagically.</p>
<p>As an added bonus, they <em>should</em> also run when creating a flatpak package, so that I don&rsquo;t accidentally push a broken built to users.</p>
<p>Here&rsquo;s the very simplistic way I accomplished this task:</p>
<ul>
<li>have your tests in a <code>tests</code> directory in the root of your project</li>
<li>add this to your <code>meson.build</code></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-meson" data-lang="meson"><span style="display:flex;"><span>envdata <span style="color:#ff79c6">=</span> <span style="color:#8be9fd;font-style:italic">environment</span>()
</span></span><span style="display:flex;"><span>python_paths <span style="color:#ff79c6">=</span> [<span style="color:#8be9fd;font-style:italic">join_paths</span>(meson.current_build_dir(), <span style="color:#f1fa8c">&#39;..&#39;</span>)]
</span></span><span style="display:flex;"><span>envdata.append(<span style="color:#f1fa8c">&#39;PYTHONPATH&#39;</span>, python_paths)
</span></span><span style="display:flex;"><span>envdata.append(<span style="color:#f1fa8c">&#39;TESTS_BUILDDIR&#39;</span>, meson.current_build_dir())
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">test</span>(
</span></span><span style="display:flex;"><span>  <span style="color:#f1fa8c">&#39;unit-tests&#39;</span>,
</span></span><span style="display:flex;"><span>  import(<span style="color:#f1fa8c">&#39;python&#39;</span>).find_installation(<span style="color:#f1fa8c">&#39;python3&#39;</span>),
</span></span><span style="display:flex;"><span>  args: [<span style="color:#f1fa8c">&#39;-m&#39;</span>, <span style="color:#f1fa8c">&#39;unittest&#39;</span>, <span style="color:#f1fa8c">&#39;tests&#39;</span>],
</span></span><span style="display:flex;"><span>  env: envdata
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>While this works locally, I&rsquo;m still having problems with running them during the flatpak build in GitLab CI, so I&rsquo;ll try to solve that as well and update this post accordingly.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Feeds moved to World and design overhaul</title>
      <link>https://gabmus.org/posts/feeds-moved-to-world-and-design-overhaul/</link>
      <pubDate>Wed, 11 Sep 2019 12:03:41 +0000</pubDate>
      
      <guid>https://gabmus.org/posts/feeds-moved-to-world-and-design-overhaul/</guid>
      <description><![CDATA[<p>It finally happened, Feeds is now part of the <a href="https://gitlab.gnome.org/World">World</a> group! <a href="https://gitlab.gnome.org/World/gfeeds">Here&rsquo;s the new repository</a>! But don&rsquo;t worry, I&rsquo;ll keep up with the existing GitLab mirror.</p>
<p>This doesn&rsquo;t change too much in terms of development, but it surely makes the app more discoverable. It also provides <a href="https://world.pages.gitlab.gnome.org/gfeeds/">this very nice new URL for Feeds&rsquo; website</a> which is under gnome&rsquo;s domain name (that&rsquo;s pretty cool, uh?).</p>
<p>As for Feeds&rsquo; development, lately I&rsquo;ve begun a process of small cleanups here and there, and most importantly design changes.</p>
<p><img src="/images/post_pics/Feeds-moved-to-World-and-design-overhaul/feeds-next-window.avif" alt="Feeds&rsquo; new UI"></p>
<p>Thanks to the help of <a href="https://gitlab.gnome.org/bertob">Tobias Bernard</a>, <a href="https://gitlab.gnome.org/exalm">Alexander Mikhaylenko</a> and some other awesome people from the <a href="https://matrix.to/#/!nrNOrVsRZxzaDdspgs:talk.puri.sm?via=talk.puri.sm&amp;via=matrix.org&amp;via=librem.one">libhandy community</a>, I brought Feeds back in line with the <a href="https://developer.gnome.org/hig/stable/">GNOME HIG</a>. These are small changes, but surely needed ones to have a more consistent desktop experience.</p>
<p>Some of the features I had planned, detailed in some of my previous posts in this blog, still haven&rsquo;t made it, tho. To be fair, some of them require a decent amount of work, and I&rsquo;d be nice if someone wanted to help with implementing them.</p>
<p>That&rsquo;s why I&rsquo;ll try to cleanup the code more, using Glade templates where possible, splitting different classes in different files and uncluttering the logic a bit. It&rsquo;s probably one of the most boring things you could do, but if I want people to contribute, it has to happen.</p>
<p>That&rsquo;s it for now, stay tuned.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Feeds 0.8: naming change and how to deal with Flatpak</title>
      <link>https://gabmus.org/posts/feeds-0-8-naming-change-and-how-to-deal-with-flatpak/</link>
      <pubDate>Tue, 03 Sep 2019 16:34:14 +0000</pubDate>
      
      <guid>https://gabmus.org/posts/feeds-0-8-naming-change-and-how-to-deal-with-flatpak/</guid>
      <description><![CDATA[<p><a href="https://flathub.org/apps/details/org.gabmus.gfeeds">Feeds version 0.8 is coming</a>. This release have been a little overdue, since I finally decided to change the name of the app from <code>gnome-feeds</code> to just <code>gfeeds</code>. This has to do with the use of the GNOME name, and besides even GNOME apps don&rsquo;t use the <em>GNOME</em> prefix in their names.</p>
<p>Someone criticized the name <code>gfeeds</code>, because it&rsquo;s kind of dumb to have GNOME/GTK app names to start with a <em>g</em>, similarly to how dumb it is to have KDE apps start with a <em>k</em>. But here&rsquo;s the catch: the <em>g</em> in <code>gfeeds</code> stands for <em>Gabriele</em>, <em>I pulled a sneaky on you</em>.</p>
<p>Anyway, the new name should have no problems whatsoever, and besides I learned how to transition to a new appid on Flathub.</p>
<p>It&rsquo;s not as seamless as you would think, but it&rsquo;s easy enough. You&rsquo;ll have to re-submit your app as if it was a new one, and specify that it&rsquo;s just an appid change. There&rsquo;s some infrastructure in place to transition from one appid to another automagically, and it involves <a href="https://github.com/flathub/org.gabmus.gnome-feeds">a little extra json file</a> in the old repo. <a href="https://github.com/flathub/flathub/pull/1141">Here you can see my PR for the new appid</a> and the whole discussion around it. Hope it helps.</p>
<p>As for new stuff, let&rsquo;s see what we have:</p>
<ul>
<li>More stability, broken feeds are automatically removed and error messages will be shown in an upcoming version (already in <code>master</code>).</li>
<li>OPML file association. Now you can just open your OPMLs from your file manager and have them imported in Feeds.</li>
<li>If you&rsquo;re offline, feeds won&rsquo;t be refreshed. Instead, an infobar will tell you that you&rsquo;re offline. Saved articles are still accessible.</li>
<li>CPU utilization is now lower, having removed some unnecessary busy waits from the code.</li>
</ul>
<p>That&rsquo;s it for now. Again, still a lot of work to do, but little by little Feeds is coming together pretty well.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>GitLab CI/CD for Flatpak</title>
      <link>https://gabmus.org/posts/gitlab-ci-cd-for-flatpak/</link>
      <pubDate>Sat, 31 Aug 2019 12:24:16 +0000</pubDate>
      
      <guid>https://gabmus.org/posts/gitlab-ci-cd-for-flatpak/</guid>
      <description><![CDATA[<p>Thanks to <a href="https://gitlab.gnome.org/GNOME/Initiatives/wikis/DevOps-with-Flatpak">this document</a>, and a quick look at <a href="https://gitlab.gnome.org/GNOME/nautilus/tree/master">nautilus&rsquo;s git repo</a> I was able to automatically generate fresh flatpaks directly from GitLab by using the integrated CI/CD system.</p>
<p>My current setup isn&rsquo;t perfect but it manages to automate flatpak builds. This is incredibly useful, as when people open issues and I try to fix them, I can just tell them to install the lastest flatpak from the CI/CD and test the fixes.</p>
<p>Also, for power users and curious minds I imagine it could be compelling to be able to install the latest development snapshot without jumping through many hoops.</p>
<p>Anyway, let&rsquo;s get down to business, here&rsquo;s the <code>.gitlab-ci.yml</code> file to let the magic happen:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#ff79c6">include</span>: <span style="color:#f1fa8c">&#39;https://gitlab.gnome.org/GNOME/citemplates/raw/master/flatpak/flatpak_ci_initiative.yml&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">BUNDLE</span>: <span style="color:#f1fa8c">&#34;gfeeds.flatpak&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">flatpak</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">image</span>: <span style="color:#f1fa8c">&#39;registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:3.32&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">MANIFEST_PATH</span>: <span style="color:#f1fa8c">&#34;dist/flatpak/org.gabmus.gfeeds.json&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">MESON_ARGS</span>: <span style="color:#f1fa8c">&#34;-Dprofile=Devel&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">FLATPAK_MODULE</span>: <span style="color:#f1fa8c">&#34;gfeeds&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">RUNTIME_REPO</span>: <span style="color:#f1fa8c">&#34;https://flathub.org/repo/flathub.flatpakrepo&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">APP_ID</span>: <span style="color:#f1fa8c">&#34;org.gabmus.gfeeds&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">extends</span>: .flatpak
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">review</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">stage</span>: deploy
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">dependencies</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#f1fa8c">&#39;flatpak&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">extends</span>: <span style="color:#f1fa8c">&#39;.review&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">stop_review</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">stage</span>: deploy
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">extends</span>: <span style="color:#f1fa8c">&#39;.stop_review&#39;</span>
</span></span></code></pre></div><p>Hope you find a good use for it.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Early adopter experience with the new Radeon RX 5700XT on Arch Linux</title>
      <link>https://gabmus.org/posts/early-adopter-experience-with-the-new-radeon-rx-5700-xt-on-arch-linux/</link>
      <pubDate>Sat, 31 Aug 2019 09:04:26 +0000</pubDate>
      
      <guid>https://gabmus.org/posts/early-adopter-experience-with-the-new-radeon-rx-5700-xt-on-arch-linux/</guid>
      <description><![CDATA[<p>So I recently got myself a shiny new AMD Radeon RX 5700 XT GPU (that&rsquo;s a mouthful).</p>
<p>Let&rsquo;s see how it does on Linux.</p>
<h2 id="installation">Installation</h2>
<p>First, I use Arch (btw) and that makes things a lot easier, at least for now. As of writing this, Navi support is still not in the mainline kernel, but it should come with Linux 5.3.</p>
<p>Because of the AUR and many other fun things, this isn&rsquo;t much of a problem in Arch.</p>
<p>Don&rsquo;t be surprised when you install the GPU in your PC and don&rsquo;t see Xorg or anything resembling a GUI popping up.</p>
<p>First, you need to add a new repo to your system. Put the following in <code>/etc/pacman.conf</code>, just above the <code>[core]</code> repository:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>[mesa-git]
</span></span><span style="display:flex;"><span>Server = https://pkgbuild.com/~lcarlier/$repo/$arch
</span></span><span style="display:flex;"><span>SigLevel = Optional
</span></span></code></pre></div><p>Now, let&rsquo;s install some packages:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo pacman -Sy amdvlk clang-git libclc-git libdrm-git llvm-git llvm-libs-git mesa-git opencl-mesa-git vulkan-mesa-layer-git vulkan-radeon-git xf86-video-amdgpu-git lib32-mesa-git lib32-mesa-git lib32-vulkan-radeon-git lib32-vulkan-mesa-layer-git
</span></span></code></pre></div><p>This will cover most of the stuff needed to make the GPU work correctly.</p>
<p>Now we need the new kernel modules. For this, you have two routes: install <code>amdgpu-dkms</code> from the AUR, <strong>or</strong> install <code>linux-git</code> from the AUR. I personally suggest you to go with <code>linux-git</code> as the dkms driver is not official as far as I know and doesn&rsquo;t cover all the stuff that&rsquo;s needed to make the GPU work 100% (like sensors support).</p>
<p>Whatever route you choose, there&rsquo;s still one more thing you need to install from the AUR: the <code>linux-firmware-agd5f-radeon-navi10</code> package. This is a package I made myself containing <a href="https://people.freedesktop.org/~agd5f/radeon_ucode/navi10/">the latest firmware for Navi 10 GPUs right from the driver developer</a>.</p>
<p>So, to summarize, here&rsquo;s a copy-paste command you can run:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>yay -S linux-git linux-firmware-agd5f-radeon-navi10
</span></span></code></pre></div><p>Quick note, I tried installing <code>mesa-aco-git</code>, even just to give it a try, but unfortunately I wasn&rsquo;t able to get Xorg (or Wayland for that matter) running with it. Maybe in the future, we&rsquo;ll see.</p>
<h2 id="gaming-performance">Gaming performance</h2>
<p>First, all of the games I&rsquo;m gonna talk about were ran at 1440p. They were all tested in GNOME Wayland, and the ones showing problems were also tested in GNOME Xorg and XFCE.</p>
<p>So, performance is of course great, especially in native games. Here&rsquo;s a list of games I maxed out without any hiccup or visible problem:</p>
<ul>
<li><strong>Rocket League</strong></li>
<li><strong>Dark Souls 3</strong></li>
<li><strong>Road Redemption</strong></li>
<li><strong>CS:GO</strong></li>
<li><strong>DOOM 2016</strong></li>
<li><strong>The Witcher 3</strong></li>
<li><strong>Fallout 4</strong> (except for a mouse problem unrelated to the GPU)</li>
<li><strong>Tabletop Simulator</strong></li>
<li><strong>For The King</strong></li>
<li><strong>Keep Talking and Nobody Explodes</strong></li>
<li><strong>Xonotic</strong></li>
</ul>
<p>Small note on The Witcher 3: it runs pretty much flawlessly, but I experienced some occasional frame rate drops, especially early on when I loaded the game. This usually goes away after 10 minutes or so of gameplay.</p>
<p>I had various problems with other games, and pretty weird ones, too. I think they&rsquo;re worth documenting.</p>
<h3 id="important-update">IMPORTANT UPDATE</h3>
<p>The games that are crossed out below don&rsquo;t actually have anything wrong with them. I am just dumb enough to have used the <strong>non git versions of the mesa 32 bit libraries</strong>. I updated the install command above, just to make sure nobody gets the same problems.</p>
<ul>
<li><del><strong>Distance</strong>: the game launches, but performance is terrible (2 fps in the menu at 1440p). Obviously there&rsquo;s something wrong, as the game is far from being <em>that</em> heavy.</del></li>
<li><del><strong>Screencheat</strong>: about 7 fps in game, 12 or so in the menu. Very similar to the problem with Distance.</del></li>
<li><del><strong>Hover: Revolt of Gamers</strong>: As above, performance is unexplainably terrible even in the main menu.</del></li>
<li><strong>Rise of the Tomb Raider</strong>: game seems to work fine, launches and the menu is perfectly responsive, but 5 seconds or so after I get in game, the whole game freezes into an unrecoverable state.</li>
<li><strong>Quake Champions</strong>: Similar to Rise of the Tomb Raider, menu works perfectly, almost instant freeze as soon as I get in game. But Quake Champions is far from being a technically well engineered game.</li>
<li><strong>Enter the Gungeon</strong>: Perfectly playable, but some of the graphics show some small amount of glitches.</li>
<li><strong>No Man&rsquo;s Sky</strong>: Games works almost perfectly, but the framerate drops heavily inside menus.</li>
</ul>
<p><del>An interesting fact: in the games I&rsquo;ve tested, where the framerate is unexplainably low, <code>sensors</code> shows the power draw of the GPU to be stuck at 30W (same as idle). Other games that give no problems like The Witcher 3 manage to raise the power draw between 150W and 300W. I later confirmed that (of course) the clock speeds (monitored via <code>sudo watch cat /sys/kernel/debug/dri/0/amdgpu_pm_info</code>) are directly proportional to the power draw.</del></p>
<p><del>This probably shows something&rsquo;s wrong with the driver. It made me wonder if I could force a clock speed on the GPU manually to see if those games work fine. I didn&rsquo;t want to go that route since it would kinda count as an overclock and I&rsquo;m still not confident enough in my abilities or in the driver&rsquo;s.</del></p>
<p>What I did try to do was force the higher pstates (power states) on the GPU to see if that would work. Of course, it doesn&rsquo;t. Following the <a href="https://wiki.archlinux.org/index.php/AMDGPU#Overclocking">AMDGPU page on the Arch Wiki</a> I tried these commands:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo -i <span style="color:#6272a4"># better be root for this</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;manual&#34;</span> &gt; /sys/class/drm/card0/device/power_dpm_force_performance_level
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;2&#34;</span> &gt; /sys/class/drm/card0/device/pp_dpm_mclk <span style="color:#6272a4"># set highest pstate 2 on the memory</span>
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;5 6 7&#34;</span> &gt; /sys/class/drm/card0/device/pp_dpm_sclk <span style="color:#6272a4"># set higher pstates 5, 6 and 7 on the actual gpu</span>
</span></span></code></pre></div><p>This didn&rsquo;t work at all, and showed some rather interesting errors in dmesg, similar to this one: <code>Failed to send message 0x20, response 0xfffffffb param 0x5</code>. Of course searching for these messages gave me zero results.</p>
<p><strong>About OpenCL</strong>: Unsurprisingly Clover doesn&rsquo;t work, <del>but so doesn&rsquo;t the AMDGPU-PRO OpenCL implementation. Still need to investigate that further, I didn&rsquo;t get into much detail with OpenCL. Will do soon after I have these more serious problems sorted out.</del> OpenCL now works using the <code>opencl-amdgpu-pro-pal</code> package from AUR.</p>
<h2 id="conclusions">Conclusions</h2>
<p>It&rsquo;s not an entirely bad thing to be an early adopter. I mean, I am starting to understand how the driver works (kinda?) and digging into lower level stuff with the kernel and the GPU than I&rsquo;ve ever had.</p>
<p>This said, there&rsquo;s still much I don&rsquo;t understand. I am willing to learn and help. If you have more experience with this stuff and want to add your voice to the conversation, don&rsquo;t hesitate to comment or contact me personally.</p>
<p>Will keep this blog updated with any news regarding making this GPU work.</p>
]]></description>
      
        <media:thumbnail url="https://gabmus.org/images/post_pics/Early-adopter-experience-with-the-new-Radeon-RX-5700-XT-on-Arch-Linux/navi.avif" />
      
    </item>
    
    
    
    <item>
      <title>Feeds 0.7 and more features to come</title>
      <link>https://gabmus.org/posts/feeds-0-7-and-more-features-to-come/</link>
      <pubDate>Fri, 23 Aug 2019 11:51:36 +0000</pubDate>
      
      <guid>https://gabmus.org/posts/feeds-0-7-and-more-features-to-come/</guid>
      <description><![CDATA[<p><a href="https://flathub.org/apps/details/org.gabmus.gnome-feeds">Feeds version 0.7 should be out soon</a> with all the features detailed in <a href="/2019/08/20/Feeds-saving-articles-offline-and-marking-stuff-as-read/">this post</a>.</p>
<p>The code starts to feel a little more complex, and it would probably use another small refactoring, but that&rsquo;s not too important right now.</p>
<p>Let&rsquo;s lay out a new roadmap, with the features I implemented, and some new ones I want to add:</p>
<ul>
<li><input checked="" disabled="" type="checkbox"> <del>Read and unread articles</del></li>
<li><input disabled="" type="checkbox"> Keeping history of older articles locally (still not sure if I actually want this or not)</li>
<li><input disabled="" type="checkbox"> Adblocking</li>
<li><input checked="" disabled="" type="checkbox"> <del>Saving articles</del></li>
<li><input disabled="" type="checkbox"> Suggestions</li>
<li><input disabled="" type="checkbox"> Integration with GNOME Online Accounts</li>
<li><input disabled="" type="checkbox"> <a href="https://gitlab.gnome.org/GabMus/gnome-feeds/issues/4">Integration with TTRSS</a></li>
<li><input disabled="" type="checkbox"> Ability to open rss links and OPML files</li>
<li><input disabled="" type="checkbox"> <em>Offline mode</em> with a banner indicating that the system is offline</li>
</ul>
<p>There&rsquo;s still some work to do. Of course any help is welcome and appreciated.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Feeds: saving articles offline and marking stuff as read</title>
      <link>https://gabmus.org/posts/feeds-saving-articles-offline-and-marking-stuff-as-read/</link>
      <pubDate>Tue, 20 Aug 2019 10:26:53 +0000</pubDate>
      
      <guid>https://gabmus.org/posts/feeds-saving-articles-offline-and-marking-stuff-as-read/</guid>
      <description><![CDATA[<h2 id="recap">Recap</h2>
<p><a href="https://gabmus.gitlab.io/2019/08/12/feeds-0-6-and-roadmap-md/">In a previous post</a> I layed out a roadmap for my feed reader application <a href="https://flathub.org/apps/details/org.gabmus.gnome-feeds"><strong>Feeds</strong></a>. Here&rsquo;s a little update on how things are going.</p>
<p>Alright, first I want to thank <a href="https://gitlab.com/etamuk">@Etamuk</a> again. He&rsquo;s being very active with the German translation, and any time I make a commit, soon after I already see he has opened a merge request with the updated translation. That&rsquo;s just impressive, and I&rsquo;m truly greatful for his awesome contributions!</p>
<p>But now let&rsquo;s get down to business with the new features.</p>
<h2 id="saving-articles">Saving articles</h2>
<p><img src="/images/post_pics/Feeds-saving-articles-offline-and-marking-stuff-as-read/save_article.avif" alt=""></p>
<p>Every article now has a right click or longpress popover that contains two buttons. The first one is a <em>Save</em> button. You can use it to save or <del>unsave</del> delete an article.</p>
<p>Once an article is saved, its content is downloaded and available offline. This allows easy consultation whenever you want and also provides an alternative to browser bookmarks or online &ldquo;read it later&rdquo; services.</p>
<h2 id="read-and-unread">Read and unread</h2>
<p><img src="/images/post_pics/Feeds-saving-articles-offline-and-marking-stuff-as-read/read_unread.avif" alt=""></p>
<p>A feature that I should have implemented from the beginning made its way into Feeds just now. Articles you read, are marked as read.</p>
<p>The marking was made in the laziest way possible: it just changes the article opacity to 50%. But in my opinion it works alright. Of course you can manually set an article as read or unread by using the dedicated button in the right click or longpress popover.</p>
<h2 id="conclusions">Conclusions</h2>
<p>Maybe these sound like easy things to implement, but to be honest due to the structure of the application, and to the way things work to minimize disk usage and make the whole thing more dynamic and faster overall, to implement these features I had to touch up almost every little piece of the application.</p>
<p>It was worth it in the end, and I&rsquo;m happy of what I accomplished. Hope you like it too when it comes out. I&rsquo;m thinking of doing a 0.7 release. I don&rsquo;t have the feeling that Feeds is worth a 1.0 at the moment. Maybe I need someone to test it out and see how things are working.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>On privacy conscious comments and analytics</title>
      <link>https://gabmus.org/posts/on_comments_and_analytics/</link>
      <pubDate>Wed, 14 Aug 2019 19:40:00 +0000</pubDate>
      
      <guid>https://gabmus.org/posts/on_comments_and_analytics/</guid>
      <description><![CDATA[<p>This blog is fairly new, and so I still have to set up everything. Comments are obviously a very important part of a blog, especially if you want any kind of feedback on the stuff you&rsquo;re doing.</p>
<p>The lazy solution is pretty obvious: <a href="https://en.wikipedia.org/wiki/Disqus#Criticism,_privacy,_and_security_concerns"><em>Disqus</em> the privacy nightmare</a>.</p>
<p>I like the wild idea of respecting my readers&rsquo; privacy, and so I started looking for alternative solutions.</p>
<p>The one solution I chose to adopt is an open source comment platform called <a href="https://commento.io/"><strong>Commento</strong></a>. It works just like Disqus, except since it doesn&rsquo;t make money from violating people&rsquo;s privacy, you can&rsquo;t really use it for free.</p>
<p>You can either choose to pay what you want for it (with a minimum of 3 USD) or host it yourself.</p>
<p>I already pay for a VPS for <a href="https://techpills.technology">Tech Pills&rsquo; website</a>, but it&rsquo;s not subject to heavy load, so a service more running on it wouldn&rsquo;t really hurt.</p>
<p>You can install Commento using <code>docker-compose</code>, which is very convenient. I made some changes to the <code>docker-compose.yml</code> file to better suit my needs, and put an nginx reverse proxy in front of it to add https into the mix.</p>
<p>After writing some CSS to adapt it to the blog&rsquo;s theme, the new comment box is ready to roll.</p>
<p>As a nice (unexpected) bonus, Commento also provides some basic analytics like total page views and number of comments. And that&rsquo;s all totally anonymous, it&rsquo;s just a faceless number that&rsquo;s not associated to any identity.</p>
<p>And that&rsquo;s about it, if you&rsquo;re looking for a privacy respecting, open source, powerful comment platform, that&rsquo;s also pretty easy to install and offers some bonus analytics on top, Commento is the way to go.</p>
]]></description>
      
    </item>
    
    
    
    <item>
      <title>Feeds 0.6 and future roadmap</title>
      <link>https://gabmus.org/posts/feeds-0-6-and-roadmap-md/</link>
      <pubDate>Mon, 12 Aug 2019 09:51:13 +0000</pubDate>
      
      <guid>https://gabmus.org/posts/feeds-0-6-and-roadmap-md/</guid>
      <description><![CDATA[<p><a href="https://flathub.org/apps/details/org.gabmus.gnome-feeds">Feeds version 0.6 is out</a> with two important changes:</p>
<ul>
<li>German translation thanks to <a href="https://gitlab.com/etamuk">@Etamuk</a></li>
<li>Concurrent feeds refresh</li>
</ul>
<p>About the translation, I know when a project starts becoming interesting, people want to contribute. And translations are in my experience one of the indication of people really getting passionate.</p>
<p>I am really flattered by this contribution and it really means a lot to see this project getting some love.</p>
<p>Moving on to the more technical stuff, concurrent refresh or parallel refresh.</p>
<p>It was surprisingly easy to implement, and that really shows how important having a modular and well-thought-out code structure is.</p>
<p>You see, the feed download and object creation (along with the creation of all the feed items) happens asynchronously already, but adding that feed, and all of its articles to the active lists doesn&rsquo;t. There is this wonderful function <code>GLib.idle_add(function, *args)</code> that makes this whole process so easy and elegant. This function does no more than telling the main thread to run <code>function(*args)</code> whenever it can, no rush (although to a human it&rsquo;s almost instantaneous). With this method the GUI thread doesn&rsquo;t get interrupted and the UI doesn&rsquo;t freeze, but the function is still ran synchronously to it, avoiding any problems of mutual exclusion to precious variables.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6272a4"># https://gitlab.com/gabmus/gnome-feeds/blob/0.6/gfeeds/feeds_manager.py#L139</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">class</span> <span style="color:#50fa7b">FeedsManager</span>(metaclass<span style="color:#ff79c6">=</span>Singleton):
</span></span><span style="display:flex;"><span>    <span style="color:#6272a4"># ...</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">def</span> <span style="color:#50fa7b">refresh</span>(self, <span style="color:#ff79c6">*</span>args):
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4"># ...</span>
</span></span><span style="display:flex;"><span>        threads_pool <span style="color:#ff79c6">=</span> []
</span></span><span style="display:flex;"><span>        threads_alive <span style="color:#ff79c6">=</span> []
</span></span><span style="display:flex;"><span>        MAX_THREADS <span style="color:#ff79c6">=</span> self<span style="color:#ff79c6">.</span>confman<span style="color:#ff79c6">.</span>conf[<span style="color:#f1fa8c">&#39;max_refresh_threads&#39;</span>]
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">for</span> f_link <span style="color:#ff79c6">in</span> self<span style="color:#ff79c6">.</span>confman<span style="color:#ff79c6">.</span>conf[<span style="color:#f1fa8c">&#39;feeds&#39;</span>]:
</span></span><span style="display:flex;"><span>            t <span style="color:#ff79c6">=</span> threading<span style="color:#ff79c6">.</span>Thread(
</span></span><span style="display:flex;"><span>                group <span style="color:#ff79c6">=</span> <span style="color:#ff79c6">None</span>,
</span></span><span style="display:flex;"><span>                target <span style="color:#ff79c6">=</span> self<span style="color:#ff79c6">.</span>_add_feed_async_worker,
</span></span><span style="display:flex;"><span>                name <span style="color:#ff79c6">=</span> <span style="color:#ff79c6">None</span>,
</span></span><span style="display:flex;"><span>                args <span style="color:#ff79c6">=</span> (f_link, <span style="color:#ff79c6">True</span>)
</span></span><span style="display:flex;"><span>            )
</span></span><span style="display:flex;"><span>            threads_pool<span style="color:#ff79c6">.</span>append(t)
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">while</span> <span style="color:#8be9fd;font-style:italic">len</span>(threads_pool) <span style="color:#ff79c6">&gt;</span> <span style="color:#bd93f9">0</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">if</span> <span style="color:#8be9fd;font-style:italic">len</span>(threads_alive) <span style="color:#ff79c6">&lt;</span> MAX_THREADS:
</span></span><span style="display:flex;"><span>                t <span style="color:#ff79c6">=</span> threads_pool<span style="color:#ff79c6">.</span>pop(<span style="color:#bd93f9">0</span>)
</span></span><span style="display:flex;"><span>                t<span style="color:#ff79c6">.</span>start()
</span></span><span style="display:flex;"><span>                threads_alive<span style="color:#ff79c6">.</span>append(t)
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">for</span> i, t <span style="color:#ff79c6">in</span> <span style="color:#8be9fd;font-style:italic">enumerate</span>(threads_alive):
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">if</span> <span style="color:#ff79c6">not</span> t<span style="color:#ff79c6">.</span>is_alive():
</span></span><span style="display:flex;"><span>                    threads_alive<span style="color:#ff79c6">.</span>pop(i)
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">while</span> t<span style="color:#ff79c6">.</span>is_alive():
</span></span><span style="display:flex;"><span>                <span style="color:#ff79c6">while</span> Gtk<span style="color:#ff79c6">.</span>events_pending():
</span></span><span style="display:flex;"><span>                    Gtk<span style="color:#ff79c6">.</span>main_iteration()
</span></span></code></pre></div><p>This is the wonderful code that manages the parallel refresh. It&rsquo;s fairly straight forward, it keeps a pool of threads to run, and runs n of them at a time, where n is the maximum number of threads set by the user (defaults to a very mild 2).</p>
<p>Apart from making the refresh process faster by itself, it also reduces the impact of problems like one feed taking longer than the others to download or parse. It&rsquo;s not a likely occurrence, but if and when it happens, it doesn&rsquo;t injure the user experience too much. That&rsquo;s because the slow feed is being processed in its own thread, but the whole refresh operation doesn&rsquo;t have to wait on it.</p>
<p>Moving on, I wanted to lay out a roadmap of where I want Feeds to go in the future.</p>
<ul>
<li><strong>Read and unread articles</strong>: currently Feeds doesn&rsquo;t keep track of your actions, and if you did or didn&rsquo;t read an article. This would require some strcuture, like the serialization and deserialization of articles to a skinnier, more targeted data structure (probably something like tsv or json, but the latter is more likely)</li>
<li><strong>Keeping history of older articles locally</strong>: rss feeds typically don&rsquo;t contain the whole archive, mostly because the resulting file would be huge and unwieldy. But people may want to be able to re-read articles they saw some time prior. That&rsquo;s why there needs to be an option to store them up to the user&rsquo;s preference. Again, this needs serialization/deserialization as described in the bullet point above</li>
<li><strong>Adblocking</strong>: this speaks for itself. Modern websites are a mess, disabling JavaScript helps, but it&rsquo;s neither a soltion nor a silver bullet</li>
<li><strong>Saving articles</strong>: kinda like bookmarks, but not quite. Let&rsquo;s say you add this awesome blog to your feeds, and you read a super interesting article about how to make feeds refreshing concurrent with just a bunch of lines of code. You surely want to save that! I mean, it could be useful someday right? That&rsquo;s a likely usecase for me. Also, you&rsquo;d wanna keep it offline as well, just in case you need it when you&rsquo;re on a plane or something</li>
<li><strong>Suggestions</strong>: many online news readers give you suggestions on what to follow. It&rsquo;s an interesing feature and I&rsquo;d want to add that. People are so addicted and accustomed to social media websites that this is what they expect news consumption to be.</li>
<li><strong>Integration with GNOME Online Accounts</strong>: mostly to be able to sync with services like Nextcloud News. Would be a hell of a feature.</li>
</ul>
<p>And that&rsquo;s it folks. This is the list of what I&rsquo;d want to accomplish with Feeds. Not all of these features are easy, and some are prone to make the whole app slower, and that&rsquo;s one thing I 100% want to avoid.</p>
]]></description>
      
        <media:thumbnail url="https://gitlab.com/gabmus/gnome-feeds/raw/0.6/data/icons/org.gabmus.gnome-feeds.svg" />
      
    </item>
    
    
    
    <item>
      <title>Hello, world</title>
      <link>https://gabmus.org/posts/hello-world/</link>
      <pubDate>Sun, 11 Aug 2019 10:38:00 +0000</pubDate>
      
      <guid>https://gabmus.org/posts/hello-world/</guid>
      <description><![CDATA[<h2 id="my-new-web-home">My new web home</h2>
<p>Hey people. I finally got around at creating a static blog.</p>
<p>After using <a href="https://gohugo.io/">Hugo</a> for a while, and initially deciding to try out <a href="https://blog.getpelican.com/">Pelican</a>, just to realize it&rsquo;s in a sad, outdated state right now, I settled on <a href="https://hexo.io">Hexo</a>.</p>
<p>It&rsquo;s not like I&rsquo;m crazy for JavaScript, quite the contrary. But I do realize it has its place sometimes. Using <code>npm</code> to install plugins is pretty clever, and besides at the end of the day I am the only one that has to use it.</p>
<p>It has a more complex structure compared to the aforementioned solutions, but if it gets the job done I can&rsquo;t complain too much.</p>
<h2 id="recap">Recap</h2>
<p>This is an interesting point in my life. In July I got my bachelor&rsquo;s in Computer Science, it was a pretty great achievement, and soon I&rsquo;ll be heading to Milan to get a master degree.</p>
<p>About my projects, things are going very well. I am focusing a lot on GTK/GNOME apps, probably because I&rsquo;m making things I want to use in the first place.</p>
<p><a href="https://gabmus.gitlab.io/HydraPaper">HydraPaper</a> got a pretty substantial update, introducing <a href="https://source.puri.sm/Librem5/libhandy">libhandy</a> into the mix to spice up its looks somehow, and mostly to make use of some comodity widgets it offers, namely the awesome <code>HdyPreferencesWindow</code>.</p>
<p>For the thesis of my bachelor&rsquo;s, I presented another interesting app called <a href="https://gitlab.com/GabMus/unifydmin">Unifydmin</a>. It&rsquo;s a control center and administration panel for servers. It&rsquo;s a very interesting project, but it&rsquo;s still quite rough around the edges. I have to find the motivation to pick it up again, but right now I&rsquo;m focused on another project.</p>
<p>And this project is <a href="https://gabmus.gitlab.io/gnome-feeds">Feeds</a>, a feed reader. It may sound dumb, but I couldn&rsquo;t find a single feed reader app for linux that I liked. Everything is either old, slow or electron based. After some time complaining about this (my girlfriend and friends can tell you about it), and basically using reddit and the podcasts I follow to get the latest news, I said F it, and made my own thing. It&rsquo;s going pretty well and the people that tried it seem to like it, and that makes me actually very proud of it.</p>
<p>And I think that&rsquo;s about it for now. I hope I manage to get my S together and start blogging more frequently, because I quite enjoy being able to share dev stuff online.</p>
<p>Let&rsquo;s see where that goes.</p>
]]></description>
      
    </item>
    
    
    
    
    
    
    
    
    
    
  </channel>
</rss>
