Tuesday, August 05, 2008

Xmonad: optimizing working space

Some later I wrote about Gnome and how to change default metacity wm (that very good and cannot be in use if you want to make your works faster) with windowmaker.
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:
Free Image Hosting at www.ImageShack.us

Thursday, July 31, 2008

ReadLeaf: research pane

Today, how I've wrote later, readleaf 0.1 was released and finished lazy stage - now it's a platform for research and implementation.
Both, MuiString and ReadLeaf has a one general idea, but MuiString is more deeper in implementation and some decisions need to be tested on other layer - quick and fast before this innovation will be implemented within MuiString and Jari overally.
ReadLeaf is a good start point for this type of research, I will use new way in development primary here - in area of the project.
I'm too lazy for repeations - you can see the general overview here - http://readleaf.berlios.de/concept.html
Currently already implemented:
  • redleafd
  • internal mm
  • basic research
Version 0.2 will be already with new features, now it's on the testing and development stage, but not so long from finish.

Monday, July 21, 2008

MuiString: Introduction to the architecture #1

Today we're have a many implementations of the microkernel architectures, but there are too similar and have many problems.
The first problem is a speed, the second one is a security.
Generally microkernel more secure than monolithic-based operating system, but with some restrictions, if you grow up speed, you are loosing in security.
But it's a general problems - more complicated thing - we're using C/C++/ObjC to implement architecture, and there are language takes a many time and limits in the implementing.
My general idea is to use a functional programming language - and I've choose scheme - it's better, simply.
Look for the block diagram:
MuiString separated to the three logical parts, the first low two layers implemented on C (or on Cext in future), the third layer implemented on C/Cext and on scheme itself.
All hardware specific things are hidden in the MuiString.
All services should be implemented on scheme, yes, we're can use C-written servers and libs (it should be done for liblinux/libposix), but in general case all other will be implemented on scheme and ran on scheme VM.
All IPC will be operate via scheme forms - in this case all security models will be implemented without IPC calls restrictions, and it can be made on the VM (or language) level.
There are several security models and several objects on the higher level:
  • Trusted servers
  • Key signed
  • Group politics
Trusted servers can allow secure transmit via determined servers, for example - you trust to the TCP/IP server, LAN server also trust it, but there are no directions from you to the LAN server, but LAN server and you can get access to execute scheme forms from/on LAN server, because there are trusted via TCP/IP server.

I will write more researching results in the next post.