Si estás utilizando la librería python-oauth para autenticarte contra un servicio por medio de OAuth y te consigues con el error:
AttributeError: 'module' object has no attribute 'OAuthSignatureMethod_HMAC_SHA1'

Entonces tengo la solución para ti.

El problema es que la clase no está en el módulo oauth, sino en oauth.oauth. Entonces, en lugar de hacer:
import oauth

Prueba con:
try:
import oauth.oauth
except:
import oauth

De nada ;)

Referencias:

Leer más

Buscando opciones para un gestor de inicio de sesión bonito y ligero recordé a LightDM. Sé que seguramente me van a hablar de SLiM, pero ha estado abandonado mucho tiempo y no es tan lindo y funcional como LightDM, así que decidí no utilizarlo.

Instalación

LightDM se encuentra disponible en AUR, así que una buena forma de instalarlo es con yaourt: $ yaourt -S lightdm lightdm-unity-greeter
Para más información de cómo instalar LigthDM visita la wiki de ArchLinux.

Configuración

Esta es la parte divertida. Muchas recetas en internet te hablan de editar el infame /etc/lightdm/lightdm-unity-greeter.conf, pero la verdad es que la versión más nueva no utiliza ese archivo sino que, "en teoría", utiliza dinámicamente la imagen establecida como fondo de pantalla para el fondo del greeter. En mi caso siempre usaba la misma imagen, la que trae Ubuntu por defecto. Incluso agregué mi imagen a la ruta /usr/share/backgrounds/ y tampoco funcionaba.

¿Qué hacemos? Pues editemos el schema del dconf a mano. Abrimos con nuestro editor favorito el archivo usr/share/glib-2.0/schemas/com.canonical.unity-greeter.gschema.xml y ajustamos los valores correspondientes.

Yo edité solamente la línea del background y del color de fondo:
<key name="background" type="s">
<default>'/usr/share/backgrounds/poweroff.jpg'</default>
<summary>Background file to use, either an image path or a color</summary>
</key>
...
<key name="background-color" type="s">
<default>'#444444'</default>
<summary>Background color (e.g. #772953), set before wallpaper is seen</summary>
</key>

En el key con nombre "background" cambié la ruta al archivo de fondo, apuntando a /usr/share/backgrounds/poweroff.jpg y en el key con nombre "background-color" cambié el color a #444444. Ustedes editen los campos como mejor les parezca.

Para que estos cambios tengan efecto debemos recompilar los esquemas del dconf, así que ejecutamos: # glib-compile-schemas /usr/share/glib-2.0/schemas/
Reiniciamos el servicio y ya estaremos disfrutando de nuestros cambios en el gestor.

Espero que les haya servido esta receta.

Referencias

Leer más

En ocasiones nos toca respaldar gran cantidad de información de un equipo a otro y una de las maneras más cómodas de hacerlo es a través de la red. Para esta tarea usaremos Rsync que, al igual que netcat, son unas navajas suizas en lo que a red se refiere. Rsync, entre otras cosas, nos permite sincronizar directorios a través de la red y de esa forma lo usaremos para enviar nuestros archivos de un equipo a otro.

Comando y parámetros

Lo primero que necesitamos es acceso vía SSH al equipo remoto. Puede ser por clave pública o con usuario y contraseña. Luego nos ubicamos en la carpeta que queremos respaldar y ejecutamos el comando: $ rsync -ravz archivo_a_respaldar usuario@IP:directorio_destino Donde:
  • r: recorre los directorios de manera recursiva
  • a: conserva los atributos de los archivos (usuario, permisos, etc)
  • v: imprime información en pantalla
  • z: comprime los datos antes de enviar
Luego de la autenticación comenzará la copia de los archivos. Es importante resaltar que en el directorio_destino debemos colocar la ruta absoluta a la carpeta.

Copiar un archivo

Supongamos que queremos respaldar el archivo backup.tar.gz en un equipo remoto con IP 192.168.0.3, el usuario es satanas y el directorio destino es el home del usuario, el comando nos quedaría así:
$ rsync -avz backup.tar.gz satanas@192.168.0.3:~
El símbolo ~ puede sustituirse por la ruta absoluta /home/satanas.

Copiar un directorio y todos sus subdirectorios

Ahora supongamos que queremos respaldar el directorio /tmp/music en el mismo equipo anterior pero ahora el destino es la carpeta /home/satanas/musica, el comando nos quedaría así: $ rsync -ravz /tmp/music satanas@192.168.0.3:/home/satanas/musica

Con esta receta podremos pasar nuestro respaldo de archivos de un equipo a otro sin mucho inconveniente. Espero que les sirva

Leer más

Situación

Queremos conocer el espacio en disco usado por un directorio (y sus subdirectorios inmediatos) con un comando de consola.

Comando

$ du -h -s /tu/directorio/*

Resultado

El comando te imprimirá algo como:
$ du -h --summarize /tu/directorio/*
2.5M /var/backups
286M /var/cache
139M /var/lib
4.1k /var/local
4.1k /var/lock
98M /var/log
4.4M /var/mail
4.1k /var/opt
103k /var/run
66k /var/spool
4.1k /var/tmp

Explicación

El comando du te da un estimado del espacio utilizado por un directorio, la opción -h te imprime los números en potencias de 1024, --sumarize muestra solo el total para cada elemento y /tu/directorio/* corresponde al directorio que deseas analizar. El * al final es la clave para indicarle al comando que queremos ver los subdirectorios.

Leer más

Cada vez que voy a instalar una gema con el comando gem uso los parámetros --no-rdoc y --no-ri para que no me genere la documentación, principalmente porque nunca la uso y además demora demasiado el tiempo de instalación de las gemas. Pero escribir esos parámetros cada vez que ejecuto el comando es fastidioso.

Afortunadamente gem busca el archivo de configuración ~/.gemrc en el directorio personal del usuario y aplica las opciones que estén definidas, así que creando ese archivo en nuestro home con la siguiente línea no tendremos que tipear las opciones nunca más:

gem: --no-rdoc --no-ri

Leer más

D-Bus es un sistema que permite la comunicación entre diferentes procesos. Es desarrollado como parte del proyecto freedesktop.org buscando ofrecer una solución simple y común para los distintos entornos de escritorio.

Conceptos básicos


Tipos de Bus

Hay dos tipos de buses que se pueden usar con D-Bus. El bus de sesión, que se crea con cada sesión de usuario y es local a esa sesión, y el bus de sistema. Este último es global, se inicia cuando arranca el equipo y generalmente se utiliza para comunicarse con procesos como udev, NetworkManager o HAL.

Rutas a Objetos

Cada lenguaje de programación tiene sus objetos nativos (usualmente representados por clases). La ruta a un objeto es la forma en que D-Bus permite hacer referencia a un objeto nativo y que las aplicaciones remotas puedan usarlo. Un ruta a un objeto luce como la ruta de un archivo (Unix-like) y es común generarlas como un nombre de dominio en reversa, por ejemplo: /org/gnome/myapp/MyObject. Sin embargo, cada desarrollador puede usar la ruta que mejor le parezca, siempre y cuando sea única.

Métodos y Señales

Los métodos son operaciones (con o sin parámetros) que pueden invocarse en un objeto y que eventualmente pueden devolver un resultado. Las señales son notificaciones que se envían al bus y son recibidas por los observadores (objetos que escuchan o se conectan a esas señales). Estas señales también pueden enviar datos de interés para el receptor.

Objetos Proxy

Un objeto proxy no es más que un objeto Python que viene a representar a un objeto remoto en otro proceso. Esto nos permite emplear los métodos del objeto remoto como si fueran métodos nativos. Para instanciar un objeto proxy necesitamos la "ruta del objeto".

Estructura Básica

Para usar D-Bus es importante entender la estructura e interacciones básicas. En palabras simples, lo que tenemos son dos aplicaciones que se "hablan" entre sí a través de un canal común. Generalmente una de ellas actúa como "servidor", ofreciendo métodos y señales que podrán ser usados por una aplicación "cliente". En la imagen a continuación se ilustra claramente el concepto.



Un ejemplo bastante común es el funcionamiento de un reproductor de música (como el servidor) que ofrece métodos para informar sobre su estado actual y una aplicación de notificaciones (como cliente) que usa estos métodos para mostrar diálogos en el escritorio cada vez que cambia una canción.

Aplicación "servidor"


Lo primero que debemos hacer para empezar a trabajar con D-Bus es conectarnos a un bus. En nuestro caso será al bus de sesión porque no nos interesa interactuar con procesos del sistema operativo.

Adicionalmente, para ejecutar llamadas asíncronas a los métodos (y que la aplicación no se bloquee mientras espera) debemos configurar un bucle principal. Para el momento de escribir este post, python-dbus solo soporta el bucle principal de GLib, así que usaremos esas librerías para crear nuestro loop.

Los import que necesitamos para esto serían:
import dbus
import dbus.service
import gobject

from dbus.mainloop.glib import DBusGMainLoop

Luego, definimos (en variable globales) el nombre del bus y la ruta de nuestro objeto:
DBUS_BUSNAME = 'org.example.ExampleDBus'
DBUS_MYOBJECT_PATH = '/org/example/ExampleDBus/MyObject'

Ahora definimos la clase de nuestro server, con la inicialización mínima para que funcione D-Bus:
class DBusService(dbus.service.Object):

def __init__(self):
# Le indicamos a D-Bus que usaremos el loop de GLib como bucle
# predeterminado
DBusGMainLoop(set_as_default=True)

# Establecemos la conexión al bus de sesión
self.session_bus = dbus.SessionBus()
name = dbus.service.BusName(DBUS_BUSNAME, self.session_bus)

# Inicializamos el objeto D-Bus
dbus.service.Object.__init__(self, self.session_bus, DBUS_MYOBJECT_PATH)

# Arrancamos el bucle principal
loop = gobject.MainLoop()

# Colocamos el loop dentro de un try/except para detectar cuando el
# usuario presione Ctrl + C y finalizar la aplicación limpiamente
try:
print "Servicio DBus iniciado"
loop.run()
except KeyboardInterrupt:
loop.quit()
print "Servicio DBus finalizado"

Si se fijan en el código anterior, nuestra clase hereda de dbus.service.Object (porque estamos construyendo un objeto D-Bus que será instanciado por una aplicación remota). En la inicialización le indicamos a D-Bus que usaremos el loop de GLib, establecemos la conexión al bus de sesión y arrancamos el loop.

He decidido colocar la ejecución del loop principal dentro de un try/except para que el usuario pueda usar Ctrl + C para salir elegantemente de la aplicación.

Hecho esto podemos proceder a definir un par de métodos. Hagamos un método que imprima un saludo y otro que nos devuelva un valor, en este caso la hora.
    @dbus.service.method(DBUS_BUSNAME)
def say_hello(self, name):
print "Hola, %s" % name

@dbus.service.method(DBUS_BUSNAME)
def get_time(self):
return time.strftime("%H:%M")

En los párrafos previos comenté que un objeto proxy no es más que una representación de un objeto D-Bus en un objeto Python, así que para exportar un método nativo como método D-Bus usamos el decorador @dbus.service.method y le pasamos como parámetro el nombre del bus. Luego definimos nuestras funciones como lo haríamos normalmente en cualquier clase de Python y ya con esto tendríamos un servidor muy básico listo para funcionar.

El código completo debería quedar así:
#!/usr/bin/python
# -*- coding: utf-8 -*-

import time
import dbus
import gobject
import dbus.service

from dbus.mainloop.glib import DBusGMainLoop

DBUS_BUSNAME = 'org.example.ExampleDBus'
DBUS_MYOBJECT_PATH = '/org/example/ExampleDBus/MyObject'

class DBusService(dbus.service.Object):

def __init__(self):
# Le indicamos a D-Bus que usaremos el loop de GLib como bucle
# predeterminado
DBusGMainLoop(set_as_default=True)

# Establecemos la conexión al bus de sesión
self.session_bus = dbus.SessionBus()
name = dbus.service.BusName(DBUS_BUSNAME, self.session_bus)

# Inicializamos el objeto D-Bus
dbus.service.Object.__init__(self, self.session_bus, DBUS_MYOBJECT_PATH)

# Arrancamos el bucle principal
loop = gobject.MainLoop()

# Colocamos el loop dentro de un try/except para detectar cuando el
# usuario presione Ctrl + C y finalizar la aplicación limpiamente
try:
print "Servicio DBus iniciado"
loop.run()
except KeyboardInterrupt:
loop.quit()
print "Servicio DBus finalizado"

@dbus.service.method(DBUS_BUSNAME)
def say_hello(self, name):
print "Hola, %s" % name

@dbus.service.method(DBUS_BUSNAME)
def get_time(self):
return time.strftime("%H:%M")

@dbus.service.signal(DBUS_BUSNAME)
def kill(self):
return 'killed'

if __name__ == '__main__':
service = DBusService()

Aplicación "cliente"

La aplicación cliente es mucho más simple. La única librería que necesitamos es la de D-Bus, el nombre del bus y la ruta del objeto.
import dbus

DBUS_BUSNAME = 'org.example.ExampleDBus'
DBUS_MYOBJECT_PATH = '/org/example/ExampleDBus/MyObject'

Ahora pasemos a crear la clase. Al igual que en el servidor, necesitamos establecer la conexión con el bus y "mapear" el objeto D-Bus en un objeto Python. En este caso la variable self.my_object es la que contiene la representación de dicho objeto.
class DBusClient:

def __init__(self):
self.session_bus = dbus.SessionBus()
self.my_object = self.session_bus.get_object(DBUS_BUSNAME,
DBUS_MYOBJECT_PATH)

Para llamar a un método del objeto remoto debemos importar el método correspondiente, y como estaremos usando varios métodos vamos a crear una función interna que nos ayude con esta tarea.
    def __get_dbus_method(self, name):
return self.my_object.get_dbus_method(name)

Procedemos a crear nuestros métodos nativos que obtendrán el método remoto del objeto D-Bus y lo llamarán con los parámetros correspondientes según sea el caso.
    def say_hello(self, name):
# Almacenamos el método en una variable y luego lo llamamos con los
# parámetros correspondientes
method = self.__get_dbus_method('say_hello')
method(name)

def print_time(self):
method = self.__get_dbus_method('get_time')
# Obtenemos el valor de retorno del método y la imprimimos en pantalla
current_time = method()
print "Son las %s" % current_time

Con eso ya deberíamos tener un cliente funcional. El código completo del cliente debería quedar algo como:
#!/usr/bin/python
# -*- coding: utf-8 -*-

import dbus

DBUS_BUSNAME = 'org.example.ExampleDBus'
DBUS_MYOBJECT_PATH = '/org/example/ExampleDBus/MyObject'

class DBusClient:

def __init__(self):
self.session_bus = dbus.SessionBus()
self.my_object = self.session_bus.get_object(DBUS_BUSNAME,
DBUS_MYOBJECT_PATH)

def __get_dbus_method(self, name):
return self.my_object.get_dbus_method(name)

def say_hello(self, name):
# Almacenamos el método en una variable y luego lo llamamos con los
# parámetros correspondientes
method = self.__get_dbus_method('say_hello')
method(name)

def print_time(self):
method = self.__get_dbus_method('get_time')
# Obtenemos el valor de retorno del método y la imprimimos en pantalla
current_time = method()
print "Son las %s" % current_time

if __name__ == '__main__':
client = DBusClient()
client.say_hello('Pedro')
client.say_hello('Maria')
client.print_time()
Es importante que observe que al ejecutar el script del cliente se llamará al método say_hello dos veces (con los parámetros 'Pedro' y 'Maria' respectivamente) y luego al método print_time.

Pruebas

Para probar abrimos dos terminales. En la primera ejecutamos el script del servidor (yo lo llamé dbus_service.py) y veremos algo como:
$ python dbus_service.py 
Servicio DBus iniciado
Con un mensaje indicándonos que el servicio está corriendo y esperando ser utilizado.

En la segunda terminal ejecutamos el script del cliente (lo llamé dbus_client.py) y veremos algo como:
$ python dbus_client.py 
Son las 12:55
Muy bien, imprimió la hora pero... ¿No se suponía que también habíamos llamado al método say_hello dos veces? ¿Qué pasó?

Bueno, volvamos a ver la terminal del servicio y veremos nuestro ansiado resultado:
$ python dbus_service.py 
Servicio DBus iniciado
Hola, Pedro
Hola, Maria

Tal como lo esperabamos, el método say_hello imprime en la instancia del servidor el nombre envíado desde el cliente y por otro lado, el cliente imprime la hora que le devuelve el método get_time desde el servidor. Hemos enviado mensajes en ambas direcciones usando D-Bus, ¡todo un éxito!

Espero que esta receta simple les haya ayudado a comprender como funciona D-Bus y como usarlo desde Python. Más adelante estaré escribiendo otros posts sobre cómo emitir/recibir señales y cómo enviar grandes cantidades de datos "al vuelo" a través de D-Bus.

Fuentes:

Leer más

Si usualmente utilizas la consola interactiva de Ruby On Rails para realizar pruebas, correr tareas de Rake, entre otras cosas y te resultaría sumamente útil ver el log de lo que esta haciendo el ActiveRecord, entonces este tip es para ti.

Simplemente ejecuta la consola desde la carpeta raíz de tu aplicación Rails:

Para rails 3.x:
$ rails c
Para rails 2.x:
$ script/console
Y luego indícale al logger que use la salida estándar:
ActiveRecord::Base.logger = Logger.new(STDOUT)
Con eso los mensajes de log del ActiveRecord se imprimirán en la consola.

Leer más

Singleton es un patrón de diseño cuya función es evitar que un objeto pueda ser instanciado más de una vez. En este post traigo una receta simple para implementar el patrón singleton en Python.

En esta implementación utilizaremos un archivo de bloqueo (lock file) para indicar si la aplicación está en ejecución o no. Python cuenta con el módulo fcntl que nos proporciona una interfaz bastante cómoda para el control de archivos pero está disponible solo para Linux/Unix, eso implica que debemos usar medidas alternativas para Windows.

Detectar SO e importar módulos

Lo primero que debemos hacer es detectar el sistema operativo y ejecutar los import correspondientes para cada caso. Usaremos además el módulo tempfile para generar el lock file como un archivo temporal del sistema.
#!/usr/bin/python2
# -*- coding: utf-8 -*-

import os
import sys
import tempfile

OS = None
if sys.platform.startswith('linux'):
OS = 'linux'
import fcntl
elif sys.platform.startswith('win32'):
OS = 'windows'


Definir la clase Singleton

Después de detectar el sistema operativo definimos la clase Singleton. Básicamente, esta clase será la encargada de crear el lock file al inicio o generar una advertencia y termina la ejecución en caso de que el archivo ya exista (es decir, que ya existe una instancia de la aplicación en ejecución).
class Singleton:
def __init__(self):
# Variable para almacenar el file descriptor
self.fd = None
# Ruta para el lock file en la carpeta temporal del sistema
self.filepath = os.path.abspath(os.path.join(tempfile.gettempdir(),
'myapp.pid'))

if OS == 'linux':
# Para el caso de linux usamos el módulo fcntl para crear el archivo
# y bloquearlo automáticamente. Si la operación falla es porque el
# archivo ya existe y está bloqueado.
self.fd = open(self.filepath, 'w')
try:
fcntl.lockf(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
self.__exit()
elif OS == 'windows':
try:
# Para el caso windows simplemente creamos el archivo "a mano",
# pero verificamos primero si el archivo existe e intentamos
# removerlo (para casos en que la ejecución previa haya sido
# interrumpida)
if os.path.exists(self.filepath):
os.unlink(self.filepath)
self.fd = os.open(self.filepath, os.O_CREAT|os.O_EXCL|os.O_RDWR)
except OSError, err:
if err.errno == 13:
self.__exit()

def __del__(self):
# Para el caso de windows también debemos destruir el archivo "a mano"
# al finalizar la ejecución del programa.
if OS == 'windows':
if self.fd:
os.close(self.fd)
os.unlink(self.filepath)

def __exit(self):
print 'Ya hay una instancia en ejecución. Saliendo'
sys.exit(-1)
En el __init__ se observa que creamos una variable para almacenar el file descriptor (fd), luego generamos una ruta para el lock file. Posteriormente creamos un lock file con el módulo fcntl (en el caso linux) o creamos un archivo regular (para el caso windows). Adicionalmente, en el caso windows necesitamos hacernos cargo del archivo al finalizar la ejecución, para eso sobreescribimos el método __del__ y colocamos nuestro código. Adicionalmente tenemos la función __exit(), que es la encargada de detener la ejecución del programa de forma elegante.

Clase de pruebas

Con los pasos anteriores tenemos lista nuestra implementación simple del patrón singleton. Ahora, ¿Cómo la usamos? Creamos una clase (por ejemplo MyApp) que herede de singleton y ponemos un bucle infinito para que se mantenga haciendo "algo".
class MyApp(Singleton):
def __init__(self):
Singleton.__init__(self)
print 'Ejecutando MyApp'
# Creamos un bucle infinito solo para mantener la aplicación en
# ejecución
while 1:
continue


¿Cómo se vería nuestro script?

El código completo de nuestro script se vería así:
#!/usr/bin/python2
# -*- coding: utf-8 -*-

import os
import sys
import tempfile

OS = None
if sys.platform.startswith('linux'):
OS = 'linux'
import fcntl
elif sys.platform.startswith('win32'):
OS = 'windows'

class Singleton:
def __init__(self):
# Variable para almacenar el file descriptor
self.fd = None
# Ruta para el lock file en la carpeta temporal del sistema
self.filepath = os.path.abspath(os.path.join(tempfile.gettempdir(),
'myapp.pid'))

if OS == 'linux':
# Para el caso de linux usamos el módulo fcntl para crear el archivo
# y bloquearlo automáticamente. Si la operación falla es porque el
# archivo ya existe y está bloqueado.
self.fd = open(self.filepath, 'w')
try:
fcntl.lockf(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
self.__exit()
elif OS == 'windows':
try:
# Para el caso windows simplemente creamos el archivo "a mano",
# pero verificamos primero si el archivo existe e intentamos
# removerlo (para casos en que la ejecución previa haya sido
# interrumpida)
if os.path.exists(self.filepath):
os.unlink(self.filepath)
self.fd = os.open(self.filepath, os.O_CREAT|os.O_EXCL|os.O_RDWR)
except OSError, err:
if err.errno == 13:
self.__exit()

def __del__(self):
# Para el caso de windows también debemos destruir el archivo "a mano"
# al finalizar la ejecución del programa.
if OS == 'windows':
if self.fd:
os.close(self.fd)
os.unlink(self.filepath)

def __exit(self):
print 'Ya hay una instancia en ejecución. Saliendo'
sys.exit(-1)

class MyApp(Singleton):
def __init__(self):
Singleton.__init__(self)
print 'Ejecutando MyApp'
# Creamos un bucle infinito solo para mantener la aplicación en
# ejecución
while 1:
continue

if __name__ == '__main__':
app = MyApp()


Probando el singleton

Para probarlo abrimos un terminal, nos colocamos en la carpeta donde esté ubicado nuestro script y lo ejecutamos por primera vez. Eso nos dará como resultado algo como:
$ python myapp.py 
Ejecutando MyApp


Abrimos una terminal nueva (sin cerrar la terminal anterior) e intentamos ejecutar la aplicación por segunda vez. Eso nos devolverá:
$ python myapp.py 
Ya hay una instancia en ejecución. Saliendo
¡Y voilá! Logramos que un script de Python pueda ser ejecutado una sola vez.

Hay implementaciones más complejas que almacenan el ID del proceso dentro del lock file y cada vez que se intenta ejecutar una nueva instancia se lee el ID y se verifica que realmente exista un proceso en ejecución con ese identificador. Pero como dije al principio, esta es una receta simple, así que as implementaciones más complejas las dejamos como tareas para el lector ;)

Leer más

Actualmente estoy colaborando con el desarrollo de canaima-instalador, una aplicación para Canaima GNU/Linux que permitirá al usuario instalar/probar Canaima de una manera fácil e intuitiva, y he decidido hacer una propuesta sobre cómo debe lucir y funcionar el nuevo instalador. La propuesta a continuación.

Paso 1: LiveCD

Lo primero que hace falta cambiar es el menú de inicio del liveCD. Actualmente tenemos una pantalla llena de opciones que, más allá de facilitar el uso, pueden confundir a los usuarios menos experimentados. Mi sugerencia es que el liveCD arranque automáticamente y presente un menú inferior con las posibles opciones para usuarios más experimentados.


Paso 2: Bienvenida

Aquí es donde comienza a ejecutarse canaima-instalador. La idea es que sea tipo OEM, es decir, que no cargue el escritorio sino lo mínimo necesario para ejecutarse. Se le preguntará al usuario si desea probar la distribución o instalar. Para el primer caso se cierra canaima-instalador y se continúa con la carga del escritorio, para el segundo caso se sigue el flujo normal de la aplicación.


Paso 3: Requisitos

Se le muestra al usuario cuáles son los requisitos mínimos necesarios para obtener mejores resultados al instalar Canaima. En caso de que no se cumpla con un requisito crítico (por ejemplo, el espacio en disco) la instalación no debería continuar.


Paso 4: Modo de instalación

Acá (luego de haber analizado el hardware) se le presentan al usuario las distintas opciones que tiene para instalar. En teoría todas deberían ser automáticas excepto la de "Particionamiento Avanzado" que ejecutará una instancia de gparted y bloqueará canaima-instalador hasta que el usuario termine de definir sus particiones. Una vez que el usuario haga clic en "Siguiente" no podrá volver atrás. Acá termina la primera fase de instalación y comienza el copiado de los archivos en el disco duro en segundo plano. La idea es aprovechar el tiempo, mientras se copian los archivos solicitamos al usuario el resto de la información y una vez terminada la copia se ejecutan las tareas correspondientes. De esta forma logramos minimizar considerablemente el tiempo de instalación (tal como lo hace Ubuntu).


Paso 5: Configuración de teclado

Como expliqué en el punto anterior, mientras se copian los archivos en segundo plano solicitamos el resto de la información al usuario. Acá se le pedirá que seleccione la distribución del teclado. Es importante observar que el botón de "Anterior" no estará disponible en este paso.


Paso 6: Configuración de usuarios

En esta fase se le pedirán los datos de las cuentas (root y usuario regular). Es importante habilitar un link (o botón) de ayuda donde se pueda explicar qué significa root y por qué es importante definir esa contraseña. Es importante recalcar que si el usuario no ha terminado de introducir la información complementaria y la copia de archivos finaliza entonces las tareas en segundo plano se detendrán hasta que se culmine esta fase.


Paso 7: Imágenes aleatorias sobre las bondades de Canaima

Al igual que Ubuntu y otros sistemas operativos, podemos mostrar una serie de imágenes aleatorias que informen al usuario sobre las bondades de Canaima GNU/Linux y del Software Libre mientras termina la instalación.


Paso 8: Fin de la instalación

Al finalizar todo exitosamente se le mostrará un pequeño diálogo al usuario para que reinicie el equipo y comience a disfrutar de las bondandes del SL.



Bueno, esta es mi idea de lo que debería ser un instalador fácil e intuitivo para Canaima GNU/Linux. Se escuchan comentarios Update para los haters: Sí, mi propuesta está basada casi enteramente en el instalador de Ubuntu porque me parece un excelente instalador ¿Cuál es el problema con eso?

Leer más

En un artículo anterior expliqué cómo configurar el video Intel i915 con KMS en una HP Pavilion dm4. Esta vez explicaré cómo configurar el Touchpad Synaptics de esa misma laptop.

Leer el manual

Lo primero que debes hacer si deseas configurar correctamente el Touchpad es leer el manual (RTFM).
$ man synaptics

Yo configuré el touchpad a mi gusto, así que puedes probarlo y si no te gusta lo adaptas al tuyo. Sin embargo es una tarea de ensayo y error que no podrás completar a menos que conozcas todas las opciones que tienes disponibles.

Dependencias

Lo único que necesitamos es un Xorg bien configurado y los drivers synaptics. Para instalar los drivers ejecutamos:

En Debian:
# aptitude install xserver-xorg-input-synaptics


Entender cómo funciona el Touchpad

Básicamente no es un Touchpad, es un ClickPad y eso lo hace un poco especial. ¿Diferencias? Pues la superficie táctil también funciona como botón, que actua como uno u otro (left, right) dependiendo de la zona que se presione. No tenemos un área táctil con unos botones separados, tenemos con todo incluido, eso debe quedar muy claro.



Configuración

Xorg provee una serie de plantillas predeterminadas para la configuración de dispositivos. Estas plantillas se encuentran en /usr/share/X11/xorg.conf.d/ y basta con copiar a /etc/X11/xorg.conf.d/ la que necesitemos y comenzar a modificarla. Xorg se encargará de cargar estos valores luego de los valores predeterminados. Para nuestro caso usaremos la plantilla 50-synaptics.conf.

# mkdir /etc/X11/xorg.conf.d/
# cp /usr/share/X11/xorg.conf.d/50-synaptics.conf /etc/X11/xorg.conf.d/

Lo que haremos será modificar el InputClass para agregar los parámetros que deseamos configurar. En el manual de synaptics tenemos bien documentados cada una de las opciones con su descripción y posibles valores.

Mi configuración quedó así:
Section "InputClass"
Identifier "touchpad catchall"
Driver "synaptics"
MatchIsTouchpad "on"
MatchDevicePath "/dev/input/event*"
Option "Protocol" "auto-dev"
Option "SHMConfig" "true"
# Edges
Option "LeftEdge" "1500"
Option "RightEdge" "5200"
Option "TopEdge" "1350"
Option "BottomEdge" "3500"
# Finger press
Option "FingerLow" "28"
Option "FingerHigh" "35"
Option "FingerPress" "70"
# Taps time
Option "MaxTapTime" "180"
Option "MaxTapMove" "220"
Option "MaxDoubleTapTime" "180"
Option "SingleTapTimeout" "180"
Option "ClickTime" "100"
Option "FastTaps" "0"
# Emulate
Option "EmulateMidButtonTime" "75"
Option "EmulateTwoFingerMinZ" "70"
Option "EmulateTwoFingerMinW" "5"
# Scrolling
Option "VertScrollDelta" "100"
Option "HorizScrollDelta" "0"
Option "VertEdgeScroll" "1"
Option "HorizEdgeScroll" "0"
Option "CornerCoasting" "0"
Option "VertTwoFingerScroll" "1"
Option "HorizTwoFingerScroll" "1"
# Pointer speed
Option "MinSpeed" "0.5"
Option "MaxSpeed" "7.0"
Option "AccelFactor" "0.35"
Option "TrackstickSpeed" "0"
Option "EdgeMotionMinZ" "29"
Option "EdgeMotionMaxZ" "59"
Option "EdgeMotionMinSpeed" "1"
Option "EdgeMotionMaxSpeed" "40"
Option "EdgeMotionUseAlways" "0"
# Scrolling flags
Option "UpDownScrolling" "0"
Option "LeftRightScrolling" "0"
Option "UpDownScrollRepeat" "0"
Option "LeftRightScrollRepeat" "0"
Option "ScrollButtonRepeat" "100"
# Touchpad mouse on/off
Option "TouchpadOff" "0"
Option "GuestMouseOff" "0"
# Dragging
Option "LockedDrags" "0"
Option "LockedDragsTimeout" "5000"
# Corners
Option "RTCornerButton" "0"
Option "RBCornerButton" "2"
Option "LTCornerButton" "0"
Option "LBCornerButton" "1"
# Tap
Option "TapButton1" "1"
Option "TapButton2" "3"
Option "TapButton3" "2"
# Click
Option "ClickFinger1" "1"
Option "ClickFinger2" "0"
Option "ClickFinger3" "0"
# Circular
Option "CircularScrolling" "0"
Option "CircScrollDelta" "0.1"
Option "CircScrollTrigger" "0"
Option "CircularPad" "0"
# Palm
Option "PalmDetect" "1"
Option "PalmMinWidth" "7"
Option "PalmMinZ" "40"
Option "CoastingSpeed" "0"
# Grab
Option "GrabEventDevice" "1"
Option "TapAndDragGesture" "1"
# Area
Option "AreaLeftEdge" "0"
Option "AreaRightEdge" "0"
Option "AreaTopEdge" "0"
Option "AreaBottomEdge" "3500"
EndSection

Section "InputClass"
Identifier "Ignore mouse devs"
MatchDevicePath "/dev/input/mouse*"
Driver "synaptics"
Option "Ignore" "on"
EndSection

Actualización: Acá pueden ver la configuración actual que tengo en mi repo github

No explicaré en detalle cada una de las opciones (para eso está el manual de synaptics), solo explicaré las más relevantes o las "problemáticas". Lo primero es ignorar las pulsaciones en el área de los botones. Para eso definimos el rango de acción del dispositivo (límites):

# Edges
Option "LeftEdge" "1500"
Option "RightEdge" "5200"
Option "TopEdge" "1350"
Option "BottomEdge" "3500"

# Area
Option "AreaLeftEdge" "0"
Option "AreaRightEdge" "0"
Option "AreaTopEdge" "0"
Option "AreaBottomEdge" "3500"

Colocando el límite inferior (BottomEdge y AreaBottomEdge) en 3500 evitamos que el cursor se mueva al pasar sobre el área de botones. Estableciendo el límite derecho (RightEdge) a 5200 evitamos que el cursor se mueva en la columna derecha (para habilitar la zona de scrolling vertical). La imagen a continuación ilustra estos ajustes:



El dispositivo solo tiene un botón y a simple vista no encontré la forma de configurarlo para que funcione como ambos. Estuve compilando el driver synaptics con un patch para agregar el soporte de clickpads pero no he logrado generar el .ko, así que simplemente dejé su funcionamiento por defecto (left click) y configuré el click izquierdo con tap, el doble click con doble tap y el click derecho con tap de dos dedos.

Eso podemos verlo en:

# Tap
Option "TapButton1" "1"
Option "TapButton2" "3"
Option "TapButton3" "2"

El botón para activar/desactivar el clickpad tampoco pude hacerlo funcionar pero creo que es cuestión de seguir trasteando hasta lograr una solución. Además de eso, el resto de la configuración es el ajuste de valores para la sensibilidad, aceleración y velocidad del puntero, pero eso lo dejo como tarea para el lector.

Tarea para la casa

Una de las herramientas más importantes para lograr una efectiva configuración es synclient. Gracias a esta herramienta pude determinar las coordenadas que definen los límites de mi dispositivo (que en las imágenes de arriba están marcados con azul y rojo) y verificar los valores de configuración

Usando el siguiente comando podemos verificar la actividad del touchpad en vivo:
$ synclient -m 10

La salida debería ser algo como:
   time     x    y   z f  w  l r u d m     multi  gl gm gr gdx gdy
874.169 5392 4317 3 0 0 0 0 0 0 0 00000000
874.269 5392 4317 0 0 0 0 0 0 0 0 00000000
874.670 5392 4317 1 0 0 0 0 0 0 0 00000000
874.770 5392 4317 7 0 0 0 0 0 0 0 00000000
874.870 4143 1708 9 1 11 0 0 0 0 0 00000000
874.970 4225 1811 3 0 0 0 0 0 0 0 00000000
875.070 4225 1811 2 0 0 0 0 0 0 0 00000000

Donde:
  • x, y: definen las coordenadas de la pulsación
  • z: la presión aplicada
  • f: el número de dedos tocando la superficie
  • w: es una medida del ancho del dedo
  • l,r,m: el estado de los botones left, right y middle

Para más información:
$ man synclient

Espero que con esta guía puedas configurar tu ClickPad y usarlo decentemente. Me queda pendiente por investigar la función de deshabilitar el touchpad mientras se escribe, los botones left-right y el botón para activar. Cuando lo logré postearé nuevamente :)

Fuentes:

Leer más