Мои публичные конфиги для $HOME

В этой статье со­бра­ны пуб­лич­ные час­ти мо­их де­сктоп­ных ко­нфи­гов. Из ис­ход­ни­ка статьи в фор­ма­те org-mode де­скто­пы ав­то­ма­ти­чес­ки ко­нфигу­ри­ру­ют­ся. Это про­ис­хо­дит че­рез ob-tangle — сис­те­му экс­пор­та SRC-бло­ков в ко­неч­ные фай­лы.

Исход­ник статьи в фор­ма­те org-mode до­сту­пен для ска­чи­ва­ния: dotfiles.sorg.

Чем это лучше до­тфай­лов в git? Я ви­жу та­кие яв­ные пре­иму­щест­ва:

  1. Кон­фи­ги хо­ро­шо до­ку­мен­ти­ро­ва­ны в еди­ном сти­ле;
  2. Нали­чие ме­та­кон­фи­га, за­да­юще­го об­щие кон­стан­ты меж­ду ко­нфи­га­ми, и в то же вре­мя по­зво­ля­юще­го за­да­вать раз­ные па­ра­мет­ры меж­ду раз­ны­ми де­скто­па­ми.

Моя пуб­лич­ная ко­нфиг­ура­ция для Emacs на­хо­дит­ся здесь. Но там разброд и ша­та­ние, по­ка не мо­гу ре­ко­мен­до­вать в ка­чест­ве об­раз­ца.

Преам­бу­ла, ко­нфиг для ко­нфи­гов

Есть на­бор па­ра­мет­ров, ко­то­рые под­став­ля­ют­ся макро­са­ми во все ко­нфи­ги. Зада­ют­ся при­мер­но так:

* Моё имя

#+name: person-name
#+BEGIN_SRC text
Evgenii Lepikhin
#+END_SRC

* Имя пользователя в системе

#+name: system-username
#+BEGIN_SRC text
eugene
#+END_SRC

* E-mail

#+name: person-email
#+BEGIN_SRC text
johnlepikhin@gmail.com
#+END_SRC

* Батарейка в ~/sys/class/power_supply~

#+name: sys-power-supply
#+BEGIN_SRC
BAT1
#+END_SRC

* Ширина экрана

#+name: screen-width
#+BEGIN_SRC text
1600
#+END_SRC

* Каким светодиодом показывать альтернативную раскладку

#+name: kbd-layout-led
#+BEGIN_SRC text
caps
#+END_SRC

Этот ко­нфиг следу­ет вруч­ную со­хра­нить в файл ~/.dotfiles.conf.org. К Emacs он под­клю­чен че­рез Library Of Babel:

(org-babel-lob-ingest "~/.dotfiles.conf.org")

bashrc

Кон­фиг под­кл­юча­ет­ся про­пи­сы­ва­ни­ем в .bashrc строч­ки:

. ~/.bashrc.d/00_public

на­ст­рой­ки XIM

export XMODIFIERS=@im=none
export GTK_IM_MODULE=none
export QT_IM_MODULE=none
export QT4_IM_MODULE=none

Наст­рой­ки ре­дак­то­ра

export EDITOR='emacsclient -c'

Упро­ща­ем шелл

export PATH=~/bin:~/bin/phone-shared:$PATH
alias usetmp=USETMP_DIR\=\`mktemp\ -d\ /tmp/tmpdir-XXXX\`\;\ bash\ -c\ \"cd\ \$USETMP_DIR\;\ bash\"\;\ rmdir\ \$USETMP_DIR

Косты­ли от язы­ка Go

export GOROOT=$HOME/bin/go
export GOPATH=$HOME/work/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

Косты­ли от язы­ка Perl

PATH="$HOME/perl5/bin${PATH:+:${PATH}}"; export PATH;
PERL5LIB="$HOME/perl5/lib/perl5${PERL5LIB:+:${PERL5LIB}}"; export PERL5LIB;
PERL_LOCAL_LIB_ROOT="$HOME/perl5${PERL_LOCAL_LIB_ROOT:+:${PERL_LOCAL_LIB_ROOT}}"; export PERL_LOCAL_LIB_ROOT;
PERL_MB_OPT="--install_base \"$HOME/perl5\""; export PERL_MB_OPT;
PERL_MM_OPT="INSTALL_BASE=$HOME/perl5"; export PERL_MM_OPT;

Пра­виль­ное от язы­ка Ocaml

eval `opam config env`

git

Основ­ной ко­нфиг

[user]
name = Evgenii Lepikhin
email = johnlepikhin@gmail.com
[core]
        quotePath = false
        autocrlf = input
        excludesfile = ~/.gitignore

Гло­баль­ный .gitignore

*_flymake.pl
*_flymake.pm
.PerlySenseProject
TAGS
CTAGS
GTAGS
*.elc
flycheck_*.el
tramp
\#*\#
.\#*
*~
gradlew.bat
perltidy.ERR

inputrc

По Ctrl-B уда­лять на­зад до бли­жай­ше­го про­бе­ла или слэ­ша.

C-b: unix-filename-rubout

Ctrl-left и ctrl-right для дви­же­ния по сло­вам

"\e[1;5C": forward-word
"\e[1;5D": backward-word

parcellite

Менеджер бу­фе­ра об­ме­на.

[rc]
RCVersion=1
use_copy=true
use_primary=true
synchronize=true
save_history=true
history_pos=false
history_x=1
history_y=1
history_limit=25
data_size=0
item_size=5
automatic_paste=false
auto_key=false
auto_mouse=true
key_input=false
restore_empty=true
rc_edit=true
type_search=true
case_search=false
ignore_whiteonly=false
trim_wspace_begend=false
trim_newline=false
hyperlinks_only=false
confirm_clear=true
current_on_top=true
single_line=true
reverse_history=false
item_length=50
persistent_history=false
persistent_separate=false
persistent_on_top=false
persistent_delim=\\n
nonprint_disp=false
ellipsize=2
multi_user=false
icon_name=parcellite
menu_key=<Ctrl><Alt>P
history_key=<Ctrl><Super>C
phistory_key=<Ctrl><Alt>X
actions_key=

perlcritic

severity = 1
theme = bugs + maintenance + security + complexity + pbp + cosmetic + core

#This one must be disabled, since flymake will create temp files which
#by definition never match the specified package name
[-Modules::RequireFilenameMatchesPackage]

[-Subroutines::ProhibitSubroutinePrototypes]
[-Subroutines::ProhibitExplicitReturnUndef]
[-Subroutines::RequireArgUnpacking]
[-InputOutput::RequireCheckedSyscalls]
[-ValuesAndExpressions::ProhibitInterpolationOfLiterals]
[-ValuesAndExpressions::ProhibitMagicNumbers]
[-RegularExpressions::RequireDotMatchAnything]
[-RegularExpressions::RequireExtendedFormatting]
[-RegularExpressions::RequireLineBoundaryMatching]
[-Modules::RequireVersionVar]
[-Modules::ProhibitMultiplePackages]
[-RegularExpressions::ProhibitComplexRegexes]
[-ValuesAndExpressions::ProhibitLongChainsOfMethodCalls]
[-InputOutput::RequireCheckedClose]
[-Variables::ProhibitPunctuationVars]
[-ControlStructures::ProhibitDeepNests]

[-Documentation::RequirePodAtEnd]
[-Documentation::RequirePodSections]
[-Documentation::PodSpelling]

[InputOutput::RequireBriefOpen]
lines = 20

perltidy

# Max line width is 78 cols
-l=140
# Indent level is 4 cols
-i=4
# Continuation indent is 4 cols
-ci=4
# Output to STDOUT
-st
# Errors to STDERR
-se
# Maximal vertical tightness
-vt=2
# No extra indentation for closing brackets
-cti=0
# Medium parenthesis tightness
-pt=1
# Medium brace tightness
-bt=1
# Medium square bracket tightness
-sbt=1
# Medium block brace tightness
-bbt=1
# No space before semicolons
-nsfs
# Don't outdent long quoted strings
-nolq
# Break before all operators
-wbb="% + - * / x != == >= <= =~ !~ < > | & >= < = **= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= x="
# No newline before else
-ce

-bar
-otr
-sct
-dnl
-wrs='! ++'

ssh

Базо­вая ко­нфиг­ура­ция для ни­жес­ле­ду­ющих хос­тов

Вклю­чить сжа­тие по умо­лча­нию

Host *
     Compression yes
     CompressionLevel 9

Тай­ма­уты

Host *
     ServerAliveInterval 10
     ConnectTimeout 15

Пос­то­ян­ные со­еди­не­ния

Host *
     ControlMaster auto
     ControlPath ~/.ssh/connections/%h:%p:%r
     ControlPersist yes

Зна­че­ния по умо­лча­нию для не­из­вест­ных хос­тов

Host *
     User root

stalonetray

Пред­ва­ри­тель­ные вы­чис­ле­ния

Я хо­чу вс­ег­да ра­поло­гать трей на 670 пик­се­лей ле­вее пра­во­го края эк­ра­на:

(- 1600 670)

Кон­фиг

decorations none
transparent false
dockapp_mode none
geometry 10x1+nil+0
max_geometry 5x1-325-10
background "#000000"
kludges force_icons_size
grow_gravity NE
icon_gravity NE
icon_size 16
slot_size 22
sticky true
window_type dock
window_layer bottom
#no_shrink false
skip_taskbar true

urxvt

Кусоч­ки мо­его ко­да

Заго­ло­вок

use File::Temp;

sub msg {
    my ( $self, $msg ) = @_;

    $self->{overlay} =
      $self->overlay( 0, -1, $self->ncol, 2, urxvt::OVERLAY_RSTYLE, 0 );
    $self->{overlay}->set( 0, 0, $msg );
}

sub on_start {
    my $self = shift;

    $self->bind_action( "M-e" => '%:edit_screen' );
}

Откры­тие ис­то­рии кон­со­ли во внеш­нем ре­дак­то­ре по Meta-E

sub edit_screen {
    my $self = shift;

    my $history = q{};
    my $row     = $self->top_row;
    while ( $self->nrow > $row ) {
        my $line = $self->line($row);
        $history .= $line->t() . "\n";
        $row++;
    }

    my $fh = File::Temp->new( TEMPLATE => '/tmp/urxvt_edit_screen_XXXXXX' );
    binmode( $fh, ":utf8" );
    print $fh $history;

    my $q_file = $fh->filename;
    system("editor $q_file &");
    close $fh;
}

sub on_action {
    my $self   = shift;
    my $action = shift;
    if ( $action eq 'edit_screen' ) {
        edit_screen($self);
    }

}

Про­сить под­тверж­де­ние при по­пыт­ке вста­вить мно­гост­роч­ный текст

sub on_tt_paste {
    my ( $self, $str ) = @_;

    $str =~
      s/\xc2[«»]|\xe2\x80[\xb9\xba\x9e\x9c\x9d]|\xe3\x80[\x8c-\x8f]/"/gs;
    my $count = ( $str =~ tr/\012\015// );

    if ( !$count ) {
        $self->tt_paste($str);
        return 1;
    }

    $self->{paste} = \$str;
    $self->msg("Paste of $count lines, continue? (y/n)");
    my $preview = substr $self->locale_decode($str), 0, $self->ncol;
    $preview =~ s/\n/\\n/g;
    $self->{overlay}->set( 0, 1, $self->special_encode($preview) );
    $self->enable( key_press => \&tt_key_press );

    1;
}

sub tt_leave {
    my ($self) = @_;

    $self->{paste} = undef;
    delete $self->{overlay};
    $self->disable("key_press");
}

sub tt_key_press {
    my ( $self, $event, $keysym, $string ) = @_;

    if ( $keysym == 121 ) {    # y
        $self->tt_paste( ${ $self->{paste} } );
        $self->tt_leave;
    }
    elsif ( $keysym == 110 ) {    # n
        $self->tt_leave;
    }

    1;
}

Сме­на раз­ме­ра шриф­та по Ctrl-Up/Down

Кноп­ки на­ст­ро­ены че­рез Xresources

use strict;
use warnings;

my %escapecodes = (
    "font"           => 710,
    "boldFont"       => 711,
    "italicFont"     => 712,
    "boldItalicFont" => 713
);

sub on_start
{
    my ($self) = @_;

    $self->{step} = $self->x_resource("%.step") || 1;

    foreach my $type (qw(font boldFont italicFont boldItalicFont)) {
        $self->{$type} = $self->x_resource($type) || "undef";
    }
}

sub on_user_command
{
    my ($self, $cmd) = @_;

    my $step = $self->{step};

    if ($cmd eq "font-size:increase") {
        fonts_change_size($self,  $step, 0);
    } elsif ($cmd eq "font-size:decrease") {
        fonts_change_size($self, -$step, 0);
    } elsif ($cmd eq "font-size:incglobal") {
        fonts_change_size($self,  $step, 1);
    } elsif ($cmd eq "font-size:decglobal") {
        fonts_change_size($self, -$step, 1);
    } elsif ($cmd eq "font-size:incsave") {
        fonts_change_size($self,  $step, 2);
    } elsif ($cmd eq "font-size:decsave") {
        fonts_change_size($self, -$step, 2);
    } elsif ($cmd eq "font-size:reset") {
        fonts_reset($self);
    }
}

sub fonts_change_size
{
    my ($term, $change, $save) = @_;

    my @newfonts = ();

    my $curres = $term->resource('font');
    if (!$curres) {
        $term->scr_add_lines("\r\nWarning: No font configured, trying a default.\r\nPlease set a font with the 'URxvt.font' resource.");
        $curres = "fixed";
    }
    my @curfonts = split(/,/, $curres);

    my $basefont = shift(@curfonts);
    my ($newbasefont, $newbasesize) = handle_font($term, $basefont, $change, 0);
    push @newfonts, $newbasefont;

    # Only adjust other fonts if base font changed
    if ($newbasefont ne $basefont) {
        foreach my $font (@curfonts) {
            my ($newfont, $newsize) = handle_font($term, $font, $change, $newbasesize);
            push @newfonts, $newfont;
        }
        my $newres = join(",", @newfonts);
        font_apply_new($term, $newres, "font", $save);

        handle_type($term, "boldFont",       $change, $newbasesize, $save);
        handle_type($term, "italicFont",     $change, $newbasesize, $save);
        handle_type($term, "boldItalicFont", $change, $newbasesize, $save);
    }

    if ($save > 1) {
        # write the new values back to the file
        my $xresources = readlink $ENV{"HOME"} . "/.Xresources";
        system("xrdb -edit " . $xresources);
    }
}

sub fonts_reset
{
    my ($term) = @_;

    foreach my $type (qw(font boldFont italicFont boldItalicFont)) {
        my $initial = $term->{$type};
        if ($initial ne "undef") {
            font_apply_new($term, $initial, $type, 0);
        }
    }
}

sub handle_type
{
    my ($term, $type, $change, $basesize, $save) = @_;

    my $curres = $term->resource($type);
    if (!$curres) {
        return;
    }
    my @curfonts = split(/,/, $curres);
    my @newfonts = ();

    foreach my $font (@curfonts) {
        my ($newfont, $newsize) = handle_font($term, $font, $change, $basesize);
        push @newfonts, $newfont;
    }

    my $newres = join(",", @newfonts);
    font_apply_new($term, $newres, $type, $save);
}

sub handle_font
{
    my ($term, $font, $change, $basesize) = @_;

    my $newfont;
    my $newsize;
    my $prefix = 0;

    if ($font =~ /^\s*x:/) {
        $font =~ s/^\s*x://;
        $prefix = 1;
    }
    if ($font =~ /^\s*(\[.*\])?xft:/) {
        ($newfont, $newsize) = font_change_size_xft($term, $font, $change, $basesize);
    } elsif ($font =~ /^\s*-/) {
        ($newfont, $newsize) = font_change_size_xlfd($term, $font, $change, $basesize);
    } else {
        # check whether the font is a valid alias and if yes resolve it to the
        # actual font
        my $lsfinfo = `xlsfonts -l $font 2>/dev/null`;

        if ($lsfinfo eq "") {
            # not a valid alias, ring the bell if it is the base font and just
            # return the current font
            if ($basesize == 0) {
                $term->scr_bell;
            }
            return ($font, $basesize);
        }

        my $fontinfo = (split(/\n/, $lsfinfo))[-1];
        my ($fontfull) = ($fontinfo =~ /\s+([-a-z0-9]+$)/);
        ($newfont, $newsize) = font_change_size_xlfd($term, $fontfull, $change, $basesize);
    }

    # $term->scr_add_lines("\r\nNew font is $newfont\n");
    if ($prefix) {
        $newfont = "x:$newfont";
    }
    return ($newfont, $newsize);
}

sub font_change_size_xft
{
    my ($term, $fontstring, $change, $basesize) = @_;

    my @pieces   = split(/:/, $fontstring);
    my @resized  = ();
    my $size     = 0;
    my $new_size = 0;

    foreach my $piece (@pieces) {
        if ($piece =~ /^(?:(?:pixel)?size=|[^=-]+-)(\d+(\.\d*)?)$/) {
            $size = $1;

            if ($basesize != 0) {
                $new_size = $basesize;
            } else {
                $new_size = $size + $change
            }

            $piece =~ s/(=|-)$size/$1$new_size/;
        }
        push @resized, $piece;
    }

    my $resized_str = join(":", @resized);

    # don't make fonts too small
    if ($new_size >= 6) {
        return ($resized_str, $new_size);
    } else {
        if ($basesize == 0) {
            $term->scr_bell;
        }
        return ($fontstring, $size);
    }
}

sub font_change_size_xlfd
{
    my ($term, $fontstring, $change, $basesize) = @_;

    #-xos4-terminus-medium-r-normal-*-12-*-*-*-*-*-*-1

    my @fields = qw(foundry family weight slant setwidth style pixelSize pointSize Xresolution Yresolution spacing averageWidth registry encoding);

    my %font;
    $fontstring =~ s/^-//;  # Strip leading - before split
    @font{@fields} = split(/-/, $fontstring);

    if ($font{pixelSize} eq '*') {
        $term->scr_add_lines("\r\nWarning: Font size undefined, assuming 12.\r\nPlease set the 'URxvt.font' resource to a font with a concrete size.");
        $font{pixelSize} = '12'
    }
    if ($font{registry} eq '*') {
        $font{registry} ='iso8859';
    }

    # Blank out the size for the pattern
    my %pattern = %font;
    $pattern{foundry} = '*';
    $pattern{setwidth} = '*';
    $pattern{pixelSize} = '*';
    $pattern{pointSize} = '*';
    # if ($basesize != 0) {
    #     $pattern{Xresolution} = '*';
    #     $pattern{Yresolution} = '*';
    # }
    $pattern{averageWidth} = '*';
    # make sure there are no empty fields
    foreach my $field (@fields) {
        $pattern{$field} = '*' unless defined($pattern{$field});
    }
    my $new_fontstring = '-' . join('-', @pattern{@fields});

    my @possible;
    # $term->scr_add_lines("\r\nPattern is $new_fontstring\n");
    open(FOO, "xlsfonts -fn '$new_fontstring' | sort -u |") or die $!;
    while (<FOO>) {
        chomp;
        s/^-//;  # Strip leading '-' before split
        my @fontdata = split(/-/, $_);

        push @possible, [$fontdata[6], "-$_"];
        # $term->scr_add_lines("\r\npossibly $fontdata[6] $_\n");
    }
    close(FOO);

    if (!@possible) {
        die "No possible fonts!";
    }

    if ($basesize != 0) {
        # sort by font size, descending
        @possible = sort {$b->[0] <=> $a->[0]} @possible;

        # font is not the base font, so find the largest font that is at most
        # as large as the base font. If the largest possible font is smaller
        # than the base font bail and hope that a 0-size font can be found at
        # the end of the function
        if ($possible[0]->[0] > $basesize) {
            foreach my $candidate (@possible) {
                if ($candidate->[0] <= $basesize) {
                    return ($candidate->[1], $candidate->[0]);
                }
            }
        }
    } elsif ($change > 0) {
        # sort by font size, ascending
        @possible = sort {$a->[0] <=> $b->[0]} @possible;

        foreach my $candidate (@possible) {
            if ($candidate->[0] >= $font{pixelSize} + $change) {
                return ($candidate->[1], $candidate->[0]);
            }
        }
    } elsif ($change < 0) {
        # sort by font size, descending
        @possible = sort {$b->[0] <=> $a->[0]} @possible;

        foreach my $candidate (@possible) {
            if ($candidate->[0] <= $font{pixelSize} + $change && $candidate->[0] != 0) {
                return ($candidate->[1], $candidate->[0]);
            }
        }
    }

    # no fitting font available, check whether a 0-size font can be used to
    # fit the size of the base font
    @possible = sort {$a->[0] <=> $b->[0]} @possible;
    if ($basesize != 0 && $possible[0]->[0] == 0) {
        return ($possible[0]->[1], $basesize);
    } else {
        # if there is absolutely no smaller/larger font that can be used
        # return the current one, and beep if this is the base font
        if ($basesize == 0) {
            $term->scr_bell;
        }
        return ("-$fontstring", $font{pixelSize});
    }
}

sub font_apply_new
{
    my ($term, $newfont, $type, $save) = @_;

    # $term->scr_add_lines("\r\nnew font is $newfont\n");

    $term->cmd_parse("\033]" . $escapecodes{$type} . ";" . $newfont . "\033\\");

    # load the xrdb db
    # system("xrdb -load " . X_RESOURCES);

    if ($save > 0) {
        # merge the new values
        open(XRDB_MERGE, "| xrdb -merge") || die "can't fork: $!";
        local $SIG{PIPE} = sub { die "xrdb pipe broken" };
        print XRDB_MERGE "URxvt." . $type . ": " . $newfont;
        close(XRDB_MERGE) || die "bad xrdb: $! $?";
    }
}

vim

set nowrapscan
set ch=2
map <S-Insert> <MiddleMouse>
map! <S-Insert> <MiddleMouse>

set viminfo=\'1000,f1,<500,/50

au BufNewFile,BufRead *.eliom set filetype=ocaml

let c_comment_strings=1
syntax on
set hlsearch
set tags=./TAGS
"set mouse=a

set cindent
set smartindent
set autoindent
"set expandtab
set tabstop=3
set shiftwidth=3

set pastetoggle=<F10>

map ё `
map й q
map ц w
map у e
map к r
map е t
map н y
map г u
map ш i
map щ o
map з p
map х [
map ъ ]
map ф a
map ы s
map в d
map а f
map п g
map р h
map о j
map л k
map д l
map ж ;
map я z
map ч x
map с c
map м v
map и b
map т n
map ь m
map б ,
map ю .
map Ё ~
map Й Q
map Ц W
map У E
map К R
map Е T
map Н Y
map Г U
map Ш I
map Щ O
map З P
map Х {
map Ъ }
map Ф A
map Ы S
map В D
map А F
map П G
map Р H
map О J
map Л K
map Д L
map Ж :
map Э "
map Я Z
map Ч X
map С C
map М V
map И B
map Т N
map Ь M
map Б <
map Ю >
map . /

Кон­фиг­ура­ция для OPAM/Ocaml

" ## added by OPAM user-setup for vim / base ## 93ee63e278bdfc07d1139a748ed3fff2 ## you can edit, but keep this line
let s:opam_share_dir = system("opam config var share")
let s:opam_share_dir = substitute(s:opam_share_dir, '[\r\n]*$', '', '')

let s:opam_configuration = {}

function! OpamConfOcpIndent()
  execute "set rtp^=" . s:opam_share_dir . "/ocp-indent/vim"
endfunction
let s:opam_configuration['ocp-indent'] = function('OpamConfOcpIndent')

function! OpamConfOcpIndex()
  execute "set rtp+=" . s:opam_share_dir . "/ocp-index/vim"
endfunction
let s:opam_configuration['ocp-index'] = function('OpamConfOcpIndex')

function! OpamConfMerlin()
  let l:dir = s:opam_share_dir . "/merlin/vim"
  execute "set rtp+=" . l:dir
endfunction
let s:opam_configuration['merlin'] = function('OpamConfMerlin')

let s:opam_packages = ["ocp-indent", "ocp-index", "merlin"]
let s:opam_check_cmdline = ["opam list --installed --short --safe --color=never"] + s:opam_packages
let s:opam_available_tools = split(system(join(s:opam_check_cmdline)))
for tool in s:opam_packages
  " Respect package order (merlin should be after ocp-index)
  if count(s:opam_available_tools, tool) > 0
    call s:opam_configuration[tool]()
  endif
endfor
" ## end of OPAM user-setup addition for vim / base ## keep this line

xcompose

Подклю­чить стан­дарт­ные на­бо­ры дист­ри­бу­ти­ва

include "%S/en_US.UTF-8/Compose"
include "%L"

Моё лю­би­мое

<Multi_key> <Cyrillic_yu> <Cyrillic_yu>     : "…"   ellipsis # HORIZONTAL ELLIPSIS
<Multi_key> <minus> <minus>                 : "–"   U2013 # EN DASH

<Multi_key> <Cyrillic_BE> <Cyrillic_BE>     : "«"
<Multi_key> <Cyrillic_YU> <Cyrillic_YU>     : "»"

<Multi_key> <grave> <grave>             : "́" U0301 # ударение
<Multi_key> <Cyrillic_io> <Cyrillic_io>     : "́" # ударениее (комбинирующееся)

Гре­чес­кий ал­фа­вит для вся­кой ма­те­ма­ти­ки

<Multi_key> <G> <A> : "Α"    U0391    # GREEK CAPITAL LETTER ALPHA
<Multi_key> <G> <B> : "Β"    U0392    # GREEK CAPITAL LETTER BETA
<Multi_key> <G> <G> : "Γ"    U0393    # GREEK CAPITAL LETTER GAMMA
<Multi_key> <G> <D> : "Δ"    U0394    # GREEK CAPITAL LETTER DELTA
<Multi_key> <G> <E> : "Ε"    U0395    # GREEK CAPITAL LETTER EPSILON
<Multi_key> <G> <Z> : "Ζ"    U0396    # GREEK CAPITAL LETTER ZETA
<Multi_key> <G> <H> : "Η"    U0397    # GREEK CAPITAL LETTER ETA
<Multi_key> <G> <I> : "Ι"    U0399    # GREEK CAPITAL LETTER IOTA
<Multi_key> <G> <K> : "Κ"    U039A    # GREEK CAPITAL LETTER KAPPA
<Multi_key> <G> <L> : "Λ"    U039B    # GREEK CAPITAL LETTER LAMDA
<Multi_key> <G> <M> : "Μ"    U039C    # GREEK CAPITAL LETTER MU
<Multi_key> <G> <N> : "Ν"    U039D    # GREEK CAPITAL LETTER NU
<Multi_key> <G> <P> : "Π"    U03A0    # GREEK CAPITAL LETTER PI
<Multi_key> <G> <R> : "Ρ"    U03A1    # GREEK CAPITAL LETTER RHO
<Multi_key> <G> <S> : "Σ"    U03A3    # GREEK CAPITAL LETTER SIGMA
<Multi_key> <G> <T> : "Τ"    U03A4    # GREEK CAPITAL LETTER TAU
<Multi_key> <G> <U> : "Υ"    U03A5    # GREEK CAPITAL LETTER UPSILON
<Multi_key> <G> <F> : "Φ"    U03A6    # GREEK CAPITAL LETTER PHI
<Multi_key> <G> <X> : "Χ"    U03A7    # GREEK CAPITAL LETTER CHI
<Multi_key> <G> <O> : "Ω"    U03A9    # GREEK CAPITAL LETTER OMEGA

<Multi_key> <G> <Q> <T> : "Θ"    U0398    # GREEK CAPITAL LETTER THETA
<Multi_key> <G> <Q> <O> : "Ο"    U039F    # GREEK CAPITAL LETTER OMICRON
<Multi_key> <G> <Q> <X> : "Ξ"    U039E    # GREEK CAPITAL LETTER XI
<Multi_key> <G> <Q> <P> : "Ψ"    U03A8    # GREEK CAPITAL LETTER PSI

<Multi_key> <g> <a> : "α"    U03B1    # GREEK SMALL LETTER ALPHA
<Multi_key> <g> <b> : "β"    U03B2    # GREEK SMALL LETTER BETA
<Multi_key> <g> <g> : "γ"    U03B3    # GREEK SMALL LETTER GAMMA
<Multi_key> <g> <d> : "δ"    U03B4    # GREEK SMALL LETTER DELTA
<Multi_key> <g> <e> : "ε"    U03B5    # GREEK SMALL LETTER EPSILON
<Multi_key> <g> <z> : "ζ"    U03B6    # GREEK SMALL LETTER ZETA
<Multi_key> <g> <h> : "η"    U03B7    # GREEK SMALL LETTER ETA
<Multi_key> <g> <i> : "ι"    U03B9    # GREEK SMALL LETTER IOTA
<Multi_key> <g> <k> : "κ"    U03BA    # GREEK SMALL LETTER KAPPA
<Multi_key> <g> <l> : "λ"    U03BB    # GREEK SMALL LETTER LAMDA
<Multi_key> <g> <m> : "μ"    U03BC    # GREEK SMALL LETTER MU
<Multi_key> <g> <n> : "ν"    U03BD    # GREEK SMALL LETTER NU
<Multi_key> <g> <p> : "π"    U03C0    # GREEK SMALL LETTER PI
<Multi_key> <g> <r> : "ρ"    U03C1    # GREEK SMALL LETTER RHO
<Multi_key> <g> <s> : "σ"    U03C3    # GREEK SMALL LETTER SIGMA
<Multi_key> <g> <t> : "τ"    U03C4    # GREEK SMALL LETTER TAU
<Multi_key> <g> <u> : "υ"    U03C5    # GREEK SMALL LETTER UPSILON
<Multi_key> <g> <f> : "φ"    U03C6    # GREEK SMALL LETTER PHI
<Multi_key> <g> <x> : "χ"    U03C7    # GREEK SMALL LETTER CHI
<Multi_key> <g> <o> : "ω"    U03C9    # GREEK SMALL LETTER OMEGA

<Multi_key> <g> <q> <t> : "θ"    U03B8    # GREEK SMALL LETTER THETA
<Multi_key> <g> <q> <o> : "ο"    U03BF    # GREEK SMALL LETTER OMICRON
<Multi_key> <g> <q> <p> : "ψ"    U03C8    # GREEK SMALL LETTER PSI
<Multi_key> <g> <q> <s> : "ς"    U03C2    # GREEK SMALL LETTER FINAL SIGMA
<Multi_key> <g> <q> <x> : "ξ"    U03BE    # GREEK SMALL LETTER XI

xmobar

Config {
         font = "xft:Monospace-Bold:size=9"
       , borderColor = "#303030"
       , border = BottomBM 3
       , borderWidth = 1
       , bgColor = "black"
       , fgColor = "grey"
       , position = Static { xpos = 0, ypos = 0, width = 1600, height = 32 }
       , lowerOnStart = True
       , pickBroadest = False
       , persistent = False
       , hideOnStart = False
       , iconRoot = "."
       , allDesktops = True
       , overrideRedirect = False
       , commands = [
          Run Weather "UUDD" ["-t", "<tempC>C"
                             , "-L", "18", "-H", "25"
                             , "--normal", "green"
                             , "--high", "red"
                             , "--low", "lightblue" ] 36000
         , Run Memory ["-t","Mem: <usedratio>%"] 50
         , Run Swap [] 100
         , Run Date "%b %_d %Y %H:%M:%S" "date" 10
         , Run StdinReader

TODO Бата­рей­ка

У од­но­го из де­сктопов ба­та­рей­ка слег­ка умер­шая, вы­ше 98% не по­ка­зы­ва­ет. Зада­ча — не подсве­чи­вать крас­ным в этом слу­чае. Доку­мен­та­ция.

, Run BatteryP ["BAT1"]
  [
    "-t", "<acstatus>"
  , "-L", "20"
  , "-l", "red"
  , "--", "-O", "Charging", "-o", "<fc=white,red>Bat: <left>%</fc>"
  ] 10

Хвост

]
, sepChar = "%"
, alignSep = "}{"
, template = "%StdinReader% }{ | %memory% * %swap% | %battery% | %UUDD% | %date% "
}

xmonad

Основ­ной ко­нфиг

import Data.IORef
import GHC.IO.Handle.Types
import XMonad
import XMonad.Config.Gnome
import XMonad.Util.EZConfig
import XMonad.Hooks.ManageDocks
import qualified XMonad.StackSet as Win
import XMonad.Hooks.ManageHelpers
import XMonad.Layout.NoBorders
import XMonad.Layout.Named
import XMonad.Layout.Grid
import XMonad.Util.Run
import XMonad.Hooks.EwmhDesktops

import XMonad.Hooks.DynamicLog

import XMonad.Prompt
import qualified XMonad.Prompt.Shell
import qualified XMonad.Prompt.Window

import XMonad.Util.Loggers
import XMonad.Util.Paste
import Data.Monoid
import Control.Monad (when, unless)

color1 = "#E1A81C"
color2 = "#290b64"
color3 = "#926b09"
color7 = "#808080"
color8 = "#404040"
color9 = "#101010"
color10 = "#f00000"

colorXmobarTitle = color1
colorXmobarWSHidden = color8
colorXmobarWSHiddenNoWindows = color8
colorXmobarWSActive = color10
colorXmobarSeparator = color7
colorXmobarLayoutRuBG = color10

colorPromptBG = color9
colorPromptFG = color1

colorBorderInactive = color2
colorBorderUS = color1
colorBorderRU = color10

role :: Query String
role = stringProperty "WM_WINDOW_ROLE"

gimpHooks :: Query (Endo WindowSet)
gimpHooks = composeAll [
             (role =? "gimp-toolbox" <||> role =? "gimp-image-window") --> (ask >>= doF . Win.sink)
            ]

myManageHook :: Query (Endo WindowSet)
myManageHook = composeAll [
                className =? "Xfce4-notifyd" -->  doIgnore
               , className =? "Do" --> doFloat
               , title =? "Eclipse" --> doFloat
               , className =? "skypeforlinux" --> doShift "5"
               , className =? "TelegramDesktop" --> doShift "5"
               , className =? "VirtualBox" --> doShift "8"
               , role =? "CallWindow" --> doFullFloat
               , composeOne [ isFullscreen -?> doFullFloat ]
               , gimpHooks
               ]

gridLayout = named "Grid" $ Grid

myLayouts =
    tiled ||| Mirror tiled ||| Full ||| gridLayout
        where
          tiled   = Tall nmaster delta ratio
          nmaster = 1
          ratio   = 2/3
          delta   = 3/100

myPromptConfig :: XPConfig
myPromptConfig = def {
                   height       = 25
                 , font         = "-misc-fixed-*-*-*-*-14-*-*-*-*-*-*-*"
                 , bgColor      = colorPromptBG
                 , fgColor      = colorPromptFG
                 }

myStartupHook = spawn "killall stalonetray; sleep 1; stalonetray &"

notify :: String -> String -> IO ()
notify timeout msg = safeSpawn "notify-send" ["-t", show timeout, msg]

get_internal_speakers :: IO String
get_internal_speakers = runProcessWithInput "sh" ["-c", "~/bin/sink_of_internal_speakers"] ""

setVolume :: String -> String -> IO ()
setVolume pct sink = spawn ("~/bin/set_sink_volume '" ++ sink ++ "' '" ++ pct ++ "'")

setVolumeInterval :: String -> X ()
setVolumeInterval pct =
    io (get_internal_speakers >>= setVolume pct)

getBrightness :: () -> IO String
getBrightness () = runProcessWithInput "sh" ["-c", "sleep 0.2; xbacklight -get|cut -d. -f1"] ""

setBrightness :: String -> X ()
setBrightness cmdKeys =
    io $ do
      spawn ("xbacklight -time 1 -steps 1 " ++ cmdKeys)
      v <- getBrightness ()
      notify "500" ("Brightness " ++ v)

setWindowBorder' :: String -> Window -> X ()
setWindowBorder' c w = do
    XConf { display = d } <- ask
    ~(Just pc) <- io $ initColor d c
    io $ setWindowBorder d w pc

data KeyboardLayout = KbdUs | KbdRu

stringOfKbdLayout KbdUs = "us"
stringOfKbdLayout KbdRu = "ru"

colorOfKbdLayout KbdUs = colorBorderUS
colorOfKbdLayout KbdRu = colorBorderRU

emacsKeyOfKbdLayout KbdUs = xK_F11
emacsKeyOfKbdLayout KbdRu = xK_F12

refreshKeyboardLayoutEmacs layout = do
  spawn $ "setxkbmap -layout " ++ stringOfKbdLayout KbdUs
  sendKey mod1Mask $ emacsKeyOfKbdLayout layout

refreshKeyboardLayoutOtherWindow layout =
    spawn $ "setxkbmap -layout " ++ stringOfKbdLayout layout

setKeyboardLayout currentKeyboardLayoutRef layout = do
  io $ writeIORef currentKeyboardLayoutRef layout
  withWindowSet $ \w -> case Win.peek w of
                          Nothing -> io $ return ()
                          Just w -> do
                            setWindowBorder' (colorOfKbdLayout layout) w
                            windowClass <- runQuery className w
                            case windowClass of
                              "Emacs" -> refreshKeyboardLayoutEmacs layout
                              _ -> refreshKeyboardLayoutOtherWindow layout

sendUpdateEvent :: () -> X ()
sendUpdateEvent _ =
    ask >>= logHook . config

setNewKeyboardLayout :: IORef KeyboardLayout -> KeyboardLayout -> X ()
setNewKeyboardLayout currentKeyboardLayoutRef layout =
    do
      setKeyboardLayout currentKeyboardLayoutRef layout
      sendUpdateEvent ()

loggerKbdLayout :: IORef KeyboardLayout -> XMonad.Util.Loggers.Logger
loggerKbdLayout currentKeyboardLayoutRef =
       io $ do layout <- readIORef currentKeyboardLayoutRef
               return $ Just $ case layout of
                                 KbdUs -> "<fc=white> ENG </fc>"
                                 KbdRu -> "<fc=white," ++ colorXmobarLayoutRuBG ++ "> RUS </fc>"

Ждём 0.2 се­кун­ды пе­ред бло­ки­ров­кой т.к. в ином слу­чае оно ино­гда не бло­ки­ру­ет. Пред­по­ло­жи­тель­но, xautolock по­лу­ча­ет со­бы­тия от кла­виа­ту­ры в мо­мент ло­ка и от­ме­ня­ет бло­ки­ров­ку.

lockScreen :: IORef KeyboardLayout -> X ()
lockScreen currentKeyboardLayoutRef =
    do
      setNewKeyboardLayout currentKeyboardLayoutRef KbdUs
      spawn "sleep 0.2; xautolock -locknow"

myConfig currentKeyboardLayoutRef = def {
             manageHook = manageDocks <+> myManageHook <+> manageHook gnomeConfig
           , layoutHook = avoidStruts $ smartBorders $ myLayouts
           , modMask = mod4Mask
           , terminal = "urxvtc"
           , startupHook = myStartupHook
           , borderWidth = 4
           , normalBorderColor  = colorBorderInactive
           , focusedBorderColor = colorBorderUS
           , handleEventHook    = fullscreenEventHook
           }
           `additionalKeys` [
                ((noModMask, xK_Menu), setNewKeyboardLayout currentKeyboardLayoutRef KbdUs)
                , ((controlMask, xK_Menu), setNewKeyboardLayout currentKeyboardLayoutRef KbdRu)
                , ((mod4Mask, xK_Print ), spawn "mate-screenshot -i") -- не работает именно на <Print> почему-то
                ]
           `additionalKeysP` [
                 ("M-n", spawn "firefox")
                , ("M-S-n c", spawn "google-chrome")
                , ("M-S-n i", spawn "google-chrome --incognito")
                , ("M-S-n f", spawn "firefox")

                -- other start
                , ("M-t", spawn "urxvtc")
                , ("M-e", spawn "editor")
                , ("S-M-<Space>", XMonad.Prompt.Shell.shellPrompt myPromptConfig)
                , ("M-S-s", XMonad.Prompt.Window.windowPromptGoto myPromptConfig)

                -- показать перевод слова из буфера обмена
                , ("M-S-t", spawn "sdcv -n \"`xclip -o | perl -C7 -pe 's/[^\\w-]//ig'`\" >/tmp/dict.txt 2>&1 ; editor /tmp/dict.txt")

                -- sound volume
                , ("<XF86AudioMute>", spawn "pactl -- set-sink-mute 0 toggle")
                , ("S-<XF86AudioMute>", setVolumeInterval "100%")
                , ("<XF86AudioLowerVolume>", setVolumeInterval "-2%")
                , ("<XF86AudioRaiseVolume>", setVolumeInterval "+2%")
                , ("S-<XF86AudioLowerVolume>", setVolumeInterval "-20%")
                , ("S-<XF86AudioRaiseVolume>", setVolumeInterval "+20%")

                -- LCD brightness
                , ("<XF86MonBrightnessUp>", setBrightness "-inc 10")
                , ("<XF86MonBrightnessDown>", setBrightness "-dec 10")
                , ("S-<XF86MonBrightnessUp>", setBrightness "-set 100")

                -- power management
                , ("<XF86Search>", lockScreen currentKeyboardLayoutRef)
                , ("<XF86HomePage>", lockScreen currentKeyboardLayoutRef)

                , ("M-f", withFocused $ windows . Win.sink)
                , ("M-d", sendMessage $ ToggleStrut L)

                , ("M-i d d q d", lockScreen currentKeyboardLayoutRef)

                , ("M-c c", spawn "emacsclient -c --eval '(org-capture)'")
                ]

xmobarPrinter :: IORef KeyboardLayout -> GHC.IO.Handle.Types.Handle -> PP
xmobarPrinter currentKeyboardLayoutRef handler
    = def {
        ppOutput   = hPutStrLn handler
      , ppTitle    = pangoColor colorXmobarTitle . pangoSanitize
      , ppExtras = [
         XMonad.Util.Loggers.logLayout
        , (loggerKbdLayout currentKeyboardLayoutRef)
        ]
      , ppOrder = \(workspace : _ : wintitle : layout : kbd : _ ) -> [kbd, workspace, layout, wintitle]
      , ppCurrent  = pangoColor colorXmobarWSActive . pangoSanitize
      , ppHidden   = pangoSanitize
      , ppHiddenNoWindows = pangoColor colorXmobarWSHiddenNoWindows . pangoSanitize
      , ppUrgent   = pangoColor "#E1340F"
      , ppLayout   = const ""
      , ppSep      = pangoColor colorXmobarSeparator " | "
      }

type WindowSwitchHook = Maybe Window -> Maybe Window -> X ()

windowSwitchHandler :: IORef (Maybe Window) -> WindowSwitchHook -> X () -> X ()
windowSwitchHandler lastWindowRef hook x = do
    withWindowSet $ \w -> do
        let currentWindow = Win.peek w
        lastWindow <- io $ readIORef lastWindowRef
        if lastWindow /= currentWindow then
            do
              io $ writeIORef lastWindowRef currentWindow
              hook lastWindow currentWindow
        else
            io $ return ()
    x

updateKbdLayout :: IORef KeyboardLayout -> Maybe Window -> Maybe Window -> X ()
updateKbdLayout currentKeyboardLayoutRef _ _ =
    do currentKeyboardLayout <- io $ readIORef currentKeyboardLayoutRef
       setKeyboardLayout currentKeyboardLayoutRef currentKeyboardLayout

main = do
  xmobarHdl <- spawnPipe "xmobar"
  do currentKeyboardLayoutRef <- newIORef KbdUs
     lastWindow <- newIORef Nothing
     xmonad (myConfig currentKeyboardLayoutRef) {
                  logHook = windowSwitchHandler lastWindow (updateKbdLayout currentKeyboardLayoutRef) $ dynamicLogWithPP (xmobarPrinter currentKeyboardLayoutRef xmobarHdl)
                }

pangoColor :: String -> String -> String
pangoColor fg = wrap left right
    where
      left  = "<fc=" ++ fg ++ ">"
      right = "</fc>"

pangoSanitize :: String -> String
pangoSanitize = Prelude.foldr sanitize ""
    where
      sanitize '>'  xs = "&gt;" ++ xs
      sanitize '<'  xs = "&lt;" ++ xs
      sanitize '\"' xs = "&quot;" ++ xs
      sanitize '&'  xs = "&amp;" ++ xs
      sanitize x    xs = x:xs

xmonad-session-rc

Основ­ное

#!/bin/bash

export PATH=~/bin/:"$PATH"

xrdb -merge <~/.Xresources
hsetroot -center ~/Pictures/wallpaper-cron.jpg


parcellite &
blueman-applet &
nm-applet &
pasystray &

eval $(/usr/bin/gnome-keyring-daemon --start --components=pkcs11,secrets,ssh)
export SSH_AUTH_SOCK

urxvtd &
emacs --daemon &
synclient VertEdgeScroll=1

run-parts ~/.xmonad/xmonad-session-local.d

Бло­ки­ров­ка эк­ра­на

Отка­зал­ся от xscreensaver в поль­зу xautolock+i3lock в свя­зи с тем, что xscreensaver не дру­жит с аль­тер­на­тив­ны­ми рас­клад­ка­ми кла­виа­ту­ры.

UPDATE [2019-02-04 Пн]: i3lock то­же не дру­жит, по­это­му те­перь сбра­сы­ваю рас­клад­ку пе­ред бло­ки­ров­кой.

xset dpms 180 300 360
xautolock -time 5 -locker 'setxkbmap -layout us; i3lock -f -c "#101010"' &

Рас­клад­ка кла­виа­ту­ры

Рас­клад­кой управ­ля­ет xmonad, за­дейст­во­ва­ны фи­зи­чес­кие кноп­ки:

  • Caps для пе­ре­клю­че­ния на en
  • Ctrl-Caps для пе­ре­клю­че­ния в ru

Но про­бле­ма в том, что нель­зя на­зна­чить на Caps ка­кие-ли­бо дейст­вия в кли­ент­ах, по­сколь­ку эта кноп­ка не до­ле­та­ет до них от X Server. Поэ­то­му я пе­ре­на­зна­чил по­ве­де­ние кноп­ки CapsLock на ге­не­ра­цию ко­да кноп­ки Menu, и xmonad об­ра­ба­ты­ва­ет эти кноп­ки уже как Menu и Ctrl-Menu.

Базо­вые на­ст­рой­ки, в т.ч. об­ну­ле­ние опций:

setxkbmap -option ''
setxkbmap -layout 'us,ru'

Наз­на­ча­ем XCompose на пра­вый Alt, за­став­ля­ем CapsLock ге­не­ри­ро­вать код Menu:

setxkbmap -option 'compose:ralt,caps:menu,grp_led:caps'

Разре­шить ру­ту хо­дить в наш X Server

xhost +si:localuser:root

xresources

Emacs.font: Monospace-10

Xcursor.size: 16

XTerm*background: black
XTerm*foreground: gray
XTerm*scrollTtyOutput: no
XTerm*scrollKey: yes

XTerm*faceSize: 10
Xft.antialias: true
Xft.dpi: 96
Xft.hinting: true
Xft.hintstyle: hintsfull
Xft.rgba: none

URxvt*geometry: 120x35
URxvt*font: xft:DejaVu Sans Mono-12
URxvt*background: black
URxvt*foreground: gray
URxvt*scrollstyle: rxvt
URxvt*termName: xterm
URxvt.perl-ext-common:  default,el-public-snippets,el-public-set-font-size
URxvt.urlLauncher:   chromium-browser
URxvt.matcher.button:   1
URxvt.colorUL:       #86a2be
URxvt*secondaryScroll: true
URxvt*fading: 40
URxvt*scrollBar: false

URxvt.keysym.C-Up:     perl:font-size:increase
URxvt.keysym.C-Down:   perl:font-size:decrease

URxvt*iso14755: False
URxvt*iso14755_52: False

! Home
URxvt*keysym.0xff50:    \033OH
! End
URxvt*keysym.0xff57:    \033OF

! Shift-PgUp
!URxvt*keysym.0xff55:      \033[5~
! Shift-PgDown
!URxvt*keysym.0xff56:      \033[6~

URxvt*keysym.0xffff:    \033[3~

*Rxvt*meta8: false
*Rxvt.keysym.C-Left: \033[1;5D
*Rxvt.keysym.C-Right: \033[1;5C
*Rxvt.keysym.M-Left: \033[1;3D
*Rxvt.keysym.M-Right: \033[1;3C

Однаж­ды ко мне по­до­шли и ска­за­ли, что в принте­ре в дру­гом кон­це зда­ния ле­жит мно­го лис­точ­ков с мо­ей фа­ми­ли­ей. Ока­за­лось, что urxvt при на­жа­тии кноп­ки PrintScreen пе­ча­та­ет всю ис­то­рию кон­со­ли. А я в тот день па­ру раз про­мах­нул­ся ми­мо Insert. Пришлось сроч­но за­де­лы­вать течь в безопас­нос­ти вот та­ким спо­со­бом.

URxvt*print-pipe: cat >/dev/null

Исход­ник статьи

Ссыл­ка для ска­чи­ва­ния: dotfiles.sorg.

Про­чее (tangle на сниппе­ты не вклю­чен)

crontab

Теку­щая по­го­да на кор­не­вом ок­не:

18 *            * * *           (wget -O ~/Pictures/wallpaper-cron.jpg http://images.opentopia.com/sunlight/world_sunlight_map_rectangular.jpg && DISPLAY=:0 hsetroot -center ~/Pictures/wallpaper-cron.jpg) >/dev/null 2>&1

Пере­ге­не­рить ssh-ко­нфиг из ку­соч­ков:

*/3 *           * * *           ~/bin/update-ssh-config >/dev/null 2>&1

Локаль­ная ко­пия по­чты:

*/10 *  * * *           (timeout -s9 120 ~/bin/offlineimap -o -1; notmuch new ) >/dev/null 2>&1

Про­ве­ря­ем об­но­вле­ния со­фта, со­зда­ем в org-mode таск:

0	6		* * *		~/bin/john_apt_check_updates >/dev/null 2>&1

Син­хро­ни­зи­ру­ем BBDB с google-кон­так­та­ми:

21 21   * * *   python ~/bin/ASynK/asynk.py --op=sync --name main >/dev/null 2>&1