2024-09-08 16:50:24 -06:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
"""pywmhdmon.py
|
|
|
|
|
|
|
|
WindowMaker dockapp to monitor the free space on your partitions and
|
|
|
|
the disk activity.
|
|
|
|
|
|
|
|
Copyright (C) 2003 Kristoffer Erlandsson
|
|
|
|
|
|
|
|
Licensed under the GNU General Public License.
|
|
|
|
|
|
|
|
|
|
|
|
Changes
|
|
|
|
|
|
|
|
2005-09-02 Mario Frasca
|
|
|
|
added -s option for skipping an amount of configuration items.
|
|
|
|
changed some single quotes to double quotes for use in emacs.
|
|
|
|
updated the rc sample file
|
|
|
|
|
|
|
|
2004-07-16 Mario Frasca
|
|
|
|
recognizes unmounted partitions.
|
|
|
|
configurable mouse actions.
|
|
|
|
'used' information for read-only media.
|
|
|
|
recognizes #-started numerical-coded colors.
|
|
|
|
|
|
|
|
2003-09-01 Kristoffer Erlandsson
|
|
|
|
Fixed a bug where the numbers wouldn't show if they were between 1000 and 1024.
|
|
|
|
|
|
|
|
2003-06-25 Kristoffer Erlandsson
|
|
|
|
Fixed a bug where a mouse click caused the app to enter an infinite loop
|
|
|
|
|
|
|
|
2003-06-24 Kristoffer Erlandsson
|
|
|
|
Additional fine tuning
|
|
|
|
|
|
|
|
2003-06-23 Kristoffer Erlandsson
|
|
|
|
First working version
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
usage = """pywmhdmon.py [options]
|
|
|
|
Available options are:
|
|
|
|
-h, --help print this help
|
|
|
|
-t, --textcolor <color> set the text color
|
|
|
|
-f, --barfgcolor <color> set the foregroundcolor of the act. bar
|
|
|
|
-g, --barbgcolor <color> set the background color of the act. bar
|
|
|
|
-b, --background <color> set the background color
|
|
|
|
-F, --font <file> set the font name
|
|
|
|
-r, --rgbfile <file> set the rgb file to get color codes from
|
|
|
|
-c, --configfile <file> set the config file to use
|
|
|
|
-p, --procstat <file> set the location of /proc/stat
|
|
|
|
-s, --skipconf <num> determines how many configuration items to skip
|
|
|
|
"""
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import time
|
|
|
|
import getopt
|
|
|
|
import os
|
|
|
|
|
|
|
|
import wmdocklib
|
|
|
|
|
|
|
|
width = 64
|
|
|
|
height = 64
|
|
|
|
|
|
|
|
xOffset = 4
|
|
|
|
yOffset = 5
|
|
|
|
|
|
|
|
graphStartX = 7
|
|
|
|
graphStartY = 53
|
|
|
|
graphHeight = 4
|
|
|
|
|
|
|
|
graphBgStartX = 72-64
|
|
|
|
graphBgStartY = 53+64
|
|
|
|
|
|
|
|
graphLineStartX = 2
|
|
|
|
graphLineStartY = 58+64
|
|
|
|
|
|
|
|
defaultConfigFile = os.environ['HOME']+'/.pywmhdmonrc'
|
|
|
|
defaultProcStat = '/proc/stat'
|
|
|
|
displayModes = ('bar', 'percent', 'free', 'used')
|
|
|
|
defaultMode = 'bar'
|
|
|
|
|
|
|
|
hdmon = None
|
|
|
|
|
|
|
|
class NotMounted(OSError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class PywmHDMon:
|
|
|
|
def __init__(self, pathsToMonitor, procStat='/proc/stat', actMonEnabled=1, skipping=0):
|
|
|
|
self._pathsToMonitor = pathsToMonitor
|
|
|
|
self._actMonEnabled = actMonEnabled
|
|
|
|
self._skipping = skipping
|
2024-09-09 01:26:31 -06:00
|
|
|
self._lineCount = (height - yOffset*2 - 2) // (char_height+1)
|
2024-09-08 16:50:24 -06:00
|
|
|
|
|
|
|
self._statFile = procStat
|
|
|
|
self._maxIODiff = 0
|
|
|
|
self._lastIO = -1
|
|
|
|
for i in range(max(self._lineCount, len(pathsToMonitor)-skipping)):
|
|
|
|
wmdocklib.addMouseRegion(i+1, 8, self.getY(i+1)+yOffset,
|
|
|
|
58, self.getY(i+1)+char_height+yOffset)
|
|
|
|
|
|
|
|
|
|
|
|
def addString(self, s, x, y):
|
|
|
|
try:
|
|
|
|
wmdocklib.addString(s, x, y, xOffset, yOffset, width, height)
|
2024-09-09 01:26:31 -06:00
|
|
|
except ValueError as e:
|
2024-09-08 16:50:24 -06:00
|
|
|
sys.stderr.write('Error when painting string:\n' + str(e) + '\n')
|
|
|
|
sys.exit(3)
|
|
|
|
|
|
|
|
def getHdInfo(self, path):
|
|
|
|
"""Get the free and total space of the filesystem which path is on.
|
|
|
|
|
|
|
|
Return a tuple with (<total space>, <free space>) in bytes. Raise
|
|
|
|
OSError if we can't stat the path. Raise NotMounted if not mounted.
|
|
|
|
These operations are quite costly, not adviced to perform these checks
|
|
|
|
more than once every 10 seconds.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# check if is mounted <- st_dev(/mount/point) == st_dev(/mount)
|
2024-09-09 01:26:31 -06:00
|
|
|
if path != '/':
|
2024-09-08 16:50:24 -06:00
|
|
|
statOwn = os.stat(path)
|
|
|
|
|
|
|
|
# the following is a bit ugly: it removes the trailing
|
|
|
|
# dirname from the mount point. split by '/', leave the
|
|
|
|
# last string, join back, check for empty string.
|
|
|
|
statCnt = os.stat('/'.join(path.split('/')[:-1]) or '/')
|
|
|
|
if statOwn[2] == statCnt[2]:
|
|
|
|
raise NotMounted
|
|
|
|
stat = os.statvfs(path)
|
|
|
|
blockSize = stat.f_bsize
|
|
|
|
availableBlocks = stat.f_bavail
|
|
|
|
totalBlocks = stat.f_blocks
|
|
|
|
free = blockSize * availableBlocks
|
|
|
|
total = blockSize * totalBlocks
|
|
|
|
return (total, free)
|
|
|
|
|
|
|
|
def paintGraph(self, percentFilled, x, y, w, thin=None):
|
|
|
|
"""Paint a graph with percentFilled percent filled.
|
|
|
|
|
|
|
|
Paint at position x, y and with width w.
|
|
|
|
if thin == 1, make it a thin line instead of a block.
|
|
|
|
"""
|
|
|
|
paintWidth = int(round(percentFilled/100.0 * w))
|
|
|
|
if paintWidth > 0:
|
|
|
|
wmdocklib.copyXPMArea(
|
|
|
|
graphLineStartX, graphLineStartY, paintWidth, thin or graphHeight,
|
|
|
|
x + xOffset, y + yOffset)
|
|
|
|
if w - paintWidth > 0:
|
|
|
|
wmdocklib.copyXPMArea(
|
|
|
|
graphBgStartX, graphBgStartY, w - paintWidth, thin or graphHeight,
|
|
|
|
x + paintWidth + xOffset, y + yOffset)
|
|
|
|
|
|
|
|
def getY(self, line):
|
|
|
|
"returns the y coordinate of the top line for the box"
|
2024-09-09 01:26:31 -06:00
|
|
|
interlinea = (height - yOffset*2 - 2 - self._lineCount * char_height) // (self._lineCount-1)
|
2024-09-08 16:50:24 -06:00
|
|
|
interlinea += char_height
|
|
|
|
lastBaseline = yOffset + self._lineCount * interlinea
|
|
|
|
from math import ceil
|
|
|
|
extraYOffset = int(ceil((height - yOffset - lastBaseline) / 2.0))
|
|
|
|
return extraYOffset + (line - 1) * interlinea
|
|
|
|
|
|
|
|
def paintLabel(self, line, label):
|
|
|
|
self.addString(label, 1, self.getY(line))
|
|
|
|
|
|
|
|
def paintHdData(self, line, data, mode):
|
|
|
|
total, free = data
|
2024-09-09 01:26:31 -06:00
|
|
|
xStart = (width*2)//5
|
2024-09-08 16:50:24 -06:00
|
|
|
if total==0:
|
|
|
|
self.addString(' ', width-yOffset*2-5*char_width-1, self.getY(line))
|
|
|
|
self.paintGraph(0, xStart, self.getY(line) + 4,
|
|
|
|
width - xOffset*2 - xStart - 2,
|
|
|
|
thin=1)
|
|
|
|
pass
|
|
|
|
elif mode == 'percent':
|
|
|
|
percent = (float(free) / float(total)) * 100.0
|
|
|
|
percentStr = (str(int(round(percent))) + '%').rjust(5)
|
|
|
|
self.addString(percentStr, xStart, self.getY(line))
|
|
|
|
elif mode == 'used':
|
|
|
|
totalStr = bytesToStr(total).rjust(5)
|
|
|
|
self.addString(totalStr, width-yOffset*2-5*char_width-1, self.getY(line))
|
|
|
|
elif mode == 'free':
|
|
|
|
freeStr = bytesToStr(free).rjust(5)
|
|
|
|
self.addString(freeStr, width-yOffset*2-5*char_width, self.getY(line))
|
|
|
|
elif mode == 'bar':
|
|
|
|
percentUsed = (float(total - free) / float(total)) * 100.0
|
|
|
|
self.paintGraph(percentUsed, xStart, self.getY(line) + 2,
|
|
|
|
width - xOffset*2 - xStart - 2)
|
|
|
|
else:
|
|
|
|
sys.stderr.write('Unknown display mode: %s, ignoring data.\n'
|
|
|
|
% mode)
|
|
|
|
def getHdActivity(self):
|
|
|
|
"""Return the current hd activity in percent.
|
|
|
|
|
|
|
|
Return how many percent of the max achieved activity during the
|
|
|
|
program's lifetime the current activity is. However, every time
|
|
|
|
this method is called we decrease the max achieved activity a
|
|
|
|
little bit to get a bit less affected by spikes. I think the
|
|
|
|
interesting thing is to see if the hard drive is active, not
|
|
|
|
really exactly how active.
|
|
|
|
"""
|
|
|
|
|
|
|
|
statFile = file(self._statFile, 'r')
|
|
|
|
diskIoStartTag = 'disk_io: '
|
|
|
|
ioLine = None
|
|
|
|
for line in statFile:
|
|
|
|
if line.startswith(diskIoStartTag):
|
|
|
|
ioLine = line
|
|
|
|
statFile.close()
|
|
|
|
if ioLine is None:
|
|
|
|
# Can't get HD activity
|
|
|
|
sys.stderr.write("Can't get hd activity from %s\n" %
|
|
|
|
self._statFile)
|
|
|
|
return 0.0
|
|
|
|
ioLine = ioLine[len(diskIoStartTag):]
|
|
|
|
disks = ioLine.split()
|
|
|
|
currIO = 0
|
|
|
|
for disk in disks:
|
|
|
|
dataPart = disk.split(':')[1].strip(')(')
|
|
|
|
infos = dataPart.split(',')
|
|
|
|
blocksRead = long(infos[2])
|
|
|
|
blocksWritten = long(infos[4])
|
|
|
|
currIO += blocksRead + blocksWritten
|
|
|
|
if self._lastIO == -1:
|
|
|
|
self._lastIO = currIO
|
|
|
|
currDiff = currIO - self._lastIO
|
|
|
|
self._lastIO = currIO
|
|
|
|
if currDiff > self._maxIODiff:
|
|
|
|
self._maxIODiff = currDiff
|
|
|
|
if self._maxIODiff <= 0:
|
|
|
|
self._maxIODiff = 0
|
|
|
|
return 0.0
|
|
|
|
currAct = (float(currDiff) / float(self._maxIODiff)) * 100.0
|
|
|
|
self._maxIODiff -= 1 # So spikes won't affect us too much.
|
|
|
|
return currAct
|
|
|
|
|
|
|
|
def updateHdActivity(self):
|
|
|
|
currentAct = self.getHdActivity()
|
|
|
|
self.paintGraph(currentAct, 3, height - yOffset*2 - 3 - graphHeight,
|
|
|
|
width - 2 * xOffset - 6)
|
|
|
|
|
|
|
|
def _checkEvents(self):
|
|
|
|
event = wmdocklib.getEvent()
|
|
|
|
while event is not None:
|
|
|
|
if event['type'] == 'destroynotify':
|
|
|
|
sys.exit(0)
|
|
|
|
elif event['type'] == 'buttonrelease':
|
|
|
|
area = wmdocklib.checkMouseRegion(event['x'],event['y'])
|
2024-09-09 01:26:31 -06:00
|
|
|
if area != -1:
|
2024-09-08 16:50:24 -06:00
|
|
|
self.toggleMount(area-1+self._skipping)
|
|
|
|
event = wmdocklib.getEvent()
|
|
|
|
|
|
|
|
def toggleMount(self, line):
|
|
|
|
label, path, mode, action = self._pathsToMonitor[line]
|
|
|
|
if action is None:
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
self.getHdInfo(path)
|
|
|
|
mounted = True
|
|
|
|
except NotMounted:
|
|
|
|
mounted = False
|
2024-09-09 01:26:31 -06:00
|
|
|
except OSError as e:
|
2024-09-08 16:50:24 -06:00
|
|
|
return
|
|
|
|
if mounted:
|
|
|
|
if action == 'mount':
|
|
|
|
os.spawnvp(os.P_NOWAIT, 'umount', ['umount', path])
|
|
|
|
elif action == 'eject':
|
|
|
|
os.spawnvp(os.P_WAIT, 'umount', ['umount', path])
|
|
|
|
os.spawnvp(os.P_NOWAIT, 'eject', ['eject', path])
|
|
|
|
else:
|
|
|
|
os.spawnvp(os.P_NOWAIT, 'mount', ['mount', path])
|
|
|
|
|
|
|
|
def updateMonitoredPaths(self):
|
|
|
|
index = 0
|
|
|
|
pageoffset = self._skipping
|
|
|
|
for i in self._pathsToMonitor:
|
|
|
|
index += 1
|
|
|
|
if index < pageoffset+1:
|
|
|
|
continue
|
|
|
|
if i is not None:
|
|
|
|
label, path, mode, action = i
|
|
|
|
self.paintLabel(index-pageoffset, label)
|
|
|
|
try:
|
|
|
|
hdData = self.getHdInfo(path)
|
|
|
|
except NotMounted:
|
|
|
|
hdData = (0, 0)
|
2024-09-09 01:26:31 -06:00
|
|
|
except OSError as e:
|
2024-09-08 16:50:24 -06:00
|
|
|
sys.stderr.write(
|
|
|
|
"Can't get hd data from %s: %s\n" % (path, str(e)))
|
|
|
|
hdData = (0, 0)
|
|
|
|
self.paintHdData(index-pageoffset, hdData, mode)
|
|
|
|
if index - pageoffset == self._lineCount:
|
|
|
|
break
|
|
|
|
|
|
|
|
def mainLoop(self):
|
|
|
|
self.updateMonitoredPaths()
|
|
|
|
while 1:
|
|
|
|
self._checkEvents()
|
|
|
|
if self._actMonEnabled:
|
|
|
|
self.updateHdActivity()
|
|
|
|
wmdocklib.redraw()
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
|
|
|
|
|
|
|
import signal
|
|
|
|
def handler(num, frame):
|
|
|
|
hdmon.updateMonitoredPaths()
|
|
|
|
signal.alarm(10)
|
|
|
|
|
|
|
|
def parseCommandLine(argv):
|
|
|
|
"""Parse the commandline. Return a dictionary with options and values."""
|
|
|
|
shorts = 'ht:f:g:b:r:c:p:s:F:'
|
|
|
|
longs = ['help', 'textcolor=', 'background=', 'barfgcolor=',
|
|
|
|
'rgbfile=', 'configfile=', 'barbgcolor=', 'procstat=',
|
|
|
|
'skipconf=','font=', 'debug']
|
|
|
|
try:
|
|
|
|
opts, nonOptArgs = getopt.getopt(argv[1:], shorts, longs)
|
2024-09-09 01:26:31 -06:00
|
|
|
except getopt.GetoptError as e:
|
2024-09-08 16:50:24 -06:00
|
|
|
sys.stderr.write('Error when parsing commandline: ' + str(e) + '\n')
|
|
|
|
sys.stderr.write(usage)
|
|
|
|
sys.exit(2)
|
|
|
|
d = {}
|
|
|
|
for o, a in opts:
|
|
|
|
if o in ('-h', '--help'):
|
|
|
|
sys.stdout.write(usage)
|
|
|
|
sys.exit(0)
|
|
|
|
if o in ('-t', '--textcolor'):
|
|
|
|
d['textcolor'] = a
|
|
|
|
if o in ('-b', '--background'):
|
|
|
|
d['background'] = a
|
|
|
|
if o in ('-r', '--rgbfile'):
|
|
|
|
d['rgbfile'] = a
|
|
|
|
if o in ('-c', '--configfile'):
|
|
|
|
d['configfile'] = a
|
|
|
|
if o in ('-g', '--barbgcolor'):
|
|
|
|
d['barbgcolor'] = a
|
|
|
|
if o in ('-F', '--font'):
|
|
|
|
d['font'] = a
|
|
|
|
if o in ('-f', '--barfgcolor'):
|
|
|
|
d['barfgcolor'] = a
|
|
|
|
if o in ('-p', '--procstat'):
|
|
|
|
d['procstat'] = a
|
|
|
|
if o in ('-s', '--skipconf'):
|
|
|
|
d['skipconf'] = a
|
|
|
|
if o in ('--debug'):
|
|
|
|
d['debug'] = True
|
|
|
|
return d
|
|
|
|
|
|
|
|
def makeNumDigits(num, numDigits):
|
|
|
|
"""Make a floating point number a certain number of digits, including
|
|
|
|
decimal. Return a string containing it.
|
|
|
|
"""
|
|
|
|
lenOfIntPart = len(str(int(num)))
|
|
|
|
if lenOfIntPart > numDigits:
|
|
|
|
# Can't convert a number to less digits then it's integer part...
|
|
|
|
return ''
|
|
|
|
decimalsNeeded = numDigits - lenOfIntPart
|
|
|
|
s = '%' + str(lenOfIntPart) + '.' + str(decimalsNeeded) + 'f'
|
|
|
|
s = s % round(num, decimalsNeeded)
|
|
|
|
return s
|
|
|
|
|
|
|
|
def bytesToStr(bytes):
|
|
|
|
"""Convert a number of bytes to a nice printable string.
|
|
|
|
|
|
|
|
May raise ValueError if bytes can't be seen as an float.
|
|
|
|
"""
|
|
|
|
bytes = float(bytes)
|
|
|
|
kb = 1024
|
|
|
|
mb = 1024 * 1024
|
|
|
|
gb = 1024 * mb
|
|
|
|
tb = 1024 * gb
|
|
|
|
pb = 1024 * tb
|
|
|
|
if bytes < kb:
|
|
|
|
size = bytes
|
|
|
|
letter = 'B'
|
|
|
|
#return makeNumDigits(bytes, numDigits) + 'B'
|
|
|
|
elif bytes < mb:
|
|
|
|
size = bytes / kb
|
|
|
|
letter = 'k'
|
|
|
|
#return makeNumDigits(bytes/kb, numDigits) + 'k'
|
|
|
|
elif bytes < gb:
|
|
|
|
size = bytes / mb
|
|
|
|
letter = 'M'
|
|
|
|
#return makeNumDigits(bytes/mb, numDigits) + 'M'
|
|
|
|
elif bytes < tb:
|
|
|
|
size = bytes / gb
|
|
|
|
letter = 'G'
|
|
|
|
#return makeNumDigits(bytes/gb, numDigits) + 'G'
|
|
|
|
elif bytes < pb:
|
|
|
|
size = bytes / tb
|
|
|
|
letter = 'T'
|
|
|
|
#return makeNumDigits(bytes/tb, numDigits) + 'T'
|
|
|
|
else:
|
|
|
|
size = bytes / pb
|
|
|
|
letter = 'p'
|
|
|
|
#return makeNumDigits(bytes/pb, numDigits) + 'P'
|
|
|
|
if size >= 1000:
|
|
|
|
res = makeNumDigits(size, 4)
|
|
|
|
else:
|
|
|
|
res = makeNumDigits(size, 3)
|
|
|
|
res += letter
|
|
|
|
return res
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
clConfig = parseCommandLine(sys.argv)
|
|
|
|
configFile = clConfig.get('configfile', defaultConfigFile)
|
|
|
|
configFile = os.path.expanduser(configFile)
|
|
|
|
fileConfig = wmdocklib.readConfigFile(configFile, sys.stderr)
|
|
|
|
config = fileConfig
|
2024-09-09 01:26:31 -06:00
|
|
|
for i in clConfig.items():
|
2024-09-08 16:50:24 -06:00
|
|
|
config[i[0]] = i[1]
|
|
|
|
|
|
|
|
palette = {}
|
|
|
|
palette[0] = clConfig.get('background', 'black')
|
|
|
|
palette[2] = clConfig.get('textcolor', 'cyan3')
|
|
|
|
palette[9] = clConfig.get('barfgcolor', 'cyan')
|
|
|
|
palette[8] = clConfig.get('barbgcolor', 'cyan4')
|
|
|
|
palette[5] = clConfig.get('activitycolor', 'cyan2')
|
|
|
|
|
|
|
|
font = clConfig.get('font', '6x8')
|
|
|
|
debug = clConfig.get('debug')
|
|
|
|
|
|
|
|
global char_width, char_height
|
|
|
|
char_width, char_height = wmdocklib.initPixmap(patterns=patterns,
|
|
|
|
font_name=font,
|
|
|
|
palette=palette,
|
|
|
|
margin=3,
|
|
|
|
bg=0, fg=2, debug=debug)
|
|
|
|
|
|
|
|
pathsToMonitor = []
|
|
|
|
for i in range(1,1000):
|
|
|
|
labelStr = str(i) + '.label'
|
|
|
|
pathStr = str(i) + '.path'
|
|
|
|
modeStr = str(i) + '.displaymode'
|
|
|
|
actionStr = str(i) + '.action'
|
|
|
|
label = config.get(labelStr)
|
|
|
|
if not label: break
|
|
|
|
path = config.get(pathStr)
|
|
|
|
action = config.get(actionStr, 'fixed').lower().strip()
|
|
|
|
if action not in ['mount', 'eject']:
|
|
|
|
action = None
|
|
|
|
displayMode = config.get(modeStr, defaultMode)
|
|
|
|
if not displayMode in displayModes:
|
|
|
|
sys.stderr.write(
|
|
|
|
'Unknown display mode: %s, using default.\n' % displayMode)
|
|
|
|
displayMode = defaultMode
|
|
|
|
takeChars = 3
|
|
|
|
if char_width <= 5:
|
|
|
|
takeChars = 4
|
|
|
|
pathsToMonitor.append((label[:takeChars], path, displayMode, action))
|
|
|
|
procStat = config.get('procstat', defaultProcStat)
|
|
|
|
skipping = int(config.get('skipconf', 0))
|
|
|
|
actMonEnabled = int(config.get('monitoring',0))
|
|
|
|
if not os.access(procStat, os.R_OK):
|
|
|
|
sys.stderr.write(
|
|
|
|
"Can't read your procstat file, try setting it with -p. ")
|
|
|
|
sys.stderr.write("Disabling the HD activity bar.\n")
|
|
|
|
actMonEnabled = 0
|
|
|
|
try:
|
|
|
|
programName = sys.argv[0].split(os.sep)[-1]
|
|
|
|
except IndexError:
|
|
|
|
programName = ''
|
|
|
|
sys.argv[0] = programName
|
|
|
|
wmdocklib.openXwindow(sys.argv, width, height)
|
|
|
|
|
|
|
|
signal.signal(signal.SIGCHLD, handler)
|
|
|
|
signal.signal(signal.SIGALRM, handler)
|
|
|
|
signal.alarm(10)
|
|
|
|
|
|
|
|
global hdmon
|
|
|
|
hdmon = PywmHDMon(pathsToMonitor, procStat, actMonEnabled, skipping)
|
|
|
|
hdmon.mainLoop()
|
|
|
|
|
|
|
|
patterns = \
|
|
|
|
['0000000000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0000000000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900555002220055500555000000000000000000000000000000000000000',
|
|
|
|
'0099900555002220055500555000000000000000000000000000000000000000',
|
|
|
|
'0099900555002220055500555000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550055500555000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550055500555000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550055500555000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550022200555000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550022200555000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550022200555000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550055500555000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550055500555000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550055500555000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550055500222000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550055500222000000000000000000000000000000000000000',
|
|
|
|
'0099900555005550055500222000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099900088888888888888888888888888888888888888888888888888888888',
|
|
|
|
'0099900088888888888888888888888888888888888888888888888888888888',
|
|
|
|
'0099900088888888888888888888888888888888888888888888888888888888',
|
|
|
|
'0099900088888888888888888888888888888888888888888888888888888888',
|
|
|
|
'0099900000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0099999999999999999999999999999999999999999999999999999999999999',
|
|
|
|
'0099999999999999999999999999999999999999999999999999999999999999',
|
|
|
|
'0099999999999999999999999999999999999999999999999999999999999999',
|
|
|
|
'0099999999999999999999999999999999999999999999999999999999999999',
|
|
|
|
'0000000000000000000000000000000000000000000000000000000000000000',
|
|
|
|
'0000000000000000000000000000000000000000000000000000000000000000',
|
|
|
|
]
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|