Add python wmiirc/9P client library

This commit is contained in:
Kris Maglione 2009-05-17 14:15:08 -04:00
parent 69aec39fa1
commit 9ff6791633
25 changed files with 2039 additions and 30 deletions

View File

@ -1,7 +1,7 @@
syntax: regexp
(^|/)\.((.*\.)?sw.|depend|hgignore)$
(^|/)(tags|mkfile)$
\.([oOa]|o_pic|so|orig|bak)$
\.([oOa]|o_pic|so|orig|bak|pyc|pyo)$
^cmd/(stfo|osd|wiwarp)(/|$)
syntax: glob
config.local.mk

View File

@ -0,0 +1,47 @@
import os
from pyxp import Client
if 'WMII_ADDRESS' in os.environ:
client = Client(os.environ['WMII_ADDRESS'])
else:
client = Client(namespace='wmii')
def call(*args, **kwargs):
background = kwargs.pop('background', False)
input = kwargs.pop('input', None)
import subprocess
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, cwd=os.environ['HOME'],
**kwargs)
if not background:
return p.communicate(input)[0].rstrip('\n')
def program_list(path):
names = []
for d in path:
try:
for f in os.listdir(d):
if f not in names and os.access('%s/%s' % (d, f),
os.X_OK):
names.append(f)
except Exception:
pass
return sorted(names)
def curry(func, *args, **kwargs):
def curried(*newargs, **newkwargs):
return func(*(args + newargs), **dict(kwargs, **newkwargs))
curried.__name__ = func.__name__ + '__curried__'
return curried
from pygmi import events, fs, menu, monitor
from pygmi.events import *
from pygmi.fs import *
from pygmi.menu import *
from pygmi.monitor import *
__all__ = (fs.__all__ + monitor.__all__ + events.__all__ +
menu.__all__ + ('client', 'call', 'curry', 'program_list'))
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,95 @@
import os
import re
import sys
import traceback
from pygmi import monitor, client, call, program_list
__all__ = ('bind_keys', 'bind_events', 'toggle_keys', 'event_loop',
'event')
keydefs = {}
keys = {}
events = {}
alive = True
def flatten(items):
for k, v in items.iteritems():
if not isinstance(k, (list, tuple)):
k = k,
for key in k:
yield key, v
def bind_keys(items):
for k, v in flatten(items):
keys[k % keydefs] = v
def bind_events(items):
for k, v in flatten(items):
events[k] = v
def event(fn):
bind_events({fn.__name__: fn})
@event
def Key(args):
if args in keys:
keys[args](args)
keys_enabled = False
keys_restore = None
def toggle_keys(on=None, restore=None):
if on is None:
on = not keys_enabled
keys_restore = restore
if on:
client.write('/keys', '\n'.join(keys.keys()))
else:
client.write('/keys', restore or ' ')
def dispatch(event, args=''):
if event in events:
try:
events[event](args)
except Exception, e:
traceback.print_exc(sys.stderr)
def event_loop():
from pygmi import events
toggle_keys(on=True)
for line in client.readlines('/event'):
if not events.alive:
break
dispatch(*line.split(' ', 1))
events.alive = False
class Actions(object):
which = call('which', 'which')
def __getattr__(self, name):
if name.startswith('_') or name.endswith('_'):
raise AttributeError()
if hasattr(self, name + '_'):
return getattr(self, name + '_')
def action(args=''):
cmd = call(self.which, name,
env=dict(os.environ, PATH=':'.join(confpath)))
call(shell, '-c', '$* %s' % args, '--', cmd,
background=True)
return action
def _call(self, args):
a = args.split(' ')
if a:
getattr(self, a[0])(*a[1:])
@property
def _choices(self):
return sorted(
program_list(confpath) +
[re.sub('_$', '', k) for k in dir(self)
if not re.match('^_', k) and callable(getattr(self, k))])
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,588 @@
import collections
from pyxp import *
from pyxp.client import *
from pygmi import *
__all__ = ('wmii', 'Tags', 'Tag', 'Area', 'Frame', 'Client',
'Button', 'Colors', 'Color')
class Ctl(object):
sentinel = {}
ctl_types = {}
ctl_hasid = False
def __init__(self):
pass
def ctl(self, msg):
client.write(self.ctl_path, msg)
def __getitem__(self, key):
for line in self.ctl_lines():
key_, rest = line.split(' ', 1)
if key_ == key:
if key in self.ctl_types:
return self.ctl_types[key][0](rest)
return rest
raise KeyError()
def __hasitem__(self, key):
return key in self.keys()
def __setitem__(self, key, val):
assert '\n' not in key
if key in self.ctl_types:
val = self.ctl_types[key][1](val)
self.ctl('%s %s\n' % (key, val))
def get(self, key, default=sentinel):
try:
val = self[key]
except KeyError, e:
if default is not self.sentinel:
return default
raise e
def set(self, key, val):
self[key] = val
def keys(self):
return [line.split(' ', 1)[0]
for line in self.ctl_lines()]
def iteritems(self):
return (tuple(line.split(' ', 1))
for line in self.ctl_lines())
def items(self):
return [tuple(line.split(' ', 1))
for line in self.ctl_lines()]
def ctl_lines(self):
lines = tuple(client.readlines(self.ctl_path))
if self.ctl_hasid:
lines = lines[1:]
return lines[:-1]
_id = None
@property
def id(self):
if self._id is None and self.ctl_hasid:
return client.read(self.ctl_path).split('\n', 1)[0]
return self._id
class Dir(Ctl):
ctl_hasid = True
def __init__(self, id):
if id != 'sel':
self._id = id
def __eq__(self, other):
return (self.__class__ == other.__class__ and
self.id == other.id)
class ctl_property(object):
def __init__(self, key):
self.key = key
def __get__(self, dir, cls):
return dir[self.key]
def __set__(self, dir, val):
dir[self.key] = val
class toggle_property(ctl_property):
props = {
'on': True,
'off': False,
}
def __get__(self, dir, cls):
val = dir[self.key]
if val in self.props:
return self.props[val]
return val
def __set__(self, dir, val):
for k, v in self.props.iteritems():
if v == val:
val = k
break
dir[self.key] = val
class file_property(object):
def __init__(self, name, writable=False):
self.name = name
self.writable = writable
def __get__(self, dir, cls):
return client.read('%s/%s' % (dir.path, self.name))
def __set__(self, dir, val):
if not self.writable:
raise NotImplementedError('File %s is not writable' % self.name)
return client.write('%s/%s' % (dir.path, self.name), val)
@property
def ctl_path(self):
return '%s/ctl' % self.path
@property
def path(self):
return '%s/%s' % (self.base_path, self._id or 'sel')
@classmethod
def all(cls):
return (cls(s.name)
for s in client.readdir(cls.base_path)
if s.name != 'sel')
def __repr__(self):
return '%s(%s)' % (self.__class__.__name__,
repr(self._id or 'sel'))
class Client(Dir):
base_path = '/client'
fullscreen = Dir.toggle_property('Fullscreen')
urgent = Dir.toggle_property('Urgent')
label = Dir.file_property('label', writable=True)
tags = Dir.file_property('tags', writable=True)
props = Dir.file_property('props')
def kill(self):
self.ctl('kill')
def slay(self):
self.ctl('slay')
class liveprop(object):
def __init__(self, get):
self.get = get
self.attr = str(self)
def __get__(self, area, cls):
if getattr(area, self.attr, None) is not None:
return getattr(area, self.attr)
return self.get(area)
def __set__(self, area, val):
setattr(area, self.attr, val)
class Area(object):
def __init__(self, tag, ord, offset=None, width=None, height=None, frames=None):
self.tag = tag
self.ord = str(ord)
self.offset = offset
self.width = width
self.height = height
self.frames = frames
def prop(key):
@liveprop
def prop(self):
for area in self.tag.index:
if str(area.ord) == str(self.ord):
return getattr(area, key)
return prop
offset = prop('offset')
width = prop('width')
height = prop('height')
frames = prop('frames')
def _get_mode(self):
for k, v in self.tag.iteritems():
if k == 'colmode':
v = v.split(' ')
if v[0] == self.ord:
return v[1]
mode = property(
_get_mode,
lambda self, val: self.tag.set('colmode %s' % self.ord, val))
def grow(self, dir, amount=None):
self.tag.grow(self, dir, amount)
def nudge(self, dir, amount=None):
self.tag.nudge(self, dir, amount)
class Frame(object):
live = False
def __init__(self, client, area=None, ord=None, offset=None, height=None):
self.client = client
self.ord = ord
self.offset = offset
self.height = height
@property
def width(self):
return self.area.width
def prop(key):
@liveprop
def prop(self):
for area in self.tag.index:
for frame in area.frames:
if frame.client == self.client:
return getattr(frame, key)
return prop
offset = prop('area')
offset = prop('ord')
offset = prop('offset')
height = prop('height')
def grow(self, dir, amount=None):
self.area.tag.grow(self, dir, amount)
def nudge(self, dir, amount=None):
self.area.tag.nudge(self, dir, amount)
class Tag(Dir):
base_path = '/tag'
@classmethod
def framespec(cls, frame):
if isinstance(frame, Frame):
frame = frame.client
if isinstance(frame, Area):
frame = (frame.ord, 'sel')
if isinstance(frame, Client):
if frame._id is None:
return 'sel sel'
return 'client %s' % frame.id
elif isinstance(frame, basestring):
return frame
else:
return '%s %s' % tuple(map(str, frame))
def dirspec(cls, dir):
if isinstance(dir, tuple):
dir = ' '.join(dir)
return dir
def _set_selected(self, frame):
if not isinstance(frame, basestring) or ' ' not in frame:
frame = self.framespec(frame)
self['select'] = frame
selected = property(lambda self: tuple(self['select'].split(' ')),
_set_selected)
def _get_selclient(self):
for k, v in self.iteritems():
if k == 'select' and 'client' in v:
return Client(v.split(' ')[1])
return None
selclient = property(_get_selclient,
lambda self, val: self.set('select',
self.framespec(val)))
@property
def selcol(self):
return Area(self, self.selected[0])
@property
def index(self):
areas = []
for l in [l.split(' ')
for l in client.readlines('%s/index' % self.path)
if l]:
if l[0] == '#':
if l[1] == '~':
area = Area(tag=self, ord=l[1], width=l[2], height=l[3],
frames=[])
else:
area = Area(tag=self, ord=l[1], offset=l[2], width=l[3],
frames=[])
areas.append(area)
i = 0
else:
area.frames.append(
Frame(client=Client(l[1]), area=area, ord=i,
offset=l[2], height=l[3]))
i += 1
return areas
def delete(self):
id = self.id
for a in self.index:
for f in a.frames:
if f.client.tags == id:
f.client.kill()
else:
f.client.tags = '-%s' % id
if self == Tag('sel'):
Tags.instance.select(Tags.instance.next())
def select(self, frame, stack=False):
self['select'] = '%s %s' % (
self.framespec(frame),
stack and 'stack' or '')
def send(self, src, dest, stack=False, cmd='send'):
if isinstance(src, tuple):
src = ' '.join(src)
if isinstance(src, Frame):
src = src.client
if isinstance(src, Client):
src = src._id or 'sel'
if isinstance(dest, tuple):
dest = ' '.join(dest)
self[cmd] = '%s %s' % (src, dest)
def swap(self, src, dest):
self.send(src, dest, cmd='swap')
def nudge(self, frame, dir, amount=None):
frame = self.framespec(frame)
self['nudge'] = '%s %s %s' % (frame, dir, str(amount or ''))
def grow(self, frame, dir, amount=None):
frame = self.framespec(frame)
self['grow'] = '%s %s %s' % (frame, dir, str(amount or ''))
class Button(object):
sides = {
'left': 'lbar',
'right': 'rbar',
}
def __init__(self, side, name, colors=None, label=None):
self.side = side
self.name = name
self.base_path = self.sides[side]
self.path = '%s/%s' % (self.base_path, self.name)
self.create(colors, label)
def create(self, colors=None, label=None):
with client.create(self.path, OWRITE) as f:
if colors or label:
f.write(self.getval(colors, label))
def remove(self):
client.remove(self.path)
def getval(self, colors=None, label=None):
if colors is None:
colors = self.colors
if label is None:
label = self.label
return ' '.join([Color(c).hex for c in colors] + [label])
colors = property(
lambda self: tuple(map(Color, client.read(self.path).split(' ')[:3])),
lambda self, val: client.write(self.path, self.getval(colors=val)))
label = property(
lambda self: client.read(self.path).split(' ', 3)[3],
lambda self, val: client.write(self.path, self.getval(label=val)))
@classmethod
def all(cls, side):
return (Button(side, s.name)
for s in client.readdir(cls.sides[side])
if s.name != 'sel')
class Colors(object):
def __init__(self, foreground=None, background=None, border=None):
vals = foreground, background, border
self.vals = tuple(map(Color, vals))
@classmethod
def from_string(cls, val):
return cls(*val.split(' '))
def __getitem__(self, key):
if isinstance(key, basestring):
key = {'foreground': 0, 'background': 1, 'border': 2}[key]
return self.vals[key]
def __str__(self):
return str(unicode(self))
def __unicode__(self):
return ' '.join(c.hex for c in self.vals)
def __repr__(self):
return 'Colors(%s, %s, %s)' % tuple(repr(c.rgb) for c in self.vals)
class Color(object):
def __init__(self, colors):
if isinstance(colors, Color):
colors = colors.rgb
elif isinstance(colors, basestring):
match = re.match(r'^#(..)(..)(..)$', colors)
colors = tuple(int(match.group(group), 16) for group in range(1, 4))
def toint(val):
if isinstance(val, float):
val = int(255 * val)
assert 0 <= val <= 255
return val
self.rgb = tuple(map(toint, colors))
def __getitem__(self, key):
if isinstance(key, basestring):
key = {'red': 0, 'green': 1, 'blue': 2}[key]
return self.rgb[key]
@property
def hex(self):
return '#%02x%02x%02x' % self.rgb
def __str__(self):
return str(unicode(self))
def __unicode__(self):
return 'rgb(%d, %d, %d)' % self.rgb
def __repr__(self):
return 'Color(%s)' % repr(self.rgb)
class Rules(collections.MutableMapping):
regex = re.compile(r'^\s*/(.*?)/\s*(?:->)?\s*(.*)$')
def __get__(self, obj, cls):
return self
def __set__(self, obj, val):
self.setitems(val)
def __init__(self, path, rules=None):
self.path = path
if rules:
self.setitems(rules)
def __getitem__(self, key):
for k, v in self.iteritems():
if k == key:
return v
raise KeyError()
def __setitem__(self, key, val):
items = []
for k, v in self.iteritems():
if key == k:
v = val
key = None
items.append((k, v))
if key is not None:
items.append((key, val))
self.setitems(items)
def __delitem__(self, key):
self.setitems((k, v) for k, v in self.iteritems() if k != key)
def __len__(self):
return len(tuple(self.iteritems()))
def __iter__(self):
for k, v in self.iteritems():
yield k
def __list__(self):
return list(iter(self))
def __tuple__(self):
return tuple(iter(self))
def append(self, item):
self.setitems(self + (item,))
def __add__(self, items):
return tuple(self.iteritems()) + tuple(items)
def setitems(self, items):
lines = []
for k, v in items:
assert '/' not in k and '\n' not in v
lines.append('/%s/ -> %s' % (k, v))
lines.append('')
client.write(self.path, '\n'.join(lines))
def iteritems(self):
for line in client.readlines(self.path):
match = self.regex.match(line)
if match:
yield match.groups()
def items(self):
return list(self.iteritems())
@apply
class wmii(Ctl):
ctl_path = '/ctl'
ctl_types = {
'normcolors': (Colors.from_string, lambda c: str(Colors(c))),
'focuscolors': (Colors.from_string, lambda c: str(Colors(c))),
'border': (int, str),
}
clients = property(lambda self: Client.all())
tags = property(lambda self: Tag.all())
lbuttons = property(lambda self: Button.all('left'))
rbuttons = property(lambda self: Button.all('right'))
tagrules = Rules('/tagrules')
colrules = Rules('/colrules')
class Tags(object):
PREV = []
NEXT = []
def __init__(self, normcol=None, focuscol=None):
self.tags = {}
self.sel = None
self.normcol = normcol or wmii['normcolors']
self.focuscol = focuscol or wmii['focuscolors']
for t in wmii.tags:
self.add(t.id)
for b in wmii.lbuttons:
if b.name not in self.tags:
b.remove()
self.focus(Tag('sel').id)
self.mru = [self.sel.id]
self.idx = -1
Tags.instance = self
def add(self, tag):
self.tags[tag] = Tag(tag)
self.tags[tag].button = Button('left', tag, self.normcol, tag)
def delete(self, tag):
self.tags.pop(tag).button.remove()
def focus(self, tag):
self.sel = self.tags[tag]
self.sel.button.colors = self.focuscol
def unfocus(self, tag):
self.tags[tag].button.colors = self.normcol
def set_urgent(self, tag, urgent=True):
self.tags[tag].button.label = urgent and '*' + tag or tag
def next(self, reverse=False):
tags = list(wmii.tags)
tags.append(tags[0])
if reverse:
tags.reverse()
for i in range(0, len(tags)):
if tags[i] == self.sel:
return tags[i+1]
return self.sel
def select(self, tag):
if tag is self.PREV:
self.idx -= 1
elif tag is self.NEXT:
self.idx += 1
else:
if isinstance(tag, Tag):
tag = tag.id
wmii['view'] = tag
if tag != self.mru[-1]:
self.mru.append(tag)
self.mru = self.mru[-10:]
return
self.idx = min(-1, max(-len(self.mru), self.idx))
wmii['view'] = self.mru[self.idx]
if __name__ == '__main__':
c = Client('sel')
#print c.id
#print c.items()
#print c.urgent
#print list(wmii.clients)
#print list(wmii.tags)
#print [a.frames for a in Tag('sel').index]
#print Tag('sel').selclient
#print Tag('sel').selclient.label
#print Tag('sel').selclient.tags
#print Tag('sel').selclient.props
#a = Area(Tag('sel'), 1)
#print a.width
#print a.frames
#print [[c.hex for c in b.colors] for b in wmii.lbuttons]
#print [[c.hex for c in b.colors] for b in wmii.rbuttons]
Button('left', '1').colors = ((0., 0., 0.), (1., 1., 1.), (0., 0., 0.))
Button('left', '1').label = 'foo'
Button('left', '5', label='baz')
print repr(wmii['normcolors'])
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,58 @@
from pygmi import call
__all__ = 'Menu', 'ClickMenu'
def inthread(fn, action):
def run():
res = fn()
if action:
return action(res)
return res
if not action:
return run()
from threading import Thread
Thread(target=run).start()
class Menu(object):
def __init__(self, choices=(), action=None,
histfile=None, nhist=None):
self.choices = choices
self.action = action
self.histfile = histfile
self.nhist = nhist
def call(self, choices=None):
if choices is None:
choices = self.choices
if callable(choices):
choices = choices()
def act():
args = ['wimenu']
if self.histfile:
args += ['-h', self.histfile]
if self.nhist:
args += ['-n', self.nhist]
return call(*map(str, args), input='\n'.join(choices))
return inthread(act, self.action)
class ClickMenu(object):
def __init__(self, choices=(), action=None,
histfile=None, nhist=None):
self.choices = choices
self.action = action
self.prev = None
def call(self, choices=None):
if choices is None:
choices = self.choices
if callable(choices):
choices = choices()
def act():
args = ['wmii9menu']
if self.prev:
args += ['-i', self.prev]
args += ['--'] + list(choices)
return call(*map(str, args)).replace('\n', '')
return inthread(act, self.action)
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,86 @@
from pygmi import client
from pygmi.fs import *
__all__ = 'monitors', 'defmonitor', 'Monitor'
monitors = {}
def defmonitor(*args, **kwargs):
def monitor(fn):
kwargs['action'] = fn
if not args and 'name' not in kwargs:
kwargs['name'] = fn.__name__
monitor = Monitor(*args, **kwargs)
monitors[monitor.name] = monitor
return monitor
if args and callable(args[0]):
fn = args[0]
args = args[1:]
return monitor(fn)
return monitor
class MonitorBase(type):
def __new__(cls, name, bases, attrs):
new_cls = super(MonitorBase, cls).__new__(cls, name, bases, attrs)
if name not in attrs:
new_cls.name = new_cls.__name__.lower()
try:
Monitor
if new_cls.name not in monitors:
monitors[new_cls.name] = new_cls()
except Exception, e:
pass
return new_cls
class Monitor(object):
side = 'right'
interval = 1.0
def __init__(self, name=None, interval=None, side=None,
action=None):
if side:
self.side = side
if name:
self.name = name
if interval:
self.interval = interval
if action:
self.action = action
self.button = Button(self.side, self.name)
self.tick()
def tick(self):
from pygmi import events
if not events.alive:
if client:
self.button.remove()
return
if self.active:
from threading import Timer
label = self.getlabel()
if isinstance(label, basestring):
label = None, label
self.button.create(*label)
self.timer = Timer(self.interval, self.tick)
self.timer.start()
def getlabel(self):
if self.action:
return self.action()
return ()
_active = True
def _set_active(self, val):
self._active = bool(val)
if val:
self.tick()
else:
self.button.remove()
active = property(
lambda self: self._active,
_set_active)
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,7 @@
from pyxp.client import Client
from pyxp.dial import dial
from pyxp.types import Qid, Stat
VERSION = '9P2000'
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,328 @@
# Copyright (C) 2007 Kris Maglione
# See PERMISSIONS
import operator
import os
import re
import sys
from threading import *
import traceback
import pyxp
from pyxp import fcall, fields
from pyxp.mux import Mux
from pyxp.types import *
if os.environ.get('NAMESPACE', None):
namespace = os.environ['NAMESPACE']
else:
try:
namespace = '/tmp/ns.%s.%s' % (
os.environ['USER'],
re.sub(r'\.0$', '', os.environ['DISPLAY']))
except Exception:
pass
NAMESPACE = namespace
OREAD = 0x00
OWRITE = 0x01
ORDWR = 0x02
OEXEC = 0x03
OEXCL = 0x04
OTRUNC = 0x10
OREXEC = 0x20
ORCLOSE = 0x40
OAPPEND = 0x80
ROOT_FID = 0
class ProtocolException(Exception):
pass
class RPCError(Exception):
pass
class Client(object):
ROOT_FID = 0
def __enter__(self):
return self
def __exit__(self, *args):
self.cleanup()
def __init__(self, conn=None, namespace=None, root=None):
if not conn and namespace:
conn = 'unix!%s/%s' % (NAMESPACE, namespace)
try:
self.lastfid = ROOT_FID
self.fids = []
self.files = {}
self.lock = RLock()
def process(data):
return fcall.Fcall.unmarshall(data)[1]
self.mux = Mux(conn, process, maxtag=256)
resp = self.dorpc(fcall.Tversion(version=pyxp.VERSION, msize=65535))
if resp.version != pyxp.VERSION:
raise ProtocolException, "Can't speak 9P version '%s'" % resp.version
self.msize = resp.msize
self.dorpc(fcall.Tattach(fid=ROOT_FID, afid=fcall.NO_FID,
uname=os.environ['USER'], aname=''))
if root:
path = self.splitpath(root)
resp = self.dorpc(fcall.Twalk(fid=ROOT_FID,
newfid=ROOT_FID,
wname=path))
except Exception, e:
traceback.print_exc(sys.stdout)
if getattr(self, 'mux', None):
self.mux.fd.close()
raise e
def cleanup(self):
try:
for f in self.files:
f.close()
finally:
self.mux.fd.close()
self.mux = None
def dorpc(self, req):
resp = self.mux.rpc(req)
if isinstance(resp, fcall.Rerror):
raise RPCError, "RPC returned error (%s): %s" % (
req.__class__.__name__, resp.ename)
if req.type != resp.type ^ 1:
raise ProtocolException, "Missmatched RPC message types: %s => %s" % (
req.__class__.__name__, resp.__class__.__name__)
return resp
def splitpath(self, path):
return [v for v in path.split('/') if v != '']
def getfid(self):
with self.lock:
if len(self.fids):
return self.fids.pop()
else:
self.lastfid += 1
return self.lastfid
def putfid(self, fid):
with self.lock:
self.files.pop(fid)
self.fids.append(fid)
def clunk(self, fid):
try:
self.dorpc(fcall.Tclunk(fid=fid))
finally:
self.putfid(fid)
def walk(self, path):
fid = self.getfid()
ofid = ROOT_FID
while True:
self.dorpc(fcall.Twalk(fid=ofid, newfid=fid,
wname=path[0:fcall.MAX_WELEM]))
path = path[fcall.MAX_WELEM:]
ofid = fid
if len(path) == 0:
break
@apply
class Res:
def __enter__(self):
return fid
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
self.clunk(fid)
return Res
def _open(self, path, mode, open):
resp = None
with self.walk(path) as nfid:
fid = nfid
resp = self.dorpc(open(fid))
def cleanup():
self.clunk(fid)
file = File(self, resp, fid, mode, cleanup)
self.files[fid] = file
return file
def open(self, path, mode = OREAD):
path = self.splitpath(path)
def open(fid):
return fcall.Topen(fid=fid, mode=mode)
return self._open(path, mode, open)
def create(self, path, mode = OREAD, perm = 0):
path = self.splitpath(path)
name = path.pop()
def open(fid):
return fcall.Tcreate(fid=fid, mode=mode, name=name, perm=perm)
return self._open(path, mode, open)
def remove(self, path):
path = self.splitpath(path)
with self.walk(path) as fid:
self.dorpc(fcall.Tremove(fid=fid))
def stat(self, path):
path = self.splitpath(path)
try:
with self.walk(path) as fid:
resp = self.dorpc(fcall.Tstat(fid= fid))
st = resp.stat()
self.clunk(fid)
return st
except RPCError:
return None
def read(self, path, *args, **kwargs):
with self.open(path) as f:
return f.read(*args, **kwargs)
def readlines(self, path, *args, **kwargs):
with self.open(path) as f:
for l in f.readlines(*args, **kwargs):
yield l
def readdir(self, path, *args, **kwargs):
with self.open(path) as f:
for s in f.readdir(*args, **kwargs):
yield s
def write(self, path, *args, **kwargs):
with self.open(path, OWRITE) as f:
return f.write(*args, **kwargs)
class File(object):
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def __init__(self, client, fcall, fid, mode, cleanup):
self.lock = RLock()
self.client = client
self.fid = fid
self.cleanup = cleanup
self.mode = mode
self.iounit = fcall.iounit
self.qid = fcall.qid
self.offset = 0
self.fd = None
def dorpc(self, fcall):
if hasattr(fcall, 'fid'):
fcall.fid = self.fid
return self.client.dorpc(fcall)
def stat(self):
resp = self.dorpc(fcall.Tstat())
return resp.stat
def read(self, count=None, offset=None, buf=''):
if count is None:
count = self.iounit
res = []
with self.lock:
offs = self.offset
if offset is not None:
offs = offset
while count > 0:
n = min(count, self.iounit)
count -= n
resp = self.dorpc(fcall.Tread(offset=offs, count=n))
data = resp.data
offs += len(data)
res.append(data)
if len(data) < n:
break
if offset is None:
self.offset = offs
res = ''.join(res)
if len(res) > 0:
return res
def readlines(self):
last = None
while True:
data = self.read()
if not data:
break
lines = data.split('\n')
for i in range(0, len(lines) - 1):
yield lines[i]
last = lines[-1]
if last:
yield last
def write(self, data, offset=None):
if offset is None:
offset = self.offset
off = 0
with self.lock:
offs = self.offset
if offset is not None:
offs = offset
while off < len(data):
n = min(len(data), self.iounit)
resp = self.dorpc(fcall.Twrite(offset=offs,
data=data[off:off+n]))
off += resp.count
offs += resp.count
if resp.count < n:
break
if offset is None:
self.offset = offs
return off
def readdir(self):
if not self.qid.type & Qid.QTDIR:
raise Exception, "Can only call readdir on a directory"
off = 0
while True:
data = self.read(self.iounit, off)
if not data:
break
off += len(data)
for s in Stat.unmarshall_list(data):
yield s
def close(self):
try:
if self.fd:
self.fd_close()
finally:
self.cleanup()
self.tg = None
self.fid = None
self.client = None
self.qid = None
def remove(self):
try:
self.dorpc(fcall.Tremove())
finally:
try:
self.close()
except Exception:
pass
def fd_close(self):
try:
self.fd.close()
finally:
self.fd = None
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,35 @@
from socket import *
__all__ = 'dial',
def dial_unix(address):
sock = socket(AF_UNIX, SOCK_STREAM, 0)
sock.connect(address)
return sock
def dial_tcp(host):
host = host.split('!')
if len(host) != 2:
return
host, port = host
res = getaddrinfo(host, port, AF_INET, SOCK_STREAM, 0, AI_PASSIVE)
for family, socktype, protocol, name, addr in res:
try:
sock = socket(family, socktype, protocol)
sock.connect(addr)
return sock
except error:
if sock:
sock.close()
def dial(address):
proto, address = address.split('!', 1)
if proto == 'unix':
return dial_unix(address)
elif proto == 'tcp':
return dial_tcp(address)
else:
raise Exception('invalid protocol')
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,131 @@
from pyxp.messages import MessageBase, Message
from pyxp.fields import *
from types import Qid, Stat
__all__ = 'Fcall',
NO_FID = 1<<32 - 1
MAX_WELEM = 16
class FcallBase(MessageBase):
idx = 99
def __new__(cls, name, bases, attrs):
new_cls = super(FcallBase, cls).__new__(cls, name, bases, attrs)
new_cls.type = FcallBase.idx
if new_cls.type > 99:
new_cls.types[new_cls.type] = new_cls
FcallBase.idx += 1
return new_cls
class Fcall(Message):
__metaclass__ = FcallBase
types = {}
def response(self, *args, **kwargs):
assert self.type % 2 == 0, "No respense type for response fcalls"
kwargs['tag'] = self.tag
return self.types[self.type + 1]()
@classmethod
def unmarshall(cls, data, offset=0):
res = super(Fcall, cls).unmarshall(data, offset)
if cls.type < 100:
res = cls.types[res[1].type].unmarshall(data, offset)
return res
size = Size(4, 4)
type = Int(1)
tag = Int(2)
class Tversion(Fcall):
msize = Int(4)
version = String()
class Rversion(Fcall):
msize = Int(4)
version = String()
class Tauth(Fcall):
afid = Int(4)
uname = String()
aname = String()
class Rauth(Fcall):
aqid = Qid.field()
class Tattach(Fcall):
fid = Int(4)
afid = Int(4)
uname = String()
aname = String()
class Rattach(Fcall):
qid = Qid.field()
class Terror(Fcall):
def __init__(self):
raise Error("Illegal 9P tag 'Terror' encountered")
class Rerror(Fcall):
ename = String()
class Tflush(Fcall):
oldtag = Int(2)
class Rflush(Fcall):
pass
class Twalk(Fcall):
fid = Int(4)
newfid = Int(4)
wname = Array(2, String())
class Rwalk(Fcall):
wqid = Array(2, Qid.field())
class Topen(Fcall):
fid = Int(4)
mode = Int(1)
class Ropen(Fcall):
qid = Qid.field()
iounit = Int(4)
class Tcreate(Fcall):
fid = Int(4)
name = String()
perm = Int(4)
mode = Int(1)
class Rcreate(Fcall):
qid = Qid.field()
iounit = Int(4)
class Tread(Fcall):
fid = Int(4)
offset = Int(8)
count = Int(4)
class Rread(Fcall):
data = Data(4)
class Twrite(Fcall):
fid = Int(4)
offset = Int(8)
data = Data(4)
class Rwrite(Fcall):
count = Int(4)
class Tclunk(Fcall):
fid = Int(4)
class Rclunk(Fcall):
pass
class Tremove(Tclunk):
pass
class Rremove(Fcall):
pass
class Tstat(Tclunk):
pass
class Rstat(Fcall):
sstat = Size(2)
stat = Stat.field()
class Twstat(Rstat):
pass
class Rwstat(Fcall):
pass
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,123 @@
from datetime import datetime
import operator
class Field(object):
idx = 0
def __init__(self):
Field.idx += 1
self.id = Field.idx
def repr(self):
return self.__class__.__name__
def __repr__(self):
if hasattr(self, 'name'):
return '<Field %s "%s">' % (self.repr(), self.name)
return super(Field, self).__repr__()
class Int(Field):
encoders = {}
decoders = {}
@classmethod
def encoder(cls, n):
if n not in cls.encoders:
exec ('def enc(n):\n' +
' assert n == n & 0x%s, "Arithmetic overflow"\n' % ('ff' * n) +
' return "".join((' + ','.join(
'chr((n >> %d) & 0xff)' % (i * 8)
for i in range(0, n)) + ',))\n')
cls.encoders[n] = enc
return cls.encoders[n]
@classmethod
def decoder(cls, n):
if n not in cls.decoders:
cls.decoders[n] = eval('lambda data, offset: ' + '|'.join(
'ord(data[offset + %d]) << %d' % (i, i * 8)
for i in range(0, n)))
return cls.decoders[n]
def __init__(self, size):
super(Int, self).__init__()
self.size = size
self.encode = self.encoder(size)
self.decode = self.decoder(size)
if self.__class__ == Int:
self.marshall = self.encode
def unmarshall(self, data, offset):
return self.size, self.decode(data, offset)
def marshall(self, val):
return self.encode(val)
def repr(self):
return '%s(%d)' % (self.__class__.__name__, self.size)
class Size(Int):
def __init__(self, size, extra=0):
super(Size, self).__init__(size)
self.extra = extra
def marshall(self, val):
return lambda vals, i: self.encode(
reduce(lambda n, i: n + len(vals[i]),
range(i + 1, len(vals)),
self.extra))
class Date(Int):
def __init__(self):
super(Date, self).__init__(4)
def unmarshall(self, data, offset):
val = self.decode(data, offset)
return 4, datetime.fromtimestamp(val)
def marshall(self, val):
return self.encode(int(val.strftime('%s')))
# To do: use unicode strings, ensure UTF-8.
# Not a problem in Python 3K, but there the other
# data blobs are.
class String(Int):
def __init__(self, size=2):
super(String, self).__init__(size)
def unmarshall(self, data, offset):
n = self.decode(data, offset)
offset += self.size
assert offset + n <= len(data), "String too long to unpack"
return self.size + n, data[offset:offset + n]
def marshall(self, val):
return [self.encode(len(val)), val]
class Data(String):
pass
class Array(Int):
def __init__(self, size, spec):
super(Array, self).__init__(size)
self.spec = spec
def unmarshall(self, data, offset):
start = offset
n = self.decode(data, offset)
offset += self.size
res = []
for i in range(0, n):
size, val = self.spec.unmarshall(data, offset)
if isinstance(val, list):
res += val
else:
res.append(val)
offset += size
return offset - start, res
def marshall(self, vals):
res = [self.encode(len(vals))]
for val in vals:
val = self.spec.marshall(val)
if isinstance(val, list):
res += val
else:
res.append(val)
return res
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,73 @@
from pyxp.fields import *
class MessageBase(type):
idx = 0
def __new__(cls, name, bases, attrs):
fields = []
fieldmap = {}
for k, v in attrs.items():
if isinstance(v, Field):
attrs[k] = None
fields.append(v)
fieldmap[k] = v
v.name = k
fields.sort(lambda a, b: cmp(a.id, b.id))
new_cls = super(MessageBase, cls).__new__(cls, name, bases, attrs)
map = getattr(new_cls, 'fieldmap', {})
map.update(fieldmap)
new_cls.fields = getattr(new_cls, 'fields', ()) + tuple(fields)
new_cls.fieldmap = map
for f in fields:
f.message = new_cls
return new_cls
class Message(object):
__metaclass__ = MessageBase
def __init__(self, *args, **kwargs):
if args:
args = dict(zip([f.name for f in self.fields], args))
args.update(kwargs)
kwargs = args;
for k, v in kwargs.iteritems():
assert k in self.fieldmap, "Invalid keyword argument"
setattr(self, k, v)
@classmethod
def field(cls):
class MessageField(Field):
def repr(self):
return cls.__name__
def unmarshall(self, data, offset):
return cls.unmarshall(data, offset)
def marshall(self, val):
return val.marshall()
return MessageField()
@classmethod
def unmarshall(cls, data, offset=0):
vals = {}
start = offset
for field in cls.fields:
size, val = field.unmarshall(data, offset)
offset += size
vals[field.name] = val
return offset - start, cls(**vals)
def marshall(self):
res = []
callbacks = []
for field in self.fields:
val = field.marshall(getattr(self, field.name, None))
if callable(val):
callbacks.append((val, len(res)))
if isinstance(val, list):
res += val
else:
res.append(val)
for fn, i in reversed(callbacks):
res[i] = fn(res, i)
return res
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,163 @@
# Derived from libmux, available in Plan 9 under /sys/src/libmux
# under the following terms:
#
# Copyright (C) 2003-2006 Russ Cox, Massachusetts Institute of Technology
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
from pyxp import fields
from pyxp.dial import dial
from threading import *
Condition = Condition().__class__
__all__ = 'Mux',
class Mux(object):
def __init__(self, con, process, mintag=0, maxtag=1<<16 - 1):
self.queue = set()
self.lock = RLock()
self.rendez = Condition(self.lock)
self.outlock = RLock()
self.inlock = RLock()
self.process = process
self.wait = {}
self.free = set(range(mintag, maxtag))
self.mintag = mintag
self.maxtag = maxtag
self.muxer = None
if isinstance(con, basestring):
con = dial(con)
self.fd = con
if self.fd is None:
raise Exception("No connection")
def rpc(self, dat):
r = self.newrpc(dat)
try:
self.lock.acquire()
while self.muxer and self.muxer != r and r.data is None:
r.wait()
if r.data is None:
if self.muxer and self.muxer != r:
self.fail()
self.muxer = r
self.lock.release()
try:
while r.data is None:
data = self.recv()
if data is None:
self.lock.acquire()
self.queue.remove(r)
raise Exception("unexpected eof")
self.dispatch(data)
self.lock.acquire()
finally:
self.electmuxer()
self.puttag(r)
except Exception, e:
import sys
import traceback
traceback.print_exc(sys.stdout)
print e
finally:
if self.lock._is_owned():
self.lock.release()
return r.data
def electmuxer(self):
for rpc in self.queue:
if self.muxer != rpc and rpc.async == False:
self.muxer = rpc
rpc.notify()
return
self.muxer = None
def dispatch(self, dat):
tag = dat.tag - self.mintag
r = None
with self.lock:
r = self.wait.get(tag, None)
if r is None or r not in self.queue:
print "bad rpc tag: %u (no one waiting on it)" % dat.tag
return
self.queue.remove(r)
r.data = dat
r.notify()
def gettag(self, r):
tag = 0
while not self.free:
self.rendez.wait()
tag = self.free.pop()
if tag in self.wait:
raise Exception("nwait botch")
self.wait[tag] = r
r.tag = tag
r.data.tag = r.tag
r.data = None
return r.tag
def puttag(self, r):
t = r.tag
if self.wait.get(t, None) != r:
self.fail()
del self.wait[t]
self.free.add(t)
self.rendez.notify()
def send(self, dat):
data = ''.join(dat.marshall())
n = self.fd.send(data)
return n == len(data)
def recv(self):
data = self.fd.recv(4)
if data:
len = fields.Int.decoders[4](data, 0)
data += self.fd.recv(len - 4)
return self.process(data)
def fail():
raise Exception()
def newrpc(self, dat):
rpc = Rpc(self, dat)
tag = None
with self.lock:
self.gettag(rpc)
self.queue.add(rpc)
if rpc.tag >= 0 and self.send(dat):
return rpc
with self.lock:
self.queue.remove(rpc)
self.puttag(rpc)
class Rpc(Condition):
def __init__(self, mux, data):
super(Rpc, self).__init__(mux.lock)
self.mux = mux
self.data = data
self.waiting = True
self.async = False
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,55 @@
from pyxp.messages import Message
from pyxp.fields import *
__all__ = 'Qid', 'Stat'
class Qid(Message):
QTFILE = 0x00
QTLINK = 0x01
QTSYMLINK = 0x02
QTTMP = 0x04
QTAUTH = 0x08
QTMOUNT = 0x10
QTEXCL = 0x20
QTAPPEND = 0x40
QTDIR = 0x80
type = Int(1)
version = Int(4)
path = Int(8)
class Stat(Message):
DMDIR = 0x80000000
DMAPPEND = 0x40000000
DMEXCL = 0x20000000
DMMOUNT = 0x10000000
DMAUTH = 0x08000000
DMTMP = 0x04000000
DMSYMLINK = 0x02000000
DMDEVICE = 0x00800000
DMNAMEDPIPE = 0x00200000
DMSOCKET = 0x00100000
DMSETUID = 0x00080000
DMSETGID = 0x00040000
@classmethod
def unmarshall_list(cls, data, offset=0):
while offset < len(data):
n, stat = cls.unmarshall(data, offset)
offset += n
yield stat
size = Size(2)
type = Int(2)
dev = Int(4)
qid = Qid.field()
mode = Int(4)
atime = Date()
mtime = Date()
length = Int(8)
name = String()
uid = String()
gid = String()
muid = String()
# vim:se sts=4 sw=4 et:

View File

@ -0,0 +1,208 @@
#!/usr/bin/env python
import os
import re
import sys
from pygmi import *
from pygmi import events
identity = lambda k: k
# Keys
events.keydefs = dict(
mod='Mod4',
left='h',
down='j',
up='k',
right='l')
# Bars
noticetimeout=5
noticebar=('right', '!notice')
# Theme
background = '#333333'
floatbackground='#222222'
wmii.font = 'drift,-*-fixed-*-*-*-*-9-*-*-*-*-*-*-*'
wmii.normcolors = '#000000', '#c1c48b', '#81654f'
wmii.focuscolors = '#000000', '#81654f', '#000000'
wmii.grabmod = events.keydefs['mod']
wmii.border = 2
def setbackground(color):
call('xsetroot', '-solid', color)
setbackground(background)
terminal = 'wmiir', 'setsid', 'xterm'
shell = os.environ.get('SHELL', 'sh')
@defmonitor
def load():
return re.sub(r'^.*: ', '', call('uptime')).replace(', ', ' ')
@defmonitor
def time():
from datetime import datetime
return datetime.now().strftime('%c')
wmii.colrules = (
('gimp', '17+83+41'),
('.*', '62+38 # Golden Ratio'),
)
wmii.tagrules = (
('MPlayer|VLC', '~'),
)
def unresponsive_client(client):
msg = 'The following client is not responding. What would you like to do?'
resp = call('wihack', '-transient', client.id,
'xmessage', '-nearmouse', '-buttons', 'Kill,Wait', '-print',
'%s\n %s' % (client, client.label))
if resp == 'Kill':
client.slay()
# End Configuration
confpath = os.environ['WMII_CONFPATH'].split(':')
events.confpath = confpath
events.shell = shell
client.write('/event', 'Start wmiirc')
tags = Tags()
bind_events({
'Quit': lambda args: sys.exit(),
'Start': lambda args: args == 'wmiirc' and sys.exit(),
'CreateTag': tags.add,
'DestroyTag': tags.delete,
'FocusTag': tags.focus,
'UnfocusTag': tags.unfocus,
'UrgentTag': lambda args: tags.set_urgent(args.split(' ')[1], True),
'NotUrgentTag': lambda args: tags.set_urgent(args.split(' ')[1], False),
'AreaFocus': lambda args: (args == '~' and
(setbackground(floatbackground), True) or
setbackground(background)),
'Unresponsive': lambda args: Thread(target=unresponsive_client,
args=(Client(args),)).start(),
'Notice': lambda args: notice.show(args),
('LeftBarClick', 'LeftBarDND'):
lambda args: args.split(' ')[0] == '1' and tags.select(args.split(' ', 1)[1]),
'ClientMouseDown': lambda args: menu(*args.split(' '), type='client'),
'LeftBarMouseDown': lambda args: menu(*reversed(args.split(' ')), type='lbar'),
})
@apply
class Actions(events.Actions):
def rehash(self, args=''):
program_menu.choices = program_list(os.environ['PATH'].split(':'))
def quit(self, args=''):
wmii.ctl('quit')
def eval_(self, args=''):
exec args
def exec_(self, args=''):
wmii['exec'] = args
def exit(self, args=''):
client.write('/event', 'Quit')
program_menu = Menu(histfile='%s/history.prog' % confpath[0], nhist=5000,
action=curry(call, 'wmiir', 'setsid',
shell, '-c', background=True))
action_menu = Menu(histfile='%s/history.action' % confpath[0], nhist=500,
choices=lambda: Actions._choices,
action=Actions._call)
tag_menu = Menu(histfile='%s/history.tags' % confpath[0], nhist=100,
choices=lambda: sorted(tags.tags.keys()))
def menu(target, button, type):
MENUS = {
('client', '3'): (
('Delete', lambda c: Client(c).kill()),
('Kill', lambda c: Client(c).slay()),
('Fullscreen', lambda c: Client(c).set('Fullscreen', 'on'))),
('lbar', '3'): (
('Delete', lambda t: Tag(t).delete())),
}
choices = MENUS.get((type, button), None)
if choices:
ClickMenu(choices=(k for k, v in choices),
action=lambda k: dict(choices).get(k, identity)(target)
).call()
class Notice(Button):
def __init__(self):
super(Notice, self).__init__(*noticebar)
self.timer = None
def tick(self):
self.label = ''
def show(self, notice):
if self.timer:
self.timer.stop()
self.label = notice
from threading import Timer
self.timer = Timer(noticetimeout, self.tick)
self.timer.start()
notice = Notice()
bind_keys({
'%(mod)s-Control-t': lambda k: events.toggle_keys(restore='%(mod)s-Control-t'),
'%(mod)s-%(left)s': lambda k: Tag('sel').select('left'),
'%(mod)s-%(right)s': lambda k: Tag('sel').select('right'),
'%(mod)s-%(up)s': lambda k: Tag('sel').select('up'),
'%(mod)s-%(down)s': lambda k: Tag('sel').select('down'),
'%(mod)s-Control-%(up)s': lambda k: Tag('sel').select('up', stack=True),
'%(mod)s-Control-%(down)s': lambda k: Tag('sel').select('down', stack=True),
'%(mod)s-space': lambda k: Tag('sel').select('toggle'),
'%(mod)s-Shift-%(left)s': lambda k: Tag('sel').send(Client('sel'), 'left'),
'%(mod)s-Shift-%(right)s': lambda k: Tag('sel').send(Client('sel'), 'right'),
'%(mod)s-Shift-%(up)s': lambda k: Tag('sel').send(Client('sel'), 'up'),
'%(mod)s-Shift-%(down)s': lambda k: Tag('sel').send(Client('sel'), 'down'),
'%(mod)s-Shift-space': lambda k: Tag('sel').send(Client('sel'), 'toggle'),
'%(mod)s-d': lambda k: setattr(Tag('sel').selcol, 'mode', 'default-max'),
'%(mod)s-s': lambda k: setattr(Tag('sel').selcol, 'mode', 'stack-max'),
'%(mod)s-m': lambda k: setattr(Tag('sel').selcol, 'mode', 'stack+max'),
'%(mod)s-f': lambda k: Client('sel').set('Fullscreen', 'toggle'),
'%(mod)s-Shift-c': lambda k: Client('sel').kill(),
'%(mod)s-a': lambda k: action_menu.call(),
'%(mod)s-p': lambda k: program_menu.call(),
'%(mod)s-Return': lambda k: call(*terminal, background=True),
'%(mod)s-t': lambda k: tags.select(tag_menu.call()),
'%(mod)s-Shift-t': lambda k: setattr(Client('sel'), 'tags', tag_menu.call()),
'%(mod)s-n': lambda k: tags.select(tags.next()),
'%(mod)s-b': lambda k: tags.select(tags.next(True)),
'%(mod)s-i': lambda k: tags.select(tags.NEXT),
'%(mod)s-o': lambda k: tags.select(tags.PREV),
})
def bind_num(i):
bind_keys({
'%%(mod)s-%d' % i: lambda k: tags.select(str(i)),
'%%(mod)s-Shift-%d' % i: lambda k: setattr(Client('sel'), 'tags', i),
})
map(bind_num, range(0, 10))
Actions.rehash()
# Misc Setup
#progs_file=`{namespace}^/proglist.$pid
event_loop()
# vim:se sts=4 sw=4 et:

View File

@ -155,6 +155,10 @@ fn Event-Key {
Key-$1 $1
}
fn Event-Quit {
exit
}
fn Event-Start {
if(~ $1 $wmiiscript)
exit

View File

@ -85,6 +85,10 @@ bar_create(Bar **bp, const char *name) {
b->id = id++;
utflcpy(b->name, name, sizeof b->name);
b->col = def.normcolor;
strlcat(b->buf, b->col.colstr, sizeof(b->buf));
strlcat(b->buf, " ", sizeof(b->buf));
strlcat(b->buf, b->text, sizeof(b->buf));
for(sp=screens; (s = *sp); sp++) {
i = bp - s->bar;

View File

@ -64,9 +64,9 @@ enum {
extern char* modes[];
#define TOGGLE(x) \
(x == On ? "On" : \
x == Off ? "Off" : \
x == Toggle ? "Toggle" : \
(x == On ? "on" : \
x == Off ? "off" : \
x == Toggle ? "toggle" : \
"<toggle>")
enum {
Off,

View File

@ -11,9 +11,9 @@ static void ewmh_getwinstate(Client*);
static void ewmh_setstate(Client*, Atom, int);
#define Net(x) ("_NET_" x)
#define Action(x) ("_NET_WM_ACTION_" x)
#define State(x) ("_NET_WM_STATE_" x)
#define Type(x) ("_NET_WM_WINDOW_TYPE_" x)
#define Action(x) Net("WM_ACTION_" x)
#define State(x) Net("WM_STATE_" x)
#define Type(x) Net("WM_WINDOW_TYPE_" x)
#define NET(x) xatom(Net(x))
#define ACTION(x) xatom(Action(x))
#define STATE(x) xatom(State(x))
@ -223,13 +223,13 @@ void
ewmh_getwintype(Client *c) {
static Prop props[] = {
{Type("DESKTOP"), TypeDesktop},
{Type("DOCK"), TypeDock},
{Type("DOCK"), TypeDock},
{Type("TOOLBAR"), TypeToolbar},
{Type("MENU"), TypeMenu},
{Type("MENU"), TypeMenu},
{Type("UTILITY"), TypeUtility},
{Type("SPLASH"), TypeSplash},
{Type("DIALOG"), TypeDialog},
{Type("NORMAL"), TypeNormal},
{Type("SPLASH"), TypeSplash},
{Type("DIALOG"), TypeDialog},
{Type("NORMAL"), TypeNormal},
{0, }
};
long mask;
@ -356,8 +356,7 @@ ewmh_clientmessage(XClientMessageEvent *e) {
case StateUnset: action = Off; break;
case StateSet: action = On; break;
case StateToggle: action = Toggle; break;
default:
return -1;
default: return -1;
}
Dprint(DEwmh, "\tAction: %s\n", TOGGLE(action));
ewmh_setstate(c, l[1], action);

View File

@ -245,7 +245,7 @@ void mouse_resize(Client*, Align, bool);
void mouse_resizecol(Divide*);
bool readmotion(Point*);
int readmouse(Point*, uint*);
Align snap_rect(Rectangle *rects, int num, Rectangle *current, Align *mask, int snapw);
Align snap_rect(const Rectangle *rects, int num, Rectangle *current, Align *mask, int snapw);
/* printevent.c */
void printevent(XEvent*);

View File

@ -535,6 +535,8 @@ fs_write(Ixp9Req *r) {
return;
case FsFCtags:
ixp_srv_data2cstring(r);
print("%d\n", r->ifcall.io.count);
print("%s\n", r->ifcall.io.data);
apply_tags(f->p.client, r->ifcall.io.data);
r->ofcall.io.count = r->ifcall.io.count;
respond(r, nil);
@ -707,6 +709,11 @@ fs_clunk(Ixp9Req *r) {
q = f->p.bar->text;
utflcpy(q, (char*)m.pos, sizeof ((Bar*)0)->text);
p[0] = '\0';
strlcat(p, f->p.bar->col.colstr, sizeof(f->p.bar->buf));
strlcat(p, " ", sizeof(f->p.bar->buf));
strlcat(p, f->p.bar->text, sizeof(f->p.bar->buf));
bar_draw(f->p.bar->screen);
break;
}

View File

@ -424,6 +424,8 @@ extern int fmtevent(Fmt*);
i = ixp_serverloop(&srv);
if(i)
fprint(2, "%s: error: %r\n", argv0);
else
event("Quit");
cleanup();

View File

@ -99,7 +99,7 @@ rect_morph(Rectangle *r, Point d, Align *mask) {
/* Yes, yes, macros are evil. So are patterns. */
#define frob(x, y) \
Rectangle *rp; \
const Rectangle *rp; \
int i, tx; \
\
for(i=0; i < nrect; i++) { \
@ -117,12 +117,12 @@ rect_morph(Rectangle *r, Point d, Align *mask) {
return dx \
static int
snap_hline(Rectangle *rects, int nrect, int dx, Rectangle *r, int y) {
snap_hline(const Rectangle *rects, int nrect, int dx, const Rectangle *r, int y) {
frob(y, x);
}
static int
snap_vline(Rectangle *rects, int nrect, int dx, Rectangle *r, int x) {
snap_vline(const Rectangle *rects, int nrect, int dx, const Rectangle *r, int x) {
frob(x, y);
}
@ -134,7 +134,7 @@ snap_vline(Rectangle *rects, int nrect, int dx, Rectangle *r, int x) {
* snap.
*/
Align
snap_rect(Rectangle *rects, int num, Rectangle *r, Align *mask, int snap) {
snap_rect(const Rectangle *rects, int num, Rectangle *r, Align *mask, int snap) {
Align ret;
Point d;

View File

@ -2,8 +2,6 @@
* See LICENSE file for license details.
*/
#define _X11_VISIBLE
#define ZP _ZP
#define ZR _ZR
#define pointerwin __pointerwin
#include "dat.h"
#include <limits.h>
@ -12,9 +10,6 @@
#include <unistd.h>
#include <bio.h>
#include "fns.h"
#undef ZP /* These should be allocated in read-only memory, */
#undef ZR /* but declaring them const causes too much trouble
* elsewhere. */
#undef pointerwin
const Point ZP = {0, 0};
@ -897,7 +892,8 @@ getprop_ulong(Window *w, char *prop, char *type,
char**
strlistdup(char *list[]) {
char **p, *q;
char **p;
char *q;
int i, m, n;
n = 0;
@ -1120,11 +1116,11 @@ sethints(Window *w) {
h->aspect.max.y = xs.max_aspect.y;
}
h->position = ((xs.flags & (USPosition|PPosition)) != 0);
h->position = (xs.flags & (USPosition|PPosition)) != 0;
p = ZP;
if(!(xs.flags & PWinGravity))
xs.win_gravity = NorthWestGravity;
p = ZP;
switch (xs.win_gravity) {
case EastGravity:
case CenterGravity:
@ -1150,7 +1146,7 @@ sethints(Window *w) {
break;
}
h->grav = p;
h->gravstatic = (xs.win_gravity==StaticGravity);
h->gravstatic = (xs.win_gravity == StaticGravity);
}
Rectangle

View File

@ -160,8 +160,8 @@ struct Screen {
Display *display;
Screen scr;
extern Point ZP;
extern Rectangle ZR;
extern const Point ZP;
extern const Rectangle ZR;
extern Window* pointerwin;
Point Pt(int x, int y);