суббота, 25 августа 2012 г.

Как я вставляю подсвеченный код в блог

С помощью vim. Хотя раньше для этой цели я пользовался неплохой программой source-highlight, с vim, при должной настройке, это сделать еще проще. В vim есть плагин TOhtml (он обычно сразу доступен в системном дистрибутиве) - это основа подхода, о котором я буду говорить. Еще недавно я использовал его в чистом виде, с необходимостью ручной обработки полученного HTML кода, однако теперь у меня есть волшебный скрипт, который делает всю грязную работу и подготавливает код к простой вставке в блог в режиме верстки HTML. Этот скрипт нужно добавить в файл .vimrc, его текст я привожу ниже с комментариями по строкам.

Сразу хочу отметить, что для работы в vim я использую темную цветовую схему с прозрачным фоном на основе схемы xterm16. Эта схема не годится для текста с белым фоном, который я использую в блоге. Поэтому для создания HTML кода я временно переключаюсь на светлую цветовую схему. Для этого я выбрал схему lucius (которая недавно обновилась до версии 8.1) с соответствующими настройками, которые также показаны в следующем коде.
229 " ---- lucius colorscheme settings and command MakeBlogArticle
230 " ----
231 let g:lucius_style = 'light'
232 let g:lucius_contrast = 'high'
233 let g:lucius_contrast_bg = 'high'
234 
235 fun<SID>make_blog_article(prepare_for_insertion, line1, line2)
236     let colors = g:colors_name
237     colorscheme lucius
238     let g:html_use_css = 0
239     exe a:line1.",".a:line2."TOhtml"
240     unlet g:html_use_css
241     exe "colorscheme ".colors
242     setlocal nowrap
243     if a:prepare_for_insertion
244         1;/^<font face=/-1d
245         1s/.*/<pre><tt>/
246         silent $;?^</font?+1d
247         $s/.*/<\/tt><\/pre>/
248         %s/<br>$//
249         if getline(2) =~ '^[[:blank:]]*$'
250             2;/[^[:blank:]]\+/-1d
251         endif
252         if getline(line('$') - 1) =~ '^[[:blank:]]*$'
253             silent $-1;?[^[:blank:]]\+?+1d
254         endif
255         normal ggJx0
256     endif
257 endfun
258 
259 command -range=% MakeBlogArticle
260             \ silent call <SID>make_blog_article(1<line1><line2>)
В строках 231-233 находятся настройки цветовой схемы lucius. В строках 259-260 определена команда MakeBlogArticle, которая выполняет всю работу с помощью вызова функции make_blog_article(). Команда MakeBlogArticle может выполнять подсветку как для всего кода в буфере, так и внутри отдельной выделенной области. Для этого в функцию make_blog_article() передаются два аргумента line1 и line2, которые соответствуют номерам строк, ограничивающих выделенную область. Еще один аргумент prepare_for_insertion указывает на то, что код, сгенерированный командой TOhtml нужно будет преобразовать для вставки в блог (заменить HTML хедер и футер на теги <pre><tt> и </tt></pre> соотвественно, удалить теги <br> и т.п.).

В строках 235-257 находится тело функции make_blog_article(). Разберемся, что же в ней происходит. Строки 236-237: запоминаем текущую цветовую схему и переключаемся на новую. Строки 239-241: выполняем команду TOhtml в переданном диапазоне, определяемом аргументами line1 и line2. По умолчанию в TOhtml используется CSS, для вставки в блог это не годится, поэтому мы определяем равной нулю специальную переменную g:html_use_css перед самым вызовом TOhtml, а затем удаляем ее. В строке 241 переключаемся на исходную цветовую схему. После вызова TOhtml  курсор находится в новом буфере с HTML кодом. В строке 242 указываем, что vim не следует переносить длинные строки при достижении границы видимой области (это дело вкуса, но в данном случае мне удобнее, когда длинные строки не переносятся).

В строках 243-256 происходит подготовка сгенерированного HTML кода для вставки в блог. В строке 244 переходим на первую строку в буфере и удаляем все строки вплоть до  первого вхождения ^<font face= (после него начинается полезный код). В строке 245 заменяем первую строку (которой теперь оказалась строка с первым вхождением ^<font face=) на <pre><tt>. Таким образом, мы заменили HTML хедер на <pre><tt>. В строках 246-247 производится аналогичная замена HTML футера на </tt></pre>. В строке 248 удаляем все переносы строк <br>: при использовании тега <pre> они не нужны. Далее идут в принципе необязательные изменения, настраивающие отступы впереди и в конце сгенерированного HTML кода. В строках 249-254 удаляем все пустые строки в буфере после первой строки и перед последней строкой. В строке 255 переводим курсор на первую строку буфера и соединяем ее со второй строкой.

Теперь остается выделить все строки в буфере и вставить их в редактируемое поле в блоге в режиме верстки HTML.

Кстати я не уверен, что замена HTML хедера и футера на <pre><tt> и </tt></pre> является оптимальным решением, но в моем случае она работает.

понедельник, 13 августа 2012 г.

nginx: расширяем модуль myutil

Здесь мы начали разрабатывать новый модуль nginx myutil, в котором, как мы задумывали, будут реализованы разные полезные директивы. На данный момент в модуле представлена единственная директива myutil_var_alias. В этой статье будет показано, как организовать поиск по списку переменных с целью получения первого непустого значения. Пусть соответствующая директива будет называться myutil_first_not_empty. Тогда строка в конфигурации вида
            myutil_first_not_empty $fne_data $arg_a $arg_b default;
указывает, что следует найти первое непустое значение среди списка переменных $arg_a и $arg_b (которые соответствуют аргументам a и b строки запроса) и записать его в новую переменную $fne_data; в случае, если переменные $arg_a и $arg_b отсутствуют, либо не содержат непустых значений, в переменную $fne_data следует записать значение default. Будем предполагать, что литералы типа default являются равноправными членами наряду с переменными типа $arg_a и $arg_b и могут записываться где-то в середине списка аргументов (хотя очевидно, что из смысла директивы myutil_first_not_empty следует, что литерал должен быть только один и он должен находиться в конце списка). Сам список аргументов может быть произвольной длины.

Какие типы данных нам понадобятся? Ну, во-первых, как и в случае директивы myutil_var_alias, это массив типа ngx_array_t, соответствующий множеству переменных $fne_... (не забываем совет, приведенный в конце прошлой статьи и добавляем узнаваемый префикс, в данном случае $fne_, ко всем переменным, объявленным нашей новой директивой). Добавляем его в ngx_http_myutil_loc_conf_t:
typedef struct
{
    ngx_array_t  varalias_data;
    ngx_array_t  first_not_empty_var_data;
}  ngx_http_myutil_loc_conf_t;
В директиве myutil_var_alias в качестве элементов массива varalias_data мы использовали простую структуру
typedef struct
{
    ngx_int_t    self;
    ngx_int_t    index;
}  ngx_http_myutil_var_elem_t;
При этом значение self соответствовало индексу новой переменной, созданной директивой myutil_var_alias (ее первому аргументу), а значение index - исходной переменной, т.е. той, значение которой копировалось в новую переменную (это второй аргумент myutil_var_alias).

На этот раз мы имеем дело не с двумя аргументами, а с аргументом - новой переменной, создаваемой директивой myutil_first_not_empty, и списком неопределенной длины. Очевидно, что одним из элементов новой структуры должен быть массив:
typedef struct
{
    ngx_array_t  data;
    ngx_int_t    index;
}  ngx_http_myutil_fne_elem_t;
Здесь data - указанный массив, а index - индекс новой переменной. Какой тип элементов следует выбрать для массива data? Вспомните, что мы хотели записывать в него как переменные, так и литералы. Переменная легко идентифицируется индексом, а литерал - строкой, при этом будем считать, что литерал тоже имеет индекс, равный ( ngx_uint_t )NGX_ERROR: это позволит понимать, с чем мы имеем дело (переменной или литералом) в хендлере ngx_http_myutil_get_first_not_empty_value(), в котором происходит присваивание нового значения переменной $fne_.... Итак, тип элемента из списка аргументов директивы myutil_first_not_empty имеет вид
typedef struct
{
    ngx_str_t    key;
    ngx_int_t    index;
}  ngx_http_myutil_var_handle_t;
Добавляем директиву myutil_first_not_empty в список ngx_http_myutil_commands[], теперь он выглядит так:
static ngx_command_t  ngx_http_myutil_commands[] =
{
    {
        ngx_string( "myutil_var_alias" ),
        NGX_HTTP_LOC_CONF | NGX_HTTP_LIF_CONF | NGX_CONF_TAKE2,
        ngx_http_myutil_var_alias,
        NGX_HTTP_LOC_CONF_OFFSET,
        0,
        NULL
    },
    {
        ngx_string( "myutil_first_not_empty" ),
        NGX_HTTP_LOC_CONF | NGX_HTTP_LIF_CONF | NGX_CONF_2MORE,
        ngx_http_myutil_first_not_empty,
        NGX_HTTP_LOC_CONF_OFFSET,
        0,
        NULL
    },

    ngx_null_command
};
Также обновляем функции создания и слияния конфигураций уровня location:
static void *  ngx_http_myutil_create_loc_conf( ngx_conf_t *  cf )
{
    ngx_http_myutil_loc_conf_t *  lcf = NULL;

    lcf = ngx_pcalloc( cf->pool, sizeof( ngx_http_myutil_loc_conf_t ) );

    if ( ! lcf )
        return NULL;

    if ( ngx_array_init( &lcf->varalias_data, cf->pool, 1,
                         sizeof( ngx_http_myutil_var_elem_t ) ) != NGX_OK )
        return NULL;

    if ( ngx_array_init( &lcf->first_not_empty_var_data, cf->pool, 1,
                         sizeof( ngx_http_myutil_fne_elem_t ) ) != NGX_OK )
        return NULL;

    return lcf;
}


static char *  ngx_http_myutil_merge_loc_conf( ngx_conf_t *  cf, void *  parent,
                                               void *  child )
{
    ngx_http_myutil_loc_conf_t *  prev = parent;
    ngx_http_myutil_loc_conf_t *  conf = child;

    ngx_uint_t                    i;

    /* all data in next sections will be imported from parent location in
     * addition to already collected data in child location */

    for ( i = 0; i < prev->varalias_data.nelts; ++)
    {
        ngx_http_myutil_var_elem_t *  elem = NULL;

        elem = ngx_array_push( &conf->varalias_data );

        if ( ! elem )
            return NGX_CONF_ERROR;

        *elem = ( ( ngx_http_myutil_var_elem_t * )
                                    prev->varalias_data.elts )[ i ];
    }

    for ( i = 0; i < prev->first_not_empty_var_data.nelts; ++)
    {
        ngx_http_myutil_fne_elem_t *  elem = NULL;

        elem = ngx_array_push( &conf->first_not_empty_var_data );

        if ( ! elem )
            return NGX_CONF_ERROR;

        *elem = ( ( ngx_http_myutil_fne_elem_t * )
                                    prev->first_not_empty_var_data.elts )[ i ];
    }

    return NGX_CONF_OK;
}
Хендлером конфигурации при регистрации директивы myutil_first_not_empty в списке ngx_http_myutil_commands[] была объявлена функция ngx_http_myutil_first_not_empty(), вот ее тело (строки пронумерованы для дальнейших комментариев):
367 static char *  ngx_http_myutil_first_not_empty( ngx_conf_t *  cf,
368                                             ngx_command_t *  cmd, void *  conf )
369 {
370     ngx_str_t *                   value     = cf->args->elts;
371     ngx_http_variable_t *         v         = NULL;
372     ngx_http_myutil_loc_conf_t *  lcf       = conf;
373     ngx_int_t                     v_idx     = NGX_ERROR;
374     ngx_uint_t *                  v_idx_ptr = NULL;
375     ngx_http_myutil_fne_elem_t *  resvar    = NULL;
376 
377     ngx_uint_t                    i;
378 
379     if ( value[ 1 ].data[ 0 ] != '$' )
380     {
381         ngx_conf_log_error( NGX_LOG_EMERG, cf, 0,
382                             "invalid variable name '%V'", &value[ 1 ] );
383         return NGX_CONF_ERROR;
384     }
385 
386     value[ 1 ].len--;
387     value[ 1 ].data++;
388 
389     /* It must never occur due to command arguments number restriction */
390     if ( cf->args->nelts < 3 )
391         return NGX_CONF_ERROR;
392 
393     resvar = ngx_array_push( &lcf->first_not_empty_var_data );
394 
395     if ( ! resvar )
396         return NGX_CONF_ERROR;
397 
398     if ( ngx_array_init( &resvar->data, cf->pool, cf->args->nelts - 2,
399                          sizeof( ngx_http_myutil_var_handle_t ) ) != NGX_OK )
400         return NGX_CONF_ERROR;
401 
402     for ( i = 2; i < cf->args->nelts; ++)
403     {
404         ngx_http_myutil_var_handle_t *  res   = NULL;
405         ngx_int_t                       index = NGX_ERROR;
406 
407         ngx_uint_t  isvar = value[ i ].data[ 0 ] == '$' ? 1 : 0;
408 
409         res = ngx_array_push( &resvar->data );
410 
411         if ( ! res )
412             return NGX_CONF_ERROR;
413 
414         if ( ! isvar )
415         {
416             res->index = NGX_ERROR;
417             res->key   = value[ i ];
418             continue;
419         }
420 
421         value[ i ].len--;
422         value[ i ].data++;
423 
424         index = ngx_http_get_variable_index( cf, &value[ i ] );
425 
426         if ( index == NGX_ERROR )
427             return NGX_CONF_ERROR;
428 
429         res->index = index;
430         res->key   = value[ i ];
431     }
432 
433     v = ngx_http_add_variable( cf, &value[ 1 ], NGX_HTTP_VAR_CHANGEABLE );
434 
435     if ( v == NULL )
436         return NGX_CONF_ERROR;
437 
438     v_idx = ngx_http_get_variable_index( cf, &value[ 1 ] );
439 
440     if ( v_idx == NGX_ERROR )
441         return NGX_CONF_ERROR;
442 
443     v_idx_ptr = ngx_pnalloc( cf->pool, sizeof( ngx_uint_t ) );
444 
445     if ( ! v_idx_ptr )
446         return NGX_CONF_ERROR;
447 
448     resvar->index  = v_idx;
449 
450     *v_idx_ptr     = v_idx;
451     v->data        = ( uintptr_t )v_idx_ptr;
452     v->get_handler = ngx_http_myutil_get_first_not_empty_value;
453 
454     return NGX_CONF_OK;
455 }
В строках 370-377 объявлены переменные, используемые внутри функции. В строках 379-384 проверяется, что первый аргумент директивы (переменная $fne_...) начинается с символа $. В строках 386-387 первый символ $ убирается из названия переменной (мы теперь и так знаем, что это переменная, и это нас устраивает). В строках 390-391 убеждаемся, что в директиву передано более одного аргумента (хотя эта проверка излишняя: флаг NGX_CONF_2MORE, указанный при объявлении директивы, не позволит запустить nginx, если в какой-либо из директив myutil_first_not_empty в конфигурации будет менее двух аргументов). В строках 393-400 мы создаем новый элемент массива first_not_empty_var_data из структуры ngx_http_myutil_loc_conf. На этом этапе мы знаем количество элементов внутри его подмассива data (это количество элементов директивы минус два), поэтому сразу же инициализируем data указанным значением элементов.

В строках 402-431 мы проходим по оставшимся аргументам директивы и заполняем инициализированные элементы массива data index и key значениями index и value[ i ] соответственно (строки 429-430). При этом, если текущий аргумент начинается с символа $ (т.е. является переменной), значение index получаем из возвращаемого значения функции ngx_http_get_variable_index(), а если это литерал - то записываем в качестве index значение ( ngx_uint_t )NGX_ERROR. В обоих случаях значением key будет имя текущего аргумента value[ i ] (хотя для аргумента-переменной это значение использоваться не будет).

В строках 433-441 создаем новую переменную v, соответствующую нашей переменной $fne_... и получаем ее индекс v_idx. Далее выделяем память под элемент типа ngx_uint_t (переменная v_idx_ptr): здесь будет храниться значение v_idx, а сам этот участок памяти будет передаваться в функцию ngx_http_myutul_get_first_not_empty_value() в качестве третьего аргумента для поиска нужного элемента в массиве first_not_empty_var_data (благодаря присваиванию resvar->index значения v_idx, а  v->data - адреса v_idx_ptr в строках 448-451). В строке 452 присваиваем v->get_handler() адрес функции ngx_http_myutul_get_first_not_empty_value(), а затем возвращаем значение NGX_CONF_OK.

Хендлер ngx_http_myutil_get_first_not_empty_value() выглядит так:
225 static ngx_int_t  ngx_http_myutil_get_first_not_empty_value(
226                         ngx_http_request_t *  r, ngx_http_variable_value_t *  v,
227                         uintptr_t  data )
228 {
229     ngx_http_myutil_loc_conf_t *    lcf         = NULL;
230     ngx_http_variable_value_t *     var         = NULL;
231     ngx_array_t *                   fne_data    = NULL;
232     ngx_http_myutil_fne_elem_t *    fne_elts    = NULL;
233     ngx_http_myutil_fne_elem_t *    fne_elem    = NULL;
234     ngx_array_t *                   exprarray   = NULL;
235     ngx_http_myutil_var_handle_t *  expr        = NULL;
236     ngx_int_t *                     index       = NULL;
237     ngx_uint_t                      found_index = ( ngx_uint_t )NGX_ERROR;
238 
239     ngx_uint_t                      i;
240 
241     if ( ! data )
242         return NGX_ERROR;
243 
244     lcf = ngx_http_get_module_loc_conf( r, ngx_http_myutil_module );
245 
246     fne_data = &lcf->first_not_empty_var_data;
247     fne_elts = fne_data->elts;
248     index    = ( ngx_int_t * )data;
249 
250     for ( i = 0; i < fne_data->nelts; ++)
251     {
252         if ( *index != fne_elts[ i ].index )
253             continue;
254 
255         found_index = i;
256         break;
257     }
258 
259     if ( found_index == ( ngx_uint_t )NGX_ERROR )
260         return NGX_ERROR;
261 
262     fne_elem = &fne_elts[ found_index ];
263 
264     exprarray = &fne_elem->data;
265     expr      = exprarray->elts;
266 
267     for ( i = 0; i < exprarray->nelts; ++)
268     {
269         ngx_http_variable_value_t *  found = NULL;
270 
271         if ( expr[ i ].index == NGX_ERROR )
272         {
273             v->len          = expr[ i ].key.len;
274             v->data         = expr[ i ].key.data;
275             v->valid        = 1;
276             v->no_cacheable = 0;
277             v->not_found    = 0;
278 
279             return NGX_OK;
280         }
281 
282         found = ngx_http_get_indexed_variable( r, expr[ i ].index );
283 
284         if ( found && found->len > 0 )
285         {
286             var = found;
287             break;
288         }
289     }
290 
291     if ( ! var )
292         return NGX_ERROR;
293 
294     v->len          = var->len;
295     v->data         = var->data;
296     v->valid        = 1;
297     v->no_cacheable = 0;
298     v->not_found    = 0;
299 
300     return NGX_OK;
301 }
Здесь все относительно просто. В переменную index записываем значение третьего аргумента функции (значение, которое мы предусмотрительно сохранили в ngx_http_myutil_first_not_empty()) - строка 248. В строках 250-257 ищем соответствующий элемент в массиве first_not_empty_var_data. На найденный элемент будет указывать переменная fne_elem (строка 262). Получаем элементы массива data из fne_elem (строки 264-265). Далее проходим по всем элементам. Если текущий элемент переменная, то получаем ее значение по ее индексу (строка 282). В строках 284-288 проверяем, пусто ли это значение: если пусто, то продолжаем поиск, иначе выходим из цикла и присваиваем переменной v найденное значение (строки 294-298). Если же текущий элемент - литерал, то просто берем его значение key и присваиваем переменной v (строки 271-280; заметьте, что пустой литерал '' тоже сработает, так как проверка на expr[ i ].key.len не производится!).

Ну вот и все. Осталось запустить элементарную проверку. Будем проверять на следующей конфигурации:
events {
    worker_connections  1024;
}

http {
    server {
        listen          8010;
        server_name     router;

        location /test_first_not_empty.html {
            myutil_first_not_empty $fne_1 $arg_a $arg_b default;
            myutil_first_not_empty $fne_2 $arg_aa $arg_bb 'no data';
            if ($arg_c) {
                myutil_first_not_empty $fne_1 $arg_a $arg_c;
                echo $fne_1;
                echo $fne_2;
                break;
            }
            echo $fne_1;
            echo $fne_2;
        }
    }
}
Как видим, внутри блока if в локейшне /test_first_not_empty.html переменная $fne_1 переопределена. Нам нужно проверить, действительно ли она будет переопределена. Мы в праве этого ожидать, поскольку элементы массива first_not_empty_var_data родительской конфигурации добавляются после элементов дочерней конфигурации в функции ngx_http_myutil_merge_loc_conf(). Проверяем:
$ curl 'http://localhost:8010/test_first_not_empty.html?b=HiB'
HiB
no data
$ curl 'http://localhost:8010/test_first_not_empty.html?b=HiB&c=HiC'
HiC
no data
Действительно, присутствие аргумента c в строке запроса привело к тому, что сработала директива myutil_first_not_empty $fne_1 ... внутри if. Еще пара проверок:
$ curl 'http://localhost:8010/test_first_not_empty.html?bb=HiBB&a=HiA&b=HiB'
HiA
HiBB
$ curl 'http://localhost:8010/test_first_not_empty.html?aa=907'
default
907
Исходники модуля вместе с тестовой конфигурацией можно взять здесь.

суббота, 4 августа 2012 г.

MATE и Compiz в Fedora 17

Наконец-то после почти года вынужденных мучений с Gnome 3 мне удалось вернуться на старый добрый Gnome 2 (пусть и с другим названием MATE) в придачу с Компизом, и это в Fedora 17! Как известно, какие-то нехорошие товарищи предложили напрочь выпилить из Fedora 17 Компиз дабы убрать основного конкурента Gnome 3 (конечно же, это мое личное видение ситуации и в Fedora объясняют это другими мотивами), но вот нашлись добрые волшебники и создали репозиторий с Gnome 2 / MATE, Compiz и Emerald, дабы все те, кто до этого годами пользовался этими компонентами, могли вернуться в привычную среду, не убегая из столь же привычного дистрибутива. Ну, на самом деле, я конечно же не знаю их мотивов, но надеюсь, что развитие репозитория не остановится. Информацию я нашел случайно на этом форуме, (см. также здесь). Как советуют волшебники, сперва выполняем две команды:
yum install https://dl.dropbox.com/u/49862637/Mate-desktop/fedora_17/mate-desktop-fedora-updates/noarch/mate-desktop-release-17-2.fc17.noarch.rpm
yum groupinstall MATE-Desktop
После установки MATE можно установить Компиз. Для этого в файле /etc/yum.repos.d/Mate-Desktop_fedora_17-new-application-testing.repo заменяем строку
enabled=0
в настройке [mate-desktop-fedora-new-application-testing] на
enabled=1
Теперь устанавливаем Компиз и Эмеральд:
yum install compiz emerald ccsm compiz-mate compiz-plugins-extra compiz-plugins-extra-mate compiz-plugins-main-mate compiz-plugins-unsupported compiz-plugins-unsupported-mate emerald-themes fusion-icon
Теперь самое время перелогиниться, не забыв поменять сессию на MATE. После загрузки MATE следует настроить привычный интерфейс (конфигурационные файлы MATE отличаются от файлов Gnome 2, поэтому если у вас остались настройки Gnome 2, то толку от них немного - проще заново настроить MATE). Теперь надо проверить Компиз - запускаем fusion-icon. Кликаем правой кнопкой мыши по значку fusion-icon и выбираем Compiz в качестве Window Manager и Emerald в качестве Window Decorator. Настройки Emerald у меня сохранились c прошлых лет, а вот Компиз пришлось настраивать заново (для этого можно в fusion-icon кликнуть на Settings Manager или просто вызвать из терминала команду ccsm). Для того, чтобы fusion-icon запускался автоматически при логине в MATE, можно зайти в меню Система -> Параметры на верхней панели и кликнуть на Запускаемые Приложения. В открывшемся меню во вкладке Автоматически Запускаемые Программы добавляем новый элемент со значением Команда равным fusion-icon. Но... пока лучше не делайте этого, дальше мы напишем простой скрипт, который будет запускать компоненты, зависимые от Компиз мягко, без видимых изменений отрисовки, связанных с перезагрузкой композитного менеджера.

Это еще не все. Раньше я использовал avant-window-navigator - док-бар в нижней части дисплея. К сожалению, этот компонент был также выпилен из репозиториев Fedora, но зато я опробовал cairo-dock из репозитория rpmfusion и нашел его вполне неплохой заменой. Еще одна интересная программа - screenlets, которую можно использовать для раскидывания по экрану разных полезных штуковин - скринлетов. Ее пришлось скачать отсюда и собрать из исходников самому. К счастью, это не сложно, однако в процессе установки пришлось доустановить несколько пакетов среди которых python-BeautifulSoup и gnome-python2-libwnck.

Результат изысканий представлен на следующей картинке (картинка кликабельна). Он почти полностью (наверное только за исключением avant-window-navigator, замененного на cairo-dock) соответствует тому, что у меня было на Fedora 14.


Теперь вернемся к упомянутому скрипту для запуска fusion-icon (и не только) при входе в систему. Зачем он нужен? Видите черненькую полоску на картинке внизу экрана? Это cairo-dock. Если его запустить в некомпозитном режиме, то вместо этой красивой полоски мы увидим черный непрозрачный квадрат с непрозрачными иконками. Кто у нас запускает композитный менеджер (то бишь Компиз)? Правильно, fusion-icon. Кто запускает fusion-icon? MATE. И это настраивается в диалоге Автоматически Запускаемые Программы, там же, где настраивается запуск cairo-dock. Так вот, к сожалению Gnome 2 / MATE не гарантирует последовательность запуска этих программ и мы на какое-то мгновение можем увидеть "черный квадрат" вместо нормального cairo-dock при входе в систему. Чтобы загрузка Компиз выглядела мягкой, мы будем запускать все программы, отрисовка которых зависит от наличия композитного менеджера, внутри специального скрипта StartCompizSession после гарантированного старта Компиз. Этот скрипт мы поместим в качестве нового элемента в Автоматически Запускаемые Программы, а программы, зависящие от присутствия композитного менеджера, соответственно оттуда удалим. Помимо cairo-dock сюда относятся различные скринлеты.
#!/bin/bash

fusion-icon &
sleep 1

nmbOfTry=1
nmbOfTryLimit=20

while (( $nmbOfTry < $nmbOfTryLimit )) ; do
  ps -U $USER -o args | grep '^emerald' > /dev/null
  (( $? == 0 )) && break
  (( ++$nmbOfTry ))
  sleep 1
done

if (( $nmbOfTry < $nmbOfTryLimit )) ; then
  #avant-window-navigator &
  cairo-dock -o &

  # screenlets daemon
  screenlets-daemon &

  # various screenlets
  python -u /usr/share/screenlets/PerfectClock/PerfectClockScreenlet.py &
  python -u /usr/share/screenlets/DiskSpace/DiskSpaceScreenlet.py &
  python -u /usr/share/screenlets/ClearCalendar/ClearCalendarScreenlet.py &
  python -u /usr/share/screenlets/Slideshow/SlideshowScreenlet.py &
  python -u /usr/share/screenlets/aSensors/aSensorsScreenlet.py &
fi