joe di castrohttp://joedicastro.comSat, 26 Nov 2011 00:00:00 +0100Productividad en el escritorio Linux: Xmonadhttp://joedicastro.com/productividad-en-el-escritorio-linux-xmonad.html<p><a href="http://xmonad.org">xmonad</a> es un <a href="http://joedicastro.com/productividad-en-el-escritorio-linux-tiling.html">Gestor de ventanas de mosaico</a>, uno de los más empleados y de los más potentes. Después de haber probado varios (dwm, bluetile, wmii y el plugin Compiz Grid) llevo ya unos cuantos meses trabajando con él y posiblemente siga conmigo mucho, mucho tiempo. Una vez que trabajas con un <strong>tiling window manager</strong> es muy difícil que echarse atrás, volver a un gestor de ventanas flotantes tradicional es casi impensable. Probaré uno más, <a href="http://awesome.naquadah.org">awesome</a>, que promete mucho y solo si este logra convencerme del cambio, abandonaré Xmonad.</p> <p>¿Pero que tiene de especial un <strong>twm</strong> como <strong>Xmonad</strong> para haber decidido abandonar la gestión de ventanas por defecto de Compiz, Gnome Shell, Unity, Kwin, etc y apostar por él? Cuando uno está cansado de perder el tiempo con maniobras y decisiones triviales -ajustar las ventanas en la pantalla, que si el navegador por aquí, que si este terminal por allá, etc y cambiando continuamente la mano del teclado al ratón y viceversa- pues entonces decides que hay que darle una oportunidad a un twm, que sea el gestor de ventanas quien haga el trabajo sucio por ti.</p> <p>¿Y hasta que punto resuelve bien este problema un twm como Xmonad? En este vídeo de xmonad trabajando con mi configuración, uno se puede hacer una idea de lo que es capaz.</p> <div style="text-align:center"> <iframe src="http://player.vimeo.com/video/33164152?title=0&amp;byline=0&amp;portrait=0" width="700" height="438"> </iframe> <p>Esbozo de las posibilidades de un Tiling Window Manager como Xmonad trabajando conjuntamente con Gnome 3 y Unity 2D sobre un Ubuntu 11.10. Recomiendo ver en alta resolución y a pantalla completa.</p> </div> <p><strong>Xmonad</strong> no necesita de un entorno de escritorio para trabajar, se puede iniciar una sesión directamente en él, al igual que la mayoría de gestores de ventanas de mosaico y otros gestores de ventanas como <a href="http://es.wikipedia.org/wiki/Openbox">OpenBox</a>. En el vídeo se puede apreciar que lo estoy empleando conjuntamente con Unity, en concreto <strong>Unity 2D</strong>. Esto es así porque emplear Xmonad (u otro twm) de forma autónoma te obliga a montar una serie de servicios que vienen montados por defecto normalmente en un escritorio, como salvapantallas, fondo de pantalla, bandeja de sistema, gestor de red, notificaciones, control de volumen, soporte impresoras, etc. Como estoy contento con el funcionamiento en general de Gnome, lo mejor es no reinventar la rueda y aprovechar lo mejor de los dos mundos empleando ambos a la vez. Anteriormente con Gnome 2, lo tenia funcionando sin Unity, pero con Gnome 3 me encuentro con la desagradable situación de que Gnome classic (el fallback mode) tiene un panel que es un autentico desastre y que además no es redimensionable en altura (tiene 30px y yo lo quiero con 21px). Actualmente hay una <a href="http://askubuntu.com/questions/69576/how-to-customize-the-gnome-classic-panel/76884#76884">forma de arreglar esto</a>, pero de momento lo tengo montado todo con Unity 2D y no hecho nada en falta. Eso si, de Unity solo empleo el panel, el Global Menu y la base de Gnome 3, lo demás lo descarto. Resumiendo, que básicamente sustituyo <code>metacity</code> por <code>xmonad</code>.</p> <h2 id="xmonad_con_unity_2d">Xmonad con Unity 2D</h2> <p>Aquí contaré como tengo configurado actualmente <strong>Xmonad</strong> con <strong>Ubuntu 11.10</strong> (Oneiric Ocelot) y con <strong>Gnome 3</strong> y el panel de <strong>Unity 2D</strong> (<code>unity-2p-panel</code>). Si alguien continua con Gnome 2D o alguna versión anterior de Ubuntu puede revisar <a href="http://markhansen.co.nz/xmonad-ubuntu-lucid/">este enlace</a>, la base es muy parecida, y luego solo habría que aplicar mi configuración (u otra).</p> <p>Lo primero que se necesita es instalar xmonad, que en Ubuntu viene siendo así:</p> <div class="codehilite"><pre><span class="nv">$ </span>sudo apt-get install xmonad </pre></div> <p>A continuación crearíamos un fichero <code>~/.xmonad/xmonad.hs</code> de configuración básica</p> <div class="codehilite"><pre><span class="nv">$ </span>mkdir ~/.xmonad <span class="c"># El carácter ~ se obtiene pulsando Alt Gr + 4</span> <span class="nv">$ </span>vi ~/.xmonad/xmonad.hs <span class="c"># Puedes emplear gedit en vez de vi si lo prefieres</span> </pre></div> <p>e incluiríamos lo siguiente dentro de él:</p> <div class="codehilite"><pre><span class="kr">import</span> <span class="nn">XMonad</span> <span class="kr">import</span> <span class="nn">XMonad.Config.Gnome</span> <span class="nf">myManageHook</span> <span class="ow">=</span> <span class="n">composeAll</span> <span class="p">(</span> <span class="p">[</span> <span class="n">manageHook</span> <span class="n">gnomeConfig</span> <span class="p">,</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Unity-2d-panel&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Unity-2d-launcher&quot;</span> <span class="o">--&gt;</span> <span class="n">doFloat</span> <span class="p">])</span> <span class="nf">main</span> <span class="ow">=</span> <span class="n">xmonad</span> <span class="n">gnomeConfig</span> <span class="p">{</span> <span class="n">manageHook</span> <span class="ow">=</span> <span class="n">myManageHook</span> <span class="p">}</span> </pre></div> <p>Ahora tendríamos que recompilar Xmonad, es algo necesario cada vez que realizamos algún cambio en la configuración, pero es un proceso que no suele llevar más de 5 segundos (y Xmonad se puede reiniciar sin tener que cerrar la sesión):</p> <div class="codehilite"><pre><span class="nv">$ </span>xmonad --recompile </pre></div> <p>Lo siguiente es crear los ficheros necesarios para crear una nueva sesión de Gnome. Creamos primero el fichero que define nuestra nueva sesión:</p> <div class="codehilite"><pre><span class="nv">$ </span>sudo vi /usr/share/gnome-session/sessions/xmonad.session </pre></div> <p>y dentro añadimos esto:</p> <div class="codehilite"><pre><span class="k">[GNOME Session]</span> <span class="na">Name</span><span class="o">=</span><span class="s">Xmonad Unity 2D</span> <span class="na">RequiredComponents</span><span class="o">=</span><span class="s">gnome-settings-daemon;</span> <span class="na">RequiredProviders</span><span class="o">=</span><span class="s">windowmanager;panel;</span> <span class="na">DefaultProvider-windowmanager</span><span class="o">=</span><span class="s">xmonad</span> <span class="na">DefaultProvider-panel</span><span class="o">=</span><span class="s">unity-2d-panel</span> </pre></div> <p>a continuación creamos el fichero que inicia esta sesión:</p> <div class="codehilite"><pre><span class="nv">$ </span>sudo vi /usr/share/xsessions/xmonad-unity-session.desktop </pre></div> <p>con este contenido:</p> <div class="codehilite"><pre><span class="k">[Desktop Entry]</span> <span class="na">Name</span><span class="o">=</span><span class="s">XMonad Unity 2D</span> <span class="na">Comment</span><span class="o">=</span><span class="s">Tiling window manager</span> <span class="na">TryExec</span><span class="o">=</span><span class="s">/usr/bin/gnome-session</span> <span class="na">Exec</span><span class="o">=</span><span class="s">gnome-session --session=xmonad</span> <span class="na">Type</span><span class="o">=</span><span class="s">XSession</span> </pre></div> <p>Con esto estaría ya montada una sesión con la configuración por defecto de Xmonad funcionado sobre Gnome y empleando el panel de Unity 2D. Ahora solo tendrías que cerrar la sesión e iniciar la sesión con <em>Xmonad Unity 2D</em>.</p> <blockquote> <p><strong>Advertencia:</strong> Por defecto verás que se ha creado automáticamente una sesión llamada Xmonad al instalar este. Bien, si vas iniciar esa sesión, será mejor que sepas que para salir tienes que pulsar <strong>Win + Mayús + Q</strong>, porque será una sesión donde solo tendrás Xmonad, y es la única forma de cerrar la sesión.</p> </blockquote> <p>Si queréis hacer alguna prueba con esta configuración básica por defecto, es mejor tener a mano una referencia con las combinaciones de teclas disponibles, aquí hay un <a href="http://haskell.org/wikiupload/b/b8/Xmbindings.png">mapa del teclado</a> de referencia que será muy útil.</p> <h2 id="mi_configuraci+n">Mi configuración</h2> <p>Para poder emplear la configuración que se puede ver en el vídeo simplemente habría que editar el fichero <code>~/.xmonad/xmonad.hs</code> y sustituir su contenido por el de mi fichero de configuración. El contenido del fichero lo incluyo al final del articulo para no entorpecer la lectura del mismo.</p> <h3 id="atajos_de_teclado">Atajos de teclado</h3> <p>Al comienzo del fichero enumero las combinaciones de teclas empleadas en mi configuración, que difieren de las combinaciones por defecto y se asemejan de algún modo a las que estaba habituado en Gnome y son más intuitivas para los que estamos acostumbrados a él. Aunque las que se emplean por defecto también me son muy familiares al estar basadas en parte en Vim. Voy a detallar algunas de estas combinaciones.</p> <h4 id="lanzar_aplicaciones">Lanzar aplicaciones</h4> <div class="codehilite"><pre><span class="c1">-- Win + Enter Terminal</span> <span class="c1">-- Win + F1 Nautilus</span> <span class="c1">-- Win + F2 Firefox (single instance)</span> <span class="c1">-- Win + F3 Thunderbird (single instance)</span> <span class="c1">-- Win + F4 RSSOwl (single instance)</span> <span class="c1">-- Win + F5 Hotot (single instance)</span> <span class="c1">-- Win + F6 ncmpcpp - MPD player</span> <span class="c1">-- Win + F7 taskwarrior</span> <span class="c1">-- Win + F8 PAC (single instance)</span> <span class="c1">-- Win + F9 Aptana (single instance)</span> <span class="c1">-- Win + F10 Xmind (single instance)</span> <span class="c1">-- Win + F11 Dbeaver (single instance)</span> <span class="c1">-- Win + F12 VirtualBox (single instance)</span> </pre></div> <p>Lo primero que se puede ver es una serie de atajos para lanzar aplicaciones empleando la tecla <strong>Win</strong> más las teclas de función. La tecla <strong>Win</strong> (también llamada <strong>Super</strong>) es la tecla maestra por defecto de Xmonad (<code>mod4Mask</code>) y es la que se emplea en combinación con otras para realizar todo tipo de acciones. Estas combinaciones me permiten lanzar las aplicaciones que uso con más frecuencia con un par de teclas.</p> <p>Algunas aparecen con la frase <em>single instance</em> entre paréntesis, esto quiere decir que de estas aplicaciones solamente se abrirá una ventana. De hecho, lo que ocurre si volvemos a pulsar la combinación de teclas que abre la aplicación (una vez que esta ya está abierta) es que en lugar de abrir una nueva, nos movemos a la ventana ya abierta, esté en el escritorio que esté. Lo que es muy útil para localizar la ventana de una de estas aplicaciones de forma inmediata.</p> <p>Adicionalmente la combinación <strong>Alt + Enter</strong> nos permite abrir una ventana del terminal. En este caso lo tengo predeterminado para que abra una venta de <code>terminator</code></p> <h4 id="consola_emergente">Consola emergente</h4> <div class="codehilite"><pre><span class="c1">-- Win + Space Run or Raise Shell Prompt</span> <span class="c1">-- Win + Shift + Space Run Shell Prompt</span> <span class="c1">-- Win + Control + Space Window Prompt</span> </pre></div> <p>A continuación podemos ver las teclas dedicadas a la consola emergente inferior (al estilo de Guake, Yakuake o Tilda) y que según la combinación pulsada realizara una acción u otra. La consola cuenta con auto-completado de texto, con lo cual escribiendo las primeras letras se nos muestran todas las coincidencias disponibles, a través de las que podemos desplazarnos con el tabulador.</p> <ul> <li> <p><strong>Win + Espacio</strong> nos despliega una consola que nos permite lanzar una aplicación de igual modo que los lanzadores gráficos. Si la aplicación ya se encuentra abierta, nos dirige a la ventana de la misma.</p> </li> <li> <p><strong>Win + Mayús + Espacio</strong> ejecuta el comando que escribamos. Similar a la ventana de ejecutar comando de Gnome (<em>Alt + F2</em> en Gnome)</p> </li> <li> <p><strong>Win + Control + Espacio</strong> nos muestra las ventanas que están abiertas en ese momento. Seleccionando una nos envía directamente a ella. Gracias a esto, localizar una ventana abierta entre los múltiples escritorios es cuestión de segundos. Si conocemos el nombre de la ventana podemos filtrarla escribiendo las primeras letras con el teclado. Imagina una sesión en la que tenemos abiertas más de 10 aplicaciones en varios escritorios distintos, esto nos permite localizar una ventana en segundos, es muy útil.</p> </li> </ul> <h4 id="reiniciar_xmonad">Reiniciar Xmonad</h4> <p>Con la combinación <strong>Win + q</strong> lo que hacemos es reiniciar Xmonad sin necesidad de reiniciar la sesión. Esto es muy útil cuando introducimos cambios en la configuración, después de guardar el fichero y ejecutar <code>xmonad --recompile</code> en un terminal, pulsamos esta combinación y los cambios se reflejaran de forma inmediata.</p> <h2 id="transparencias">Transparencias</h2> <p>Una de las carencias de Xmonad es que no incorpora un <a href="http://es.wikipedia.org/wiki/Gestor_de_composici%C3%B3n_de_ventanas">gestor de composición</a> por lo que si queremos manejar transparencias en nuestros terminales, notificaciones, ventanas, etc, debemos emplear uno externo. Para esto empleo <code>xcompmgr</code> que funciona perfectamente y es muy ligero. Como aún conservo las otras sesiones de Ubuntu (Unity, Unity 2 D, Gnome Shell, etc) lo que hago para que solo se ejecute en la sesión de <em>Xmonad Unity 2D</em> es emplear este script bash:</p> <div class="codehilite"><pre><span class="c">#!/bin/bash</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$DESKTOP_SESSION</span> <span class="o">=</span> <span class="s2">&quot;xmonad-unity-session&quot;</span> <span class="o">]</span>; <span class="k">then</span> <span class="k"> </span>xcompmgr -I1 -O1 -Ff; <span class="k">fi</span> </pre></div> <p>Luego añado el script a las aplicaciones que se ejecutan al inicio de la sesión y listo.</p> <h2 id="bandeja_del_sistema">Bandeja del sistema</h2> <p>Una de las carencias de Unity en la versión 11.10 es que no han incorporado una bandeja del sistema donde alojar los iconos de aplicaciones que aún no tienen soporte para los <em>indicadores</em>. Se supone que debería añadirlos de forma automática al panel, incluso hay una configuración a través de <code>dconf</code> para habilitar esto, pero no funciona para todas las aplicaciones. Para añadir un <em>systray</em> empleo un script parecido al anterior para lanzar la aplicación <code>trayer</code> en esta sesión.</p> <div class="codehilite"><pre><span class="c">#!/bin/bash</span> <span class="nv">MYARGS</span><span class="o">=</span><span class="s2">&quot;--edge top --widthtype request --transparent true --alpha 0 --distancefrom right --distance 1100 --height 24 --tint 0x4c4a44 --align right&quot;</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$DESKTOP_SESSION</span> <span class="o">=</span> <span class="s2">&quot;xmonad-unity-session&quot;</span> <span class="o">]</span>; <span class="k">then</span> <span class="k"> </span>sleep 24 <span class="o">&amp;&amp;</span> trayer <span class="nv">$MYARGS</span>; <span class="k">fi</span> </pre></div> <br /> <h2 id="como_personalizar_xmonad_haskell">Como personalizar Xmonad: Haskell</h2> <p>El mayor inconveniente de Xmonad (y a la vez una de sus ventajas) es que la configuración es necesario hacerla a través del mismo lenguaje de programación en el que está creado: <a href="http://haskell.org/haskellwiki/Haskell">Haskell</a>. Esto es una importante barrera de entrada para los que no conocen el lenguaje, yo incluido, pero a base de ir probando algunas de las configuraciones aportadas y el prueba y error, puedes salir del paso muy satisfactoriamente. Con un poco de práctica y estudiando un poco el lenguaje (que de entrada no es de los más fáciles de leer) y gracias a la documentación disponible, puedes conseguir exactamente lo que quieres. Y esa es precisamente también su mayor ventaja, que puedes llegar a un gran nivel de personalización si conoces Haskell. Además Haskell le proporciona una gran estabilidad y extensibilidad.</p> <p>Afortunadamente hay varios recursos disponibles que nos hacen el trabajo más fácil, porque Xmonad está aceptablemente bien documentado (en Inglés).</p> <ul> <li><a href="http://haskell.org/haskellwiki/Xmonad/Screenshots">Pantallazos</a></li> <li><a href="http://haskell.org/haskellwiki/Xmonad/Config_archive">Ficheros de configuración</a></li> <li><a href="http://haskell.org/haskellwiki/Xmonad">Wiki</a></li> <li><a href="http://xmonad.org/xmonad-docs/xmonad/index.html">API Docs</a></li> <li><a href="http://xmonad.org/xmonad-docs/xmonad-contrib/index.html">API Extensiones Docs</a></li> </ul> <h2 id="xmonadhs">Xmonad.hs</h2> <p>Este es mi mi fichero de configuración de Xmonad:</p> <div class="codehilite"><pre><span class="c1">-- joe di castro&#39;s xmonad.hs</span> <span class="c1">-- Based on rupa&#39;s xmonad.hs, https://github.com/rupa/xmonad</span> <span class="c1">-- =============================================================== KEY BINDINGS</span> <span class="c1">------------------------------------------------- Launch (or Raise) Applications</span> <span class="c1">-- Win + Enter Terminal</span> <span class="c1">-- Win + F1 Nautilus</span> <span class="c1">-- Win + F2 Firefox (single instance)</span> <span class="c1">-- Win + F3 Thunderbird (single instance)</span> <span class="c1">-- Win + F4 RSSOwl (single instance)</span> <span class="c1">-- Win + F5 Hotot (single instance)</span> <span class="c1">-- Win + F6 ncmpcpp - MPD player</span> <span class="c1">-- Win + F7 taskwarrior</span> <span class="c1">-- Win + F8 PAC (single instance)</span> <span class="c1">-- Win + F9 Aptana (single instance)</span> <span class="c1">-- Win + F10 Xmind (single instance)</span> <span class="c1">-- Win + F11 Dbeaver (single instance)</span> <span class="c1">-- Win + F12 VirtualBox (single instance)</span> <span class="c1">----------------------------------------------------------- Shell/Window prompts</span> <span class="c1">-- Win + Space Run or Raise Shell Prompt</span> <span class="c1">-- Win + Shift + Space Run Shell Prompt</span> <span class="c1">-- Win + Control + Space Window Prompt</span> <span class="c1">--------------------------------------------------------------------- Navigation</span> <span class="c1">-- Win + [1..9] Switch to workspace N</span> <span class="c1">-- Win + Shift + [1..9] Move client to workspace N</span> <span class="c1">-- Ctrl + Alt + Left/Right Previous/Next workspace</span> <span class="c1">-- Alt + Tab Focus next window</span> <span class="c1">-- Alt + Shift + Tab Focus previous window</span> <span class="c1">-------------------------------------------------------------- Window management</span> <span class="c1">-- Win + Shift + Left Move window to previous workspace</span> <span class="c1">-- Win + Shift + Right Move window to next workspace</span> <span class="c1">-- Win + Control + Left Move window to previous empty workspace</span> <span class="c1">-- Win + Control + Right Move window to next empty workspace</span> <span class="c1">-- Ctrl + Up/Down Move focused window up/down</span> <span class="c1">-- Win + m Toggle focused window Full Screen</span> <span class="c1">-- Win + n Refresh</span> <span class="c1">-- Win + - Move focused windows to master area</span> <span class="c1">-- Win + w (or Alt + F4) Close focused window</span> <span class="c1">-- Win + t Back to tiling (unfloat floating window)</span> <span class="c1">-- Win + Shift + t Back All to tiling (unfloat ALL windows)</span> <span class="c1">------------------------------------------------------------- Layout management</span> <span class="c1">-- Win + Tab Rotate layouts</span> <span class="c1">-- Win + Left/Right Shrink/Expand the master area</span> <span class="c1">-- Win + Up/Down Mirror Shrink/expand</span> <span class="c1">-- Win + ,/. Increment/Deincrement 1 window in master area</span> <span class="c1">-- Win + f Hide/Unhide the gnome-panel/status bar</span> <span class="c1">-- Win + Shift + n Reset current workspace to main layout</span> <span class="c1">------------------------------------------------------------------ Mosaic Layout</span> <span class="c1">-- Win + a/z Taller/Wider</span> <span class="c1">-- Win + Control + n Reset</span> <span class="c1">------------------------------------------------------------------------- Others</span> <span class="c1">-- Print Screen Capture screen</span> <span class="c1">-- Win + q Restart XMonad</span> <span class="c1">-- Win + Shift + q Close gnome session dialog</span> <span class="c1">-- ============================================================== MOUSE BINDINGS</span> <span class="c1">-- Win + Button 1 Float Window and Move by dragging</span> <span class="c1">-- Win + Button 2 Raise Window to the top</span> <span class="c1">-- Win + Button 3 Float Window and Resize by dragging</span> <span class="kr">import</span> <span class="nn">XMonad</span> <span class="kr">import</span> <span class="k">qualified</span> <span class="nn">XMonad.StackSet</span> <span class="k">as</span> <span class="n">W</span> <span class="kr">import</span> <span class="k">qualified</span> <span class="nn">Data.Map</span> <span class="k">as</span> <span class="n">M</span> <span class="kr">import</span> <span class="nn">Data.Monoid</span> <span class="kr">import</span> <span class="nn">XMonad.Actions.CycleWS</span> <span class="kr">import</span> <span class="nn">XMonad.Actions.FlexibleResize</span> <span class="k">as</span> <span class="n">Flex</span> <span class="kr">import</span> <span class="nn">XMonad.Actions.SinkAll</span> <span class="kr">import</span> <span class="nn">XMonad.Actions.UpdatePointer</span> <span class="kr">import</span> <span class="nn">XMonad.Actions.WindowGo</span> <span class="kr">import</span> <span class="nn">XMonad.Hooks.DynamicLog</span> <span class="kr">import</span> <span class="nn">XMonad.Hooks.EwmhDesktops</span> <span class="kr">import</span> <span class="nn">XMonad.Hooks.ManageDocks</span> <span class="kr">import</span> <span class="nn">XMonad.Hooks.ManageHelpers</span> <span class="kr">import</span> <span class="nn">XMonad.Layout.LayoutHints</span> <span class="kr">import</span> <span class="nn">XMonad.Layout.NoBorders</span> <span class="kr">import</span> <span class="nn">XMonad.Layout.ResizableTile</span> <span class="kr">import</span> <span class="nn">XMonad.Layout.Tabbed</span> <span class="kr">import</span> <span class="nn">XMonad.Layout.ToggleLayouts</span> <span class="kr">import</span> <span class="nn">XMonad.Layout.WindowArranger</span> <span class="kr">import</span> <span class="nn">XMonad.Layout.Mosaic</span> <span class="kr">import</span> <span class="nn">XMonad.Prompt</span> <span class="kr">import</span> <span class="nn">XMonad.Prompt.Input</span> <span class="kr">import</span> <span class="nn">XMonad.Prompt.RunOrRaise</span> <span class="kr">import</span> <span class="nn">XMonad.Prompt.Shell</span> <span class="kr">import</span> <span class="nn">XMonad.Prompt.Window</span> <span class="kr">import</span> <span class="nn">XMonad.Util.Run</span> <span class="kr">import</span> <span class="nn">XMonad.Util.Scratchpad</span> <span class="kr">import</span> <span class="nn">XMonad.Util.WorkspaceCompare</span> <span class="kr">import</span> <span class="nn">XMonad.Util.XSelection</span> <span class="kr">import</span> <span class="nn">XMonad.Config.Gnome</span> <span class="c1">-- Mod4 is the Super / Windows key</span> <span class="nf">winMask</span> <span class="ow">=</span> <span class="n">mod4Mask</span> <span class="nf">altMask</span> <span class="ow">=</span> <span class="n">mod1Mask</span> <span class="c1">-- key bindings</span> <span class="nf">myKeys</span> <span class="n">conf</span><span class="o">@</span><span class="p">(</span><span class="kt">XConfig</span> <span class="p">{</span><span class="kt">XMonad</span><span class="o">.</span><span class="n">modMask</span> <span class="ow">=</span> <span class="n">modMask</span><span class="p">})</span> <span class="ow">=</span> <span class="kt">M</span><span class="o">.</span><span class="n">fromList</span> <span class="o">$</span> <span class="p">[</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_Return</span> <span class="p">),</span> <span class="n">spawn</span> <span class="o">$</span> <span class="kt">XMonad</span><span class="o">.</span><span class="n">terminal</span> <span class="n">conf</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F1</span> <span class="p">),</span> <span class="n">spawn</span> <span class="s">&quot;nautilus ~&quot;</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F2</span> <span class="p">),</span> <span class="n">runOrRaise</span> <span class="s">&quot;firefox&quot;</span> <span class="p">(</span><span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Firefox&quot;</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F3</span> <span class="p">),</span> <span class="n">runOrRaise</span> <span class="s">&quot;thunderbird&quot;</span> <span class="p">(</span><span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Thunderbird&quot;</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F4</span> <span class="p">),</span> <span class="n">runOrRaise</span> <span class="s">&quot;./rssowl/RSSOwl&quot;</span> <span class="p">(</span><span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;RSSOwl&quot;</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F5</span> <span class="p">),</span> <span class="n">runOrRaise</span> <span class="s">&quot;hotot&quot;</span> <span class="p">(</span><span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Hotot&quot;</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F6</span> <span class="p">),</span> <span class="n">spawn</span> <span class="s">&quot;terminator -e ncmpcpp&quot;</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F7</span> <span class="p">),</span> <span class="n">spawn</span> <span class="s">&quot;terminator -e &#39;task shell&#39; -p task&quot;</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F8</span> <span class="p">),</span> <span class="n">runOrRaise</span> <span class="s">&quot;pac&quot;</span> <span class="p">(</span><span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Pac&quot;</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F9</span> <span class="p">),</span> <span class="n">runOrRaise</span> <span class="s">&quot;./Aptana Studio 3/AptanaStudio3&quot;</span> <span class="p">(</span><span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Aptana Studio 3&quot;</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F10</span> <span class="p">),</span> <span class="n">runOrRaise</span> <span class="s">&quot;/usr/local/xmind/xmind&quot;</span> <span class="p">(</span><span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;XMind&quot;</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F11</span> <span class="p">),</span> <span class="n">runOrRaise</span> <span class="s">&quot;./dbeaver/dbeaver&quot;</span> <span class="p">(</span><span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;DBeaver&quot;</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_F12</span> <span class="p">),</span> <span class="n">runOrRaise</span> <span class="s">&quot;VirtualBox&quot;</span> <span class="p">(</span><span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;VirtualBox&quot;</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_space</span> <span class="p">),</span> <span class="n">runOrRaisePrompt</span> <span class="n">mySP</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="o">.|.</span> <span class="n">shiftMask</span><span class="p">,</span> <span class="n">xK_space</span> <span class="p">),</span> <span class="n">shellPrompt</span> <span class="n">mySP</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="o">.|.</span> <span class="n">controlMask</span><span class="p">,</span> <span class="n">xK_space</span> <span class="p">),</span> <span class="n">windowPromptGoto</span> <span class="n">mySP</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="mi">0</span><span class="p">,</span> <span class="n">xK_Print</span> <span class="p">),</span> <span class="n">unsafeSpawn</span> <span class="s">&quot;gnome-screenshot&quot;</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">altMask</span> <span class="o">.|.</span> <span class="n">controlMask</span><span class="p">,</span> <span class="n">xK_Right</span> <span class="p">),</span> <span class="n">moveTo</span> <span class="kt">Next</span> <span class="p">(</span><span class="kt">WSIs</span> <span class="p">(</span><span class="n">return</span> <span class="o">$</span> <span class="n">not</span> <span class="o">.</span> <span class="p">(</span><span class="o">==</span><span class="s">&quot;NSP&quot;</span><span class="p">)</span> <span class="o">.</span> <span class="kt">W</span><span class="o">.</span><span class="n">tag</span><span class="p">)))</span> <span class="p">,</span> <span class="p">((</span><span class="n">altMask</span> <span class="o">.|.</span> <span class="n">controlMask</span><span class="p">,</span> <span class="n">xK_Left</span> <span class="p">),</span> <span class="n">moveTo</span> <span class="kt">Prev</span> <span class="p">(</span><span class="kt">WSIs</span> <span class="p">(</span><span class="n">return</span> <span class="o">$</span> <span class="n">not</span> <span class="o">.</span> <span class="p">(</span><span class="o">==</span><span class="s">&quot;NSP&quot;</span><span class="p">)</span> <span class="o">.</span> <span class="kt">W</span><span class="o">.</span><span class="n">tag</span><span class="p">)))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="o">.|.</span> <span class="n">shiftMask</span><span class="p">,</span> <span class="n">xK_Right</span> <span class="p">),</span> <span class="n">shiftTo</span> <span class="kt">Next</span> <span class="p">(</span><span class="kt">WSIs</span> <span class="p">(</span><span class="n">return</span> <span class="o">$</span> <span class="n">not</span> <span class="o">.</span> <span class="p">(</span><span class="o">==</span><span class="s">&quot;NSP&quot;</span><span class="p">)</span> <span class="o">.</span> <span class="kt">W</span><span class="o">.</span><span class="n">tag</span><span class="p">)))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="o">.|.</span> <span class="n">shiftMask</span><span class="p">,</span> <span class="n">xK_Left</span> <span class="p">),</span> <span class="n">shiftTo</span> <span class="kt">Prev</span> <span class="p">(</span><span class="kt">WSIs</span> <span class="p">(</span><span class="n">return</span> <span class="o">$</span> <span class="n">not</span> <span class="o">.</span> <span class="p">(</span><span class="o">==</span><span class="s">&quot;NSP&quot;</span><span class="p">)</span> <span class="o">.</span> <span class="kt">W</span><span class="o">.</span><span class="n">tag</span><span class="p">)))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="o">.|.</span> <span class="n">controlMask</span><span class="p">,</span> <span class="n">xK_Right</span> <span class="p">),</span> <span class="n">shiftTo</span> <span class="kt">Next</span> <span class="kt">EmptyWS</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="o">.|.</span> <span class="n">controlMask</span><span class="p">,</span> <span class="n">xK_Left</span> <span class="p">),</span> <span class="n">shiftTo</span> <span class="kt">Prev</span> <span class="kt">EmptyWS</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_Tab</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="kt">NextLayout</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="n">dynamicLogString</span> <span class="n">myPP</span> <span class="o">&gt;&gt;=</span> <span class="nf">\</span><span class="n">d</span><span class="ow">-&gt;</span><span class="n">safeSpawn</span> <span class="s">&quot;gnome-osd-client&quot;</span> <span class="p">[</span><span class="n">d</span><span class="p">]))</span> <span class="p">,</span> <span class="p">((</span><span class="n">altMask</span><span class="p">,</span> <span class="n">xK_Tab</span> <span class="p">),</span> <span class="n">windows</span> <span class="kt">W</span><span class="o">.</span><span class="n">focusDown</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">altMask</span> <span class="o">.|.</span> <span class="n">shiftMask</span><span class="p">,</span> <span class="n">xK_Tab</span> <span class="p">),</span> <span class="n">windows</span> <span class="kt">W</span><span class="o">.</span><span class="n">focusUp</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">controlMask</span><span class="p">,</span> <span class="n">xK_Down</span> <span class="p">),</span> <span class="n">windows</span> <span class="kt">W</span><span class="o">.</span><span class="n">swapDown</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">controlMask</span><span class="p">,</span> <span class="n">xK_Up</span> <span class="p">),</span> <span class="n">windows</span> <span class="kt">W</span><span class="o">.</span><span class="n">swapUp</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_Left</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="kt">Shrink</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_Right</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="kt">Expand</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_Down</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="kt">MirrorShrink</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_Up</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="kt">MirrorExpand</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_minus</span> <span class="p">),</span> <span class="n">windows</span> <span class="kt">W</span><span class="o">.</span><span class="n">shiftMaster</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_comma</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="p">(</span><span class="kt">IncMasterN</span> <span class="mi">1</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_period</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="p">(</span><span class="kt">IncMasterN</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_n</span> <span class="p">),</span> <span class="n">refresh</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="o">.|.</span> <span class="n">shiftMask</span><span class="p">,</span> <span class="n">xK_n</span> <span class="p">),</span> <span class="n">setLayout</span> <span class="o">$</span> <span class="kt">XMonad</span><span class="o">.</span><span class="n">layoutHook</span> <span class="n">conf</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="p">,</span> <span class="n">xK_a</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="kt">Taller</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="p">,</span> <span class="n">xK_z</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="kt">Wider</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="o">.|.</span> <span class="n">controlMask</span><span class="p">,</span> <span class="n">xK_n</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="kt">Reset</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_m</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="p">(</span><span class="kt">Toggle</span> <span class="s">&quot;Full&quot;</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="n">dynamicLogString</span> <span class="n">myPP</span> <span class="o">&gt;&gt;=</span> <span class="nf">\</span><span class="n">d</span><span class="ow">-&gt;</span><span class="n">safeSpawn</span> <span class="s">&quot;gnome-osd-client&quot;</span> <span class="p">[</span><span class="n">d</span><span class="p">]))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_t</span> <span class="p">),</span> <span class="n">withFocused</span> <span class="o">$</span> <span class="n">windows</span> <span class="o">.</span> <span class="kt">W</span><span class="o">.</span><span class="n">sink</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="o">.|.</span> <span class="n">shiftMask</span><span class="p">,</span> <span class="n">xK_t</span> <span class="p">),</span> <span class="n">sinkAll</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_f</span> <span class="p">),</span> <span class="n">sendMessage</span> <span class="kt">ToggleStruts</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_w</span> <span class="p">),</span> <span class="n">kill</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">altMask</span><span class="p">,</span> <span class="n">xK_F4</span> <span class="p">),</span> <span class="n">kill</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">xK_q</span> <span class="p">),</span> <span class="n">broadcastMessage</span> <span class="kt">ReleaseResources</span> <span class="o">&gt;&gt;</span> <span class="n">restart</span> <span class="s">&quot;xmonad&quot;</span> <span class="kt">True</span><span class="p">)</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span> <span class="o">.|.</span> <span class="n">shiftMask</span><span class="p">,</span> <span class="n">xK_q</span> <span class="p">),</span> <span class="n">spawn</span> <span class="s">&quot;gnome-session-quit&quot;</span><span class="p">)</span> <span class="p">]</span> <span class="o">++</span> <span class="c1">-- mod-[1..9], Switch to workspace N</span> <span class="c1">-- mod-shift-[1..9], Move client to workspace N</span> <span class="p">[</span> <span class="p">((</span><span class="n">m</span> <span class="o">.|.</span> <span class="n">winMask</span><span class="p">,</span> <span class="n">k</span><span class="p">),</span> <span class="n">windows</span> <span class="o">$</span> <span class="n">f</span> <span class="n">i</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span> <span class="ow">&lt;-</span> <span class="n">zip</span> <span class="p">(</span><span class="kt">XMonad</span><span class="o">.</span><span class="n">workspaces</span> <span class="n">conf</span><span class="p">)</span> <span class="p">[</span><span class="n">xK_1</span> <span class="o">..</span> <span class="n">xK_9</span><span class="p">]</span> <span class="p">,</span> <span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">m</span><span class="p">)</span> <span class="ow">&lt;-</span> <span class="p">[(</span><span class="kt">W</span><span class="o">.</span><span class="n">greedyView</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="kt">W</span><span class="o">.</span><span class="n">shift</span><span class="p">,</span> <span class="n">shiftMask</span><span class="p">)]</span> <span class="p">]</span> <span class="c1">-- mouse bindings</span> <span class="nf">myMouseBindings</span> <span class="p">(</span><span class="kt">XConfig</span> <span class="p">{</span><span class="kt">XMonad</span><span class="o">.</span><span class="n">modMask</span> <span class="ow">=</span> <span class="n">modMask</span><span class="p">})</span> <span class="ow">=</span> <span class="kt">M</span><span class="o">.</span><span class="n">fromList</span> <span class="o">$</span> <span class="p">[</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">button1</span><span class="p">),</span> <span class="p">(</span><span class="nf">\</span><span class="n">w</span> <span class="ow">-&gt;</span> <span class="n">focus</span> <span class="n">w</span> <span class="o">&gt;&gt;</span> <span class="n">mouseMoveWindow</span> <span class="n">w</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">button2</span><span class="p">),</span> <span class="p">(</span><span class="nf">\</span><span class="n">w</span> <span class="ow">-&gt;</span> <span class="n">focus</span> <span class="n">w</span> <span class="o">&gt;&gt;</span> <span class="n">windows</span> <span class="kt">W</span><span class="o">.</span><span class="n">shiftMaster</span><span class="p">))</span> <span class="p">,</span> <span class="p">((</span><span class="n">winMask</span><span class="p">,</span> <span class="n">button3</span><span class="p">),</span> <span class="p">(</span><span class="nf">\</span><span class="n">w</span> <span class="ow">-&gt;</span> <span class="n">focus</span> <span class="n">w</span> <span class="o">&gt;&gt;</span> <span class="kt">Flex</span><span class="o">.</span><span class="n">mouseResizeWindow</span> <span class="n">w</span><span class="p">))</span> <span class="p">]</span> <span class="c1">-- decoration theme</span> <span class="nf">myDeco</span> <span class="ow">=</span> <span class="n">defaultTheme</span> <span class="p">{</span> <span class="n">activeColor</span> <span class="ow">=</span> <span class="s">&quot;orange&quot;</span> <span class="p">,</span> <span class="n">inactiveColor</span> <span class="ow">=</span> <span class="s">&quot;#222222&quot;</span> <span class="p">,</span> <span class="n">urgentColor</span> <span class="ow">=</span> <span class="s">&quot;yellow&quot;</span> <span class="p">,</span> <span class="n">activeBorderColor</span> <span class="ow">=</span> <span class="s">&quot;orange&quot;</span> <span class="p">,</span> <span class="n">inactiveBorderColor</span> <span class="ow">=</span> <span class="s">&quot;#222222&quot;</span> <span class="p">,</span> <span class="n">urgentBorderColor</span> <span class="ow">=</span> <span class="s">&quot;yellow&quot;</span> <span class="p">,</span> <span class="n">activeTextColor</span> <span class="ow">=</span> <span class="s">&quot;orange&quot;</span> <span class="p">,</span> <span class="n">inactiveTextColor</span> <span class="ow">=</span> <span class="s">&quot;#222222&quot;</span> <span class="p">,</span> <span class="n">urgentTextColor</span> <span class="ow">=</span> <span class="s">&quot;yellow&quot;</span> <span class="p">,</span> <span class="n">decoHeight</span> <span class="ow">=</span> <span class="mi">10</span> <span class="p">}</span> <span class="c1">-- tab theme</span> <span class="nf">myTab</span> <span class="ow">=</span> <span class="n">defaultTheme</span> <span class="p">{</span> <span class="n">activeColor</span> <span class="ow">=</span> <span class="s">&quot;black&quot;</span> <span class="p">,</span> <span class="n">inactiveColor</span> <span class="ow">=</span> <span class="s">&quot;black&quot;</span> <span class="p">,</span> <span class="n">urgentColor</span> <span class="ow">=</span> <span class="s">&quot;yellow&quot;</span> <span class="p">,</span> <span class="n">activeBorderColor</span> <span class="ow">=</span> <span class="s">&quot;orange&quot;</span> <span class="p">,</span> <span class="n">inactiveBorderColor</span> <span class="ow">=</span> <span class="s">&quot;#222222&quot;</span> <span class="p">,</span> <span class="n">urgentBorderColor</span> <span class="ow">=</span> <span class="s">&quot;black&quot;</span> <span class="p">,</span> <span class="n">activeTextColor</span> <span class="ow">=</span> <span class="s">&quot;orange&quot;</span> <span class="p">,</span> <span class="n">inactiveTextColor</span> <span class="ow">=</span> <span class="s">&quot;#222222&quot;</span> <span class="p">,</span> <span class="n">urgentTextColor</span> <span class="ow">=</span> <span class="s">&quot;yellow&quot;</span> <span class="p">}</span> <span class="c1">-- shell prompt theme</span> <span class="nf">mySP</span> <span class="ow">=</span> <span class="n">defaultXPConfig</span> <span class="p">{</span> <span class="n">bgColor</span> <span class="ow">=</span> <span class="s">&quot;black&quot;</span> <span class="p">,</span> <span class="n">fgColor</span> <span class="ow">=</span> <span class="s">&quot;white&quot;</span> <span class="p">,</span> <span class="n">bgHLight</span> <span class="ow">=</span> <span class="s">&quot;gray&quot;</span> <span class="p">,</span> <span class="n">fgHLight</span> <span class="ow">=</span> <span class="s">&quot;black&quot;</span> <span class="p">,</span> <span class="n">borderColor</span> <span class="ow">=</span> <span class="s">&quot;orange&quot;</span> <span class="p">,</span> <span class="n">promptBorderWidth</span> <span class="ow">=</span> <span class="mi">2</span> <span class="p">,</span> <span class="n">position</span> <span class="ow">=</span> <span class="kt">Bottom</span> <span class="p">,</span> <span class="n">height</span> <span class="ow">=</span> <span class="mi">40</span> <span class="c1">--, autoComplete = Just 1000</span> <span class="p">,</span> <span class="n">historySize</span> <span class="ow">=</span> <span class="mi">1000</span> <span class="p">}</span> <span class="c1">-- dynamicLog theme (suppress everything but layout)</span> <span class="nf">myPP</span> <span class="ow">=</span> <span class="n">defaultPP</span> <span class="p">{</span> <span class="n">ppLayout</span> <span class="ow">=</span> <span class="p">(</span><span class="nf">\</span> <span class="n">x</span> <span class="ow">-&gt;</span> <span class="kr">case</span> <span class="n">x</span> <span class="kr">of</span> <span class="s">&quot;Hinted ResizableTall&quot;</span> <span class="ow">-&gt;</span> <span class="s">&quot;[|]&quot;</span> <span class="s">&quot;Mirror Hinted ResizableTall&quot;</span> <span class="ow">-&gt;</span> <span class="s">&quot;[-]&quot;</span> <span class="s">&quot;Hinted Tabbed Simplest&quot;</span> <span class="ow">-&gt;</span> <span class="s">&quot;[T]&quot;</span> <span class="s">&quot;Mosaic&quot;</span> <span class="ow">-&gt;</span> <span class="s">&quot;[M]&quot;</span> <span class="s">&quot;Full&quot;</span> <span class="ow">-&gt;</span> <span class="s">&quot;[ ]&quot;</span> <span class="kr">_</span> <span class="ow">-&gt;</span> <span class="n">x</span> <span class="p">)</span> <span class="p">,</span> <span class="n">ppCurrent</span> <span class="ow">=</span> <span class="n">const</span> <span class="s">&quot;&quot;</span> <span class="p">,</span> <span class="n">ppVisible</span> <span class="ow">=</span> <span class="n">const</span> <span class="s">&quot;&quot;</span> <span class="p">,</span> <span class="n">ppHidden</span> <span class="ow">=</span> <span class="n">const</span> <span class="s">&quot;&quot;</span> <span class="p">,</span> <span class="n">ppHiddenNoWindows</span> <span class="ow">=</span> <span class="n">const</span> <span class="s">&quot;&quot;</span> <span class="p">,</span> <span class="n">ppUrgent</span> <span class="ow">=</span> <span class="n">const</span> <span class="s">&quot;&quot;</span> <span class="p">,</span> <span class="n">ppTitle</span> <span class="ow">=</span> <span class="n">const</span> <span class="s">&quot;&quot;</span> <span class="p">,</span> <span class="n">ppWsSep</span> <span class="ow">=</span> <span class="s">&quot;&quot;</span> <span class="p">,</span> <span class="n">ppSep</span> <span class="ow">=</span> <span class="s">&quot;&quot;</span> <span class="p">}</span> <span class="c1">-- layouts</span> <span class="nf">myLayout</span> <span class="ow">=</span> <span class="n">avoidStruts</span> <span class="o">$</span> <span class="n">toggleLayouts</span> <span class="p">(</span><span class="n">noBorders</span> <span class="kt">Full</span><span class="p">)</span> <span class="p">(</span><span class="n">smartBorders</span> <span class="p">(</span><span class="n">tiled</span> <span class="o">|||</span> <span class="n">mosaic</span> <span class="mi">2</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">]</span> <span class="o">|||</span> <span class="kt">Mirror</span> <span class="n">tiled</span> <span class="o">|||</span> <span class="n">layoutHints</span> <span class="p">(</span><span class="n">tabbed</span> <span class="n">shrinkText</span> <span class="n">myTab</span><span class="p">)))</span> <span class="kr">where</span> <span class="n">tiled</span> <span class="ow">=</span> <span class="n">layoutHints</span> <span class="o">$</span> <span class="kt">ResizableTall</span> <span class="n">nmaster</span> <span class="n">delta</span> <span class="n">ratio</span> <span class="kt">[]</span> <span class="n">nmaster</span> <span class="ow">=</span> <span class="mi">1</span> <span class="n">delta</span> <span class="ow">=</span> <span class="mi">2</span><span class="o">/</span><span class="mi">100</span> <span class="n">ratio</span> <span class="ow">=</span> <span class="mi">1</span><span class="o">/</span><span class="mi">2</span> <span class="c1">-- special windows</span> <span class="nf">myManageHook</span> <span class="ow">=</span> <span class="n">composeAll</span> <span class="p">[</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;MPlayer&quot;</span> <span class="o">--&gt;</span> <span class="n">doFloat</span> <span class="p">,</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Gimp-2.6&quot;</span> <span class="o">--&gt;</span> <span class="n">doFloat</span> <span class="p">,</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Gnome-panel&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;XVkbd&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Cellwriter&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Gtkdialog&quot;</span> <span class="o">--&gt;</span> <span class="n">doFloat</span> <span class="p">,</span> <span class="n">resource</span> <span class="o">=?</span> <span class="s">&quot;desktop_window&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Xmessage&quot;</span> <span class="o">--&gt;</span> <span class="n">doCenterFloat</span> <span class="p">,</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Unity-2d-panel&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">isFullscreen</span> <span class="o">--&gt;</span> <span class="n">doFullFloat</span> <span class="p">,</span> <span class="n">isDialog</span> <span class="o">--&gt;</span> <span class="n">doCenterFloat</span> <span class="p">,</span> <span class="n">title</span> <span class="o">=?</span> <span class="s">&quot;RSSOwl&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">title</span> <span class="o">=?</span> <span class="s">&quot;Dbeaver&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">title</span> <span class="o">=?</span> <span class="s">&quot;Xmind-bin&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">title</span> <span class="o">=?</span> <span class="s">&quot;AptanaStudio3&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">resource</span> <span class="o">=?</span> <span class="s">&quot;sun-awt-X11-XDialogPeer&quot;</span> <span class="o">--&gt;</span> <span class="n">doCenterFloat</span> <span class="p">,</span> <span class="n">title</span> <span class="o">=?</span> <span class="s">&quot;screenkey&quot;</span> <span class="o">--&gt;</span> <span class="n">doIgnore</span> <span class="p">,</span> <span class="n">className</span> <span class="o">=?</span> <span class="s">&quot;Gloobus-preview&quot;</span> <span class="o">--&gt;</span> <span class="n">doCenterFloat</span> <span class="c1">-- x y w h</span> <span class="p">,</span> <span class="n">scratchpadManageHook</span> <span class="o">$</span> <span class="kt">W</span><span class="o">.</span><span class="kt">RationalRect</span> <span class="mi">0</span> <span class="mi">0</span> <span class="mi">1</span> <span class="mf">0.42</span> <span class="p">,</span> <span class="n">manageDocks</span> <span class="p">]</span> <span class="o">&lt;+&gt;</span> <span class="n">manageHook</span> <span class="n">defaultConfig</span> <span class="c1">-- let Gnome know about Xmonad actions</span> <span class="nf">myLogHook</span> <span class="ow">=</span> <span class="n">ewmhDesktopsLogHookCustom</span> <span class="n">scratchpadFilterOutWorkspace</span> <span class="o">&gt;&gt;</span> <span class="n">updatePointer</span> <span class="p">(</span><span class="kt">Relative</span> <span class="mf">0.5</span> <span class="mf">0.5</span><span class="p">)</span> <span class="nf">myConfig</span> <span class="ow">=</span> <span class="n">ewmh</span> <span class="n">defaultConfig</span> <span class="p">{</span> <span class="n">terminal</span> <span class="ow">=</span> <span class="s">&quot;terminator&quot;</span> <span class="p">,</span> <span class="n">borderWidth</span> <span class="ow">=</span> <span class="mi">2</span> <span class="p">,</span> <span class="n">normalBorderColor</span> <span class="ow">=</span> <span class="s">&quot;black&quot;</span> <span class="p">,</span> <span class="n">focusedBorderColor</span> <span class="ow">=</span> <span class="s">&quot;orange&quot;</span> <span class="p">,</span> <span class="n">focusFollowsMouse</span> <span class="ow">=</span> <span class="kt">True</span> <span class="p">,</span> <span class="n">modMask</span> <span class="ow">=</span> <span class="n">mod4Mask</span> <span class="p">,</span> <span class="n">keys</span> <span class="ow">=</span> <span class="n">myKeys</span> <span class="p">,</span> <span class="n">mouseBindings</span> <span class="ow">=</span> <span class="n">myMouseBindings</span> <span class="p">,</span> <span class="n">layoutHook</span> <span class="ow">=</span> <span class="n">myLayout</span> <span class="p">,</span> <span class="n">manageHook</span> <span class="ow">=</span> <span class="n">myManageHook</span> <span class="p">,</span> <span class="n">startupHook</span> <span class="ow">=</span> <span class="n">gnomeRegister</span> <span class="p">}</span> <span class="c1">-- need to override ewmh&#39;s logHook cause I&#39;m using Scratchpad</span> <span class="nf">main</span> <span class="ow">=</span> <span class="n">xmonad</span> <span class="o">$</span> <span class="n">myConfig</span> <span class="p">{</span> <span class="n">logHook</span> <span class="ow">=</span> <span class="n">myLogHook</span> <span class="p">}</span> </pre></div>joe di castroSat, 26 Nov 2011 00:00:00 +0100http://joedicastro.com/productividad-en-el-escritorio-linux-xmonad.htmllinuxubuntuunitytilingtwmproductividadxmonadgnomehaskellProductividad en el escritorio Linux: Tilinghttp://joedicastro.com/productividad-en-el-escritorio-linux-tiling.html<p>Los <strong>Gestores de Ventanas de Mosaico</strong> (<a href="http://en.wikipedia.org/wiki/Tiling_window_manager">Tiling Windows Manager</a>) son el gran desconocido entre la mayoría de usuarios de Linux. Y sin embargo, a mi juicio, suponen el salto más importante en productividad en el escritorio linux que se haya producido en mucho tiempo. Permiten manejar todo el escritorio sin apenas despegar las manos del teclado y sin verse relegado a emplear únicamente la consola. Las ventanas se distribuyen ellas solas de forma automática por la pantalla y no es necesario emplear el ratón para moverlas y redimensionarlas (aunque puede seguir empleándose, naturalmente).</p> <p>A diferencia de un gestor de ventanas tradicional, a los que estamos normalmente acostumbrados, las ventanas no se apilan y flotan unas encima de otras, si no que se redistribuyen por toda la pantalla de acorde a un esquema previo (variable) sin solaparse entre ellas, quedando todas a la vista de forma simultanea. De ahí el símil del mosaico, donde el mismo seria el escritorio y donde las ventanas serian las teselas que lo componen. Puede no ser fácil visualizar de entrada en que consiste exactamente esto y como funciona, luego los vídeos nos ayudarán a entender mejor de lo que estoy hablando. Aunque uno no aprecia realmente las ventajas que aportan hasta que prueba uno. </p> <h2 id="+que_ventajas_tienen">¿Que ventajas tienen?</h2> <p>Primero mencionar que esto no es una idea nueva, ni mucho menos, esto ya lo inventó en 1981 <strong>Xerox</strong> (el inventor a su vez de los entornos de ventanas) e incluso MS Windows 1.0 lo empleaba. La razón de que algo desarrollado desde hace dos décadas aún no haya llamado demasiado la atención hasta ahora, es bien sencilla, el triunfo de un dispositivo de entrada más amigable e intuitivo que el teclado: el ratón. La metáfora de el entorno de ventanas con el escritorio junto con la del ratón como la extensión de nuestro brazo, propiciaba que fuera lo más natural emplearlo para mover las ventanas y redimensionarlas. Pero que sea más fácil o intuitivo no quiere decir que sea más eficiente, nadie en su sano juicio escribiría un documento empleando el ratón sobre un teclado virtual ni realizaría retoque fotográfico empleando únicamente el teclado. ¿Pero cuantos dirían que el teclado es el medio más eficiente para manejar las ventanas?</p> <p>En una pantalla de grandes dimensiones y elevada resolución (e.g. un monitor de 24" y 1920x1200) es muy frecuente andar redimensionando continuamente las ventanas para acomodarlas y redistribuirlas dentro del escritorio. Nos sobra espacio por todos los lados y la mayoría de las aplicaciones que en monitores y resoluciones menores empleábamos maximizadas, ahora son incomodas de usar a pantalla completa (e.g. navegador web). ¿No sería útil que las ventanas se recolocaran y redimensionaran automáticamente por si solas? Más o menos esto es lo que viene a hacer un gestor de ventanas de mosaico. </p> <p>Pero cuales son las ventajas reales de este sistema:</p> <ul> <li> <p><strong>Las ventanas se organizan solas</strong>, muchas veces no necesitas mover o redimensionar una ventana, simplemente aparece en un lugar adecuado y con las dimensiones adecuadas. Una vez le que coges el truco te organizas de manera que esto ocurre con cierta naturalidad.</p> </li> <li> <p><strong>Se aprovecha todo el espacio de la pantalla</strong>. Las ventanas se ajustan a todo el espacio libre disponible del escritorio. Aún así pueden emplearse paneles, docks, ...</p> </li> <li> <p>Las ventanas normalmente apenas tienen marco y carecen de barra de titulo y decoraciones, por lo tanto se ahorra espacio. Combinado con un menú integrado como el <em>Global Menu</em> de Unity, <strong>se ahorra mucho espacio</strong>.</p> </li> <li> <p><strong>No es un método excluyente</strong>, aún se pueden emplear con toda normalidad las ventanas flotantes como habitualmente. Incluso pueden convivir entre ellas sin problema alguno.</p> </li> <li> <p><strong>En un sistema de múltiples monitores se facilita mucho el manejo de las múltiples ventanas</strong> y el tener simultáneamente abiertas un número elevado de ellas es algo realmente sencillo. </p> </li> <li> <p><strong>Es posible automatizar el tamaño, posición y escritorio virtual en donde se situara la ventana(s) de una aplicación al iniciarse</strong>, lo que abre un mundo de posibilidades. </p> </li> <li> <p><strong>Es posible tener múltiples escritorios virtuales (usualmente 9 por monitor) con características distintas en cada uno</strong>. Puede ser un esquema distinto (desde una simple rejilla a un escritorio normal) o un escritorio para cada tarea (e.g. 1 - navegar, 2 - correo y redes sociales, 3 - trabajo, 4 - terminales, ...)</p> </li> <li> <p><strong>Mover las ventanas, redimensionarlas, lanzar aplicaciones, cerrarlas, ... y manejar los escritorios virtuales desde el teclado</strong> es mucho más eficiente que emplear el ratón para lo mismo.</p> </li> <li> <p><strong>Te olvidas de buscar una ventana entre la pila de ventanas flotantes</strong> sobre el escritorio, es sencillo incluso encontrar una ventana dentro de otro escritorio virtual (con unos gestores más que otros). Los escritorios modernos permiten mostrar un mosaico con todas las aplicaciones abiertas (Exposé, Scale, ...), ¿pero que ocurre cuando tienes abiertas cerca de 30? con un twm la localizas enseguida si lo configuras adecuadamente, en mi caso menos de 2 segundos.</p> </li> <li> <p><strong>Te permite organizar las ventanas en función de múltiples patrones distintos:</strong> rejillas, columnas, filas, espirales, por pestañas, etc... de forma automática, acomodándose a múltiples situaciones distintas.</p> </li> <li> <p><strong>Se pueden emplear de forma autónoma, sin un escritorio detrás</strong> (gnome, kde, ...) lo que permite tener un sistema muy ligero ideal para equipos poco potentes. </p> </li> <li> <p><strong>Puede ser una manera de mejorar la accesibilidad del escritorio</strong> para aquellos que encuentren dificultades para manejar un ratón pero puedan defenderse con un teclado.</p> </li> </ul> <h2 id="inconvenientes">Inconvenientes</h2> <p>No son para todo el mundo. Soy plenamente consciente de ello, una buena mayoría de los usuarios son reticentes a aprenderse los atajos de teclado de una aplicación (hay quien no sabe cortar y pegar desde él), así que entiendo perfectamente que siquiera lleguen a plantearse el emplearlos. De hecho los tímidos avances que están realizando los escritorios en este sentido implican el uso del ratón para implantar un sucedáneo de este sistema. También es una cuestión de gustos, habrá a quien simplemente la desagradará el como funciona este sistema.</p> <p>Por otro lado los profesionales multimedia que requieren un uso elevado del ratón probablemente prefieran seguir empleando un gestor de ventanas tradicional, aunque los gestores de mosaico pueden moverse con soltura en este entorno. </p> <p>También es cierto que la mayoría de ellos requieren un esfuerzo por nuestra parte para configurarlos a nuestro gusto. Aunque es precisamente esto lo que nos permite configurarlos exactamente a nuestro gusto, con muchas posibilidades de configuración. Pero es una barrera de entrada importante para los gestores más potentes y completos. Alguno hasta requiere conocimientos en algún lenguaje de programación (C, Lua, Haskell)</p> <p>Aunque por un lado pueden suponer una ventaja en la accesibilidad a determinados discapacitados, pueden suponer un escollo para otros, pues no están tan adaptados como puedan estarlo los escritorios tradicionales para determinadas discapacidades.</p> <h2 id="tipos_de_gestores">Tipos de gestores</h2> <p>Básicamente existen dos tipos de gestores de ventanas de mosaico, en función de el modo de organización de las ventanas. </p> <ul> <li> <p><strong><em>Manuales</em></strong>, requieren que el usuario sea quien decida la posición (a veces el tamaño) de las ventanas, con el inconveniente de la perdida de tiempo a costa de lograr un control más preciso.</p> </li> <li> <p><strong><em>Automáticos</em></strong>, ajustan automáticamente la posición y el tamaño de las ventanas en función del esquema por defecto. Posteriormente podemos variar tanto el esquema como la posición y tamaño de las ventanas individuales.</p> </li> </ul> <h2 id="adaptaci+n_de_la_idea_en_los_escritorios_m+s_comunes">Adaptación de la idea en los escritorios más comunes</h2> <p>Tal y como ocurre con los lanzadores de aplicaciones, los escritorios más tradiciones de linux comienzan a incorporar esta idea a sus desarrollos, en mayor o menor medida. Unity, Compiz, Gnome y KDE se han atrevido a dar un paso en esta dirección en sus últimas versiones:</p> <ul> <li> <p><strong>Compiz Grid</strong>, es un plugin de Compiz que permite realizar un tiling primitivo, empleando el ratón o el teclado. Divide la pantalla en una rejilla de 3 x 3 y permite que las ventanas se ajusten al tamaño de una cuadricula o varias. Manejarlo desde el teclado es muy sencillo, simplemente pulsamos Ctrl + Alt + una tecla del teclado numérico del 1 al 9 según la cuadricula a la que la queramos ajustar la ventana. Para cambiar el tamaño de la ventana mantenemos pulsadas las teclas Ctrl + Alt y volvemos a pulsar la tecla numérica las veces que sea necesario hasta tener el tamaño que deseemos. Es decir, si pulsamos Ctrl+Alt+7 nos sitúa la ventana en la parte superior izquierda de la pantalla ocupando 1/9 de la misma. </p> </li> <li> <p><strong>Unity</strong>, emplea el plugin Grid de compiz anterior, pero modificado desde la versión 11.10 para que se comporte como ellos desean. En este vídeo se puede ver como se comporta este plugin manejándolo desde el teclado. Personalmente opino que mejor lo hubieran dejado como estaba.</p> </li> </ul> <div style="text-align:center"> <iframe src="http://player.vimeo.com/video/32746967?title=0&amp;byline=0&amp;portrait=0" width="700" height="438"> </iframe> <p>Demostración del tiling básico del plugin de Compiz, Grid, en Ubuntu 11.10 con Unity</p> </div> <ul> <li> <p><strong>Gnome Shell</strong>, tiene un tiling muy básico parecido al que emplea Unity. Te permite maximizar una ventana o ajustarla a la mitad derecha/izquierda de la pantalla simplemente arrastrándola al borde correspondiente.</p> </li> <li> <p><strong>KDE</strong> a partir de la versión 4.4 tiene un tiling bastante conseguido, aunque no tan avanzado como los gestores dedicados. Desde luego es el escritorio que ha apostado más fuerte por este sistema y el que mejores resultados obtiene. Aunque no viene activarlo por defecto, es necesario activarlo a mano. De momento solo permite elegir entre dos esquemas predeterminados: columnas y espiral (fibonacci).</p> </li> </ul> <h3 id="aplicaciones_que_funcionan_como_un_gestor">Aplicaciones que funcionan como un gestor</h3> <p>Para aquellos que no quieran instalar un gestor o no empleen uno de los entornos de escritorio anteriores, siempre existe la posibilidad de emplear alguna de estas aplicaciones que emulan el comportamiento básico de un gestor de ventanas de mosaico. Ambos son del tipo manual.</p> <ul> <li> <p><a href="http://ssokolow.github.com/quicktile/">QuickTile</a>, es un script en python que ofrece las mismas funcionalidades que el plugin Grid de Compiz. </p> </li> <li> <p><a href="http://www.giuspen.com/x-tile/">x tile</a>, es una aplicación instalable (viene en los repositorios de algunas distros) que ofrece más opciones que la anterior. </p> </li> </ul> <h2 id="gestores_m+s_populares">Gestores más populares</h2> <p>Hay muchos y para todos los gustos. Incluso los hay disponibles para otros SOs como Mac OS X o Windows (es muy famoso el WinSplit-Revolution para Windows, en el que se basa el plugin Grid, aunque los primeros gestores para linux son del 2000 y WinSplit aparece en el 2007). Me centrare aquí solo en los más populares.</p> <h3 id="awesomeaw"><a href="http://awesome.naquadah.org/">awesome</a></h3> <p>Automático. Escrito en <em>C</em> y <em>Lua</em>, es configurable y extensible en <em>Lua</em>. Basado en <strong>dwm</strong></p> <p>Uno de los dos grandes, junto con <strong>Xmonad</strong>. Es quizá el que más opciones presenta hoy en día. Está basado en XCB en vez de Xlib, lo que le proporciona mayor agilidad. Aporta su propia barra de información, bandeja del sistema (systray) y lanzador de aplicaciones integrado. Tiene un soporte de ratón superior a la competencia. Pero su desarrollo no es aún solido, varía demasiado a lo largo de las distintas versiones y te obliga a reconfigurarlo cada vez que sale una nueva. Le he dedicado un <a href="http://joedicastro.com/productividad-en-el-escritorio-linux-awesome.html">articulo completo</a> </p> <br /> <h3 id="bluetilebt"><a href="http://bluetile.org/">bluetile</a></h3> <p>Automático. Escrito en <em>Haskell</em>. Basado en <strong>Xmonad</strong></p> <p>Es uno de los más sencillos de utilizar y de los más sencillos de probar. Es el más adecuado para probar por primera vez para los que se inician en esto. Está pensado para emplear conjuntamente con Gnome y pensando para que los están acostumbrados al ratón lo usen indistintamente con el teclado. En el siguiente vídeo realizado por su autor, Jan Vornberger, se puede ver como funciona:</p> <div style="text-align:center"> <iframe src="http://player.vimeo.com/video/6661713?title=0&amp;byline=0&amp;portrait=0" width="667" height="500"></iframe> </div> <br /> <h3 id="dwmdwm"><a href="http://dwm.suckless.org/">dwm</a></h3> <p>Automático. Escrito, configurable y extensible en <em>C</em>. </p> <p>Uno de los más ligeros, rápidos y simples. Su configuración se hace modificando su código fuente en <em>C</em> y luego hay que recompilarlo y reiniciarlo. Esto lo limita a usuarios avanzados con conocimientos de programación. Su diseño minimalista ha sido la inspiración para los dos más grandes: Awesome y Xmonad. </p> <p>Para lanzar las aplicaciones se desarrolló <strong>dmenu</strong> un lanzador basado en texto, que es muy empleado en otros gestores. Funciona al igual que los lanzadores gráficos como Gnome Do. </p> <br /> <h3 id="i3i3"><a href="http://i3wm.org/">i3</a></h3> <p>Manual. Escrito en <em>C</em> y configurable con ficheros de texto. Basado en <strong>wmii</strong></p> <p>Muy sencillo y que presume de estar bien documentado. Fácil de usar y configurar. Es manual, lo que le permite crear esquemas muy flexibles y personalizados, pero que por otro lado le resta comodidad. </p> <br /> <h3 id="wmiiwmii"><a href="http://wmii.suckless.org/">wmii</a></h3> <p>Manual. Escrito en <em>C</em> y configurable en <em>bash</em>, <em>rc</em>, <em>ruby</em> o <em>python</em></p> <p>Escrito por el mismo desarrollador que <strong>dwm</strong>. Digamos que es el gestor equivalente a <strong>dwm</strong> pero manual. </p> <br /> <h3 id="xmonadxmnd"><a href="http://xmonad.org/">xmonad</a></h3> <p>Automático. Escrito, extensible y configurable en <em>Haskell</em>. Inspirado en <strong>dwm</strong></p> <p>Muy potente, estable y con muchas opciones. Es altamente personalizable, con muchas extensiones disponibles. Se integra muy bien con Gnome y KDE. Tiene un desarrollo muy activo y una gran comunidad de usuarios detrás. Es el que empleo desde hace meses y le he dedicado un <a href="http://joedicastro.com/productividad-en-el-escritorio-linux-xmonad.html">articulo completo</a>.</p> <br /> <p>Todos estos gestores están incluidos en los repositorios de Ubuntu, Debian, Gentoo y Arch Linux, y la mayoría están disponibles también en otras distribuciones. </p> <p>Como curiosidad, mencionar otros dos, que aunque no son muy populares, están desarrollados en Python: <a href="http://sourceforge.net/projects/tritium/">tritium</a> y <a href="http://qtile.org/">qtile</a></p>joe di castroFri, 25 Nov 2011 00:00:00 +0100http://joedicastro.com/productividad-en-el-escritorio-linux-tiling.htmllinuxubuntuunitytilingtwmproductividadxmonadNotificaciones de escritorio en Ubuntu desde Pythonhttp://joedicastro.com/notificaciones-de-escritorio-en-ubuntu-desde-python.html<p>En <strong>Ubuntu</strong><sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup> las notificaciones de escritorio son esos <strong>cuadros emergentes</strong> que nos proporcionan información sobre algún evento que se está produciendo en nuestro equipo (llegada de un email, canción que se está reproduciendo, cambio de volumen, estado de batería, etc). Sustituyen a las ventanas de dialogo que normalmente requieren la intervención del usuario para cerrarlas y son más molestas. Además frente a las ventanas de dialogo tienen la gran ventaja de que son gestionadas a través de una cola. Es decir, que si hay varias notificaciones para mostrar, no se muestran todas a la vez llenándote todo el escritorio de pequeñas ventanas, se van mostrando una después de otra. La notificaciones aparecen en una esquina del escritorio y se desvanecen por si solas al cabo de unos segundos. Son muy útiles porque llaman nuestra atención y nos informan sin interrumpir nuestro trabajo.</p> <p style="text-align:center;"><img src="pictures/notify_example.png" width="366" height="147" alt="notify_example.png" title="Ejemplo de notificación"/></p> <p>En Ubuntu esta operación la realiza el paquete <a href="https://launchpad.net/notify-osd">Notify OSD</a> que se basa en las especificaciones del proyecto <a href="http://www.galago-project.org/about.php">Galago</a> (<a href="http://www.galago-project.org/specs/notification/0.9/">freedesktop.org Desktop Notifications Specification</a>). Este emplea el demonio <em>notify-osd</em> para enviar las notificaciones a través de <a href="http://www.freedesktop.org/wiki/Software/dbus">D-Bus</a>. Sin embargo muchos paquetes aún emplean la librería <strong>libnotify</strong> del proyecto Galago para enviar sus notificaciones. Además el proyecto Galago tiene un <a href="http://es.wikipedia.org/wiki/Binding">binding</a> para <strong>Python</strong> de está librería, que es lo que emplearé en este artículo. En Ubuntu, es el paquete <a href="http://packages.ubuntu.com/es/natty/i386/python-notify">python-notify</a> y se importa desde Python como <code>pynotify</code>. También es posible emplear el paquete <a href="http://packages.ubuntu.com/natty/libnotify-bin">libnotify-bin</a> que nos proporciona el comando <code>notify-send</code> que nos permite enviar notificaciones directamente desde la línea de comandos, algo que puede ser muy útil para scripts en <a href="http://es.wikipedia.org/wiki/Bash"><em>bash</em></a>.</p> <p>Ejemplo de utilización de <code>notify-send</code>:</p> <div class="codehilite"><pre><span class="nv">$ </span>notify-send <span class="s2">&quot;Notificación&quot;</span> <span class="s2">&quot;Esto es un ejemplo de notificación&quot;</span> </pre></div> <p>que nos mostraría una notificación como esta:</p> <p style="text-align:center;"><img src="pictures/notify_send.png" width="341" height="84" alt="notify_send.png" /></p> <h2 id="notificaciones_desde_python">Notificaciones desde Python</h2> <p>Empleando <code>pynotify</code> podemos emplear estas notificaciones en nuestros programas y scripts Python y se visualizaran igual que las propias del sistema. El problema que tiene este modulo es que no tiene documentación <abbr title="As Far As I Know (en español &quot;Hasta donde yo sé&quot; o &quot;Que yo sepa&quot;)">AFAIK</abbr>, por lo que tenemos que guiarnos por algunos ejemplos (los del <a href="http://www.galago-project.org/downloads.php">propio paquete</a>, por ejemplo) o experimentar con él ayudándonos de las herramientas de introspección de Python (<code>help()</code>, <code>dir()</code>, <a href="http://docs.python.org/library/inspect.html">inspect</a>).</p> <p>Para poder emplearlo en mis programas he creado una función que hace uso de las opciones principales de las notificaciones:</p> <div class="codehilite"><pre><span class="kn">import</span> <span class="nn">pynotify</span> <span class="kn">import</span> <span class="nn">gtk</span> <span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="n">icon</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Send notification icon messages through libnotify.</span> <span class="sd"> Parameters:</span> <span class="sd"> (str) title -- The notification title</span> <span class="sd"> (str) msg -- The message to display into notification</span> <span class="sd"> (str / uri) icon -- Type of icon (ok|info|error|warm|ask|sync) or icon file</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">pynotify</span><span class="o">.</span><span class="n">is_initted</span><span class="p">():</span> <span class="n">pynotify</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">title</span><span class="p">)</span> <span class="n">gtk_icon</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;ok&#39;</span><span class="p">:</span><span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_YES</span><span class="p">,</span> <span class="s">&#39;info&#39;</span><span class="p">:</span><span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_DIALOG_INFO</span><span class="p">,</span> <span class="s">&#39;error&#39;</span><span class="p">:</span><span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_DIALOG_ERROR</span><span class="p">,</span> <span class="s">&#39;warm&#39;</span><span class="p">:</span><span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_DIALOG_WARNING</span><span class="p">,</span> <span class="s">&#39;ask&#39;</span><span class="p">:</span><span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_DIALOG_QUESTION</span><span class="p">,</span> <span class="s">&#39;sync&#39;</span><span class="p">:</span><span class="n">gtk</span><span class="o">.</span><span class="n">STOCK_JUMP_TO</span><span class="p">}</span> <span class="k">try</span><span class="p">:</span> <span class="n">note</span> <span class="o">=</span> <span class="n">pynotify</span><span class="o">.</span><span class="n">Notification</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">msg</span><span class="p">)</span> <span class="n">helper</span> <span class="o">=</span> <span class="n">gtk</span><span class="o">.</span><span class="n">Button</span><span class="p">()</span> <span class="n">gtk_icon</span> <span class="o">=</span> <span class="n">helper</span><span class="o">.</span><span class="n">render_icon</span><span class="p">(</span><span class="n">gtk_icon</span><span class="p">[</span><span class="n">icon</span><span class="p">],</span> <span class="n">gtk</span><span class="o">.</span><span class="n">ICON_SIZE_BUTTON</span><span class="p">)</span> <span class="n">note</span><span class="o">.</span><span class="n">set_icon_from_pixbuf</span><span class="p">(</span><span class="n">gtk_icon</span><span class="p">)</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="n">note</span> <span class="o">=</span> <span class="n">pynotify</span><span class="o">.</span><span class="n">Notification</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="n">icon</span><span class="p">)</span> <span class="n">note</span><span class="o">.</span><span class="n">show</span><span class="p">()</span> </pre></div> <p>Básicamente llamamos a la función y le decimos que titulo le queremos poner, el mensaje y el icono que queremos mostrar. En cuanto al icono se puede emplear ninguno, uno personalizado indicando la ruta al fichero o un icono estándar de GTK para las opciones más comunes. Veamos lo sencillo de su funcionamiento con ejemplos:</p> <div class="codehilite"><pre><span class="gp">&gt;&gt;&gt; </span><span class="c"># Notificación sin icono</span> <span class="gp">... </span><span class="n">notify</span><span class="p">(</span><span class="s">&quot;Titulo&quot;</span><span class="p">,</span> <span class="s">&quot;Esta notificación no lleva icono&quot;</span><span class="p">)</span> <span class="go">&gt;&gt;&gt;</span> </pre></div> <p style="text-align:center;"><img src="pictures/notify_noicon.png" width="339" height="87" alt="notify_noicon.png" /></p> <div class="codehilite"><pre><span class="gp">&gt;&gt;&gt; </span><span class="c"># Notificación con icono personalizado</span> <span class="gp">... </span><span class="n">notify</span><span class="p">(</span><span class="s">&quot;Ubuntu&quot;</span><span class="p">,</span> <span class="s">&quot;Esta lleva el icono personalizado&quot;</span><span class="p">,</span> <span class="go">&quot;/usr/share/icons/gnome/scalable/places/ubuntu-logo.svg&quot;)</span> <span class="go">&gt;&gt;&gt;</span> </pre></div> <p style="text-align:center;"><img src="pictures/notify_ubuntu.png" width="339" height="87" alt="notify_ubuntu.png" /></p> <div class="codehilite"><pre><span class="gp">&gt;&gt;&gt; </span><span class="c"># Notificación de información (GTK)</span> <span class="gp">... </span><span class="n">notify</span><span class="p">(</span><span class="s">&quot;Información&quot;</span><span class="p">,</span> <span class="s">&quot;Esta emplea el icono de información estándar de GTK&quot;</span><span class="p">,</span> <span class="s">&quot;info&quot;</span><span class="p">)</span> <span class="go">&gt;&gt;&gt;</span> </pre></div> <p style="text-align:center;"><img src="pictures/notify_info.png" width="339" height="87" alt="notify_info.png" /></p> <div class="codehilite"><pre><span class="gp">&gt;&gt;&gt; </span><span class="c"># Notificación de error (GTK)</span> <span class="gp">... </span><span class="n">notify</span><span class="p">(</span><span class="s">&quot;Error!&quot;</span><span class="p">,</span> <span class="s">&quot;Ejemplo de reporte de error (GTK)&quot;</span><span class="p">,</span> <span class="s">&quot;error&quot;</span><span class="p">)</span> <span class="go">&gt;&gt;&gt;</span> </pre></div> <p style="text-align:center;"><img src="pictures/notify_error.png" width="338" height="87" alt="notify_error.png" /></p> <p>Como se puede ver el proceso es realmente sencillo y hacer esto puede aportar a nuestros programas/scripts ese toque que le faltaba y que tanto contribuye a la usabilidad de los mismos. Yo por ejemplo, lo empleo en scripts programados para saber cuando se activan y cuando terminan, sobre todo cuando son procesos largos. Un ejemplo puede verse en el código de <a href="http://joedicastro.com/sincronizar-una-carpeta-local-y-una-remota-a-traves-de-ftp-lftp-mirror.html">Sincronizar una carpeta local y una remota a través de FTP: lftp-mirror</a></p> <p>El fichero con este código, <code>notify.py</code> se encuentra en el repositorio <em>Python Recipes</em> alojado en <a href="http://github.com/joedicastro/python-recipes">github</a>.</p> <div class="footnote"> <hr /> <ol> <li id="fn:1"> <p>Se puede emplear también en otras distribuciones Linux &#160;<a href="#fnref:1" rev="footnote" title="Jump back to footnote 1 in the text">&#8617;</a></p> </li> </ol> </div>joe di castroSun, 12 Jun 2011 23:05:00 +0200http://joedicastro.com/notificaciones-de-escritorio-en-ubuntu-desde-python.htmllinuxubuntupythonGenerar informes de cambios en paquetes instalados en Debian y Ubuntu.http://joedicastro.com/generar-informes-de-cambios-en-paquetes-instalados-en-debian-y-ubuntu.html<p>Una de las políticas de seguridad que tengo con mis sistemas <strong>Linux</strong>, es además de efectuar respaldos periódicos (diarios) del contenido del directorio <code>/home</code> (en mi caso siempre es una partición o disco independiente), es siempre guardar también una lista de los <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> instalados en los mismos. Prácticamente nunca hago una copia completa de la partición o disco de de sistema, y aunque si hago una copia de los directorios más importantes, prefiero tener siempre una relación actualizada de los <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> instalados en cada maquina.</p> <p>En sistemas Linux <a href="http://debian.org">Debian</a> y otras distribuciones derivadas (<a href="http://ubuntu.com">Ubuntu</a>, <a href="http://www.linuxmint.com/">Mint</a>, <a href="http://www.mepis.org/">Mepis</a>, <a href="http://www.knoppix.org/">Knoppix</a>, ...) obtener esta lista es realmente sencillo, pues solo es necesario ejecutar este comando:</p> <div class="codehilite"><pre><span class="go">dpkg -l &gt; lista_paquetes.txt</span> </pre></div> <p>Y con eso, se generaría una lista de los <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> instalados<sup id="fnref:nota"><a href="#fn:nota" rel="footnote">1</a></sup> en el sistema en el fichero <em>lista_paquetes.txt</em>. Y mantener actualizado este fichero es tan sencillo como programar (via <code>crontab</code> por ejemplo) la ejecución de este comando con un sencillo <a href="http://es.wikipedia.org/wiki/">script</a> <a href="http://es.wikipedia.org/wiki/Shell_de_UNIX">shell</a>. Y así lo he realizado durante años, hasta que quise tener aun más información.</p> <p>Entonces me interesó conocer también los cambios que se producen en los <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr>, es decir nuevas (des)instalaciones y actualizaciones. Es lógico pensar que si uno mismo es el que las realiza, pues ya sabe esta información de primera mano. Pero la memoria es frágil y no demasiado confiable, además ¿que ocurre cuando en una misma maquina tienen permisos para administrar los <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> más de un usuario? </p> <p>Se pueden conocer estos cambios de los <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> en el tiempo de varios modos, desde acudir a los logs de <strong>dpkg</strong> en <code>/var/log/dpkg.log</code> y examinarlos con algún analizador de logs (o el más sencillo e inmediato comando <code>less</code>) hasta consultarlos de una manera más sencilla y gráfica con el gestor de <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> <strong>Synaptic</strong> (en el menú <em>Archivo -&gt; Histórico</em>). Pero me interesaba automatizar esto, para conocer esos cambios poco después de que se produjeran y lo lógico era emplear el mismo script shell que empleaba para generar la lista de <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> instalados. Así lo hice entonces, combinando los comandos <code>awk</code>, <code>grep</code>, <code>diff</code> y <code>mail</code> con el comando del principio para obtener la lista, tenia los cambios en mi buzón de correo al poco tiempo de producirse. Y ha funcionado perfectamente hasta ahora.</p> <p>Pero desde que me introduje en el lenguaje <strong>Python</strong>, he ido migrando poco a poco los scripts <a href="http://es.wikipedia.org/wiki/Bash">bash</a> que tengo para reescribirlos en este lenguaje. Y recientemente le ha tocado a este. Ha sido muy fácil y la verdad es que el resultado aunque funcionalmente es el mismo, me ha permitido entregar unos informes más elegantes y fáciles de interpretar de un golpe de vista. </p> <p>Ahora, cuando se realiza algún cambio en los <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> del sistema, de forma automática, por mi o por otro usuario autorizado, yo recibo en mi correo un informe similar a este:</p> <div class="codehilite"><pre>SCRIPT ========================================================================= dpkg_diff (ver. 0.1) http://joedicastro.com Changes of packages installed on yourmachine =============================================================================== START TIME ===================================================================== Thursday 05/05/11, 10:30:01 =============================================================================== INSTALLED PACKAGES LIST FILE ___________________________________________________ /your/path/to/package_list.txt CHANGES DIFF ___________________________________________________________________ <span class="gd">--- previous Wed May 4 22:59:51 2011</span> <span class="gi">+++ current Thu May 5 10:30:01 2011</span> <span class="gu">@@ -34,1 +34,1 @@</span> <span class="gd">-ii apt 0.7.25.3ubuntu9.3</span> <span class="gi">+ii apt 0.7.25.3ubuntu9.4</span> <span class="gu">@@ -36,2 +36,2 @@</span> <span class="gd">-ii apt-transport-https 0.7.25.3ubuntu9.3</span> <span class="gd">-ii apt-utils 0.7.25.3ubuntu9.3</span> <span class="gi">+ii apt-transport-https 0.7.25.3ubuntu9.4</span> <span class="gi">+ii apt-utils 0.7.25.3ubuntu9.4</span> END TIME ======================================================================= Thursday 05/05/11, 10:30:01 =============================================================================== </pre></div> <p>Donde se puede ver la información que nos indica el script que ha generado el correo, la maquina en la que se han realizado los cambios, la localización del fichero con la relación de todos los <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> instalados en el sistema, la fecha y hora de la ejecución del script y el <a href="http://es.wikipedia.org/wiki/Diff">diff</a> con la relación de cambios en los <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> en formato <a href="http://en.wikipedia.org/wiki/Diff#Unified_format">Unified format</a>. En este ejemplo podemos ver como se han actualizado tres <abbr title="En sistemas *NIX se denomina así a los programas">paquetes</abbr> en esa maquina. No nos dice la hora en que se efectuó la modificación (podemos verlo en el log de <code>dpkg</code>) pero si podemos saber que se efectuó entre dos intervalos de ejecución del script, información que será más que suficiente la mayoría de las veces. Para datos concretos, ir al log y filtrarlo con <code>grep</code> y en segundos sabremos la respuesta. Si además como es mi caso, ejecutamos el script cada 12 o 24 horas, pues será fácil saber cuando se han realizado los cambios.</p> <p>Un fragmento de código de la parte principal del script es el siguiente:</p> <div class="codehilite"><pre><span class="k">def</span> <span class="nf">pretty_diff</span><span class="p">(</span><span class="n">diff</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Better format for package lines in diff.&quot;&quot;&quot;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="p">{}</span> <span class="c"># diff&#39;s packages lines</span> <span class="c"># Get columns info for diff package lines</span> <span class="k">for</span> <span class="n">idx</span><span class="p">,</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">diff</span><span class="p">):</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">findall</span><span class="p">(</span><span class="s">&quot;^-{3}|^\+{3}|^@{2}&quot;</span><span class="p">,</span> <span class="n">line</span><span class="p">):</span> <span class="c"># split the line in columns and remove the description column</span> <span class="n">cols</span> <span class="o">=</span> <span class="n">split</span><span class="p">(</span><span class="s">&quot;\s{2,}&quot;</span><span class="p">,</span> <span class="n">line</span><span class="p">,</span> <span class="mi">3</span><span class="p">)[:</span><span class="mi">3</span><span class="p">]</span> <span class="c"># A nested dict, for each line index we have a dict that contains </span> <span class="c"># the package line columns: &#39;s&#39; (status), &#39;n&#39; (name) &amp; &#39;v&#39; (version)</span> <span class="c"># and the width of the name column: w(width)</span> <span class="n">pkg</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;s&#39;</span><span class="p">:</span><span class="n">cols</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="s">&#39;n&#39;</span><span class="p">:</span><span class="n">cols</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s">&#39;v&#39;</span><span class="p">:</span><span class="n">cols</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="s">&#39;w&#39;</span><span class="p">:</span><span class="nb">len</span><span class="p">(</span><span class="n">cols</span><span class="p">[</span><span class="mi">1</span><span class="p">])}</span> <span class="c"># maximum width in packages&#39; name column for all lines</span> <span class="n">mxw</span> <span class="o">=</span> <span class="nb">max</span><span class="p">((</span><span class="n">pkg</span><span class="p">[</span><span class="n">index</span><span class="p">][</span><span class="s">&#39;w&#39;</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">pkg</span><span class="p">))</span> <span class="c"># Replace each package line for a prettier one (more legible) </span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">diff</span><span class="p">)):</span> <span class="k">if</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">pkg</span><span class="p">:</span> <span class="n">diff</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="s">&quot;{0} {1} {2}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">pkg</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">&#39;s&#39;</span><span class="p">],</span> <span class="n">pkg</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">&#39;n&#39;</span><span class="p">]</span> <span class="o">+</span> <span class="s">&quot; &quot;</span> <span class="o">*</span> <span class="p">(</span><span class="n">mxw</span> <span class="o">-</span> <span class="n">pkg</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">&#39;w&#39;</span><span class="p">]),</span> <span class="n">pkg</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="s">&#39;v&#39;</span><span class="p">]))</span> <span class="k">return</span> <span class="n">diff</span> <span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">old</span><span class="o">=</span><span class="s">&quot;&quot;</span><span class="p">):</span> <span class="sd">&quot;&quot;&quot;Main section&quot;&quot;&quot;</span> <span class="c"># The path to store the debian packages list file</span> <span class="n">pkg_lst_file</span> <span class="o">=</span> <span class="s">&quot;./package_list.txt&quot;</span> <span class="c"># Start logging</span> <span class="n">log</span> <span class="o">=</span> <span class="n">Logger</span><span class="p">()</span> <span class="n">url</span> <span class="o">=</span> <span class="s">&quot;http://joedicastro.com&quot;</span> <span class="n">head</span> <span class="o">=</span> <span class="s">&quot;Changes of packages installed on {0}&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">platform</span><span class="o">.</span><span class="n">node</span><span class="p">())</span> <span class="n">log</span><span class="o">.</span><span class="n">header</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">head</span><span class="p">)</span> <span class="n">log</span><span class="o">.</span><span class="n">time</span><span class="p">(</span><span class="s">&quot;Start time&quot;</span><span class="p">)</span> <span class="c"># Read the old file and clean the list</span> <span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">pkg_lst_file</span><span class="p">):</span> <span class="n">old</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">pkg_lst_file</span><span class="p">,</span> <span class="s">&#39;r&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">readlines</span><span class="p">()</span> <span class="n">old_date</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">ctime</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="n">pkg_lst_file</span><span class="p">)</span><span class="o">.</span><span class="n">st_mtime</span><span class="p">)</span> <span class="c"># Get the current list of debian packages installed on system </span> <span class="n">current</span> <span class="o">=</span> <span class="n">Popen</span><span class="p">([</span><span class="s">&quot;dpkg&quot;</span><span class="p">,</span> <span class="s">&quot;-l&quot;</span><span class="p">],</span> <span class="n">stdout</span><span class="o">=</span><span class="n">PIPE</span><span class="p">)</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">readlines</span><span class="p">()</span> <span class="c"># First, save the list file</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">pkg_lst_file</span><span class="p">,</span> <span class="s">&#39;w&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">out</span><span class="p">:</span> <span class="n">out</span><span class="o">.</span><span class="n">writelines</span><span class="p">(</span><span class="n">current</span><span class="p">)</span> <span class="n">curr_date</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">ctime</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">stat</span><span class="p">(</span><span class="n">pkg_lst_file</span><span class="p">)</span><span class="o">.</span><span class="n">st_mtime</span><span class="p">)</span> <span class="c"># Compare both lists</span> <span class="k">if</span> <span class="n">old</span><span class="p">:</span> <span class="n">file_path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">realpath</span><span class="p">(</span><span class="n">pkg_lst_file</span><span class="p">)</span> <span class="n">diff</span> <span class="o">=</span> <span class="p">[</span><span class="n">ln</span> <span class="k">for</span> <span class="n">ln</span> <span class="ow">in</span> <span class="n">unified_diff</span><span class="p">(</span><span class="n">old</span><span class="p">,</span> <span class="n">current</span><span class="p">,</span> <span class="n">fromfile</span><span class="o">=</span><span class="s">&quot;previous&quot;</span><span class="p">,</span> <span class="n">tofile</span><span class="o">=</span><span class="s">&quot;current &quot;</span><span class="p">,</span> <span class="n">fromfiledate</span><span class="o">=</span><span class="n">old_date</span><span class="p">,</span> <span class="n">tofiledate</span><span class="o">=</span><span class="n">curr_date</span><span class="p">,</span> <span class="n">n</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">lineterm</span><span class="o">=</span><span class="s">&quot;&quot;</span><span class="p">)]</span> <span class="c"># If there are differences write the log to disk and send mail</span> <span class="k">if</span> <span class="n">diff</span><span class="p">:</span> <span class="n">log</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">&quot;Installed packages list file&quot;</span><span class="p">,</span> <span class="n">file_path</span><span class="p">)</span> <span class="n">log</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">&quot;Changes diff&quot;</span><span class="p">,</span> <span class="n">pretty_diff</span><span class="p">(</span><span class="n">diff</span><span class="p">))</span> <span class="n">log</span><span class="o">.</span><span class="n">time</span><span class="p">(</span><span class="s">&quot;End time&quot;</span><span class="p">)</span> <span class="n">log</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span> <span class="c"># Send mail to current system user. For other options, see logger </span> <span class="c"># module info</span> <span class="n">log</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s">&quot;Debian packages changes&quot;</span><span class="p">)</span> </pre></div> <p>Este script hace uso del modulo logger, que comento en este <a href="http://joedicastro.com/logger_informes_legibles_para_tus_scripts_python">artículo</a>. </p> <p>Para obtener la versión más reciente del script y del modulo logger, consultar mi repositorio <em>Python Recipes</em> en <a href="http://github.com/joedicastro/python-recipes">github</a>.</p> <div class="footnote"> <hr /> <ol> <li id="fn:nota"> <p>Es una relación de los programas instalados empleando el sistema de paqueteria de Debian, los programas instalados manualmente vía compilación u otros medios no aparecerán en ella. &#160;<a href="#fnref:nota" rev="footnote" title="Jump back to footnote 1 in the text">&#8617;</a></p> </li> </ol> </div>joe di castroFri, 06 May 2011 22:55:00 +0200http://joedicastro.com/generar-informes-de-cambios-en-paquetes-instalados-en-debian-y-ubuntu.htmlpythonscriptpaqueteslinuxdebianubuntudpkg