Jekyll2023-10-06T03:28:17+00:00https://vlasiuk.com/feed.xmlAlex VlasiukAlex's homepage.Alex VlasiukFixing rewritten links in plaintext email2021-02-10T13:00:00+00:002021-02-10T13:00:00+00:00https://vlasiuk.com/code/tools/link-rewriting<p><a href="https://gist.github.com/OVlasiuk/7afbbe4fc75e27ed408a332a2b3f2494">Get urldec.py</a></p>
<p>Over the last couple of years, an increasing number of universities in the US<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> have introduced a link rewriting system to their email
services, the commonly used ones being
<a href="https://www.proofpoint.com/us/resources/data-sheets/essentials-url-defense"><em>URL Defense</em></a>
and
<a href="https://support.microsoft.com/en-us/office/advanced-outlook-com-security-for-office-365-subscribers-882d2243-eab9-4545-a58a-b36fee4a46e2"><em>Safelinks</em></a>
from <em>Outlook</em> (URLD and SL). These tools are hailed as <em>the</em> solution to scam and phishing
emails, even though there apparently is no data<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup> to support such claims. Instead, it is implied that the university is better off paying an external company than educating its own employees. That, in spite of being the ultimate education tool. As a finishing touch of mild absurdity, link rewriting is often not applied to emails originating from another university address. What better way to encourage sharing kitten websites with colleagues.</p>
<p>One consequence of the above<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup> is that some university users will be comfortable embedding in their professional correspondence links redirecting to third-party trackers, which — however <em>not malicious</em> they might be — still have <em>no</em> business snooping on anyone. Alas — all this because a link is deemed not a transparent address, but a cryptic object, to be understood only by the “experts”.</p>
<hr />
<p>Regardless of the efficacy of link rewriting, one thing is certain: it makes a
mess out of plaintext email. And if that’s the kind of email you like
reading — good old text in the terminal — you will find that a harmless homepage in
your colleague’s signature will explode to several insanity-inducing lines in
<code class="language-plaintext highlighter-rouge">mutt</code> (or your favorite plaintext MUA) after being rewritten first by URLD, and then SL.</p>
<p>Enter <code class="language-plaintext highlighter-rouge">display_filter</code>. From <code class="language-plaintext highlighter-rouge">neomutt</code>’s manual:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>display_filter
Type: command
Default: (empty)
When set, specifies a command used to filter messages. When a message is viewed
it is passed as standard input to $display_filter, and the filtered message is
read from the standard output.
</code></pre></div></div>
<p>That is, we should be able to pipe our emails through an external utility that
will filter out rewritten links and fix them.</p>
<p>Fortunately, <em>Proofpoint</em> (the company behind URLD) have kindly
<a href="https://help.proofpoint.com/Threat_Insight_Dashboard/Concepts/How_do_I_decode_a_rewritten_URL%3F">provided</a>
a Python script to decode their links. The version of the script on their
website contains a bug which causes it to break on URLs with non-Latin
characters, such as this one:</p>
<p><code class="language-plaintext highlighter-rouge">https://uk.wikipedia.org/wiki/Остроградський_Михайло_Васильович</code></p>
<p>After fixing this bug and adding the ability to rewrite <em>Safelinks</em> URLs, we
have a utility that restores the jumbled mess of a rewritten link to its original
glory. To fix links, we pipe them through the <a href="https://gist.github.com/OVlasiuk/7afbbe4fc75e27ed408a332a2b3f2494">script</a> and collect <code class="language-plaintext highlighter-rouge">stdout</code>.</p>
<p><br /></p>
<p>For incoming mail, we store <code class="language-plaintext highlighter-rouge">urldec.py</code> somewhere, make it executable, and then add</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>set display_filter="~/script/folder/urldec.py"
</code></pre></div></div>
<p>to <code class="language-plaintext highlighter-rouge">muttrc</code>. This still will not fix the outgoing emails, as
<code class="language-plaintext highlighter-rouge">display_filter</code> is used only in the pager, and not for response. To rectify that,
we can use <code class="language-plaintext highlighter-rouge">vim</code>’s ability to pipe buffers through external commands (naturally we use <code class="language-plaintext highlighter-rouge">vim</code> to edit our plaintext tirades), and add</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>set editor = "vim -c '%!~/script/folder/urldec.py'"
</code></pre></div></div>
<p>to <code class="language-plaintext highlighter-rouge">muttrc</code>. This way, <code class="language-plaintext highlighter-rouge">vim</code> will pipe the message through <code class="language-plaintext highlighter-rouge">urldec.py</code> every
time it is invoked from <code class="language-plaintext highlighter-rouge">mutt</code>.</p>
<p>As an added bonus, we now get mutt to display links properly in any
plaintext-only emails we may receive, while the web version of our email service
(Outlook?) will show rewritten gibberish.</p>
<p><br /></p>
<p>Conclusion:</p>
<ul>
<li>Running the script every time an email is opened is still very fast. In fact,
rendering html emails in <code class="language-plaintext highlighter-rouge">w3m</code> is likely the slowest step in opening them.</li>
<li>Even though using <code class="language-plaintext highlighter-rouge">mutt</code> to read email in the terminal is the sign of an invincible hacker, it is desirable not to follow obscure links, plaintext or otherwise.
<br /></li>
</ul>
<hr />
<p><br /></p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://faq.its.fsu.edu/email/spam-and-junk-email-protection/proofpoint-targeted-attack-protection/why-am-i-seeing">FSU</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://blink.ucsd.edu/technology/email/security/url-defense.html">UCSD</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="https://support.uidaho.edu/TDClient/40/Portal/KB/ArticleDet?ID=1326">UIdaho</a> <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>There seem to be some studies on other anti-phishing techniques. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>From personal experience. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>Alex VlasiukGet urldec.pyAn aside about digression2019-03-29T13:00:00+00:002019-03-29T13:00:00+00:00https://vlasiuk.com/english/aside-digression<p>If it were not entirely clear that Google Books too often offers misleading
statistics, one would lament the demise of the noble art of digression, uprooted by the surge of careless <em>asides</em>.</p>
<iframe name="ngram_chart" src="https://books.google.com/ngrams/interactive_chart?content=an+aside%2C+a+digression&year_start=1800&year_end=2000&corpus=15&smoothing=3&direct_url=t1%3B%2Can%20aside%3B%2Cc0%3B.t1%3B%2Ca%20digression%3B%2Cc0" width="900" height="500" marginwidth="0" marginheight="0" hspace="0" vspace="0" frameborder="0" scrolling="no"></iframe>Alex VlasiukIf it were not entirely clear that Google Books too often offers misleading statistics, one would lament the demise of the noble art of digression, uprooted by the surge of careless asides.Workspace grid for Cinnamon2017-12-27T13:00:00+00:002017-12-27T13:00:00+00:00https://vlasiuk.com/code/python/wspace-torus<p>I usually work with <em>many</em> windows open at the same time. Say, writing something
in \(\LaTeX\) means having editor and a couple of PDF-viewer windows open; throw
in browser, reference manager, a terminal or two, and you easily end up at 8-10
windows. Unfortunately, switching between as many as 5 on a single workspace is
already rather tedious, be it by clicking and dragging, or by <code class="language-plaintext highlighter-rouge">Alt-Tab</code>bing your
way around. One would think that a good window manager, <a href="https://en.wikipedia.org/wiki/Cinnamon_(software)">Cinnamon</a> being
an example of such, should allow to fix this.</p>
<p>For some reason though, Cinnamon only provides left/right inter-workspace motions out of
the box; I find it more natural to think of the workspaces as sitting on a 2d grid. Apparently, there <em>are</em> people sharing this sentiment: a quick search reveals a <a href="https://unix.stackexchange.com/questions/127897/cinnamon-2-2-how-to-add-custom-shortuct-move-workspace-up">discussion</a> on unix stackexchange, where somebody mentions using 16(!) workspaces. Besides, there already is an applet that purports to do precisely what I want; the fact that it conflicts with Expo and does not allow to move windows around makes it somewhat suboptimal.</p>
<p>The <del>suitable solution</del> hack was to write a basic wrapper around <code class="language-plaintext highlighter-rouge">wmctrl</code> in
Python and to tie shortcuts to it using the Cinnamon config. There is also
<code class="language-plaintext highlighter-rouge">xdotool</code>, which would probably do the trick as well.</p>
<p>Enabling the “grid” option in Expo further means being able to get an overview of all
the windows like so:</p>
<p><img src="https://vlasiuk.com/assets/images/Expo9.png" alt="Expo view" /></p>
<p>I must add that my default config only includes 4 workspaces; nothing outrageous like shown here. The
<code class="language-plaintext highlighter-rouge">wmctrl</code> allows to both jump between workspaces and move windows around:</p>
<blockquote>
<p>wspacetorus.py -d up</p>
</blockquote>
<blockquote>
<p>wspacetorus.py -t left</p>
</blockquote>
<p>etc., with “-d” for “desktop” and “-t” for “to (desktop)” performing jumping and moving the active window in the specified direction. The script is below and in the <a href="https://github.com/OVlasiuk/snippets">repo</a>. I imagine it would run on any WM compatible with <code class="language-plaintext highlighter-rouge">wmctrl</code> so, according to the manpage, any “EWMH/NetWM compatible X Window Manager”.</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
</pre></td><td class="code"><pre><span class="c1">#!/usr/bin/env python
</span>
<span class="c1"># This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">optparse</span> <span class="kn">import</span> <span class="n">OptionParser</span>
<span class="n">help_text</span> <span class="o">=</span> <span class="s">"""
Adapts inter-workspace motions in any wmctrl-compatible window manager as if the
workspaces are positioned on a X_WORKSPACESxY_WORKSPACES flat torus: periodic in
x- and y-coordinate. Indexing is row-major. Defaults to 2x2 workspaces.
Usage:
Install wmctrl;
to jump to an adjacent workspace, do
> wspacetorus.py -d DIRECTION
with DIRECTION in ['left', 'right', 'top', 'bottom']; to move the current
window to such workspace, do
> wspacetorus.py -t DIRECTION
to use with 3x2 (columns-rows) grid, do
> wspacetorus.py -t DIRECTION -x 3 -y 2
To see more options, call
> wpacetorus.py -h
Check https://github.com/OVlasiuk/snippets for updates.
You may also want to look at the plugin
https://cinnamon-spices.linuxmint.com/applets/view/116
"""</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">)</span> <span class="o"><</span> <span class="mi">2</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">help_text</span><span class="p">)</span>
<span class="nb">exit</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_current_desktop</span><span class="p">():</span>
<span class="n">desktopstring</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">popen</span><span class="p">(</span><span class="s">'wmctrl -d'</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
<span class="n">regcurrent</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nb">compile</span><span class="p">(</span><span class="sa">r</span><span class="s">"\d+.*\*"</span><span class="p">)</span>
<span class="n">regdigits</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nb">compile</span><span class="p">(</span><span class="sa">r</span><span class="s">"\d+"</span><span class="p">)</span>
<span class="n">strcurrentspan</span> <span class="o">=</span> <span class="n">regcurrent</span><span class="p">.</span><span class="n">search</span><span class="p">(</span><span class="n">desktopstring</span><span class="p">).</span><span class="n">span</span><span class="p">()</span>
<span class="n">strcurrent</span> <span class="o">=</span> <span class="n">desktopstring</span><span class="p">[</span><span class="n">strcurrentspan</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span><span class="n">strcurrentspan</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
<span class="n">strdigitsspan</span> <span class="o">=</span> <span class="n">regdigits</span><span class="p">.</span><span class="n">match</span><span class="p">(</span><span class="n">strcurrent</span><span class="p">).</span><span class="n">span</span><span class="p">()</span>
<span class="n">strdigits</span> <span class="o">=</span> <span class="n">strcurrent</span><span class="p">[</span><span class="n">strdigitsspan</span><span class="p">[</span><span class="mi">0</span><span class="p">]:</span><span class="n">strdigitsspan</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
<span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="n">strdigits</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">set_current_desktop</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
<span class="n">s</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">(</span><span class="s">'wmctrl -s '</span> <span class="o">+</span> <span class="n">s</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">move_to_desktop</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
<span class="n">s</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="n">os</span><span class="p">.</span><span class="n">system</span><span class="p">(</span><span class="s">'wmctrl -r :ACTIVE: -t '</span> <span class="o">+</span> <span class="n">s</span> <span class="o">+</span> <span class="s">' && wmctrl -s '</span> <span class="o">+</span> <span class="n">s</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_options</span><span class="p">(</span><span class="n">parser</span><span class="p">):</span>
<span class="s">""" Define command line options."""</span>
<span class="n">parser</span><span class="p">.</span><span class="n">add_option</span><span class="p">(</span>
<span class="s">"-d"</span><span class="p">,</span> <span class="s">"--desktop"</span><span class="p">,</span> <span class="n">dest</span><span class="o">=</span><span class="s">"desktop"</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s">"Jump to an adjacent workspace, given by 'up', 'down', 'left',</span><span class="se">\
</span><span class="s"> 'right'."</span><span class="p">)</span>
<span class="n">parser</span><span class="p">.</span><span class="n">add_option</span><span class="p">(</span>
<span class="s">"-t"</span><span class="p">,</span>
<span class="s">"--todesktop"</span><span class="p">,</span>
<span class="n">dest</span><span class="o">=</span><span class="s">"todesktop"</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s">"Move the current window to an adjacent workspace, given by 'up',</span><span class="se">\
</span><span class="s"> 'down', 'left', 'right'."</span><span class="p">)</span>
<span class="n">parser</span><span class="p">.</span><span class="n">add_option</span><span class="p">(</span>
<span class="s">"-x"</span><span class="p">,</span>
<span class="s">"--xworkspaces"</span><span class="p">,</span>
<span class="n">dest</span><span class="o">=</span><span class="s">"xworkspaces"</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s">"The number of columns in the workspace grid. Default: 2."</span><span class="p">)</span>
<span class="n">parser</span><span class="p">.</span><span class="n">add_option</span><span class="p">(</span>
<span class="s">"-y"</span><span class="p">,</span>
<span class="s">"--yworkspaces"</span><span class="p">,</span>
<span class="n">dest</span><span class="o">=</span><span class="s">"yworkspaces"</span><span class="p">,</span>
<span class="n">default</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
<span class="n">help</span><span class="o">=</span><span class="s">"The number of rows in the workspace grid. Default: 2."</span><span class="p">)</span>
<span class="n">options</span><span class="p">,</span> <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">()</span>
<span class="k">return</span> <span class="n">options</span><span class="p">.</span><span class="n">desktop</span><span class="p">,</span> <span class="n">options</span><span class="p">.</span><span class="n">todesktop</span><span class="p">,</span> <span class="n">options</span><span class="p">.</span><span class="n">xworkspaces</span><span class="p">,</span>\
<span class="n">options</span><span class="p">.</span><span class="n">yworkspaces</span>
<span class="k">def</span> <span class="nf">numerize</span><span class="p">(</span><span class="n">inp</span><span class="p">,</span> <span class="n">X_WORKSPACES</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s">'left'</span><span class="p">:</span> <span class="n">X_WORKSPACES</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span>
<span class="s">'right'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="s">'up'</span><span class="p">:</span> <span class="o">-</span><span class="n">X_WORKSPACES</span><span class="p">,</span>
<span class="s">'down'</span><span class="p">:</span> <span class="n">X_WORKSPACES</span>
<span class="p">}.</span><span class="n">get</span><span class="p">(</span><span class="n">inp</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">gridparse</span><span class="p">(</span><span class="n">cdesk</span><span class="p">,</span> <span class="n">X_WORKSPACES</span><span class="p">):</span>
<span class="n">cdeskx</span> <span class="o">=</span> <span class="n">cdesk</span> <span class="o">%</span> <span class="n">X_WORKSPACES</span>
<span class="n">cdesky</span> <span class="o">=</span> <span class="p">(</span><span class="n">cdesk</span> <span class="o">-</span> <span class="n">cdeskx</span><span class="p">)</span> <span class="o">//</span> <span class="n">X_WORKSPACES</span>
<span class="k">return</span> <span class="n">cdeskx</span><span class="p">,</span> <span class="n">cdesky</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">desktop</span><span class="p">,</span> <span class="n">todesktop</span><span class="p">,</span> <span class="n">X_WORKSPACES</span><span class="p">,</span> <span class="n">Y_WORKSPACES</span><span class="p">):</span>
<span class="k">if</span> <span class="n">desktop</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">cdesk</span> <span class="o">=</span> <span class="n">get_current_desktop</span><span class="p">()</span>
<span class="n">cdeskx</span><span class="p">,</span> <span class="n">cdesky</span> <span class="o">=</span> <span class="n">gridparse</span><span class="p">(</span><span class="n">cdesk</span><span class="p">,</span> <span class="n">X_WORKSPACES</span><span class="p">)</span>
<span class="n">delta</span> <span class="o">=</span> <span class="n">numerize</span><span class="p">(</span><span class="n">desktop</span><span class="p">,</span> <span class="n">X_WORKSPACES</span><span class="p">)</span>
<span class="n">deltax</span><span class="p">,</span> <span class="n">deltay</span> <span class="o">=</span> <span class="n">gridparse</span><span class="p">(</span><span class="n">delta</span><span class="p">,</span> <span class="n">X_WORKSPACES</span><span class="p">)</span>
<span class="n">cdeskx</span> <span class="o">=</span> <span class="p">(</span><span class="n">cdeskx</span> <span class="o">+</span> <span class="n">deltax</span><span class="p">)</span> <span class="o">%</span> <span class="n">X_WORKSPACES</span>
<span class="n">cdesky</span> <span class="o">=</span> <span class="p">(</span><span class="n">cdesky</span> <span class="o">+</span> <span class="n">deltay</span><span class="p">)</span> <span class="o">%</span> <span class="n">Y_WORKSPACES</span>
<span class="n">cdesk</span> <span class="o">=</span> <span class="n">cdesky</span> <span class="o">*</span> <span class="n">X_WORKSPACES</span> <span class="o">+</span> <span class="n">cdeskx</span>
<span class="n">set_current_desktop</span><span class="p">(</span><span class="n">cdesk</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">todesktop</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">cdesk</span> <span class="o">=</span> <span class="n">get_current_desktop</span><span class="p">()</span>
<span class="n">cdeskx</span><span class="p">,</span> <span class="n">cdesky</span> <span class="o">=</span> <span class="n">gridparse</span><span class="p">(</span><span class="n">cdesk</span><span class="p">,</span> <span class="n">X_WORKSPACES</span><span class="p">)</span>
<span class="n">delta</span> <span class="o">=</span> <span class="n">numerize</span><span class="p">(</span><span class="n">todesktop</span><span class="p">,</span> <span class="n">X_WORKSPACES</span><span class="p">)</span>
<span class="n">deltax</span><span class="p">,</span> <span class="n">deltay</span> <span class="o">=</span> <span class="n">gridparse</span><span class="p">(</span><span class="n">delta</span><span class="p">,</span> <span class="n">X_WORKSPACES</span><span class="p">)</span>
<span class="n">cdeskx</span> <span class="o">=</span> <span class="p">(</span><span class="n">cdeskx</span> <span class="o">+</span> <span class="n">deltax</span><span class="p">)</span> <span class="o">%</span> <span class="n">X_WORKSPACES</span>
<span class="n">cdesky</span> <span class="o">=</span> <span class="p">(</span><span class="n">cdesky</span> <span class="o">+</span> <span class="n">deltay</span><span class="p">)</span> <span class="o">%</span> <span class="n">Y_WORKSPACES</span>
<span class="n">cdesk</span> <span class="o">=</span> <span class="n">cdesky</span> <span class="o">*</span> <span class="n">X_WORKSPACES</span> <span class="o">+</span> <span class="n">cdeskx</span>
<span class="n">move_to_desktop</span><span class="p">(</span><span class="n">cdesk</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">parser</span> <span class="o">=</span> <span class="n">OptionParser</span><span class="p">()</span>
<span class="n">desktop</span><span class="p">,</span> <span class="n">todesktop</span><span class="p">,</span> <span class="n">X_WORKSPACES</span><span class="p">,</span> <span class="n">Y_WORKSPACES</span> <span class="o">=</span> <span class="n">get_options</span><span class="p">(</span><span class="n">parser</span><span class="p">)</span>
<span class="n">main</span><span class="p">(</span><span class="n">desktop</span><span class="p">,</span> <span class="n">todesktop</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">X_WORKSPACES</span><span class="p">),</span> <span class="nb">int</span><span class="p">(</span><span class="n">Y_WORKSPACES</span><span class="p">))</span>
</pre></td></tr></tbody></table></code></pre></figure>Alex VlasiukI usually work with many windows open at the same time. Say, writing something in \(\LaTeX\) means having editor and a couple of PDF-viewer windows open; throw in browser, reference manager, a terminal or two, and you easily end up at 8-10 windows. Unfortunately, switching between as many as 5 on a single workspace is already rather tedious, be it by clicking and dragging, or by Alt-Tabbing your way around. One would think that a good window manager, Cinnamon being an example of such, should allow to fix this.Speed-data-ing with R2017-10-10T22:14:01+00:002017-10-10T22:14:01+00:00https://vlasiuk.com/r/code/speeddataing<p>Due to p̶o̶p̶u̶l̶a̶r̶ some demand for the slides of my last week talk at <a href="https://my.vanderbilt.edu/undergradseminar/">the
Undergraduate Seminar in Mathematics</a>, they are now available <a href="https://vlasiuk.com/assets/slides/Rslides.html"><strong>here</strong></a>.</p>
<p>Protip: pressing <code class="language-plaintext highlighter-rouge">Esc</code> while in the slideshow mode brings you to the overview mode, pressing it one more
time returns back to the slideshow.</p>Alex VlasiukDue to p̶o̶p̶u̶l̶a̶r̶ some demand for the slides of my last week talk at the Undergraduate Seminar in Mathematics, they are now available here.The code is up!2017-10-10T22:14:01+00:002017-10-10T22:14:01+00:00https://vlasiuk.com/code/code-is-up<p>The code is up! O frabjous day! Callooh! Callay<a href="https://en.wikipedia.org/wiki/Jabberwocky">!</a></p>
<p>The aggregated code page is up now, as shown by the link in the top right corner
(<em>up</em> refers to that corner in fact). Over time, things probably will keep
appearing there.</p>Alex VlasiukThe code is up! O frabjous day! Callooh! Callay!