<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.smelov.lt/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.smelov.lt/" rel="alternate" type="text/html" /><updated>2026-07-28T08:08:41+00:00</updated><id>https://blog.smelov.lt/feed.xml</id><title type="html">Programming Notes</title><subtitle>Various things I&apos;ve learned.</subtitle><entry><title type="html">Electron includes an Xbox controller driver</title><link href="https://blog.smelov.lt/2026/07/28/electron-xbox-controller-driver.html" rel="alternate" type="text/html" title="Electron includes an Xbox controller driver" /><published>2026-07-28T00:00:00+00:00</published><updated>2026-07-28T00:00:00+00:00</updated><id>https://blog.smelov.lt/2026/07/28/electron-xbox-controller-driver</id><content type="html" xml:base="https://blog.smelov.lt/2026/07/28/electron-xbox-controller-driver.html"><![CDATA[<p>Every so often I remember the great <a href="https://josephg.com/blog/electron-is-flash-for-the-desktop/">Electron is flash for the desktop</a> blog post which mentions that on macOS, Chrome (and by extension Electron) includes a user-space Xbox controller driver:</p>

<blockquote>
  <p>And its not a stretch to call chrome an OS. By lines of code, chrome is about the same size as the linux kernel. […] On MacOS it even contains a userland USB driver for xbox360 controllers. (I know its there because I wrote it. Sorry.)</p>

  <p>Does slack contain my code to use xbox controllers? Does the slack team know? Does anyone know?</p>
</blockquote>

<p>Until one day I realized that the question if Electron contains an Xbox driver is 100% answerable. Let’s take Obsidian as an example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cd "/Applications/Obsidian.app/Contents/Frameworks/Electron Framework.framework/Versions/Current"
$ strings "Electron Framework" | grep "Error listening for Xbox controller add events:"
Error listening for Xbox controller add events:
Error listening for Xbox controller add events:
</code></pre></div></div>

<p>This message appears in the source code for Chromium in <a href="https://github.com/chromium/chromium/blob/8048cf8c5d1a55937d162ae36728b260e0870343/device/gamepad/xbox_data_fetcher_mac.cc#L239">xbox_data_fetcher_mac.cc</a>. The same binary also contains the name of the source file:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ strings "Electron Framework" | grep xbox_data_fetcher_mac.cc
../../device/gamepad/xbox_data_fetcher_mac.cc
../../device/gamepad/xbox_data_fetcher_mac.cc
</code></pre></div></div>

<p>Which means Electron <em>does</em> contain an Xbox controller driver. Case closed. Figuring out how to make use of it is a rabbit hole for another day.</p>

<p>P.S. Someone please make an extension for VS Code to add controller support. 😛</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Every so often I remember the great Electron is flash for the desktop blog post which mentions that on macOS, Chrome (and by extension Electron) includes a user-space Xbox controller driver:]]></summary></entry><entry><title type="html">Adding hardware debounce to Das Keyboard 4</title><link href="https://blog.smelov.lt/2026/07/22/das-keyboard-hardware-debounce.html" rel="alternate" type="text/html" title="Adding hardware debounce to Das Keyboard 4" /><published>2026-07-22T00:00:00+00:00</published><updated>2026-07-22T00:00:00+00:00</updated><id>https://blog.smelov.lt/2026/07/22/das-keyboard-hardware-debounce</id><content type="html" xml:base="https://blog.smelov.lt/2026/07/22/das-keyboard-hardware-debounce.html"><![CDATA[<p>Turns out <a href="/2026/07/06/das-keyboard-volume-knob.html">replacing the rotary encoder</a> did not fix the root cause of the problem, and the volume knob was still registering multiple increments. After struggling to understand why, I decided to disassemble another Das Keyboard 4 that I own from around 2019 that has never had this problem despite the heavy use.</p>

<p>After looking at both the old and the new PCBs I’ve spotted something: on the new board pull-up and series resistors seem to have been misplaced.</p>

<p><img src="https://blog.smelov.lt/assets/dk-revision-comparison.jpg" alt="Old and new revision compared" /></p>

<p>Left side is the old PCB, right side is the new broken one. The slight differences in values are not important here (1.2 kOhm vs 820 Ohm), the important thing is that the two outside resistors are pull-up resistors, and the two middle ones are series resistors that connect the output pins of the encoder to the microcontroller.</p>

<p>On the old board, the pull-up resistors are both 4.7 kOhm, and the series resistors are 1.2 kOhm. On the new board one channel doesn’t look right: the pull-up resistor is 820 Ohm, and the series resistor is 4.7 kOhm.</p>

<p>Here’s a crude schematic overlaid on top of the photo:</p>

<p><img src="https://blog.smelov.lt/assets/dk-encoder-channels.jpg" alt="PCB circuit schematic" /></p>

<p>Doesn’t look intentional to me, must have been a mistake :) This put some doubt in my mind that if they could mess up resistors in this revision, perhaps they also messed up debounce. The microcontroller is different on the new board, availability issues might have been the reason for the new revision.</p>

<p>The bottom two resistors need to be switched around.</p>

<h2 id="adding-an-rc-filter">Adding an RC filter</h2>

<p>What’s missing for the debounce filter is a capacitor, and the existing series resistors should work great for the RC filter. Fortunately, to the right of the series resistors there is a huge ground plane that we could solder to. I ordered some 0603 capacitors, ended up using 220 nF ones.</p>

<p>Here’s an in-progress photo:</p>

<p><img src="https://blog.smelov.lt/assets/dk-debounce-progress.jpg" alt="In-progress photo of adding a single capacitor" /></p>

<p>I soldered the two capacitors to the right side of the series resistors, then scraped away the solder mask right next to the new capacitors, then soldered them to the ground plane.</p>

<p><img src="https://blog.smelov.lt/assets/dk-final-result.jpg" alt="Final result with two capacitors fully soldered" /></p>

<p>Very happy with the final result, looks great :) This is the first time I repaired something not just by replacing a broken part, but by fixing a design issue.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Turns out replacing the rotary encoder did not fix the root cause of the problem, and the volume knob was still registering multiple increments. After struggling to understand why, I decided to disassemble another Das Keyboard 4 that I own from around 2019 that has never had this problem despite the heavy use.]]></summary></entry><entry><title type="html">Screw Ads</title><link href="https://blog.smelov.lt/2026/07/08/screw-ads.html" rel="alternate" type="text/html" title="Screw Ads" /><published>2026-07-08T00:00:00+00:00</published><updated>2026-07-08T00:00:00+00:00</updated><id>https://blog.smelov.lt/2026/07/08/screw-ads</id><content type="html" xml:base="https://blog.smelov.lt/2026/07/08/screw-ads.html"><![CDATA[<p>I’ve been blocking ads since around 2008. I started using Ubuntu as my primary and only operating system around that time - no cheating with dual booting! Back then, Flash ads were super popular on the web, and Flash Player for Linux would consume a ton of resources, making the experience of using a web browser unbearable. Blocking ads made browsing the web possible again.</p>

<p>After seeing how clean the web can be without ads I just… never stopped blocking them. Installing uBlock Origin is one of the first steps I take when setting up a new computer.</p>

<p>If at first my main concern was computer performance, my concerns have since shifted towards resisting manipulation (in a broad sense) by advertisers, and not letting some big tech company collect data about me. Ad tech and tracking users go hand in hand: the more information they have, the better they can target the users and the pricier the ad spots get.</p>

<p>One more thing! Do you have friends or relatives that run into computer problems often? Installing some malware off of the internet? Asking you if they can trust this no-name online store that seems to sell tech gadgets suspiciously cheap? They probably found the scam website through ads! Install an ad blocker for them, and they’ll magically have fewer computer problems. I sometimes jokingly refer to ad blockers as the best antivirus.</p>

<p>I recommend uBlock Origin on Firefox. There’s also an extension called SponsorBlock that uses a crowdsourced database of sponsored segments on YouTube and skips them. On iOS, I use Safari with Wipr 2 and SponsorBlock extensions. Both of these extensions are paid though, but are well worth the money.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[I’ve been blocking ads since around 2008. I started using Ubuntu as my primary and only operating system around that time - no cheating with dual booting! Back then, Flash ads were super popular on the web, and Flash Player for Linux would consume a ton of resources, making the experience of using a web browser unbearable. Blocking ads made browsing the web possible again.]]></summary></entry><entry><title type="html">Connecting USB-A devices to MacBooks is easier than you think</title><link href="https://blog.smelov.lt/2026/07/07/usb-a-macbook.html" rel="alternate" type="text/html" title="Connecting USB-A devices to MacBooks is easier than you think" /><published>2026-07-07T00:00:00+00:00</published><updated>2026-07-07T00:00:00+00:00</updated><id>https://blog.smelov.lt/2026/07/07/usb-a-macbook</id><content type="html" xml:base="https://blog.smelov.lt/2026/07/07/usb-a-macbook.html"><![CDATA[<p>Get a cheap USB-A female to USB-C male adapter like this one. Buy a bunch, put some in your laptop bag just in case. :)</p>

<p><img src="https://blog.smelov.lt/assets/cheap-usb-a-adapter.jpg" alt="A cheap USB-A to USB-C adapter" /></p>

<p>It even supports USB 3.0 at 5 Gbps.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Get a cheap USB-A female to USB-C male adapter like this one. Buy a bunch, put some in your laptop bag just in case. :)]]></summary></entry><entry><title type="html">Replacing rotary encoder in Das Keyboard 4</title><link href="https://blog.smelov.lt/2026/07/06/das-keyboard-volume-knob.html" rel="alternate" type="text/html" title="Replacing rotary encoder in Das Keyboard 4" /><published>2026-07-06T00:00:00+00:00</published><updated>2026-07-06T00:00:00+00:00</updated><id>https://blog.smelov.lt/2026/07/06/das-keyboard-volume-knob</id><content type="html" xml:base="https://blog.smelov.lt/2026/07/06/das-keyboard-volume-knob.html"><![CDATA[<p><strong>July 22 update:</strong> turns out the rotary encoder is probably fine and isn’t the root cause of reliability problems. I have written another blog post on <a href="/2026/07/22/das-keyboard-hardware-debounce.html">how to add hardware debounce</a>.</p>

<p>I have a Das Keyboard 4 with a volume knob that sometimes registers multiple times when rotating the knob, sometimes in the wrong direction. This indicates a bad rotary encoder.</p>

<p>Let’s see what encoder the keyboard has. Pop off the volume knob by pulling it straight up. It may need some force if you’ve never done this before, but nothing excessive. There we can find a rotary encoder with “CTS” marked on its side. After spending a bit of time, I was able to find the closest match on their website: <a href="https://www.ctscorp.com/Product-Series/290.htm">CTS Series 290 rotary encoder</a>.</p>

<h2 id="reproducing-and-visualizing-the-problem">Reproducing and visualizing the problem</h2>

<p>According to <a href="https://www.ctscorp.com/Files/DataSheets/Encoders/encoders-290-datasheet.pdf">the data sheet</a>, the middle pin is the ground, and the other two pins are output pins. The rotary encoder shorts these two pins to ground (middle pin) to generate the signal. Let’s connect a cheap logic analyzer to see what’s going on with the signal. I’ve used PulseView to record the signal.</p>

<p><img src="https://blog.smelov.lt/assets/pic1-logic-analyzer.jpg" alt="Attaching logic analyzer" /></p>

<p>We can see here that the rotary encoder is too chatty. According to the data sheet, contact chatter should last up to 3 ms, but here we can see contacts bouncing for around 15 ms, which is way out of spec.</p>

<p><img src="https://blog.smelov.lt/assets/pic2-pulseview.png" alt="PulseView output showing problem" /></p>

<h3 id="finding-a-replacement-part">Finding a replacement part</h3>

<p>The datasheet has no variant with such a short shaft like the one we have here. This means the part we’re dealing with is custom made for Das Keyboard and isn’t available off-the-shelf.</p>

<p>This is where I got stuck for a while. I even considered options to buy the version with the shortest shaft and cut or grind it down to be even shorter, however, that would still have made it too tall compared to the original.</p>

<p>The closest match is 290VAA5F201A2. All the specs are the same except for the shaft length.</p>

<p>Turns out the rotary encoder itself is modular, so it’s possible to reuse the shaft from the old encoder and put it into the new one. Fortunately, the shaft is not the part that causes excessive chatter, so by combining the new and the old parts together we can get a fully working rotary encoder with the correct shaft length.</p>

<p>I ordered mine from DigiKey, but Mouser also stocks it. I’ve also found them on eBay, but they seem to be overpriced for the number you get.</p>

<h3 id="disassembly">Disassembly</h3>

<p>Getting into the keyboard is quite straightforward if you have the right bit for your screwdriver - Hex 2.5 mm. Desoldering the old part without destroying it turned out to be the most painful part of the process. Some parts of the encoder are made of plastic, so you can’t just blast it with hot air.</p>

<p>Another problem is that the mounting pins are pressed tightly against the holes in the PCB, creating enough friction that the part is held in place even if there were no solder left. I can see why they did this: any slack in the mounting position would introduce too much variability and the volume knob would not be placed correctly against the metal cutout for the knob.</p>

<p>There’s black tape / spacer on top of the encoder that you need to remove before desoldering. We’ll put it back on the new one once it’s soldered.</p>

<p>Desolder the old part. Good luck :)</p>

<h3 id="combining-the-old-and-new-parts">Combining the old and new parts</h3>

<p><img src="https://blog.smelov.lt/assets/pic3-collage.jpg" alt="Collage of this process" /></p>

<h4 id="step-1-removing-the-custom-shaft-from-the-old-encoder">Step 1: removing the custom shaft from the old encoder</h4>

<ul>
  <li>Pull the mounting legs away from each other. The metal is soft enough this can be done with your bare hands.</li>
  <li>Bend it just enough to pull metal cover off.</li>
  <li>Pull away the plastic parts from the metal shaft. They are held together with 4 cylindrical plastic pins. Be super careful here because the contact pins that brush against the encoder ring are exposed, and bending them would likely make the encoder not work properly!</li>
  <li>Pull the plastic encoder ring and then also the little retention pin out.</li>
  <li>Take the metal shaft and put it away - we’ll use it later. The rest can be thrown away.</li>
</ul>

<h4 id="step-2-disassemble-the-new-encoder-and-combine-parts">Step 2: disassemble the new encoder and combine parts</h4>

<p>Follow the same process from Step 1 with the new encoder. Once you get to the last step, we’ll do the opposite: we’ll use everything BUT the shaft from the new encoder.</p>

<ul>
  <li>Take the shaft from the older encoder.</li>
  <li>Put the retention pin into the old shaft.</li>
  <li>Put the encoder ring into the old shaft.</li>
  <li>Put the combined shaft onto the plastic parts from the new encoder.</li>
  <li>Put the metal cover over that and bend back the metal to take its normal shape.</li>
</ul>

<p>That’s it, you’ve made the custom part that the keyboard needs!</p>

<h4 id="step-3-verification">Step 3: verification</h4>

<p>This is not what I’ve done originally, but in hindsight that would have saved me a lot of time. Once you rebuild the new part, use the logic analyzer to verify if the new part is working properly. I’ve soldered the new encoder only to realize it’s also just as chatty as the old one. Had to redo this multiple times.</p>

<h3 id="some-random-tips">Some random tips</h3>

<ul>
  <li>Verify if the new encoder is clicky. Some encoders that I got (brand new!) were not clicky for some reason. Only after soldering I’ve realized they’re super dull and not fun to use.</li>
  <li>Be super careful with flux - don’t let it get inside the encoder!</li>
  <li>Isopropanol causes the encoder to become squeaky. At first I didn’t realize that it was isopropanol that I used for cleaning flux that was causing this and wasted a bunch of time resoldering new encoders.</li>
  <li>The encoder seems to be quite heat sensitive. If you melt the plastic parts inside, it’s game over.</li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[July 22 update: turns out the rotary encoder is probably fine and isn’t the root cause of reliability problems. I have written another blog post on how to add hardware debounce.]]></summary></entry><entry><title type="html">Patching Electron app menu behavior</title><link href="https://blog.smelov.lt/2021/04/09/patching-electron-app-menu-behavior.html" rel="alternate" type="text/html" title="Patching Electron app menu behavior" /><published>2021-04-09T20:00:00+00:00</published><updated>2021-04-09T20:00:00+00:00</updated><id>https://blog.smelov.lt/2021/04/09/patching-electron-app-menu-behavior</id><content type="html" xml:base="https://blog.smelov.lt/2021/04/09/patching-electron-app-menu-behavior.html"><![CDATA[<p>For a few weeks now, I’ve been trying out <a href="https://github.com/rbreaves/kinto">Kinto</a> – an application for Linux (and Windows) that remaps keyboard keys so you can use macOS shortcuts on your Linux computer. While not perfect, I’ve been surprised by how well it worked.</p>

<p>On macOS, the shortcut to delete a word is Alt-Backspace. When you press it on a Linux computer with Kinto installed, it correctly remaps that to Ctrl-Backspace. This works well in almost all applications, except for Electron apps. Electron apps activate the main menu when you release Alt.</p>

<p>Apparently, this is a feature that comes from the way most applications on Windows work: if you press Alt without pressing anything else, it will focus the first menu item. This is not a common behavior on Linux however – neither GTK nor Qt apps do this.</p>

<p>Even though this can be worked around in Kinto with varying levels of success, the real fix needs to happen in the Electron framework. With this in mind, I’ve set out to dig into this issue.</p>

<h1 id="looking-into-electrons-source-code">Looking into Electron’s source code</h1>

<p>After going through <a href="https://github.com/electron/electron/pull/15302">the pull request that introduced this</a>, I realized there doesn’t seem to be a way to disable this behavior. The culprit is <a href="https://github.com/electron/electron/blob/95e26e2fd4bb096cbcc7e7803da7dedebfa1e4cf/shell/browser/ui/views/root_view.cc#L137-L159">this piece of code</a>. Since a proper fix won’t come anytime soon, let’s binary patch compiled executables ✨.</p>

<h1 id="analyzing-the-binary">Analyzing the binary</h1>

<p>First, we need to find where the binary is. Let’s run Signal and list running processes:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ps aux | <span class="nb">grep </span>signal
<span class="o">[</span>...]
user      211853  2.5  0.8 6837048 267392 ?      SLl  01:08   0:02 /opt/Signal/signal-desktop <span class="nt">--no-sandbox</span>
user      211865  0.0  0.1 208664 45708 ?        S    01:08   0:00 /opt/Signal/signal-desktop <span class="nt">--type</span><span class="o">=</span>zygote <span class="nt">--no-zygote-sandbox</span> <span class="nt">--no-sandbox</span>
user      211866  0.0  0.1 208664 45932 ?        S    01:08   0:00 /opt/Signal/signal-desktop <span class="nt">--type</span><span class="o">=</span>zygote <span class="nt">--no-sandbox</span>
<span class="o">[</span>...]
</code></pre></div></div>

<p>There it is! Let’s open <code class="language-plaintext highlighter-rouge">/opt/Signal/signal-desktop</code> in IDA Pro. There’s a <a href="https://www.hex-rays.com/products/ida/support/download_freeware/">free version</a> that will do just fine for our needs. Signal binary is huge (~130 MB), so the analysis takes a looong time.</p>

<p>After the analysis is finished, our main goal is to find where that piece of code is in the binary. Sadly, the executable doesn’t have any debugging symbols, so we can’t just search for the function by its name. Without compiling Electron ourselves (which I guess takes like half a day), we need to find something unique about this piece of code. String literals work best, however, there seem to be no string literals around.</p>

<p>I then found the following function:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="kt">int</span> <span class="n">kMenuBarHeight</span> <span class="o">=</span> <span class="mi">25</span><span class="p">;</span>
<span class="c1">// ...</span>
<span class="kt">int</span> <span class="n">RootView</span><span class="o">::</span><span class="n">GetMenuBarHeight</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span>
  <span class="k">return</span> <span class="n">kMenuBarHeight</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>While not perfect, we can search for this value in IDA Pro by pressing Alt-I. Enter 25, press Enter. 32265 results. Oof. Let’s try to narrow them down.</p>

<p>If we reference <a href="https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI">the standard calling conventions</a> we should expect the function to store the constant into the <code class="language-plaintext highlighter-rouge">rax</code>/<code class="language-plaintext highlighter-rouge">eax</code>/<code class="language-plaintext highlighter-rouge">ax</code>/<code class="language-plaintext highlighter-rouge">al</code> register. So let’s press Ctrl-F and enter</p>

<pre><code class="language-assembly">mov     eax, 19h
</code></pre>

<p>57 results. That’s better. Let’s go through all of them and find a really short function. To my surprise, the function I needed was the first in the list. Let’s rename it to <code class="language-plaintext highlighter-rouge">GetMenuBarHeight</code>:</p>

<pre><code class="language-assembly">.text:0000000001A5FAA0 GetMenuBarHeight proc near
.text:0000000001A5FAA0     push    rbp
.text:0000000001A5FAA1     mov     rbp, rsp
.text:0000000001A5FAA4     mov     eax, 19h
.text:0000000001A5FAA9     pop     rbp
.text:0000000001A5FAAA     retn
.text:0000000001A5FAAA GetMenuBarHeight endp
</code></pre>

<p>This function by itself is not very important, but if we go through adjacent functions, we’ll realize the compiled binary follows the source code pretty closely. I figured out the next functions were: <code class="language-plaintext highlighter-rouge">SetAutoHideMenuBar</code>, <code class="language-plaintext highlighter-rouge">IsMenuBarAutoHide</code>, <code class="language-plaintext highlighter-rouge">IsMenuBarVisible</code> and then <code class="language-plaintext highlighter-rouge">HandleKeyEvent</code>, the one we need to patch!</p>

<h1 id="patch-idea">Patch idea</h1>

<p>If we pretend that the Alt key press never happened, the menu will not be activated. We need to make a patch as if <a href="https://github.com/electron/electron/blob/95e26e2fd4bb096cbcc7e7803da7dedebfa1e4cf/shell/browser/ui/views/root_view.cc#L141">this line</a> was</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">menu_bar_alt_pressed_</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</code></pre></div></div>

<h1 id="analyzing-handlekeyevent-function">Analyzing HandleKeyEvent function</h1>

<p>If we look at the source code again, the function has an early return which ensures that <code class="language-plaintext highlighter-rouge">menu_bar_</code> is set:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">RootView</span><span class="o">::</span><span class="n">HandleKeyEvent</span><span class="p">(</span><span class="k">const</span> <span class="n">content</span><span class="o">::</span><span class="n">NativeWebKeyboardEvent</span><span class="o">&amp;</span> <span class="n">event</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">menu_bar_</span><span class="p">)</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Before we go further, I need to mention that methods in C++ have an implicit first parameter which is a pointer to the object (<code class="language-plaintext highlighter-rouge">this</code>), so the method above actually has two (<code class="language-plaintext highlighter-rouge">RootView *this, NativeWebKeyboardEvent &amp;event</code>). This is what the method looks like in the disassembly:</p>

<pre><code class="language-assembly">HandleKeyEvent proc near
push    rbp                 ; init stack frame, save register values, etc.
mov     rbp, rsp
push    r14
push    rbx
mov     r14, rdi            ; rdi stores the first argument (`this` keyword)
                            ; it is then copied to r14

mov     rdi, [rdi+288h]     ; rdi = this-&gt;menu_bar_;
test    rdi, rdi            ; if (!rdi)
jz      loc_1A5FBE7         ;     return;
</code></pre>

<p>This gives us some important information:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">r14</code> stores the pointer to the <code class="language-plaintext highlighter-rouge">RootView</code> object.</li>
  <li><code class="language-plaintext highlighter-rouge">288h</code> is the offset in the object for <code class="language-plaintext highlighter-rouge">menu_bar_</code> variable.</li>
</ul>

<p>Now, if we now look at <a href="https://github.com/electron/electron/blob/95e26e2fd4bb096cbcc7e7803da7dedebfa1e4cf/shell/browser/ui/views/root_view.h">the header file</a>, we can find the offset for <code class="language-plaintext highlighter-rouge">menu_bar_alt_pressed_</code>:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">RootView</span> <span class="o">:</span> <span class="k">public</span> <span class="n">views</span><span class="o">::</span><span class="n">View</span> <span class="p">{</span>
 <span class="nl">public:</span>
  <span class="c1">// ...</span>
  <span class="c1">// Menu bar.</span>
  <span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o">&lt;</span><span class="n">MenuBar</span><span class="o">&gt;</span> <span class="n">menu_bar_</span><span class="p">;</span>   <span class="c1">// r14+288h</span>
  <span class="kt">bool</span> <span class="n">menu_bar_autohide_</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>      <span class="c1">// r14+288h+8   (8 is sizeof(std::unique_ptr&lt;MenuBar&gt;)</span>
  <span class="kt">bool</span> <span class="n">menu_bar_visible_</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>       <span class="c1">// r14+288h+8+1 (1 is sizeof(bool))</span>
  <span class="kt">bool</span> <span class="n">menu_bar_alt_pressed_</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>   <span class="c1">// r14+288h+8+1+1 = r14+292h</span>
</code></pre></div></div>

<p>Now we need to look for <code class="language-plaintext highlighter-rouge">[r14+292h]</code> in the disassembly. There are 4 matches:</p>

<pre><code class="language-assembly">.text:0000000001A5FB39    mov   byte ptr [r14+292h], 1   ; set to true
.text:0000000001A5FB50    cmp   byte ptr [r14+292h], 0   ; compare to 0
.text:0000000001A5FB5E    mov   byte ptr [r14+292h], 0   ; set to false
.text:0000000001A5FBDF    mov   byte ptr [r14+292h], 0   ; set to false
</code></pre>

<p>The first one is exactly what we were looking for. If you now go to the <code class="language-plaintext highlighter-rouge">mov byte ptr [r14+292h], 1</code> line, IDA Pro will tell you the offset where the instruction in the input file is. In my case, it was <code class="language-plaintext highlighter-rouge">01A5EB39</code>.</p>

<h1 id="digging-into-the-instruction">Digging into the instruction</h1>

<p>If you open the hex view in IDA Pro while the instruction is selected, it will highlight instruction’s machine code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>41 C6 86 92 02 00 00 01    mov   byte ptr [r14+292h], 1
</code></pre></div></div>

<p>Using <a href="http://ref.x86asm.net/coder64.html">x86-64 opcode reference</a> we can figure out what each byte means:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>41               register extension prefix (REX.B), allows access to r14
C6               instruction: mov r/m8, imm8
86               ModR/M byte which corresponds to [R14/R14D]+disp32
92 02 00 00      disp32 value: 0x292 (encoded as little endian)
01               imm8 value (0x01)
</code></pre></div></div>

<p>The last byte is the one we need to patch.</p>

<h1 id="patching">Patching</h1>

<p>We’re very close now! Let’s open this file in GHex:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>ghex /opt/Signal/signal-desktop
</code></pre></div></div>

<p>Press Ctrl-J and type the offset. At offset 0x01A5EB40, there is the value 1 that corresponds to <code class="language-plaintext highlighter-rouge">true</code>. Change it to <code class="language-plaintext highlighter-rouge">00</code> (<code class="language-plaintext highlighter-rouge">false</code>) and save.</p>

<p>Run Signal, press and release Alt, and there it is, the menu no longer gets activated! 🎉🎉🎉</p>

<h1 id="next-steps">Next steps</h1>

<p>The biggest issue is that this patch needs to be reapplied after each update. The other obvious issue is that each Electron app needs to be patched.</p>

<p>Hang on until the next blog post where I’ll show you how to write a generic patcher that works with any Electron version and any Electron app – until developers make changes to that part of the code, of course.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[For a few weeks now, I’ve been trying out Kinto – an application for Linux (and Windows) that remaps keyboard keys so you can use macOS shortcuts on your Linux computer. While not perfect, I’ve been surprised by how well it worked.]]></summary></entry><entry><title type="html">Tip: improve your mouse accuracy</title><link href="https://blog.smelov.lt/2021/04/06/linear-mouse-acceleration.html" rel="alternate" type="text/html" title="Tip: improve your mouse accuracy" /><published>2021-04-06T15:00:00+00:00</published><updated>2021-04-06T15:00:00+00:00</updated><id>https://blog.smelov.lt/2021/04/06/linear-mouse-acceleration</id><content type="html" xml:base="https://blog.smelov.lt/2021/04/06/linear-mouse-acceleration.html"><![CDATA[<p>Each operating system has its own rules for determining how far mouse pointer needs to go when you move the mouse. The default settings most often use mouse acceleration – an idea that the pointer should respond not only to the distance traveled by the mouse, but also the speed. The faster you move the mouse, the farther the pointer goes.</p>

<h1 id="the-problem-with-mouse-acceleration">The problem with mouse acceleration</h1>

<p>Mouse acceleration makes it harder to develop muscle memory to hit far away targets accurately. You need to be precise not only in the distance you move the mouse but also the speed, which is harder to do consistently. The other issue is that acceleration curves are different on each operating system, so you’ll have to get used to each one.</p>

<h1 id="solution-flat-pointer-acceleration-curve-aka-no-acceleration">Solution: flat pointer acceleration curve (aka no acceleration)</h1>

<p>Flat acceleration curve ignores mouse velocity completely and only applies some constant factor to the distance and direction the mouse moves. What is more, it’s completely the same on all operating systems, which is a great thing for people who regularly use different operating systems.</p>

<h1 id="setting-it-up">Setting it up</h1>

<p>macOS</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults write <span class="nt">-g</span> com.apple.mouse.scaling <span class="nt">-integer</span> <span class="nt">-1</span>
</code></pre></div></div>

<p>Linux (GNOME)</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gsettings <span class="nb">set </span>org.gnome.desktop.peripherals.mouse accel-profile flat
</code></pre></div></div>

<p>Windows</p>

<p>Open the “Mouse Properties” control panel dialog, uncheck “Enhance pointer precision” in the “Pointer Options” tab.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Each operating system has its own rules for determining how far mouse pointer needs to go when you move the mouse. The default settings most often use mouse acceleration – an idea that the pointer should respond not only to the distance traveled by the mouse, but also the speed. The faster you move the mouse, the farther the pointer goes.]]></summary></entry><entry><title type="html">Sublime Text for Ruby on Rails</title><link href="https://blog.smelov.lt/2020/08/08/sublime-text-for-ruby-on-rails.html" rel="alternate" type="text/html" title="Sublime Text for Ruby on Rails" /><published>2020-08-08T00:00:00+00:00</published><updated>2020-08-08T00:00:00+00:00</updated><id>https://blog.smelov.lt/2020/08/08/sublime-text-for-ruby-on-rails</id><content type="html" xml:base="https://blog.smelov.lt/2020/08/08/sublime-text-for-ruby-on-rails.html"><![CDATA[<p><img src="https://blog.smelov.lt/assets/sublime-text-sidekiq.png" alt="Sublime Text 3 on macOS with Sidekiq source file open" /></p>

<p>I’ve been using Sublime Text for over 5 years now primarily for Ruby on Rails development. I want to go over the configuration and plugins I’ve used that have served me well during all those years.</p>

<p>This isn’t a beginner’s guide though. If you’re unsure how to install plugins or how to edit configuration, look up a guide on the internet and then go back to this post.</p>

<h2 id="plugins">Plugins</h2>

<ul>
  <li>
    <p><a href="https://github.com/skuroda/Sublime-AdvancedNewFile">AdvancedNewFile</a></p>

    <p>Allows creating new files relative to the current file easily. Instead of choosing the name and location of the file in a native save dialog, you can instead press Alt+Cmd+N, type in <code class="language-plaintext highlighter-rouge">:foo/bar.rb</code> and it’ll create a folder named <code class="language-plaintext highlighter-rouge">foo</code> and a file <code class="language-plaintext highlighter-rouge">bar.rb</code> relative to current file. Without the <code class="language-plaintext highlighter-rouge">:</code> it’ll create the file relative to the project root.</p>
  </li>
  <li>
    <p><a href="https://github.com/facelessuser/ApplySyntax">ApplySyntax</a></p>

    <p>In Ruby, you’ll have files that need different syntax highlighting schemes even though they’re all have the same <code class="language-plaintext highlighter-rouge">.rb</code> extension. For example, a file might be a regular Ruby source file, the other might be an RSpec file. This plugin has a list of rules to apply the right syntax.</p>
  </li>
  <li>
    <p><a href="https://github.com/kemayo/sublime-text-git">Git</a></p>

    <p>Git integration. The only function I use it for is to show <code class="language-plaintext highlighter-rouge">git diff</code>. Since I don’t need it very often, I don’t have a shortcut bound for it, I use the command palette.</p>
  </li>
  <li>
    <p><a href="https://packagecontrol.io/packages/GitGutter">GitGutter</a></p>

    <p>Shows which lines were added or removed next to line numbers. It’s very convenient because it allows you to concentrate on the changed areas.</p>
  </li>
  <li>
    <p><a href="https://github.com/alienhard/SublimeAllAutocomplete">All Autocomplete</a></p>

    <p>Suggests words from other open files.</p>
  </li>
  <li>
    <p><a href="https://github.com/colinta/SublimeChangeQuotes">ChangeQuotes</a></p>

    <p>Toggles single/double quotes. Depending on your code style, you might need to switch between single and double quotes quite often. This plugin doesn’t ship with default keybindings, I’ve bound the primary function to Cmd+’</p>
  </li>
  <li>
    <p><a href="https://github.com/facelessuser/MarkdownPreview">MarkdownPreview</a></p>

    <p>Renders a markdown file into HTML and shows it in the browser. Something I didn’t realize straight away is that you don’t need to run this command over and over after a change, the plugin will rerender the file for you, it’s enough to refresh the page in the browser.</p>
  </li>
  <li>
    <p><a href="https://github.com/SublimeLinter/SublimeLinter">SublimeLinter</a>, <a href="https://github.com/SublimeLinter/SublimeLinter-rubocop">SublimeLinter-rubocop</a>, <a href="https://github.com/SublimeLinter/SublimeLinter-shellcheck">SublimeLinter-shellcheck</a></p>

    <p>A linter framework with linter plugins for RuboCop and Shellcheck. They provide feedback on your code while you’re typing.</p>
  </li>
  <li>
    <p><a href="https://github.com/astrauka/TestRSpec">TestRSpec</a></p>

    <p>Allows you to switch between code and spec, create the spec if it doesn’t exist, and run specs in Sublime.</p>
  </li>
  <li>
    <p><a href="https://github.com/michaelworm/SoDaReloaded-Theme">Theme - SoDaReloaded</a> (Dark)</p>

    <p>The theme I’ve used for a while, try it out!</p>
  </li>
</ul>

<h2 id="settings">Settings</h2>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"font_face"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Source Code Pro"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"font_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>I’ve been using <a href="https://github.com/adobe-fonts/source-code-pro">Source Code Pro</a> as my primary coding font.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"tab_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
  </span><span class="nl">"translate_tabs_to_spaces"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
  </span><span class="nl">"trim_trailing_white_space_on_save"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
  </span><span class="nl">"ensure_newline_at_eof_on_save"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Use 2 spaces for indentation, remove trailing newline on save, add a newline at the end of file if it’s missing.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"word_separators"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./</span><span class="se">\\</span><span class="s2">()</span><span class="se">\"</span><span class="s2">'-:,.;&lt;&gt;~@#$%^&amp;*|+=[]{}`~"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>It’s the default list with <code class="language-plaintext highlighter-rouge">!</code> and <code class="language-plaintext highlighter-rouge">?</code> removed, since they can be a part of the method name in Ruby.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"file_exclude_patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">".DS_Store"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"folder_exclude_patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"log"</span><span class="p">,</span><span class="w">
    </span><span class="s2">".git"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"node_modules"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"tmp"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"__pycache__"</span><span class="p">,</span><span class="w">
    </span><span class="s2">".vscode"</span><span class="p">,</span><span class="w">
    </span><span class="s2">".idea"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"index_exclude_patterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"*/log/*"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"*/node_modules/*"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"*/tmp/*"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Rules to exclude irrelevant files and folders.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"rulers"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">99</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Adds a vertical line at 99 columns to let you know if you’re under line length limit. Adjust to match your project’s conventions.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Stress testing suspend on Linux</title><link href="https://blog.smelov.lt/2019/12/18/stress-testing-suspend-on-linux.html" rel="alternate" type="text/html" title="Stress testing suspend on Linux" /><published>2019-12-18T00:00:00+00:00</published><updated>2019-12-18T00:00:00+00:00</updated><id>https://blog.smelov.lt/2019/12/18/stress-testing-suspend-on-linux</id><content type="html" xml:base="https://blog.smelov.lt/2019/12/18/stress-testing-suspend-on-linux.html"><![CDATA[<p>Here’s a script you can use to stress test suspend on Linux:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>

<span class="c"># Watch kernel logs for the word `corrupt`</span>
dmesg <span class="nt">-w</span> | <span class="nb">grep</span> <span class="nt">--color</span> <span class="nt">-i</span> corrupt &amp;

<span class="nb">echo</span> <span class="s2">"Starting stress test in 5 seconds..."</span>
<span class="nb">sleep </span>5

<span class="c"># Stress test by repeatedly putting computer to sleep for 10s</span>
<span class="k">for </span>i <span class="k">in</span> <span class="si">$(</span><span class="nb">seq </span>100<span class="si">)</span><span class="p">;</span> <span class="k">do
  </span><span class="nb">echo</span> <span class="s2">"Iteration #</span><span class="nv">$i</span><span class="s2">..."</span>
  rtcwake <span class="nt">-m</span> mem <span class="nt">-s</span> 10
  <span class="nb">sleep </span>10
<span class="k">done</span>
</code></pre></div></div>

<p>Save the file as <code class="language-plaintext highlighter-rouge">stress-test-suspend.sh</code>, then run:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">chmod</span> +x stress-test-suspend.sh
<span class="nv">$ </span><span class="nb">sudo</span> ./stress-test-suspend.sh
</code></pre></div></div>

<p>The idea comes from the page <a href="https://01.org/blogs/rzhang/2015/best-practice-debug-linux-suspend/hibernate-issues">Best practice to debug Linux* suspend/hibernate issues</a>.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Here’s a script you can use to stress test suspend on Linux:]]></summary></entry><entry><title type="html">Notes on building computers</title><link href="https://blog.smelov.lt/2019/11/22/notes-on-building-computers.html" rel="alternate" type="text/html" title="Notes on building computers" /><published>2019-11-22T00:00:00+00:00</published><updated>2019-11-22T00:00:00+00:00</updated><id>https://blog.smelov.lt/2019/11/22/notes-on-building-computers</id><content type="html" xml:base="https://blog.smelov.lt/2019/11/22/notes-on-building-computers.html"><![CDATA[<p>I’ve had to build a few computers for work, mainly to be used as Linux workstations. It didn’t always go smoothly, so let’s go through some things I’ve learned.</p>

<h3 id="new-hardware-is-unproven-hardware">New hardware is unproven hardware</h3>

<p>If you buy a new piece of hardware the day it comes out, you’re more likely to encounter issues with both hardware and software. We’ve bought a couple of RTX 2080 Ti GPUs, and we’ve had problems with failing hardware and misbehaving drivers.</p>

<h3 id="memory">Memory</h3>

<p>When choosing RAM, look for RAM confirmed to work with your motherboard. Motherboard manufacturers usually publish Memory and CPU compatibility lists called QVL on their websites. Buy the exact model specified.</p>

<h3 id="extreme-memory-profile-xmp">Extreme Memory Profile (XMP)</h3>

<p>Even though DDR4 RAM is branded as having speeds such as 3200 MHz, usually it will only run at 2133 MHz out of the box. You’ll have to enable XMP in your BIOS to unlock the full speed.</p>

<p>XMP is marketed as stable manufacturer-approved settings for your RAM. However, I’ve had multiple cases where XMP caused system instability. <strong>Treat XMP as overclock.</strong> If you’re experiencing random reboots, crashes, computer not waking up from sleep, monitor not turning on, this could all be caused by XMP. Run at stock speed for a while and see if the problem goes away.</p>

<p>If XMP didn’t work, you can try overclocking RAM yourself. The process is quite complicated, though, as there are a lot of other settings besides clock speed.</p>

<h3 id="ram-slots">RAM slots</h3>

<p>I’ve recently learned that it might also matter which of the two slots of the same channel you use. For the last computer I’ve built, I’ve had to use A2 and B2 slots instead of A1 and B1 to make RAM work at full speed.</p>

<p>Refer to motherboard’s manual for what manufacturer recommends. Here’s an example from the manual for ASUS TUF GAMING X570-PLUS:</p>

<p><img src="https://blog.smelov.lt/assets/x570-memory-configurations.png" alt="Recommended memory configurations for X570 motherboard" /></p>

<h3 id="run-a-benchmark">Run a benchmark</h3>

<p>It’s not enough to order the parts and put them together. You also want to make sure your computer performs as expected, and that’s what benchmarks are for.</p>

<p>I recommend <a href="https://www.geekbench.com/">Geekbench</a>. It’s free if you don’t mind publishing your benchmark results on the internet. If your scores are significantly lower than the typical result for your CPU, you have a problem somewhere.</p>

<h3 id="run-a-stress-test">Run a stress test</h3>

<p>Benchmarks can show that your computer is fast, but is it stable? I usually run <a href="https://www.mersenne.org/download/">Prime95</a> in stress test mode for up to a day.</p>

<h3 id="how-to-debug-stability-issues">How to debug stability issues</h3>

<p>After a crash caused by instability, you are unlikely to find any relevant logs after a reboot. However, logs are extremely useful when trying to figure out what went wrong. To access kernel logs, you need to <a href="https://wiki.archlinux.org/index.php/Netconsole">send logs to another computer using netconsole</a>.</p>

<p>If you’re having issues with suspend, there’s a great guide on <a href="https://01.org/blogs/rzhang/2015/best-practice-debug-linux-suspend/hibernate-issues">how to debug suspend issues</a>.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[I’ve had to build a few computers for work, mainly to be used as Linux workstations. It didn’t always go smoothly, so let’s go through some things I’ve learned.]]></summary></entry></feed>