Eugeni's blog

One blog to rule them all. Kinda.

Browsing the topic programming

Every time I try to switch to a different desktop environment/window manager, there is just one simple feature which always make me go back to xfce – the possibility to quickly switch back to previous virtual desktop. A simple but extremely addictive feature, which only xfwm4 was offering…

…until yesterday! I became tired on metacity not offering this feature, so I just went ahead and implemented it overnight (learning a lot about gnome development during the process).

So, if anyone out there was missing this feature – feel free to grab this patch and apply it to the metacity source. Basically, it will add one new gconf entry (switch_to_previous_workspace), and will make metacity remember the last workspace. So you can press ctrl-f2 to switch to from workspace 1 workspace 2, and when you press ctrl-f2 again metacity will recognize that you want switch back, to whatever workspace you were before, and will do it.

Once again, this is the beauty of the open source. If there is something you need, but nobody implemented it, you always have the possibility of just doing it yourself. At least, sometimes :) .

diff -p -up metacity-2.30.1/src/core/prefs.c.switch metacity-2.30.1/src/core/prefs.c
--- metacity-2.30.1/src/core/prefs.c.switch 2010-03-30 11:35:40.000000000 -0300
+++ metacity-2.30.1/src/core/prefs.c    2010-05-06 17:47:14.000000000 -0300
@@ -97,6 +97,7 @@ static char *cursor_theme = NULL;
 static int   cursor_size = 24;
 static gboolean compositing_manager = FALSE;
 static gboolean resize_with_right_button = FALSE;
+static gboolean switch_to_previous_workspace = TRUE;
 static gboolean force_fullscreen = TRUE;

 static MetaVisualBellType visual_bell_type = META_VISUAL_BELL_FULLSCREEN_FLASH;
@@ -412,6 +413,11 @@ static MetaBoolPreference preferences_bo
       &resize_with_right_button,
       FALSE,
     },
+    { "/apps/metacity/general/switch_to_previous_workspace",
+      META_PREF_SWITCH_TO_PREVIOUS_WORKSPACE,
+      &switch_to_previous_workspace,
+      FALSE,
+    },
     { NULL, 0, NULL, FALSE },
   };

@@ -1757,6 +1763,9 @@ meta_preference_to_string (MetaPreferenc

     case META_PREF_FORCE_FULLSCREEN:
       return "FORCE_FULLSCREEN";
+
+    case META_PREF_SWITCH_TO_PREVIOUS_WORKSPACE:
+      return "SWITCH_TO_PREVIOUS_WORKSPACE";
     }

   return "(unknown)";
@@ -2719,6 +2728,12 @@ meta_prefs_get_mouse_button_menu (void)
   return resize_with_right_button ? 2: 3;
 }

+guint
+meta_prefs_switch_to_previous_workspace (void)
+{
+  return switch_to_previous_workspace;
+}
+
 gboolean
 meta_prefs_get_force_fullscreen (void)
 {
diff -p -up metacity-2.30.1/src/core/screen-private.h.switch metacity-2.30.1/src/core/screen-private.h
--- metacity-2.30.1/src/core/screen-private.h.switch    2010-01-14 22:31:32.000000000 -0200
+++ metacity-2.30.1/src/core/screen-private.h   2010-05-06 17:47:14.000000000 -0300
@@ -80,7 +80,7 @@ struct _MetaScreen
   MetaUI *ui;
   MetaTabPopup *tab_popup;

-  MetaWorkspace *active_workspace;
+  MetaWorkspace *active_workspace, *last_workspace;

   /* This window holds the focus when we don't want to focus
    * any actual clients
diff -p -up metacity-2.30.1/src/core/workspace.c.switch metacity-2.30.1/src/core/workspace.c
--- metacity-2.30.1/src/core/workspace.c.switch 2010-01-14 22:31:32.000000000 -0200
+++ metacity-2.30.1/src/core/workspace.c    2010-05-06 18:00:39.000000000 -0300
@@ -377,7 +377,31 @@ meta_workspace_activate_with_focus (Meta
                 meta_workspace_index (workspace));

   if (workspace->screen->active_workspace == workspace)
-    return;
+  {
+      meta_verbose("Switching to same workspace detected, going back to previous one!!\n");
+      if (meta_prefs_switch_to_previous_workspace()) {
+          if (workspace->screen->last_workspace) {
+              meta_verbose("Going to desktop %d\n", meta_workspace_index(workspace));
+              meta_workspace_activate_with_focus(workspace->screen->last_workspace,
+                      NULL, timestamp);
+              return;
+          } else {
+              meta_verbose("No old workspace..\n");
+              return;
+          }
+      } else {
+          meta_verbose("Last workspace switching disabled..\n");
+          return;
+      }
+  }
+  else
+  {
+      if (workspace->screen->active_workspace)
+          meta_verbose("Updating last workspace.. current: %d, new: %d\n",
+                  meta_workspace_index(workspace->screen->active_workspace),
+                  meta_workspace_index(workspace));
+      workspace->screen->last_workspace = workspace->screen->active_workspace;
+  }

   if (workspace->screen->active_workspace)
     workspace_switch_sound(workspace->screen->active_workspace, workspace);
diff -p -up metacity-2.30.1/src/include/prefs.h.switch metacity-2.30.1/src/include/prefs.h
--- metacity-2.30.1/src/include/prefs.h.switch  2010-01-14 22:31:32.000000000 -0200
+++ metacity-2.30.1/src/include/prefs.h 2010-05-06 17:47:14.000000000 -0300
@@ -60,7 +60,8 @@ typedef enum
   META_PREF_CURSOR_SIZE,
   META_PREF_COMPOSITING_MANAGER,
   META_PREF_RESIZE_WITH_RIGHT_BUTTON,
-  META_PREF_FORCE_FULLSCREEN
+  META_PREF_FORCE_FULLSCREEN,
+  META_PREF_SWITCH_TO_PREVIOUS_WORKSPACE
 } MetaPreference;

 typedef void (* MetaPrefsChangedFunc) (MetaPreference pref,
diff -p -up metacity-2.30.1/src/metacity.schemas.in.in.switch metacity-2.30.1/src/metacity.schemas.in.in
--- metacity-2.30.1/src/metacity.schemas.in.in.switch   2010-05-06 17:47:14.000000000 -0300
+++ metacity-2.30.1/src/metacity.schemas.in.in  2010-05-06 18:01:16.000000000 -0300
@@ -4,6 +4,24 @@
     <!-- General preferences -->        

     <schema>
+      <key>/schemas/apps/metacity/general/switch_to_previous_workspace</key>
+      <applyto>/apps/metacity/general/switch_to_previous_workspace</applyto>
+      <owner>metacity</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+         <short>Remember and recall previous workspace when switching via keyboard shortcuts</short>
+         <long>
+           Set this to true to allow to switch to previous workspace when
+           using the current workspace keybinding. For example, if you switched
+           from workspace 2 to workspace 1 by pressing keyboard shortcut for
+           workspace 1, pressing the same shortcut again while on workspace 1
+           will switch to workspace 2.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
       <key>/schemas/apps/metacity/general/mouse_button_modifier</key>
       <applyto>/apps/metacity/general/mouse_button_modifier</applyto>
       <owner>metacity</owner>
diff -p -up metacity-2.30.1/src/metacity.schemas.in.switch metacity-2.30.1/src/metacity.schemas.in
--- metacity-2.30.1/src/metacity.schemas.in.switch  2010-04-06 07:10:38.000000000 -0300
+++ metacity-2.30.1/src/metacity.schemas.in 2010-05-06 18:01:06.000000000 -0300
@@ -4,6 +4,24 @@
     <!-- General preferences -->        

     <schema>
+      <key>/schemas/apps/metacity/general/switch_to_previous_workspace</key>
+      <applyto>/apps/metacity/general/switch_to_previous_workspace</applyto>
+      <owner>metacity</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+         <short>Remember and recall previous workspace when switching via keyboard shortcuts</short>
+         <long>
+           Set this to true to allow to switch to previous workspace when
+           using the current workspace keybinding. For example, if you switched
+           from workspace 2 to workspace 1 by pressing keyboard shortcut for
+           workspace 1, pressing the same shortcut again while on workspace 1
+           will switch to workspace 2.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
       <key>/schemas/apps/metacity/general/mouse_button_modifier</key>
       <applyto>/apps/metacity/general/mouse_button_modifier</applyto>
       <owner>metacity</owner>
@@ -247,7 +265,7 @@
       <applyto>/apps/metacity/general/theme</applyto>
       <owner>metacity</owner>
       <type>string</type>
-      <default>Clearlooks</default>
+      <default>Ia Ora Steel</default>
       <locale name="C">
          <short>Current theme</short>
          <long>

Working on Mandriva network tools, I looked on one of the most essential ones the network monitor (net_monitor). It was introduced a couple of releases before, and was mostly doing its job. However, it has a number of flaws and lack of features that motivated us to look closer at it.

Our old friend net_monitor, present in your favorite Mandriva distro!

Our old friend net_monitor, present in your favorite Mandriva distro!

The net_monitor currently used in all Mandriva versions is written in perl, is using internal drakx-net api (and is, therefore, only usable on Mandriva), and also have some issues such as memory leaks and non-usual interface. After a few thoughts and discussions we came to conclusion that it would be more adequate to project and rewrite it from scratch, turning it more modular, expansible and focused on common use cases.

Initially, I thought on using perl to write it, so it would still be part of drakx-net suite. However, after thinking on the code and the way it should work I felt that my brain was going to melt down :) (perl is a nice language, but it is certainly not that compatible with me). So I ended up with python, which is my language of choice (together with C). Also, I’ve received many comments saying that the net_monitor is no more relevant, as every desktop environment provides its own network monitoring tool, and it should be dropped from drakx-net. By combining those issues, we came to decision that it would be more proper to separate net_monitor into a different package – this way, it won’t depend on any drakx-net internal functionalities, and user could uninstall it if required and use his own network monitoring tool if he wants to. And, at the same time, users would still have a cute little network monitoring application on their machines.

So, as a picture says more than a thousand words, I guess I’ll just add some pictures here than additional KBs of text :) (EDIT: please note that the look and features of net_monitor have changed significantly in Mandriva since this post):

net_monitor monitoring a wireless connection

net_monitor monitoring a wireless connection

net_monitor monitoring a connection for which network accounting was not enabled

net_monitor monitoring a connection for which network accounting was not enabled

net_monitor displaying some statistics about your network usage (provided by vnstat)

net_monitor displaying some statistics about your network usage (provided by vnstat)

looking at daily traffic statistics on my notebook for the past month

looking at daily traffic statistics on my notebook for the past month

...and hourly statistics...

...and hourly statistics...

...and finding our when I killed my bandwidth..

...and finding our when I killed my bandwidth..

Surely, this is just an early and preliminary version, with many missing features and such. If you want to give it a try, just install net_monitor package, and it will create /usr/bin/net_monitor executable for you. It won’t conflict with existent net_monitor from drakx-net which is installed in /usr/sbin, so both of them may coexist on your system. If you look at /usr/share/doc/net_monitor/TODO, you’ll see some of the ideas that I intend to add to it, but the idea is to keep it simple and not transform it into an emacs of network monitoring :) . And, of course, feel free to add your comments and suggestions (and bug reports) here!

P.S.: Just to prevent comments like ‘you should focus on fixing bugs instead of wasting time writing new things’. Net_monitor is present in Mandriva for years now, and if you look at bugzilla list it has a number of bugs and issues. So I am not creating a new app – I am bringing back from the land of the dead an old one :) .

P.P.S.: Answering in advance to another question – yes, it would work on any Linux distro which has python and pygtk. You’ll just have to add some tricks into your network startup scripts to enable vnstat integration, but it will work just fine even without that.

Time has come for the first msec release since Mandriva 2009.1!

This time we have several improvements, such as:

  • support for audit plugins
  • more msec auditing checks
  • improved auditing logging
  • and, of course, bugfixes.

So let me introduce some details about each one of them.

Support for audit plugins

You may remember that msec shipped with Mandriva 2009.1 introduced support for plugins infrastructure (take a look at your /usr/share/msec/plugins/ directory to see some examples). This new msec, which will be shipped with Mandriva 2010, also introduces auditing plugins.

Well, you might be asking what the ..? what is the difference between those plugins?, so let me clarify it a bit.

Msec has two main functionalities:

  • Security configuration
  • Security auditing

The security configuration is what you configure using msecgui or using security levels – basically, you say what settings should be used on your machine for ssh, user logins, and all kind of system configuration. The security auditing are those background checks that run daily on your machine, to determine what has changed since the last run and let you know about that.

In old msec, this security auditing was performed by security.sh, security_check.sh and diff_check.sh, so we had just three large and complex files with a lot of duplicated code. With new msec version, everything was split to reduce code duplication, improve readability and simplify plugins creation.

Let me show you a sample plugin which checks for changes in system users:

    #!/bin/bash
    # msec: check for changes in local users

    # check if we are run from main script
    if [ -z "$MSEC_TMP" -o -z "$INFOS" -o -z "$SECURITY" -o -z "$DIFF" ]; then
            # variables are set in security.sh and propagated to the subscripts
            echo "Error: this check should be run by the main msec security check!"
            echo "       do not run it directly unless you know what you are doing."
            return 1
    fi

    # files to log the list of today's and yesterday's, and difference between them
    USERS_LIST_TODAY="/var/log/security/users_list.today"
    USERS_LIST_YESTERDAY="/var/log/security/users_list.yesterday"
    USERS_LIST_DIFF="/var/log/security/users_list.diff"

    # update yesterday's list
    if [[ -f ${USERS_LIST_TODAY} ]]; then
        mv ${USERS_LIST_TODAY} ${USERS_LIST_YESTERDAY};
    fi

    # check for changes in users
    if [[ ${CHECK_USERS} == yes ]]; then
        getent passwd | cut -f 1 -d : | sort > ${USERS_LIST_TODAY}
        Diffcheck ${USERS_LIST_TODAY} ${USERS_LIST_YESTERDAY} ${USERS_LIST_DIFF} "local users"
    fi

that’s it. You just drop this file into /usr/share/msec/scripts/01_check_for_users.sh and this check will be executed every time msec security checks are run. The security log will be updated, the diff check mail will be created and mailed (along with all other checks), and it will be working automatically from now on.

More msec auditing checks

A few additional msec auditing checks were added:

  • CHECK_FIREWALL — checks for changes in iptables configuration
  • CHECK_USERS — checks for changes in local users (most of its code was shown above actually)
  • CHECK_GROUPS — checks for changes in local groups
  • FIX_OWNER — if unowned files are found on the system, this check gives the opportunity to change their ownership to nobody/nogroup, instead of blindly doing it automatically
  • CHECK_RPM_PACKAGES — checks for changes in installed RPM packages
  • CHECK_RPM_INTEGRITY — checks all the installed packages for changed files. Both those checks were run before under the CHECK_RPM check, but, as they are quite expensive, these two new checks were introduced instead

If you are using cooker or 2010 alpha, these options will not be added automatically to your /etc/security/msec/security.conf configuration file. The best way to experiment with them is by using msecgui, or running msec -f standard or msec -f secure to install default configuration for standard and secure levels.

Besides those items, I was thinking on an option to check for changes in PAM authentication, check for failed login attempts and support for rkhunter. And, as always, if you have any idea on some other functionality that should be interesting to have in msec, feel free to comment!

Improved auditing logging

The logging format of /var/log/security.log was changed to be compatible with syslog-based logging. This should make it easier for system applications to parse it, and for administrator to examine its contents. Now it is way easier to find information by date, kind of message and check type.

Other ideas

Among other ideas for msec I thought on the following:

  • msec supports an arbitrary number of custom security levels, but msecgui only supports two basic ones (standard and secure). It could be nice to have a combobox to select a custom profile..
  • gui for TOMOYO security framework, since the AppArmor project looks quite stone-cold dead. This is already a work in progress, so probably I’ll post some update on this later.
  • Support for administrator-supplied rules for security and diff checks. For example, to exclude everything matching ‘/var/tmp’ from any kind of checks and reports, or excluding network ports from 3000 to 5000 from open port checks.

Besides that, there is a number of bugfixes (which are going to be backported to 2009.1 shortly).

So msec is definitely is alive and getting better and better. Stay tuned for more news! :)

I wondered why my .git directory of drakx-net was using about 70MB of disk space (I am accessing the SVN repository using git svn). Of course, I have read that periodic git repository clean could drastically save space and speed, but – what the heck – it is just a bunch of text files. So I never bothered with it.

However, after running git gc on top of drakx-net directory, the .git directory size went from 70MB down to 4MB. A 17.5x improvement! Unbelievable!

So I did the same to msec repository, with quite similar results — from 21MB down to 3MB!

So a mental note to myself – run git gc always. It rocks.

Trabalhando com mandriva, vira e mexe encontro gente falando em francês. Como já faz quase 14 que não falo essa língua ;) , decidi apelar pro meu querido python e google translator.

Nada como uns 10 minutos de programação voltada a gambiarras :) .

#!/usr/bin/python
#
# quick and dirty french2english translator
#

import urllib
import urllib2
from subprocess import Popen, PIPE

URL='http://translate.google.com/translate_t#fr|en|'

print "Paste your message"
msg = []
while 1:
    try:
        line = raw_input('>> ')
        msg.append(line)
    except:
        break

s = ">>" + "\n>>".join(msg)

data = "http://translate.google.com/translate_a/t?client=t&%s&fr&tl=en" % urllib.urlencode({'text':s})
req = urllib2.Request('http://translate.google.com/translate_t', data, {'User-Agent':'Mozilla/5.0'})
resp = urllib2.urlopen(req).read()
p = Popen("lynx -dump -stdin | grep -- '>>'", shell=True, stdin=PIPE, stdout=PIPE)
p.stdin.write(resp)
p.stdin.close()
trans = p.stdout.read()
print trans

Agora é só dar ctrl-c no texto, depois rodar o programinha e dar ctrl-v seguido por ctrl-d :) .

Perl would be Voodoo – An incomprehensible series of arcane incantations that involve the blood of goats and permanently corrupt your soul. Often used when your boss requires you to do an urgent task at 21:00 on friday night.

huauhahahahaha

via Aegisub: If programming languages were religions….

Ultimamente eu uso bastante programação funcional em python para tudo. Entretanto, fiquei brincando hoje e descobri que nem sempre ela oferece os melhores resultados.

Por exemplo, vamos imaginar a função que vai fazer todas as combinações entre os elementos de uma lista. Ela pode ser escrita de forma funcional:

# permutate-func
def permutate(l):
    '''Returns all possible combinations of list values'''
    return reduce(lambda x, y: y + x, [ [(x, y) for y in l if x != y] for x in l ])

ou de forma iterativa:

# permutate-iter
def permutate(l):
    '''Returns all possible combinations of list values'''
    res = []
    for x in l:
        for y in l:
            if x != y:
                res.append((x, y))
    return res

Para listas pequenas não temos muita diferença de desempenho. Agora para listas maiores (por exemplo, com 1000 strings):

dups=['string%d' % x for x in range(1000)]
permutate(dups)

temos os seguintes resultados para programação funcional:

# time permutate-func
real    0m24.494s
user    0m20.763s
sys 0m1.698s

e os seguintes para o algoritmo iterativo:

# time permutate-iter
real    0m1.191s
user    0m1.150s
sys 0m0.036s

O tempo de 24 segundos caiu para apenas 1 segundo!

Qual conclusão tiramos disso? Embora programação funcional é extremamente poderosa, em alguns casos é melhor usar outras soluções para o problema :) .

Mais um exemplo do poder de python + gtk. Para tirar um screenshot da tela, salvando-o em formato JPEG, o seguinte código é suficiente:

from gtk.gdk import *
screenshot = Pixbuf(COLORSPACE_RGB, False, 8, screen_width(), screen_height())
screenshot.get_from_drawable(get_default_root_window(), colormap_get_system(), \
    0, 0, 0, 0, screen_width(), screen_height())
screenshot.save("tela.jpg", "jpeg", {"quality": "75"})

(Tá certo, dividi em 5 linhas para melhorar a legibilidade, mas no fundo são só 4 linhas mesmo).

Uma das maiores vantagens do python para mim é a legibilidade do código, facilidade de programação, e eficiência. Com quantas linhas de código seria possível fazer esse exemplo em qualquer outra linguagem? (Não vale apelar para import -window root tela.jpg porque aí é ImageMagick que faz toda a mágica!) :)

Cada vez mais e mais eu gosto dos recursos de programação funcional em python. E, como eu uso essa linguagem para resolver tarefas mais variáveis, não dá para não perceber que a velocidade de desenvolvimento e eficiência do código tornam-se cada vez melhores, por simplesmente aplicar técnicas de programação funcional.

Por exemplo, sem a programação funcional, eu usava o seguinte código antes para ler um arquivo com uma série de números, e determinar a média dos valores:

fd = open("data.log")
data = fd.readlines()
valores = []

le os dados

for line in data: line_val = float(line.strip()) valores.append(line_val)

calcula a media

media = 0 for item in valores: media += item media /= len(media)

Re-escrevendo isso com técnicas de programação funcional, temos o seguinte:

le os valores do arquivo, transformando-os para floats, e ignorando strings vazias

valores = [float(val) for val in open("data.log").readlines() if len(val) > 1]

calcula a media, somando todos os valores e dividindo pelo número de elementos no vetor

media = reduce(lambda x, y: x+y, valores) / len(valores)

(É óbvio que é só um exemplo rápido, não estou tratando as excessões aqui, mas para resolver problemas simples do dia-a-dia é mais de que suficiente!)

O essencial são as funções lambda, map, reduce e filter. Todos os detalhes interessantes sobre esse estilo de programação eu achei aqui, e recomendo essa apresentação para todos!

Uma apresentação excellente sobre programação funcional em python. Recomendo!

Functional Programming with Python (RuPy 2008)

Pois é – é possível fazer uma implementação completa de rede peer-to-peer usando somente 15 linhas de python (com um pouco de gambiarras envolvendo lambdas :) ).

Slashdot | World’s Shortest P2P App: 15 Lines

Impressionante.. como tudo em python!

Talvez pode ser útil para alguém :) .

Este script pega a lista de músicas do mpd, seleciona um playlist aleatório, e coloca no ipod shuffle.

!/usr/bin/python

import os,random

ipod mountpoint - must be mounted!

MOUNTPOINT="/mnt/ipod"

def get_files(): '''Reads the list of mp3 files from mpd database''' data = open(".mpd.db").readlines() files = [] for l in data: fields = l.strip().split(":",2) if len(fields) < 2: continue if fields[0] == "file": files.append(fields[1].strip()) return files

def get_free_space(): '''Gets available space from IPOD partition''' line = os.popen("df -k %s | awk '{print $4}'" % MOUNTPOINT).readlines()[-1] return int(line)

def fullcp(file, target): '''copies file and directory structure''' os.system('tar cf - "%s" | (cd %s && tar xf -)' % (file, target))

if name == "main": try: os.mkdir("%s/mp3" % MOUNTPOINT) except: print "Not creating %s/mp3!" % MOUNTPOINT files = get_files() freespace = get_free_space() while 1: # pega arquivo aleatorio pos = random.randint(0, len(files)) curfile = files[pos] del files[pos] # determina o tamanho res = os.stat(curfile) size = res[6] / 1000 if freespace - size < 1: break freespace -= size # copia print "Copying [%8dK left]: %s" % (freespace, curfile) fullcp(curfile, "%s/mp3/" % MOUNTPOINT)

É melhor usar ele junto com Shuffle-DB.

Acabou de sair a versão beta do OpenOffice 3.

Muita gente já escreveu sobre isso, com opiniões variando entre “é a salvação do universo” e “e daí??”; agora eu queria falar a minha opinião, que não é nenhuma das duas.

Primeiro, vamos relembrar um pouco a situação como ela estava a alguns poucos anos atrás (vou comparar a evolução do OpenOffice com o Microsoft Office… porque todos os outros pacotes de Office, seja de IBM, de Corel, da SUN, ou de qualquer outra empresa perdem significativamente para o próprio OpenOffice. Temos o Google Docs, mas, obviamente, é um caso a parte!).

Na época que não tinha OpenOffice 1.0, e só tinha os milestones que – com muita sorte – até funcionavam e abriam alguns (poucos e seletos) arquivos do office. Você acha que o OpenOffice hoje em dia é lento?? Ha-ha. Instale algum dos milestones antigos – ou o 1.0 mesmo para ver a diferença. A maior vantagem dessas primeiras versões é que era possível abrir e editar uma grande parte dos arquivos de Microsoft Office. Obviamente, não todos, mas muitos. Nem isso não era possível antes (abiword? koffice? staroffice, siagoffice? prefiro não comentar).

A versão 1.0 foi um marco gigante – foi uma versão estável (na medida do possível), multi-plataforma, completamente livre e que abria a maior parte dos documentos existentes. Mais uma vez – não absolutamente todos, mas a grande maioria. Abrir lentamente, com interface diferente, com diversos problemas de layout, posicionamento e funcionamento, mas.. ela funcionava!

A versão 1.1 melhorou significamente o problema de desempenho, e essas melhorias continuaram com todas as versões posteriores. Versão 2 (e suas sub-versões) introduziu nova interface, suporte a ODF e desempenho muito melhor. E logo-logo vamos ter a versão 3.0.

O que é possível notar nessa evolução das versões?? Primeiramente, o número de reclamações caiu significativamente! Vejamos:

  • Interface diferente: com o lançamento de Microsoft Office 2007 esta reclamação perdeu completamente o sentido. OpenOffice é muito mais parecido com as versões antigas do Office de que o próprio software de microsoft…
  • Suporte incompleto a documentos de office: por mais reclamações que é possível encontrar sobre isso, tem que aceitar que o número de problemas de compatibilidade decresceu absurdamente nos últimos anos. Antes era sorte ter um documento .doc(.ppt, .xls) que abriria corretamente no OpenOffice. Hoje, em contra-partida, é difícil achar um documento com problemas. Chega a casos curiosos, onde OpenOffice consegue abrir documentos que travam o próprio Microsoft Office :) , e a própria Microsoft assume isso (obviamente, não publicamente :) ).
  • Desempenho inadequado: esta afirmação também perdeu o sentido ao avaliarmos o Microsoft Office 2007.
  • Falta de funcionalidades: o OpenOffice ainda não implementa todas as 100% das funcionalidades que o Microsoft Office oferece. Porém… você conhece alguém que usa TODOS os recursos do Word?? Pois é, as funcionalidades presentes são mais de que adequadas…
  • e assim por diante..

Qual é a conclusão que dá para tirar, avaliando as versões atuais de OpenOffice??

Elas oferecem todas as funcionalidade que a absoluta maioria dos usuários precisam. Nem sempre do jeito idéntico ao Microsoft Office; nem sempre com a mesma interface; e nem sempre com todas as variações, mas oferecem!

E quanto a desempenho.. Seguindo os conselhos básicos localizaveis facilmente no google (desligar java, diminuir o cache, diminuir o uso de memória; otimizar o carregamento; desativar funcionalidades avançadas, etc) é possível melhorar o tempo de execução inicial em mais de 10x, e uso de memória em mais de 4x (eu comprovei isso com projetos que fizemos com Intel e Ardence em 2004-2005; inclusive tem até publicações da Intel sobre isso – as de Ardence continuam sendo sob n.d.a. até onde eu sei :) ). Mas, se alguém tiver curiosidade, fiquem a vontade para perguntar por aqui mesmo!

Bem.. passando por esta introdução pequena ;) , o que vamos ver na nova versão de OpenOffice?

  • Suporte a Mac OS X – bastante interessante, porque vai ser possível rodar o OpenOffice em cima de MAC sem precisar de servidor X.
  • Suporte a ODF 1.2 e OOXML – preciso falar alguma coisa?? :)
  • Suporte a PDFs editáveis – conhece algum outro escritório que permite isso?
  • Suporte a macros em VBA – uma das maiores limitações atuais foi a execução de macros voltados para Microsoft Office. Não mais.
  • Suporte a extensionsextensions fizeram do firefox o browser tão popular o quanto ele é hoje em dia. Enquanto plugins para OpenOffice existem já faz alguns anos, nunca foi simples ou intuitivo instalar e usar eles. Acredito que agora isso vai ser resolvido definitivamente.
  • Melhor suporte a multi-midia – suporte a reprodução de sons em background, suporte a múltiplos monitores, melhor suporte para CSV, HTML, melhor na renderização de fontes; melhorias na edição de imagens, e assim por diante. A lista é grande.
  • Novas funcionalidades – novas possibilidades relacionadas a planilhas, formulas, gráficos, renderização de páginas WEB, etc
  • e muitos outras melhorias menos significativas

O que dá para extrair de tudo disso? É simples – o desenvolvimento do OpenOffice é feito de forma evolucionária, e não revolucionária. O que acontece é que ele fica melhor e melhor gradativamente, e não visa fazer milagres de uma hora para outra. É bom isso? Sim, porque é possível ver o que podemos esperar das próximas versões. Isso tem lados negativos? Claro, porque sempre vamos ver comentários do tipo “office não está evoluindo”, “versão 2 é parecida com 1″, “microsoft é mais diferente”…

O resto vamos ver logo, na versão 3.0 do OpenOffice :) .

Só um pequeno resumo de vantagens e desvantagens de sistemas de controle de versões distribuídos que eu já cheguei a usar.

  • SVN + SVK – parecido com SVN, tem os mesmos problemas que ele (cria um monte de diretórios, etc). Na minha opinião, não tem muito uso prático – é melhor usar soluções distribuídas mesmo (bzr, git ou mercurial), porque todas elas tem gateways para SVN propriamente dito.
  • BZR – acho que é o meu favorito para a maioria das coisas. Facil de usar (o mais fácil de todos, na minha opinião), rápido – principalmente nas últimas versões, extensível. Os meus maiores problemas com ele foi o desempenho (que foi resolvido na versão 1.0), e o tratamento de arquivos binários grandes – ele fazia questão de colocar o commit inteiro na memória, em formato ASCII ainda. Ou seja, para fazer commit de 100MB ele usava 2GB de RAM. Mas aparentemente, isso foi arrumado recentemente.
  • GIT – extremamente poderoso, rápido e eficiente.Em contra-partida, ele tem milhares de comandos, sub-comandos, parâmetros e opções. Se você aprender tudo com ele, ele é o melhor. Entretanto, eu vivo me perdendo na hora de fazer coisas mais complexas (tipo, fazer um cherry-pick de um repositório remoto em um branch diferente). Por outro lado, só ele que permite fazer cherry-pick de forma fácil (para quem está por fora – cherry-pick permite você pegar um commit independente e embutir ele em outro branch. Se isso não fez sentido para você, provavelmente você não precisa dele :) ). A melhor coisa do GIT para mim é o suporte dele para multiplos branches locais, no mesmo diretório. E a velocidade, é claro. Porém, a complexidade dele acaba complicando demais a vida as vezes.
  • Mercurial – parecido com o BZR e GIT. Atualmente suporta branches e cherry-picks também (de forma diferente). Ele suporta queues de patches também nativamente – mas, como nunca precisei disso, não posso falar muito detalhes. Fora isso, nunca cheguei a mexer muito com ele (mas, para quem tiver interesse, tem um tutorial interessante aqui). A escolha entre ele, bzr e git muitas vezes é questão de religião mesmo :) .
  • DARCS – bastante interessante, e diferente de todos os outros sistemas. Basicamente, todo o mecanismo de commits e diferenças entre versões dele é baseado em filas de patches, aplicados em determinada ordem. A filosofia dele também é parecida com GIT – ele visa manter controle do conteúdo, e não da estrutura de versões. Isso tem lados bons (o mesmo conteúdo pode migrar de um lugar para outro – arquivo, diretório, etc) e ruins (se alguém fez essa migração, você não vai ter controle exato sobre o que foi feito).
  • GNU ARCH – acredito que hoje em dia ele tem mais interesse histórico de que prático. Muito mais complexo de usar (se bem que, comparando com GIT, acho que dá uma briga boa). As ideias dele são utilizadas em outros sistemas de controle de versões distribuídas, mas ele em si – pelo que eu sei – está parado. Eu cheguei a usar ele faz alguns anos, mas desisti logo devido à complexidade dele.
  • MONOTONE – também em desuso nos últimos tempos, o MONOTONE serviu como inspiração para GIT. Fora isso, nunca vi ninguém usar ele, então não tenho opinião formada sobre ele.

Além disso, tem diversas outras soluções caseiras (com svn, cvs (eca..), etc) que fazem a mesma coisa que DRCS’es usando algumas gambiarras (hehehe). Mas eu fico entre BZR e GIT na maioria dos casos.

Por alguns meses fiquei pensando – para que servem os decorators em python? Ultimamente é fácil ver código do tipo: @algum_metodo def function(params): ____... Aí hoje finalmente decidi descobrir como que isso funciona, e para que serve.

Em poucas palavras – realmente, decoratos são bem úteis. Eles não introduzem muitas novidades na linguagem, mas possibilitam evitar duplicação de código, e facilitar a implementação.

Por exemplo, suponhamos que precisamos rastrear todas as chamadas a uma de terminada função. Quais são as alternativas que temos?

  • Mudar a função para ela fazer um print toda vez que ela é executada, e toda vez que ela termina;
  • Fazer um wrapper para essa função;
  • Usar um decorator.

Vamos pensar em uma função bem simples: def minhafunc(s): ____print "<< %s >> " % s

Como que poderiamos fazer o wrapper para esta função esta função? Por exemplo: def wrapper(func): ____print "entrando na func!" ____ret = func() ____print "saindo da func!" ____return ret

result = wrapper(minhafunc()) Obviamente, isso funciona.. Mas para funções bem simples.

Um outro jeito seria transformar função automaticamente: def logger(func): def wrapper(param): _____print "entrando na func!" _______ret = func(param) ________print "saindo da func!" ________return ret ____return wrapper

minhafunc = logger(minhafunc)

E agora vem a parte “mágica”. Decorators simplesmente permitem com que você evite a transformação de python em LISP, tirando a necessidade de empacotamento explícito dessas funções. Em outras palavras:

@logger def minhafunc(): ...

faz a mesma coisa que: minhafunc = logger(minhafunc)

só que logo após a declaração da função.

Só isso :) . É claro, que tem várias outras utilidades os decorators – facilitar o uso de threads em PyGTK; facilitar desenvolvimento de código sincronizado, etc.

Sem falar que fica bem mais legível o código: @synchronized @logged def minhafunc(): ____....

P.S.: O wordpress, para variar, deixa zoado o código.. Mas logo-logo este site vai migrar para Django. Desde que aprendi a mexer com ele, a minha opinião sobre os frameworks web mudou.. e muito! :)