I've decide reject Gnome and use pure wm without desktop environment, but also I've decided to try tiling wm written on Haskell - XMonad.
It's really usable, for indication I'm using a dzen2 and some set of handmade icons.
Here my ~/.xmonad/xmonad.hs
import XMonad hiding ((|||))
import XMonad.Operations
import System.Exit
import Graphics.X11
import System.IO
import qualified XMonad.StackSet as W
import qualified Data.Map as M
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.UrgencyHook
import XMonad.Layout.Tabbed
import XMonad.Layout.NoBorders
import XMonad.Hooks.ManageDocks
import XMonad.Layout.Grid
import XMonad.Layout.ToggleLayouts
import XMonad.Layout.LayoutCombinators ((|||))
import XMonad.Util.Run (spawnPipe)
------------------------------------------------------------------------
-- Common variables
--
myTerminal = "urxvt"
myBorderWidth = 1
myModMask = mod4Mask
myNumlockMask = mod2Mask
myWorkspaces = ["Shells","Emacs","WWW","IM", "Docs", "Media" ] ++ map show [6..9]
myNormalBorderColor = "#3e7464"
myFocusedBorderColor = "#0b610b"
myNormalBg = "#0b0b0b"
myNormalFg = "#385c64"
myFocusedBg = "#598867"
myFocusedFg = "#760202"
myFont = "-misc-*-*-*-*-*-10-*-*-*-*-*-*-*"
myBitmapsDir = "/home/tirra/Icons/"
myDefaultGaps :: [(Int,Int,Int,Int)]
myDefaultGaps = [(14,0,0,0)] -- 15 for default dzen
menuCmd = "dmenu -fn '" ++ myFont ++ "' -nb '" ++ myNormalBg ++ "' -nf '" ++ myNormalFg ++ "' -sb '" ++ myFocusedBg ++ "' -sf '" ++ myFocusedFg ++ "'"
dzen2 = "dzen2 -p -h 14 -ta l -bg '" ++ myNormalBg ++ "' -fg '" ++ myNormalFg ++ "' -w 600 -sa c -fn '" ++ myFont ++ "'"
------------------------------------------------------------------------
-- Key bindings. Add, modify or remove key bindings here.
--
myKeys conf@(XConfig {XMonad.modMask = modMask}) = M.fromList $
[ ((modMask, xK_r ), spawn ("exec `dmenu_path | " ++ menuCmd ++ "`"))
, ((modMask, xK_q ), kill)
, ((modMask .|. shiftMask, xK_a ), spawn "urxvt")
, ((modMask .|. shiftMask, xK_e ), spawn "emacs")
, ((modMask .|. shiftMask, xK_m ), spawn "urxvt -e mutt")
, ((modMask, xK_space ), sendMessage NextLayout)
, ((modMask .|. shiftMask, xK_space ), setLayout $ XMonad.layoutHook conf)
, ((modMask, xK_n ), refresh)
, ((modMask, xK_Tab ), windows W.focusDown)
, ((modMask, xK_j ), windows W.focusDown)
, ((modMask, xK_k ), windows W.focusUp )
, ((modMask, xK_m ), windows W.focusMaster )
, ((modMask, xK_Return), windows W.swapMaster)
, ((modMask .|. shiftMask, xK_j ), windows W.swapDown )
, ((modMask .|. shiftMask, xK_k ), windows W.swapUp )
, ((modMask, xK_h ), sendMessage Shrink)
, ((modMask, xK_l ), sendMessage Expand)
, ((modMask, xK_t ), withFocused $ windows . W.sink)
, ((modMask , xK_comma ), sendMessage (IncMasterN 1))
, ((modMask , xK_period), sendMessage (IncMasterN (-1)))
, ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess))
, ((modMask , xK_f), (sendMessage (Toggle "Full")) >> sendMessage (Toggle "Grid"))
, ((modMask .|. shiftMask, xK_r ),
broadcastMessage ReleaseResources >> restart "xmonad" True)
]
++
--
-- mod-[1..9], Switch to workspace N
-- mod-shift-[1..9], Move client to workspace N
--
[((m .|. modMask, k), windows $ f i)
| (i, k) <- zip (XMonad.workspaces conf) [xK_1 .. xK_9]
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
------------------------------------------------------------------------
-- Mouse bindings: default actions bound to mouse events
--
myMouseBindings (XConfig {XMonad.modMask = modMask}) = M.fromList $
-- mod-button1, Set the window to floating mode and move by dragging
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w))
-- mod-button2, Raise the window to the top of the stack
, ((modMask, button2), (\w -> focus w >> windows W.swapMaster))
-- mod-button3, Set the window to floating mode and resize by dragging
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w))
-- you may also bind events to the mouse scroll wheel (button4 and button5)
]
------------------------------------------------------------------------
-- Layouts:
-- You can specify and transform your layouts by modifying these values.
-- If you change layout bindings be sure to use 'mod-shift-space' after
-- restarting (with 'mod-q') to reset your layout state to the new
-- defaults, as xmonad preserves your old layout settings by default.
--
-- The available layouts. Note that each layout is separated by |||,
-- which denotes layout choice.
--
myTabConfig = defaultTheme {
activeColor = "#6666cc"
, activeBorderColor = "#000000"
, inactiveColor = "#666666"
, inactiveBorderColor = "#000000"
, decoHeight = 10
}
------------------------------------------------------------------------
-- Window rules:
myManageHook = composeAll [ className =? "Firefox-bin" --> doF(W.shift "WWW")
, className =? "Iceweasel" --> doF(W.shift "WWW")
, className =? "Gajim.py" --> doF(W.shift "IM")
, className =? "Evince" --> doF(W.shift "Docs")
, className =? "Emacs" --> doF(W.shift "Emacs")
, title =? "mutt" --> doF(W.shift "WWW")
, className =? "URxvt" --> doF(W.shift "Shell")
, className =? "Stardict" --> doF(W.shift "Docs")
]
------------------------------------------------------------------------
-- dzen2 configuration
--
myPP h = defaultPP {
ppOutput = hPutStrLn h
, ppCurrent = dzenColor myFocusedFg myFocusedBg . pad
, ppVisible = dzenColor myNormalFg myNormalBg . pad
, ppTitle = dzenColor "#117418" "" . wrap "{ " " }"
, ppHiddenNoWindows = const ""
, ppWsSep = " ^fg(#113d41)^r(1x12)^fg() "
, ppSep = " ^fg(#113d41)^r(2x2)^fg() " -- "|"
-- , ppLayout = dzenColor "#946144" "" .
-- (\x -> case x of
-- "Tall" -> "^i(/home/dk/.icons/dzen2/tall.xbm)"
-- "Mirror Tall" -> "^i(/home/dk/.icons/dzen2/mtall.xbm)"
-- "Full" -> "^i(/home/dk/.icons/dzen2/full.xbm)"
-- )
}
myUrgencyHook = withUrgencyHook dzenUrgencyHook
{ args = ["-bg", myNormalBg, "-fg", "#117418"] }
------------------------------------------------------------------------
-- Run xmonad with the settings you specify. No need to modify this.
--
main = do din <- spawnPipe dzen2
xmonad $ myUrgencyHook $
defaultConfig {
-- simple stuff
terminal = myTerminal,
-- focusFollowsMouse = myFocusFollowsMouse,
borderWidth = myBorderWidth,
modMask = myModMask,
numlockMask = myNumlockMask,
workspaces = myWorkspaces,
normalBorderColor = myNormalBorderColor,
focusedBorderColor = myFocusedBorderColor,
defaultGaps = myDefaultGaps,
-- key bindings
keys = myKeys,
mouseBindings = myMouseBindings,
-- hooks, layouts
layoutHook = toggleLayouts (noBorders Full) $
smartBorders $ tiled ||| Mirror tiled ||| Full ||| Grid ||| tabbed shrinkText myTabConfig,
manageHook = (manageHook defaultConfig <+> myManageHook) <+> manageDocks,
logHook = dynamicLogWithPP $ myPP din
}
where
tiled = Tall 1 0.03 0.68
There are many tuning for yourself can be made. For dzen2 I wrote two files - one of this just runs it, other is a daemon that takes information and provide it to dzen2 panel.
Main file - ~/.xmonad/panel.sh
#!/bin/bash
export LANG=en_US.UTF-8
FONT='-misc-*-*-*-*-*-10-*-*-*-*-*-*-*'
DZEN="dzen2 -p -h 14 -fn $FONT"
BG="#0b0b0b"
FG="#385c64"
MONITOR=/home/tirra/.xmonad/monitor.pl
SW=1280
Y=0
X=600
$MONITOR | $DZEN -e '' -x $X -y $Y -ta r -bg $BG -w $(($SW-$X)) -fg $FG -sa c
And ~/.xmonad/monitor.pl where you need to made some modifications:
#!/usr/bin/perl
use strict;
###
# Configurable constants
#
## Xbm icons directory
use constant XBM_IDIR => '/home/tirra/Icons';
## Colors, separators and other stuff of this kind
use constant FG_COLOR => '#385c64';
use constant BG_COLOR => '#010101';
use constant TITLE_COLOR => '#946144';
use constant ELEM_COLOR => '#5c0000';
use constant ICON_COLOR => '#00baff';
use constant VSEP => '^fg(#113d41)^r(1x12)^fg()';
use constant ISEP => '^p(6)^fg(#113d41)^r(2x2)^fg()^p(6)';
## IDLE time
use constant IDLE => 1;
## Constants for CPU load measurment
use constant FSTAT => '/proc/stat';
use constant NCPUS => 2; # number of CPUs in system
## Battery constants
use constant BAT_STATUS => '/sys/class/power_supply/BAT1/status';
use constant BAT_CHARGE_NOW => '/sys/class/power_supply/BAT1/charge_now';
use constant BAT_CHARGE_FULL => '/sys/class/power_supply/BAT1/charge_full';
use constant BAT_FULL_ICON => XBM_IDIR . '/b_full.xbm';
use constant BAT_CRITICAL_ICON => XBM_IDIR . '/b_critical.xbm';
use constant BAT_EMPTY_ICON => XBM_IDIR . '/b_empty.xbm';
use constant BAT_25_ICON => XBM_IDIR . '/b_25.xbm';
use constant BAT_50_ICON => XBM_IDIR . '/b_50.xbm';
use constant BAT_75_ICON => XBM_IDIR . '/b_75.xbm';
use constant BAT_CHRG_ICON => XBM_IDIR . '/ac_01.xbm';
## Constants for memory monitoring
use constant MEMINFO => '/proc/meminfo';
## Net interface constants
use constant ROUTE => '/sbin/route';
use constant NETPATH => '/sys/class/net/';
use constant NET_UP_ICON => XBM_IDIR . '/arr_up.xbm';
use constant NET_DOWN_ICON => XBM_IDIR . '/arr_down.xbm';
use constant NET_PPPDOWN_ICON => XBM_IDIR . '/ppp_down.xbm';
use constant NET_PPPUP_ICON => XBM_IDIR . '/ppp_up.xbm';
use constant NET_ETHDOWN_ICON => XBM_IDIR . '/eth_down.xbm';
use constant NET_ETHUP_ICON => XBM_IDIR . '/eth_up.xbm';
use constant NET_WLANDOWN_ICON => XBM_IDIR . '/wlan_down.xbm';
use constant NET_WLANUP_ICON => XBM_IDIR . '/wlan_up.xbm';
## Jabber constatns
use constant JNAME => 'IM';
use constant ACOLOR => '#117418';
## Weather constants
use constant WTITLE => 'Out';
use constant WID => 'yourweatheridhere';
use constant WUPD_INT => 30 * 60;
######
# Cpu load measurment in percents.
#
my @cpus = undef;
my $stat_fh = undef;
sub init_cpus()
{
for (my $i = 0; $i < NCPUS; $i++) {
$cpus[$i] = { 'old_idle' => 1, 'old_load' => 1 };
}
open($stat_fh, '<', FSTAT) or die "Can't open file " . FSTAT . " for reading $!";
}
sub get_cpus_load_perc()
{
my @cpus_load = undef;
for (my $i = 0; $i < NCPUS; $i++) {
while (<$stat_fh>) {
next if (!/^cpu$i.+/);
# /proc/stat should contain all data about CPU in the following
# format(all data about particular CPU is written in one line):
# cpu:
#
my @load = split /\s/;
my $idle_time = $load[4];
my $load_time = $load[1] + $load[2] + $load[3] + $load[5] + $load[6] + $load[7];
$cpus_load[$i] = int(($load_time - $cpus[$i]->{'old_load'}) /
($idle_time + $load_time - $cpus[$i]->{'old_idle'} -
$cpus[$i]->{'old_load'}) * 100);
$cpus[$i]->{'old_idle'} = $idle_time;
$cpus[$i]->{'old_load'} = $load_time;
last if ($i++ == NCPUS);
}
}
die "Can't make seek on " . FSTAT . "$!" if (seek($stat_fh, 0, 0) < 0);
return @cpus_load;
}
sub show_cpu_info()
{
my @cpu_info = get_cpus_load_perc();
my $tmp = "";
print "^fg(" . TITLE_COLOR . ")CPU:^fg()^p(6)";
for (my $i = 0; $i < NCPUS; $i++) {
$tmp .= "$cpu_info[$i]%";
$tmp .= " " . VSEP . " " if ($i + 1 != NCPUS);
}
print $tmp;
}
######
# Laptop's battery state
#
# returns an array of two elements
# where first element is a current power of battery in percens
# and the second one is a boolean value showing if the battry is
# charged or not.
sub get_battery_state()
{
my @bat_state = undef;
my $bat_max = 0;
my $bat_cur = 0;
open(BFC, '<', BAT_CHARGE_FULL) or die "Can't open file " . BAT_CHARGE_FULL . "for reading $!";
while () {
$bat_max = $_;
last;
}
close(BFC);
open(BFC, '<', BAT_CHARGE_NOW) or die "Can't open file " . BAT_CHARGE_NOW . "for reading $!";
while () {
$bat_cur = $_;
last;
}
close(BFC);
$bat_state[0] = int($bat_cur / $bat_max * 100);
open(BFC, '<', BAT_STATUS) or die "Can't open file " . BAT_STATUS . "for reading $!";
while () {
$bat_state[1] = $_;
last;
}
close(BFC);
return @bat_state;
}
sub show_battery_info()
{
my @binfo = get_battery_state();
my $icon = BAT_EMPTY_ICON;
if($binfo[0] <= 1) {
$icon = BAT_EMPTY_ICON;
}
elsif($binfo[0] <= 10) {
$icon = BAT_CRITICAL_ICON;
}
elsif($binfo[0] <= 25) {
$icon = BAT_25_ICON;
}
elsif($binfo[0] <=50) {
$icon = BAT_50_ICON;
}
elsif($binfo[0] < 99) {
$icon = BAT_75_ICON;
}
elsif($binfo[0] >= 99) {
$icon = BAT_FULL_ICON;
}
print "^fg(" . ICON_COLOR . ")^i($icon)^fg()";
}
######
# Memory usage monitor.
#
sub get_meminfo()
{
my ($mtotal, $mactive, $stotal, $sfree, $cached) = (0, 0, 0, 0, 0);
my %minfo = undef;
open(MIF, '<', MEMINFO) or die "Can't open file " . MEMINFO . " for reading $!";
while () {
if (/^MemTotal:\s+(\d+).*$/) {
$mtotal = $1;
}
elsif (/^Active:\s+(\d+).*$/) {
$mactive = $1;
}
elsif (/^SwapTotal:\s+(\d+).*$/) {
$stotal = $1;
}
elsif (/^SwapFree:\s+(\d+).*$/) {
$sfree = $1;
}
elsif (/^Buffers:\s+(\d+).*$/) {
$cached = $1;
}
}
close(MIF);
$minfo{'in_use'} = int($mactive / $mtotal * 100);
$minfo{'cached'} = int($cached / $mtotal * 100);
$minfo{'swap'} = int(($stotal - $sfree) / $stotal * 100);
return %minfo;
}
sub show_mem_info()
{
my %minfo = get_meminfo();
print "^fg(" . ICON_COLOR . ")MEM:^fg() ";
print $minfo{'in_use'} . "% ";
}
######
# Network monitor.
#
my @old_netstat = (0, 0);
my $network_avail = 0;
# Get current speed of the interface and
# quantoty of received and transmitted trough it bytes
sub get_net_info(@_)
{
my $ifname = shift;
my $file = NETPATH . "/$ifname/statistics/rx_bytes";
my @stat = undef;
open(RXF, '<', $file) or die "Can't open file $file for reading $!";
my $rx_bytes =;
close(RXF);
$file = NETPATH . "/$ifname/statistics/tx_bytes";
open(TXF, '<', $file) or die "Can't open file $file for reading $!";
my $tx_bytes =;
close(TXF);
$stat[0] = int(($rx_bytes - $old_netstat[0]) / 1024);
$stat[1] = int(($tx_bytes - $old_netstat[1]) / 1024);
$old_netstat[0] = $rx_bytes;
$old_netstat[1] = $tx_bytes;
return @stat;
}
sub show_net_info()
{
my $cmd = ROUTE . " -n | grep -e '^0.0.0.0' | awk '{print \$8}'";
my $iface = `$cmd`;
my $iconppp = NET_PPPDOWN_ICON;
my $iconwlan = NET_WLANDOWN_ICON;
my $iconeth = NET_ETHDOWN_ICON;
chomp($iface);
if ($iface eq 'ppp0') {
$iconppp = NET_PPPUP_ICON;
}
if ($iface eq 'wlan00') {
$iconwlan = NET_WLANUP_ICON;
}
if ($iface eq 'eth0') {
$iconeth = NET_ETHUP_ICON;
}
if ($iface eq '') {
$network_avail = 0;
@old_netstat = (0, 0);
print " ^fg(" . ICON_COLOR . ")^i($iconeth)^fg()";
print " ^fg(" . ICON_COLOR . ")^i($iconwlan)^fg()";
print " ^fg(" . ICON_COLOR . ")^i($iconppp)^fg()";
return;
}
my @stat = get_net_info($iface);
$network_avail = 1;
print " ^fg(" . ICON_COLOR . ")^i($iconeth)^fg()";
print " ^fg(" . ICON_COLOR . ")^i($iconwlan)^fg()";
print " ^fg(" . ICON_COLOR . ")^i($iconppp)^fg()";
# print "^fg(" . TITLE_COLOR . ")$iface:^fg()^p(6)$stat[0]";
# print "^fg(" . ELEM_COLOR . ")^i(" . NET_DOWN_ICON . ")^fg() ";
# print VSEP . " $stat[1]^fg(" . ELEM_COLOR . ")^i(" . NET_UP_ICON . ")^fg()";
}
####
# Jabber monitor.
#
sub get_nmessages()
{
my @procs = split('\n', `ps aux | grep gajim`);
my $found = 0;
foreach (@procs) {
next if (/.*?\s+e?grep.*$/);
$found = 1;
last;
}
if ($found) {
my $num = `gajim-remote get_unread_msgs_number`;
chomp($num);
return $num;
}
return '-';
}
sub show_jabberinfo()
{
my $nmsg = get_nmessages();
print "^fg(" . ICON_COLOR . ")" . JNAME . ":^fg()^p(6)";
if (($nmsg ne '-') && ($nmsg > 0)) {
print "^fg(" . ACOLOR . ")$nmsg^fg()";
}
else {
print "$nmsg";
}
}
###
# Weather monitor
#
my $last_wupd_time = 0;
my $last_temp = undef;
sub get_temperature()
{
my $cmd = 'weather -i ' . WID;
my @data = split(/\n/, `$cmd`);
my $temp = undef;
foreach (@data) {
next until(/\s+Temperature:.+?\((-?\d+)\s+C\).*$/);
$temp = "$1";
$temp = "+$temp" if ($1 > 0);
$temp .= "C";
last;
}
return $temp;
}
sub show_temperature()
{
my $time = time();
my $temp = undef;
if (!$network_avail) {
$temp = '-';
}
elsif (($time - $last_wupd_time) >= WUPD_INT) {
$temp = get_temperature();
$last_temp = $temp;
$last_wupd_time = $time;
}
else {
$temp = $last_temp;
}
print "^fg(" . ICON_COLOR . ")" . WTITLE . ":^fg()^p(6)$temp";
}
####
# Time
#
sub show_time()
{
my $time = `date +'%H:%M'`;
chomp($time);
print $time;
}
my $old_fh = select(STDOUT);
$| = 1;
select($old_fh);
init_cpus();
for (;;) {
print ISEP;
show_time();
print ISEP;
show_jabberinfo();
print ISEP;
show_temperature();
print ISEP;
show_mem_info();
print ISEP;
show_battery_info();
show_net_info();
print "\n";
sleep(IDLE);
}
There are my Icons : Icons.tar.gz
And this is a snap of my workspace: