paritybit.ca

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit ba9123266db55f247e2511db544fb12a218afd0c
parent cb6656a431e851e2561538f912e9adffd9334178
Author: Jake Bauer <jbauer@paritybit.ca>
Date:   Fri, 26 Jun 2020 02:45:38 -0400

Publish new blog post

Diffstat:
Mpages/blog.md | 1+
Apages/blog/why-dwm-swallowing-cant-swallow-tmux.md | 0
Mpages/home.md | 4++--
Mpublic/feeds/sitewide-feed.xml | 338+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpublic/sitemap.xml | 1+
Apublic/vid/noswallow-animated-thumb.webm | 0
Apublic/vid/noswallow-animated.webm | 0
Apublic/vid/swallow-animated-thumb.webm | 0
Apublic/vid/swallow-animated.webm | 0
9 files changed, 342 insertions(+), 2 deletions(-)

diff --git a/pages/blog.md b/pages/blog.md @@ -20,6 +20,7 @@ href="https://social.paritybit.ca/@jbauer">Mastodon</a>. ### 2020 <ul> + <li>2020-06-26 <a href="blog/why-dwm-swallowing-cant-swallow-tmux">Why dwm's Window Swallowing Patch Can't Swallow tmux</a></li> <li>2020-06-23 <a href="blog/switching-to-debian-sid">Switching to Debian Sid</a></li> <li>2020-06-22 <a href="blog/my-lwn-theme">My LWN Theme</a></li> <li>2020-06-21 <a href="blog/reflecting-on-50-days-of-blogging">Reflecting on 50 Days of Blogging</a></li> diff --git a/pages/blog/why-dwm-swallowing-cant-swallow-tmux.md b/pages/blog/why-dwm-swallowing-cant-swallow-tmux.md Binary files differ. diff --git a/pages/home.md b/pages/home.md @@ -20,6 +20,8 @@ extent)! Access through `gopher://paritybit.ca` or `gemini://paritybit.ca`. src="/img/feed-icon.png" width="15" height="15" alt="Click for RSS Feed"/> </a> </div> +2020-06-26 <a class="feed-item" href="blog/why-dwm-swallowing-cant-swallow-tmux">Why dwm's Window Swallowing Patch Can't Swallow tmux</a> + 2020-06-23 <a class="feed-item" href="blog/switching-to-debian-sid">Switching to Debian Sid</a> 2020-06-22 <a class="feed-item" href="blog/my-lwn-theme">My LWN Theme</a> @@ -37,8 +39,6 @@ extent)! Access through `gopher://paritybit.ca` or `gemini://paritybit.ca`. 2020-06-15 <a class="feed-item" href="blog/my-new-t420s">My New T420s</a> 2020-06-14 <a class="feed-item" href="blog/announcing-kontaktb">Announcing kontaktdb</a> - -2020-06-13 <a class="feed-item" href="blog/how-i-manage-my-dotfiles">How I Manage My Dotfiles</a> ### What is a Parity Bit? It is a bit (in the 1's and 0's sense) used in checking for errors in digital diff --git a/public/feeds/sitewide-feed.xml b/public/feeds/sitewide-feed.xml @@ -7,6 +7,344 @@ <description>The feed that covers all notable additions, updates, announcements, and other changes for the entire paritybit.ca website.</description> <item> + <title>Why dwm's Window Swallowing Patch Can't Swallow tmux</title> + <link>https://www.paritybit.ca/blog/why-dwm-swallowing-cant-swallow-tmux</link> + <guid>https://www.paritybit.ca/blog/why-dwm-swallowing-cant-swallow-tmux</guid> + <pubDate>Fri, 26 Jun 2020 02:45:18 -0400</pubDate> + <description><![CDATA[<h2 id="why-dwms-window-swallowing-patch-cant-swallow-tmux">Why dwm’s Window Swallowing Patch Can’t Swallow tmux</h2> +<div class="byline"> +<p><b>Written By:</b> Jake Bauer | <b>Posted:</b> 2020-06-26 | <b>Last Updated:</b> 2020-06-26</p> +</div> +<p><em><strong>Note</strong>: Code snippets in this blog post are governed by the MIT/X Consortium License as they are snippets taken from dwm code. See <a href="https://git.suckless.org/dwm/file/LICENSE.html">the dwm LICENSE file</a>.</em></p> +<p>The dwm swallow patch is my favourite patch for dwm because it allows me to launch GUI applications from my terminal and have the launched application take over the terminal instead of spawning next to the terminal and rendering the terminal window useless. You can sort of do this by just appending <code>&amp;</code> and then closing the terminal, but you also don’t get the terminal back once the application has closed. Something like launching a video with <code>mpv &lt;video&gt;</code> and having the mpv window replace the terminal window is very nice.</p> +<p>I was also playing around with running every terminal window with tmux specifically because it would allow me to remove the scrollback patch from my terminal emulator st and becuse it handles redrawing the text when the terminal window is resized. However, I noticed that launching applications from within a terminal running tmux wouldn’t get swallowed. A brief search on the interwebs turned up no useful information, so I decided to get my hands dirty and investigate why this was the case.</p> +<p>Before we get started: yes, I know I should learn to use gdb. I have used it before but frequently find printfs do a good enough job that I don’t have to faff about with gdb.</p> +<p><a href="#summary">Jump down to the summary</a> to skip the journey and go right to the explanation.</p> +<h3 id="demonstration">Demonstration</h3> +<p>Take a look at the animations below to see what happens when a GUI application is launched while tmux is running:</p> +<video src="/vid/swallow-animated-thumb.webm" alt="A demonstration of window +swallowing where the Zathura PDF viewer takes over the terminal emulator +window." controls> +Your browser does not support the video tag. +</video> +<p><a href="/vid/swallow-animated.webm">Click for a higher resolution version of the above video.</a></p> +<video src="/vid/noswallow-animated-thumb.webm" alt="A demonstration of how, +when tmux is running in a terminal, zathura does not swallow the terminal window +and instead spawns as a regular window." controls> +Your browser does not support the video tag. +</video> +<p><a href="/vid/noswallow-animated.webm">Click for a higher resolution version of the above video.</a></p> +<h3 id="beginning-the-search">Beginning the Search</h3> +<p>So, what’s the difference between a normal terminal launching a GUI application and a terminal running tmux launching a GUI application?</p> +<p>By this point, I had no idea if the actual cause was something wrong with my configuration of dwm. Perhaps my recent conversion from the scratchpad to the named-scratchpads patch messed something up so I started by looking at the values of the <code>rules[]</code> array in <code>config.h</code> to see if my terminal window was being wrongly interpreted as being unable to swallow:</p> +<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb1-1" title="1"><span class="dt">static</span> <span class="dt">const</span> Rule rules[] = {</a> +<a class="sourceLine" id="cb1-2" title="2"> <span class="co">/* class instance title tags mask isfloating isterminal noswallow monitor scratch key*/</span></a> +<a class="sourceLine" id="cb1-3" title="3"> { <span class="st">&quot;Galculator&quot;</span>, NULL, NULL, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>, -<span class="dv">1</span>, -<span class="dv">1</span>, <span class="dv">0</span> },</a> +<a class="sourceLine" id="cb1-4" title="4"> { <span class="st">&quot;Gimp&quot;</span>, NULL, NULL, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">0</span>, -<span class="dv">1</span>, -<span class="dv">1</span>, <span class="dv">0</span> },</a> +<a class="sourceLine" id="cb1-5" title="5"> { <span class="st">&quot;Firefox&quot;</span>, NULL, NULL, <span class="dv">1</span> &lt;&lt; <span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">0</span>, -<span class="dv">1</span>, -<span class="dv">1</span>, <span class="dv">0</span> },</a> +<a class="sourceLine" id="cb1-6" title="6"> { <span class="st">&quot;St&quot;</span>, NULL, NULL, <span class="dv">0</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>, -<span class="dv">1</span>, <span class="dv">0</span> },</a> +<a class="sourceLine" id="cb1-7" title="7"> { NULL, NULL, <span class="st">&quot;scratchpad&quot;</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, -<span class="dv">1</span>, <span class="ch">&#39;s&#39;</span> },</a> +<a class="sourceLine" id="cb1-8" title="8"> { NULL, NULL, <span class="st">&quot;calculator&quot;</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="dv">1</span>, -<span class="dv">1</span>, <span class="ch">&#39;c&#39;</span> },</a> +<a class="sourceLine" id="cb1-9" title="9"> { NULL, NULL, <span class="st">&quot;Event Tester&quot;</span>, <span class="dv">0</span>, <span class="dv">1</span>, <span class="dv">0</span>, <span class="dv">1</span>, -<span class="dv">1</span>, <span class="dv">0</span> },</a> +<a class="sourceLine" id="cb1-10" title="10">};</a></code></pre></div> +<p>These rules are interpreted by the function <code>applyrules()</code> which applies rules to a launched client based on what’s in the <code>rules[]</code> array. A few <code>printf</code>s later and I had found out that everything was being correctly interpreted here.</p> +<h3 id="finding-the-answer">Finding The Answer</h3> +<p>Since that thread didn’t lead anywhere, I examined the content of the swallow patch itself to see what code it added and where I should look next. It led me to look inside the <code>manage()</code> function which calls <code>swallow()</code> if the variable <code>term</code> is a truthy value.</p> +<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb2-1" title="1"><span class="dt">void</span></a> +<a class="sourceLine" id="cb2-2" title="2">manage(Window w, XWindowAttributes *wa)</a> +<a class="sourceLine" id="cb2-3" title="3">{</a> +<a class="sourceLine" id="cb2-4" title="4"> ...</a> +<a class="sourceLine" id="cb2-5" title="5"> <span class="cf">if</span> (term)</a> +<a class="sourceLine" id="cb2-6" title="6"> swallow(term, c);</a> +<a class="sourceLine" id="cb2-7" title="7"> focus(NULL);</a> +<a class="sourceLine" id="cb2-8" title="8">}</a></code></pre></div> +<p><code>term</code> is of type <code>Client *</code> and is set to <code>NULL</code> at the beginning of the function:</p> +<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb3-1" title="1"><span class="dt">void</span></a> +<a class="sourceLine" id="cb3-2" title="2">manage(Window w, XWindowAttributes *wa)</a> +<a class="sourceLine" id="cb3-3" title="3">{</a> +<a class="sourceLine" id="cb3-4" title="4"> Client *c, *t = NULL, *term = NULL;</a> +<a class="sourceLine" id="cb3-5" title="5"> ...</a> +<a class="sourceLine" id="cb3-6" title="6">}</a></code></pre></div> +<p><code>term</code> is then later set by the <code>termforwin()</code> function:</p> +<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb4-1" title="1"><span class="dt">void</span></a> +<a class="sourceLine" id="cb4-2" title="2">manage(Window w, XWindowAttributes *wa)</a> +<a class="sourceLine" id="cb4-3" title="3">{</a> +<a class="sourceLine" id="cb4-4" title="4"> ...</a> +<a class="sourceLine" id="cb4-5" title="5"> <span class="cf">if</span> (XGetTransientForHint(dpy, w, &amp;trans) &amp;&amp; (t = wintoclient(trans))) {</a> +<a class="sourceLine" id="cb4-6" title="6"> c-&gt;mon = t-&gt;mon;</a> +<a class="sourceLine" id="cb4-7" title="7"> c-&gt;tags = t-&gt;tags;</a> +<a class="sourceLine" id="cb4-8" title="8"> } <span class="cf">else</span> {</a> +<a class="sourceLine" id="cb4-9" title="9"> c-&gt;mon = selmon;</a> +<a class="sourceLine" id="cb4-10" title="10"> applyrules(c);</a> +<a class="sourceLine" id="cb4-11" title="11"> term = termforwin(c);</a> +<a class="sourceLine" id="cb4-12" title="12"> }</a> +<a class="sourceLine" id="cb4-13" title="13"> ...</a> +<a class="sourceLine" id="cb4-14" title="14">}</a></code></pre></div> +<p><code>termforwin()</code> returns a <code>Client *</code> value which represents the terminal which launched the client application. It determines which terminal window the GUI application was just launched from so it knows which window should be swallowed.</p> +<p>My <code>printfs</code> up to this point told me that <code>termforwin()</code> was returning <code>NULL</code> when a GUI application was launched inside of a terminal running tmux which is why <code>swallow()</code> was not being called for these applications:</p> +<pre><code>Applying rules to client st +Rules for window: +Title: (null) +Class: St +Instance: (null) +IsTerminal: 1 +NoSwallow: 0 +IsFloating: 0 +Tags: 0 +Scratchkey: +IN MANAGE(): term: (nil) +Applying rules to client org.pwmt.zathura +IN MANAGE(): term: 0x5626932ad110 &lt;--- No tmux +Entered swallow function +IN SWALLOW(): org.pwmt.zathura should be swallowing st +Applying rules to client org.pwmt.zathura +IN MANAGE(): term: (nil) &lt;--- With tmux</code></pre> +<p>Now I had to take a look inside <code>termforwin()</code> to discover why it was returning <code>NULL</code> for these windows. Since there were only two places where <code>NULL</code> could be returned, I checked which of the two conditions were failing. It turned out that the second <code>return NULL;</code> was being reached which meant that the <code>if</code> statement inside the <code>for</code> loops was evaluating to false for every value of <code>c</code>.</p> +<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb6-1" title="1">Client *</a> +<a class="sourceLine" id="cb6-2" title="2">termforwin(<span class="dt">const</span> Client *w)</a> +<a class="sourceLine" id="cb6-3" title="3">{</a> +<a class="sourceLine" id="cb6-4" title="4"> Client *c;</a> +<a class="sourceLine" id="cb6-5" title="5"> Monitor *m;</a> +<a class="sourceLine" id="cb6-6" title="6"></a> +<a class="sourceLine" id="cb6-7" title="7"> <span class="cf">if</span> (!w-&gt;pid || w-&gt;isterminal)</a> +<a class="sourceLine" id="cb6-8" title="8"> <span class="cf">return</span> NULL;</a> +<a class="sourceLine" id="cb6-9" title="9"></a> +<a class="sourceLine" id="cb6-10" title="10"> <span class="cf">for</span> (m = mons; m; m = m-&gt;next) {</a> +<a class="sourceLine" id="cb6-11" title="11"> <span class="cf">for</span> (c = m-&gt;clients; c; c = c-&gt;next) {</a> +<a class="sourceLine" id="cb6-12" title="12"> <span class="cf">if</span> (c-&gt;isterminal &amp;&amp; !c-&gt;swallowing &amp;&amp; c-&gt;pid &amp;&amp; isdescprocess(c-&gt;pid, w-&gt;pid))</a> +<a class="sourceLine" id="cb6-13" title="13"> <span class="cf">return</span> c;</a> +<a class="sourceLine" id="cb6-14" title="14"> }</a> +<a class="sourceLine" id="cb6-15" title="15"> }</a> +<a class="sourceLine" id="cb6-16" title="16"></a> +<a class="sourceLine" id="cb6-17" title="17"> <span class="cf">return</span> NULL;</a> +<a class="sourceLine" id="cb6-18" title="18">}</a></code></pre></div> +<p>A few more <code>printfs</code> later and I discovered that the cause of the failure was that <code>isdescprocess()</code> was returning <code>0</code>. <code>isdescprocess()</code> is a function that determines if a process is a descendant of another process by walking up the process tree. In the case above, it is trying to determine if the next client <code>c</code> in the list of clients is a parent process of our newly launched process <code>w</code>.</p> +<p>This check consistently failed every time a GUI application was launched from a terminal running tmux which meant that the variable <code>term</code> was always <code>NULL</code>. To get a closer look at why this might be failing, I added some more <code>printfs</code> in strategic locations and ran <code>pstree -g</code> alongside every test I performed.</p> +<p>Here are the three scenarios I tested with relevant sections from the output log I created (<code>dwm.log.6</code>) along with the relevant snippets from the <code>pstree</code> output:</p> +<h4 id="zathura-with-just-st">Zathura with Just st</h4> +<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb7-1" title="1">MANAGE(): XGetTransientForHint: <span class="dv">0</span></a> +<a class="sourceLine" id="cb7-2" title="2">MANAGE(): wintoclient: (nil)</a> +<a class="sourceLine" id="cb7-3" title="3">APPLYRULES(): applying to client st</a> +<a class="sourceLine" id="cb7-4" title="4">TERMFORWIN(): pid: <span class="dv">33340</span>, isterminal: <span class="dv">1</span></a> +<a class="sourceLine" id="cb7-5" title="5">MANAGE(): term: (nil)</a> +<a class="sourceLine" id="cb7-6" title="6">MANAGE(): <span class="cf">if</span> term exists then c will swallow</a> +<a class="sourceLine" id="cb7-7" title="7">MANAGE(): XGetTransientForHint: <span class="dv">0</span></a> +<a class="sourceLine" id="cb7-8" title="8">MANAGE(): wintoclient: (nil)</a> +<a class="sourceLine" id="cb7-9" title="9">APPLYRULES(): applying to client st</a> +<a class="sourceLine" id="cb7-10" title="10">TERMFORWIN(): pid: <span class="dv">33369</span>, isterminal: <span class="dv">1</span></a> +<a class="sourceLine" id="cb7-11" title="11">MANAGE(): term: (nil)</a> +<a class="sourceLine" id="cb7-12" title="12">MANAGE(): <span class="cf">if</span> term exists then c will swallow</a> +<a class="sourceLine" id="cb7-13" title="13">MANAGE(): XGetTransientForHint: <span class="dv">0</span></a> +<a class="sourceLine" id="cb7-14" title="14">MANAGE(): wintoclient: (nil)</a> +<a class="sourceLine" id="cb7-15" title="15">APPLYRULES(): applying to client org.pwmt.zathura</a> +<a class="sourceLine" id="cb7-16" title="16">TERMFORWIN(): pid: <span class="dv">33382</span>, isterminal: <span class="dv">0</span></a> +<a class="sourceLine" id="cb7-17" title="17">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33369</span> is parent of c <span class="dv">33382</span></a> +<a class="sourceLine" id="cb7-18" title="18">ISDESCPROCESS(): pid <span class="dv">33382</span></a> +<a class="sourceLine" id="cb7-19" title="19">ISDESCPROCESS(): pid <span class="dv">33341</span></a> +<a class="sourceLine" id="cb7-20" title="20">ISDESCPROCESS(): pid <span class="dv">33340</span></a> +<a class="sourceLine" id="cb7-21" title="21">ISDESCPROCESS(): pid <span class="dv">33305</span></a> +<a class="sourceLine" id="cb7-22" title="22">ISDESCPROCESS(): pid <span class="dv">33292</span></a> +<a class="sourceLine" id="cb7-23" title="23">ISDESCPROCESS(): pid <span class="dv">33259</span></a> +<a class="sourceLine" id="cb7-24" title="24">ISDESCPROCESS(): pid <span class="dv">33254</span></a> +<a class="sourceLine" id="cb7-25" title="25">ISDESCPROCESS(): pid <span class="dv">1</span></a> +<a class="sourceLine" id="cb7-26" title="26">TERMFORWIN(): isterm: <span class="dv">1</span>, swallowing: (nil), pid: <span class="dv">33369</span>, isdescproc: <span class="dv">0</span></a> +<a class="sourceLine" id="cb7-27" title="27">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33369</span> is parent of c <span class="dv">33382</span></a> +<a class="sourceLine" id="cb7-28" title="28">ISDESCPROCESS(): pid <span class="dv">33382</span></a> +<a class="sourceLine" id="cb7-29" title="29">ISDESCPROCESS(): pid <span class="dv">33341</span></a> +<a class="sourceLine" id="cb7-30" title="30">ISDESCPROCESS(): pid <span class="dv">33340</span></a> +<a class="sourceLine" id="cb7-31" title="31">ISDESCPROCESS(): pid <span class="dv">33305</span></a> +<a class="sourceLine" id="cb7-32" title="32">ISDESCPROCESS(): pid <span class="dv">33292</span></a> +<a class="sourceLine" id="cb7-33" title="33">ISDESCPROCESS(): pid <span class="dv">33259</span></a> +<a class="sourceLine" id="cb7-34" title="34">ISDESCPROCESS(): pid <span class="dv">33254</span></a> +<a class="sourceLine" id="cb7-35" title="35">ISDESCPROCESS(): pid <span class="dv">1</span></a> +<a class="sourceLine" id="cb7-36" title="36">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33340</span> is parent of c <span class="dv">33382</span></a> +<a class="sourceLine" id="cb7-37" title="37">ISDESCPROCESS(): pid <span class="dv">33382</span></a> +<a class="sourceLine" id="cb7-38" title="38">ISDESCPROCESS(): pid <span class="dv">33341</span></a> +<a class="sourceLine" id="cb7-39" title="39">TERMFORWIN(): isterm: <span class="dv">1</span>, swallowing: (nil), pid: <span class="dv">33340</span>, isdescproc: <span class="dv">33340</span></a> +<a class="sourceLine" id="cb7-40" title="40">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33340</span> is parent of c <span class="dv">33382</span></a> +<a class="sourceLine" id="cb7-41" title="41">ISDESCPROCESS(): pid <span class="dv">33382</span></a> +<a class="sourceLine" id="cb7-42" title="42">ISDESCPROCESS(): pid <span class="dv">33341</span></a> +<a class="sourceLine" id="cb7-43" title="43">MANAGE(): term: <span class="bn">0x55b88574e0e0</span></a> +<a class="sourceLine" id="cb7-44" title="44">MANAGE(): <span class="cf">if</span> term exists then c will swallow</a> +<a class="sourceLine" id="cb7-45" title="45">MANAGE(): swallowing...</a> +<a class="sourceLine" id="cb7-46" title="46">IN SWALLOW()</a> +<a class="sourceLine" id="cb7-47" title="47">SWALLOW(): org.pwmt.zathura should be swallowing st</a> +<a class="sourceLine" id="cb7-48" title="48">=======================================================</a></code></pre></div> +<pre><code>systemd(1)-+-ModemManager(1278)-+-{ModemManager}(1278) + |-login(33254)---startx(33254)---xinit(33254)-+-Xorg(33293)-+-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | `-{Xorg}(33293) + | `-dwm(33305)-+-compton(33305)-+-{compton}(33305) + | | |-{compton}(33305) + | | |-{compton}(33305) + | | `-{compton}(33305) + | |-dunst(33305)-+-{dunst}(33305) + | | `-{dunst}(33305) + | |-lxpolkit(33305)-+-{lxpolkit}(33305) + | | `-{lxpolkit}(33305) + | |-slstatus(33305) + | |-st(33340)---bash(33341)---zathura(33382)-+-{zathura}(33382) + | | |-{zathura}(33382) + | | |-{zathura}(33382) + | | |-{zathura}(33382) + | | |-{zathura}(33382) + | | |-{zathura}(33382) + | | `-{zathura}(33382) + | |-st(33369)---bash(33370)---pstree(33390) + | |-unclutter(33305) + | `-xautolock(33305)</code></pre> +<h4 id="zathura-with-st-running-tmux">Zathura with st Running tmux</h4> +<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb9-1" title="1">MANAGE(): XGetTransientForHint: <span class="dv">0</span></a> +<a class="sourceLine" id="cb9-2" title="2">MANAGE(): wintoclient: (nil)</a> +<a class="sourceLine" id="cb9-3" title="3">APPLYRULES(): applying to client org.pwmt.zathura</a> +<a class="sourceLine" id="cb9-4" title="4">TERMFORWIN(): pid: <span class="dv">33414</span>, isterminal: <span class="dv">0</span></a> +<a class="sourceLine" id="cb9-5" title="5">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33369</span> is parent of c <span class="dv">33414</span></a> +<a class="sourceLine" id="cb9-6" title="6">ISDESCPROCESS(): pid <span class="dv">33414</span></a> +<a class="sourceLine" id="cb9-7" title="7">ISDESCPROCESS(): pid <span class="dv">33398</span></a> +<a class="sourceLine" id="cb9-8" title="8">ISDESCPROCESS(): pid <span class="dv">22354</span></a> +<a class="sourceLine" id="cb9-9" title="9">TERMFORWIN(): isterm: <span class="dv">1</span>, swallowing: (nil), pid: <span class="dv">33369</span>, isdescproc: <span class="dv">0</span></a> +<a class="sourceLine" id="cb9-10" title="10">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33369</span> is parent of c <span class="dv">33414</span></a> +<a class="sourceLine" id="cb9-11" title="11">ISDESCPROCESS(): pid <span class="dv">33414</span></a> +<a class="sourceLine" id="cb9-12" title="12">ISDESCPROCESS(): pid <span class="dv">33398</span></a> +<a class="sourceLine" id="cb9-13" title="13">ISDESCPROCESS(): pid <span class="dv">22354</span></a> +<a class="sourceLine" id="cb9-14" title="14">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33340</span> is parent of c <span class="dv">33414</span></a> +<a class="sourceLine" id="cb9-15" title="15">ISDESCPROCESS(): pid <span class="dv">33414</span></a> +<a class="sourceLine" id="cb9-16" title="16">ISDESCPROCESS(): pid <span class="dv">33398</span></a> +<a class="sourceLine" id="cb9-17" title="17">ISDESCPROCESS(): pid <span class="dv">22354</span></a> +<a class="sourceLine" id="cb9-18" title="18">TERMFORWIN(): isterm: <span class="dv">1</span>, swallowing: (nil), pid: <span class="dv">33340</span>, isdescproc: <span class="dv">0</span></a> +<a class="sourceLine" id="cb9-19" title="19">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33340</span> is parent of c <span class="dv">33414</span></a> +<a class="sourceLine" id="cb9-20" title="20">ISDESCPROCESS(): pid <span class="dv">33414</span></a> +<a class="sourceLine" id="cb9-21" title="21">ISDESCPROCESS(): pid <span class="dv">33398</span></a> +<a class="sourceLine" id="cb9-22" title="22">ISDESCPROCESS(): pid <span class="dv">22354</span></a> +<a class="sourceLine" id="cb9-23" title="23">TERMFORWIN(): Passed pid and isterminal check but returning NULL</a> +<a class="sourceLine" id="cb9-24" title="24">MANAGE(): term: (nil)</a> +<a class="sourceLine" id="cb9-25" title="25">MANAGE(): <span class="cf">if</span> term exists then c will swallow</a> +<a class="sourceLine" id="cb9-26" title="26">===========================================================</a></code></pre></div> +<pre><code>systemd(1)-+-ModemManager(1278)-+-{ModemManager}(1278) + |-login(33254)---startx(33254)---xinit(33254)-+-Xorg(33293)-+-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | `-{Xorg}(33293) + | `-dwm(33305)-+-compton(33305)-+-{compton}(33305) + | | |-{compton}(33305) + | | |-{compton}(33305) + | | `-{compton}(33305) + | |-dunst(33305)-+-{dunst}(33305) + | | `-{dunst}(33305) + | |-lxpolkit(33305)-+-{lxpolkit}(33305) + | | `-{lxpolkit}(33305) + | |-slstatus(33305) + | |-st(33340)---bash(33341)---tmux: client(33397) + | |-st(33369)---bash(33370)---pstree(33418) + | |-unclutter(33305) + | `-xautolock(33305) + |-tmux: server(22354)-+-bash(22355) + | |-bash(22513) + | |-bash(24710) + | `-bash(33398)---zathura(33414)-+-{zathura}(33414) + | |-{zathura}(33414) + | `-{zathura}(33414)</code></pre> +<h4 id="zathura-with-st-running-screen">Zathura with st Running screen</h4> +<div class="sourceCode" id="cb11"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb11-1" title="1">APPLYRULES(): applying to client org.pwmt.zathura</a> +<a class="sourceLine" id="cb11-2" title="2">TERMFORWIN(): pid: <span class="dv">33439</span>, isterminal: <span class="dv">0</span></a> +<a class="sourceLine" id="cb11-3" title="3">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33369</span> is parent of c <span class="dv">33439</span></a> +<a class="sourceLine" id="cb11-4" title="4">ISDESCPROCESS(): pid <span class="dv">33439</span></a> +<a class="sourceLine" id="cb11-5" title="5">ISDESCPROCESS(): pid <span class="dv">33431</span></a> +<a class="sourceLine" id="cb11-6" title="6">ISDESCPROCESS(): pid <span class="dv">33430</span></a> +<a class="sourceLine" id="cb11-7" title="7">ISDESCPROCESS(): pid <span class="dv">33429</span></a> +<a class="sourceLine" id="cb11-8" title="8">ISDESCPROCESS(): pid <span class="dv">33341</span></a> +<a class="sourceLine" id="cb11-9" title="9">ISDESCPROCESS(): pid <span class="dv">33340</span></a> +<a class="sourceLine" id="cb11-10" title="10">ISDESCPROCESS(): pid <span class="dv">33305</span></a> +<a class="sourceLine" id="cb11-11" title="11">ISDESCPROCESS(): pid <span class="dv">33292</span></a> +<a class="sourceLine" id="cb11-12" title="12">ISDESCPROCESS(): pid <span class="dv">33259</span></a> +<a class="sourceLine" id="cb11-13" title="13">ISDESCPROCESS(): pid <span class="dv">33254</span></a> +<a class="sourceLine" id="cb11-14" title="14">ISDESCPROCESS(): pid <span class="dv">1</span></a> +<a class="sourceLine" id="cb11-15" title="15">TERMFORWIN(): isterm: <span class="dv">1</span>, swallowing: (nil), pid: <span class="dv">33369</span>, isdescproc: <span class="dv">0</span></a> +<a class="sourceLine" id="cb11-16" title="16">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33369</span> is parent of c <span class="dv">33439</span></a> +<a class="sourceLine" id="cb11-17" title="17">ISDESCPROCESS(): pid <span class="dv">33439</span></a> +<a class="sourceLine" id="cb11-18" title="18">ISDESCPROCESS(): pid <span class="dv">33431</span></a> +<a class="sourceLine" id="cb11-19" title="19">ISDESCPROCESS(): pid <span class="dv">33430</span></a> +<a class="sourceLine" id="cb11-20" title="20">ISDESCPROCESS(): pid <span class="dv">33429</span></a> +<a class="sourceLine" id="cb11-21" title="21">ISDESCPROCESS(): pid <span class="dv">33341</span></a> +<a class="sourceLine" id="cb11-22" title="22">ISDESCPROCESS(): pid <span class="dv">33340</span></a> +<a class="sourceLine" id="cb11-23" title="23">ISDESCPROCESS(): pid <span class="dv">33305</span></a> +<a class="sourceLine" id="cb11-24" title="24">ISDESCPROCESS(): pid <span class="dv">33292</span></a> +<a class="sourceLine" id="cb11-25" title="25">ISDESCPROCESS(): pid <span class="dv">33259</span></a> +<a class="sourceLine" id="cb11-26" title="26">ISDESCPROCESS(): pid <span class="dv">33254</span></a> +<a class="sourceLine" id="cb11-27" title="27">ISDESCPROCESS(): pid <span class="dv">1</span></a> +<a class="sourceLine" id="cb11-28" title="28">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33340</span> is parent of c <span class="dv">33439</span></a> +<a class="sourceLine" id="cb11-29" title="29">ISDESCPROCESS(): pid <span class="dv">33439</span></a> +<a class="sourceLine" id="cb11-30" title="30">ISDESCPROCESS(): pid <span class="dv">33431</span></a> +<a class="sourceLine" id="cb11-31" title="31">ISDESCPROCESS(): pid <span class="dv">33430</span></a> +<a class="sourceLine" id="cb11-32" title="32">ISDESCPROCESS(): pid <span class="dv">33429</span></a> +<a class="sourceLine" id="cb11-33" title="33">ISDESCPROCESS(): pid <span class="dv">33341</span></a> +<a class="sourceLine" id="cb11-34" title="34">TERMFORWIN(): isterm: <span class="dv">1</span>, swallowing: (nil), pid: <span class="dv">33340</span>, isdescproc: <span class="dv">33340</span></a> +<a class="sourceLine" id="cb11-35" title="35">ISDESCPROCESS(): Walking up the process tree to find <span class="cf">if</span> p <span class="dv">33340</span> is parent of c <span class="dv">33439</span></a> +<a class="sourceLine" id="cb11-36" title="36">ISDESCPROCESS(): pid <span class="dv">33439</span></a> +<a class="sourceLine" id="cb11-37" title="37">ISDESCPROCESS(): pid <span class="dv">33431</span></a> +<a class="sourceLine" id="cb11-38" title="38">ISDESCPROCESS(): pid <span class="dv">33430</span></a> +<a class="sourceLine" id="cb11-39" title="39">ISDESCPROCESS(): pid <span class="dv">33429</span></a> +<a class="sourceLine" id="cb11-40" title="40">ISDESCPROCESS(): pid <span class="dv">33341</span></a> +<a class="sourceLine" id="cb11-41" title="41">MANAGE(): term: <span class="bn">0x55b88574e0e0</span></a> +<a class="sourceLine" id="cb11-42" title="42">MANAGE(): <span class="cf">if</span> term exists then c will swallow</a> +<a class="sourceLine" id="cb11-43" title="43">MANAGE(): swallowing...</a> +<a class="sourceLine" id="cb11-44" title="44">IN SWALLOW()</a> +<a class="sourceLine" id="cb11-45" title="45">SWALLOW(): org.pwmt.zathura should be swallowing <span class="dv">12</span>:<span class="dv">0</span>:bash - <span class="st">&quot;geras&quot;</span></a> +<a class="sourceLine" id="cb11-46" title="46">===========================================================</a></code></pre></div> +<pre><code>systemd(1)-+-ModemManager(1278)-+-{ModemManager}(1278) + |-login(33254)---startx(33254)---xinit(33254)-+-Xorg(33293)-+-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | |-{Xorg}(33293) + | | `-{Xorg}(33293) + | `-dwm(33305)-+-compton(33305)-+-{compton}(33305) + | | |-{compton}(33305) + | | |-{compton}(33305) + | | `-{compton}(33305) + | |-dunst(33305)-+-{dunst}(33305) + | | `-{dunst}(33305) + | |-lxpolkit(33305)-+-{lxpolkit}(33305) + | | `-{lxpolkit}(33305) + | |-slstatus(33305) + | |-st(33340)---bash(33341)---screen(33429)---screen(33430)---b+ + | |-st(33369)---bash(33370)---pstree(33443) + | |-unclutter(33305) + | `-xautolock(33305) + |-tmux: server(22354)-+-bash(22355) + | |-bash(22513) + | `-bash(24710)</code></pre> +<p>Aha! So we can see that the reason <code>isdescprocess()</code> fails every time a GUI application is launched from a terminal running tmux is because the GUI application is launched as a child of the tmux server process which itself is a child of the init process. There’s no path back up from this new GUI application to the terminal application, so there’s no way for dwm to determine which window launched the application and therefore which window should be swallowed.</p> +<p>I also tested this using screen and you can see that screen runs as a child of the terminal emulator window which is why swallowing using screen does still work whereas it doesn’t for tmux.</p> +<h3 id="summary">Summary</h3> +<p>dwm’s swallow patch is incapable of handling applications launched from terminal emulators running tmux because the patch figures out which window should be swallowed by finding the parent process of the recently launched GUI application and this fails when running tmux.</p> +<p>tmux forks applications from its server process which is a direct child of PID 1 (the init process) so there’s no direct path up the process tree from the GUI application to the terminal emulator. Therefore, dwm can’t find which terminal should be swallowed so applications launched from within tmux sessions do not swallow the terminals from which they were launched.</p> +<p>Swallowing still works with screen because screen is a child process of the terminal emulator and so are applications launched from it. In this case, there is a direct path up from the GUI application to the terminal emulator so dwm can find out which terminal to swallow.</p> +<h3 id="workaround">Workaround</h3> +<p>Right now, there is an okay-ish workaround to still get window swallowing while running tmux in the form of the <a href="https://github.com/salman-abedin/devour/">devour</a> application. It mimics swallowing using the <code>xdo</code> program but it doesn’t provide a true swallowing experience because it doesn’t maintain the properties of the window it is swallowing.</p> +<p>For example, running a GUI application from a floating terminal emulator will spawn the application as a regular tiled window whereas, with dwm’s swallow patch, the GUI application will take over the exact position, size, and properties of the terminal emulator window (as you saw in the first animation at the top of this post).</p> +<h3 id="conclusion">Conclusion</h3> +<p>Here is <a href="http://ftp.paritybit.ca/dwm-debugging">the build of dwm that I used for testing</a> with source code+printf statements, logs, and pstree outputs. I know it’s a bit messy but I thought I’d provide it anyways in case someone wants it.</p> +<p>Since this behaviour is fundamental to the design of tmux, a modification of the swallow patch would need to be done or an extra dwm patch would need to be created to solve this problem. Looking at the process trees though, I’m not sure how it would be possible to make tmux work with window swallowing.</p> +<p>dwm would somehow have to find out which tmux client corresponds to which application forked from the tmux server process. It seems to me that the approach the swallow patch uses is simply incompatible with the way that tmux works. If there is some kind of tmux API, then there might be a way to interact with the tmux server and client processes to determine which terminal window launched a certain GUI application but I don’t know how tmux’s internals work so this is just a guess. At the moment I don’t have the time to look into fixing this, so if anyone reading this wants to have a go, please feel free (and <a href="mailto:jbauer@paritybit.ca">let me know about it</a>)!</p> +<p>In the meantime, the <code>devour</code> application can be used as a rough replacement for swallowing if you are dead set on running tmux and want swallowing. For me though, I’m not too attached to tmux and I won’t be using it. The only tangible advantage it offered me over my existing workflow was better redrawing of terminal contents when the terminal gets resized which I can easily live without.</p> +<p><em>This is my fifty-fourth post for the <a href="https://social.paritybit.ca/tags/100DaysToOffload">#100DaysToOffload</a> challenge. You can learn more about this challenge over at <a href="https://100daystooffload.com">https://100daystooffload.com</a>.</em></p>]]></description> + </item> +<item> <title>Switching to Debian Sid</title> <link>https://www.paritybit.ca/blog/switching-to-debian-sid</link> <guid>https://www.paritybit.ca/blog/switching-to-debian-sid</guid> diff --git a/public/sitemap.xml b/public/sitemap.xml @@ -3,6 +3,7 @@ <url><loc>https://www.paritybit.ca</loc></url> <url><loc>https://www.paritybit.ca/home</loc></url> <url><loc>https://www.paritybit.ca/blog</loc></url> + <url><loc>https://www.paritybit.ca/blog/why-dwm-swallowing-cant-swallow-tmux</loc></url> <url><loc>https://www.paritybit.ca/blog/switching-to-debian-sid</loc></url> <url><loc>https://www.paritybit.ca/blog/my-lwn-theme</loc></url> <url><loc>https://www.paritybit.ca/blog/reflecting-on-50-days-of-blogging</loc></url> diff --git a/public/vid/noswallow-animated-thumb.webm b/public/vid/noswallow-animated-thumb.webm Binary files differ. diff --git a/public/vid/noswallow-animated.webm b/public/vid/noswallow-animated.webm Binary files differ. diff --git a/public/vid/swallow-animated-thumb.webm b/public/vid/swallow-animated-thumb.webm Binary files differ. diff --git a/public/vid/swallow-animated.webm b/public/vid/swallow-animated.webm Binary files differ.