wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

wmiirc.py (11618B)


      1 import datetime
      2 import operator
      3 import os
      4 import re
      5 import sys
      6 import traceback
      7 from threading import Thread, Timer
      8 
      9 import pygmi
     10 from pygmi import *
     11 from pygmi import event
     12 
     13 identity = lambda k: k
     14 
     15 # Begin Configuration
     16 #
     17 # Note: This file loads ~/.wmii/wmiirc_local.py if it exists.
     18 # Configuration should be placed in that file, and this file
     19 # left unmodified, if possible. wmiirc_local should import
     20 # wmiirc or any other modules it needs.
     21 #
     22 # Do *not* copy this file to wmiirc_local.py lest you want it
     23 # executed twice.
     24 
     25 # Keys
     26 keys.defs = dict(
     27     mod='Mod4',
     28     left='h',
     29     down='j',
     30     up='k',
     31     right='l')
     32 
     33 # Bars
     34 noticetimeout=5
     35 noticebar=('right', '!notice')
     36 
     37 # Theme
     38 background = '#333333'
     39 floatbackground='#222222'
     40 
     41 wmii['font'] = 'drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*'
     42 wmii['normcolors'] = '#000000', '#c1c48b', '#81654f'
     43 wmii['focuscolors'] = '#000000', '#81654f', '#000000'
     44 wmii['grabmod'] = keys.defs['mod']
     45 wmii['border'] = 2
     46 
     47 def setbackground(color):
     48     call('xsetroot', '-solid', color, background=True)
     49 setbackground(background)
     50 
     51 terminal = 'wmiir', 'setsid', '@TERMINAL@'
     52 pygmi.shell = os.environ.get('SHELL', 'sh')
     53 tray = 'witray',
     54 
     55 @defmonitor
     56 def load(self):
     57     return wmii.cache['normcolors'], re.sub(r'^.*: ', '', call('uptime')).replace(', ', ' ')
     58 @defmonitor
     59 def time(self):
     60     return wmii.cache['focuscolors'], datetime.datetime.now().strftime('%c')
     61 
     62 wmii.rules = (
     63     # Apps with system tray icons like to their main windows
     64     # Give them permission.
     65     (ur'^Pidgin:',       dict(allow='+activate')),
     66 
     67     # MPlayer and VLC don't float by default, but should.
     68     (ur'MPlayer|VLC',   dict(floating=True)),
     69 
     70     # ROX puts all of its windows in the same group, so they open
     71     # with the same tags.  Disable grouping for ROX Filer.
     72     (ur'^ROX-Filer:',   dict(group=0)),
     73 )
     74 
     75 def unresponsive_client(client):
     76     msg = 'The following client is not responding. What would you like to do?'
     77     resp = call('wihack', '-transient', str(client.id),
     78                 'xmessage', '-nearmouse', '-buttons', 'Kill,Wait', '-print',
     79                 '%s\n  %s' % (msg, client.label))
     80     if resp == 'Kill':
     81         client.slay()
     82 
     83 # End Configuration
     84 
     85 client.awrite('/event', 'Start wmiirc')
     86 
     87 tags = Tags()
     88 events.bind({
     89     ('Quit', Match('Start', 'wmiirc')): lambda *a: sys.exit(),
     90     'CreateTag':    tags.add,
     91     'DestroyTag':   tags.delete,
     92     'FocusTag':     tags.focus,
     93     'UnfocusTag':   tags.unfocus,
     94     'UrgentTag':    lambda args: tags.set_urgent(args.split()[1], True),
     95     'NotUrgentTag': lambda args: tags.set_urgent(args.split()[1], False),
     96 
     97     'AreaFocus':    lambda args: (args == '~' and
     98                                   (setbackground(floatbackground), True) or
     99                                   setbackground(background)),
    100 
    101     'Unresponsive': lambda args: Thread(target=unresponsive_client,
    102                                         args=(Client(args),)).start(),
    103 
    104     'Notice':       lambda args: notice.show(args),
    105 
    106     Match(('LeftBarClick', 'LeftBarDND'), 1): lambda e, b, tag: tags.select(tag),
    107     Match('LeftBarClick', 4): lambda *a: tags.select(tags.next(True)),
    108     Match('LeftBarClick', 5): lambda *a: tags.select(tags.next()),
    109 
    110     Match('LeftBarMouseDown', 3):   lambda e, n, tag: clickmenu((
    111             ('Delete',     lambda t: Tag(t).delete()),
    112         ), (tag,)),
    113     Match('ClientMouseDown', _, 3): lambda e, client, n: clickmenu((
    114             ('Delete',     lambda c: Client(c).kill()),
    115             ('Kill',       lambda c: Client(c).slay()),
    116             ('Fullscreen', lambda c: Client(c).set('Fullscreen', 'on')),
    117         ), (client,)),
    118 
    119     Match('ClientClick', _, 4): lambda e, c, n: Tag('sel').select('up'),
    120     Match('ClientClick', _, 5): lambda e, c, n: Tag('sel').select('down'),
    121 })
    122 
    123 @apply
    124 class Actions(event.Actions):
    125     def rehash(self, args=''):
    126         program_menu.choices = program_list(os.environ['PATH'].split(':'))
    127     def showkeys(self, args=''):
    128         message(keys.help)
    129     def quit(self, args=''):
    130         wmii.ctl('quit')
    131     def eval_(self, args=''):
    132         exec args
    133     def exec_(self, args=''):
    134         wmii['exec'] = args
    135     def exit(self, args=''):
    136         client.awrite('/event', 'Quit')
    137 
    138 program_menu = Menu(histfile='%s/history.progs' % confpath[0], nhist=5000,
    139                     action=curry(call, 'wmiir', 'setsid',
    140                                  pygmi.shell, '-c', background=True))
    141 action_menu = Menu(histfile='%s/history.actions' % confpath[0], nhist=500,
    142                    choices=lambda: Actions._choices,
    143                    action=Actions._call)
    144 tag_menu = Menu(histfile='%s/history.tags' % confpath[0], nhist=100,
    145                 choices=lambda: sorted(tags.tags.keys()))
    146 
    147 def clickmenu(choices, args):
    148     ClickMenu(choices=(k for k, v in choices),
    149               action=lambda choice: dict(choices).get(choice, identity)(*args)
    150              ).call()
    151 
    152 class Notice(Button):
    153     def __init__(self):
    154         super(Notice, self).__init__(*noticebar, colors=wmii.cache['normcolors'])
    155         self.timer = None
    156         self.show(' ')
    157 
    158     def tick(self):
    159         self.create(wmii.cache['normcolors'], ' ')
    160 
    161     def write(self, notice):
    162         client.awrite('/event', 'Notice %s' % notice.replace('\n', ' '))
    163 
    164     def show(self, notice):
    165         if self.timer:
    166             self.timer.cancel()
    167         self.create(wmii.cache['normcolors'], notice)
    168         self.timer = Timer(noticetimeout, self.tick)
    169         self.timer.start()
    170 notice = Notice()
    171 
    172 keys.bind('main', (
    173     "Moving around",
    174     ('%(mod)s-%(left)s',  "Select the client to the left",
    175         lambda k: Tag('sel').select('left')),
    176     ('%(mod)s-%(right)s', "Select the client to the right",
    177         lambda k: Tag('sel').select('right')),
    178     ('%(mod)s-%(up)s',    "Select the client above",
    179         lambda k: Tag('sel').select('up')),
    180     ('%(mod)s-%(down)s',  "Select the client below",
    181         lambda k: Tag('sel').select('down')),
    182 
    183     ('%(mod)s-space',     "Toggle between floating and managed layers",
    184         lambda k: Tag('sel').select('toggle')),
    185 
    186     "Moving through stacks",
    187     ('%(mod)s-Control-%(up)s',   "Select the stack above",
    188         lambda k: Tag('sel').select('up', stack=True)),
    189     ('%(mod)s-Control-%(down)s', "Select the stack below",
    190         lambda k: Tag('sel').select('down', stack=True)),
    191 
    192 
    193     "Moving clients around",
    194     ('%(mod)s-Shift-%(left)s',  "Move selected client to the left",
    195         lambda k: Tag('sel').send(Client('sel'), 'left')),
    196     ('%(mod)s-Shift-%(right)s', "Move selected client to the right",
    197         lambda k: Tag('sel').send(Client('sel'), 'right')),
    198     ('%(mod)s-Shift-%(up)s',    "Move selected client up",
    199         lambda k: Tag('sel').send(Client('sel'), 'up')),
    200     ('%(mod)s-Shift-%(down)s',  "Move selected client down",
    201         lambda k: Tag('sel').send(Client('sel'), 'down')),
    202 
    203     ('%(mod)s-Shift-space',     "Toggle selected client between floating and managed layers",
    204         lambda k: Tag('sel').send(Client('sel'), 'toggle')),
    205 
    206     "Client actions",
    207     ('%(mod)s-f',       "Toggle selected client's fullsceen state",
    208         lambda k: Client('sel').set('Fullscreen', 'toggle')),
    209     ('%(mod)s-Shift-c', "Close client",
    210         lambda k: Client('sel').kill()),
    211 
    212     "Changing column modes",
    213     ('%(mod)s-d', "Set column to default mode",
    214         lambda k: setattr(Tag('sel').selcol, 'mode', 'default-max')),
    215     ('%(mod)s-s', "Set column to stack mode",
    216         lambda k: setattr(Tag('sel').selcol, 'mode', 'stack-max')),
    217     ('%(mod)s-m', "Set column to max mode",
    218         lambda k: setattr(Tag('sel').selcol, 'mode', 'stack+max')),
    219 
    220     "Running programs",
    221     ('%(mod)s-a',      "Open wmii actions menu",
    222         lambda k: action_menu()),
    223     ('%(mod)s-p',      "Open program menu",
    224         lambda k: program_menu()),
    225 
    226     ('%(mod)s-Return', "Launch a terminal",
    227         lambda k: call(*terminal, background=True)),
    228 
    229     "Tag actions",
    230     ('%(mod)s-t',       "Change to another tag",
    231         lambda k: tags.select(tag_menu())),
    232     ('%(mod)s-Shift-t', "Retag the selected client",
    233         lambda k: setattr(Client('sel'), 'tags', tag_menu())),
    234 
    235     ('%(mod)s-n', "Move to the view to the left",
    236         lambda k: tags.select(tags.next())),
    237     ('%(mod)s-b', "Move to the view to the right",
    238         lambda k: tags.select(tags.next(True))),
    239     ('%(mod)s-Shift-n', "Move to the view to the left, take along current client",
    240         lambda k: tags.select(tags.next(), take_client=Client('sel'))),
    241     ('%(mod)s-Shift-b', "Move to the view to the right, take along current client",
    242         lambda k: tags.select(tags.next(True), take_client=Client('sel'))),
    243 
    244     ('%(mod)s-i', "Move to the newer tag in the tag stack",
    245         lambda k: tags.select(tags.NEXT)),
    246     ('%(mod)s-o', "Move to the older tag in the tag stack",
    247         lambda k: tags.select(tags.PREV)),
    248     ('%(mod)s-Shift-i', "Move to the newer tag in the tag stack, take along current client",
    249         lambda k: tags.select(tags.NEXT, take_client=Client('sel'))),
    250     ('%(mod)s-Shift-o', "Move to the older tag in the tag stack, take along current client",
    251         lambda k: tags.select(tags.PREV, take_client=Client('sel'))),
    252 
    253 ))
    254 def bind_num(i):
    255     keys.bind('main', (
    256         "Tag actions",
    257         ('%%(mod)s-%d' % i,       "Move to view '%d'" % i,
    258             lambda k: tags.select(str(i))),
    259         ('%%(mod)s-Shift-%d' % i, "Retag selected client with tag '%d'" % i,
    260             lambda k: setattr(Client('sel'), 'tags', i)),
    261     ))
    262 map(bind_num, range(0, 10))
    263 
    264 keys.bind('main', (
    265     "Changing modes",
    266     ('%(mod)s-Control-r', "Enter resize mode",
    267         lambda k: setattr(keys, 'mode', 'resize')),
    268     ('%(mod)s-Control-t', "Enter passthrough mode",
    269         lambda k: setattr(keys, 'mode', 'passthrough')),
    270 ));
    271 keys.bind('passthrough', (
    272     "Changing modes",
    273     ('%(mod)s-Control-t', "Leave passthrough mode",
    274         lambda k: setattr(keys, 'mode', 'main')),
    275 ));
    276 
    277 keys.bind('resize', (
    278     ('Escape', "Leave resize mode",
    279         lambda k: setattr(keys, 'mode', 'main')),
    280 ), import_={'main': ('%(mod)s-%(left)s', '%(mod)s-%(right)s',
    281                      '%(mod)s-%(up)s', '%(mod)s-%(down)s',
    282                      '%(mod)s-Space')})
    283 
    284 def addresize(mod, desc, cmd, *args):
    285     keys.bind('resize', (
    286         (mod + '%(left)s',  "%s selected client to the left" % desc,
    287             lambda k: Tag('sel').ctl(cmd, 'sel sel', 'left',
    288                                      *args)),
    289         (mod + '%(right)s', "%s selected client to the right" % desc,
    290             lambda k: Tag('sel').ctl(cmd, 'sel sel', 'right',
    291                                      *args)),
    292         (mod + '%(up)s',    "%s selected client up" % desc,
    293             lambda k: Tag('sel').ctl(cmd, 'sel sel', 'up',
    294                                      *args)),
    295         (mod + '%(down)s',  "%s selected client down" % desc,
    296             lambda k: Tag('sel').ctl(cmd, 'sel sel', 'down',
    297                                      *args)),
    298     ));
    299 addresize('',         'Grow', 'grow')
    300 addresize('Control-', 'Shrink', 'grow', '-1')
    301 addresize('Shift-',   'Nudge', 'nudge')
    302 
    303 Thread(target=lambda: Actions.rehash()).start()
    304 
    305 if not os.environ.get('WMII_NOPLUGINS', ''):
    306     dirs = filter(curry(os.access, _, os.R_OK),
    307                   ('%s/plugins' % dir for dir in confpath))
    308     files = filter(re.compile(r'\.py$').search,
    309                    reduce(operator.add, map(os.listdir, dirs), []))
    310     for f in ['wmiirc_local'] + ['plugins.%s' % file[:-3] for file in files]:
    311         try:
    312             __import__(f)
    313         except Exception, e:
    314             traceback.print_exc(sys.stdout)
    315 
    316 call(*tray, background=True)
    317 
    318 # vim:se sts=4 sw=4 et: